Skip to content

bug(server): preserve binary cursor signing keys on Windows #1463

Description

@knqiufan

Describe the bug

On Windows, creating a persistent REST cursor signing key can silently change its bytes. The Server initially starts successfully, but a subsequent start fails with ValueError: cursor signing key must contain 32 bytes.

src/powercontext/server/cursor_secret.py opens the key with os.open without O_BINARY. Windows text-mode writes translate each LF byte into CRLF. Generated keys containing LF are therefore longer on disk than the 32-byte value used by the first process. The binary reader rejects the file on restart.

This is a Server persistence defect introduced in #1438, found by the native Windows lifecycle check on #1459. It is independent of the DSH automatic-path changes. It affects both the file-backed SQLite key and the locally persisted key used without an explicit secret for other database configurations.

Steps to reproduce

On Windows at upstream commit fc75f9270cf4b493411e1c3bef9965bd3b313edb, with the development dependencies installed, run:

from pathlib import Path
from tempfile import TemporaryDirectory
from unittest.mock import patch

from powercontext.builtin.persistence.sqlite import SQLiteConfig
from powercontext.server.cursor_secret import resolve_cursor_secret

with TemporaryDirectory() as directory:
    root = Path(directory)
    database = SQLiteConfig(url=f"sqlite+aiosqlite:///{root / 'powercontext.db'}")
    with patch("powercontext.server.cursor_secret.secrets.token_bytes", return_value=bytes(range(32))):
        resolve_cursor_secret(database, None)
    print((root / ".powercontext.db.cursor-key").stat().st_size)
    resolve_cursor_secret(database, None)

The fixed input includes LF, making the failure deterministic. It changes neither filesystem access nor repository permissions and does not require a model or external database.

Expected behavior

The persisted key contains exactly the generated 32 bytes. The Server can restart and accept pagination cursors issued before the restart.

Actual behavior

The reproduction prints 33 and the next read raises the length validation error. The existing Server restart test also fails deterministically when its generated key contains LF.

Native Windows CI failure, artifact windows-native-service-logs, contains:

16:26:31 PowerContext Server is ready
16:26:42 Started server process [2248]
ValueError: cursor signing key must contain 32 bytes: .../.powercontext.db.cursor-key
Application startup failed. Exiting.

The test assertion only reported an unreachable service. Randomly generated keys without LF avoid the defect, so rerunning can hide it without fixing it.

Environment

Are you willing to submit a PR to fix this bug?

  • Yes, I would like to submit a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions