Summary
On Windows, _keyring_available() reports the keyring as usable, the server logs π Using system keyring for token storage, and then the actual session save silently falls back to a plaintext file β because a real cookie-mode session is too large for Windows Credential Manager. Users who believe their credentials live in the encrypted OS vault instead have their live Monarch session cookies sitting in a plaintext file, with no clear signal that this happened.
Root cause
_keyring_available() (in src/monarch_mcp_server/secure_session.py) probes the backend by round-tripping a 1-byte sentinel ("1"):
keyring.set_password(KEYRING_SERVICE, _PROBE_USERNAME, "1")
That always succeeds on Windows. But Windows Credential Manager (keyring.backends.Windows.WinVaultKeyring) has a hard cap on the credential blob size. A cookie-mode session blob β {"auth_mode": "cookie", "device_uuid": ..., "cookies": {session_id, csrftoken, cf_clearance, __cf_bm, ...}} β is ~950 chars, which exceeds the cap. So save_session_blob() throws inside the try, hits the except, and quietly writes the plaintext file instead:
except Exception as e:
logger.warning(f"β οΈ Keyring save failed, falling back to file: {e}")
self._save_token_file(blob) # plaintext
The only trace is a single WARNING line that's easy to miss; check_auth_status then also reports "no token found in keyring" even though auth works via the file, which compounds the confusion.
Reproduction / evidence
On Windows 11 (Credential Manager backend), CredWrite starts failing well below the documented limit:
600 chars: OK
700 chars: FAIL -> (8, 'CredWrite', 'Not enough memory resources are available to process this command.')
1500 chars: FAIL -> (1783, 'CredWrite', 'The stub received bad data.')
The documented Windows limit is 2560 UTF-16 chars; some machines are stricter. Either way, a cookie session blob exceeds it. This is the same platform limitation OpenAI Codex hit for OAuth tokens on Windows: openai/codex#10353 (closed, no platform-level fix β the token simply cannot go in the vault).
Impact
- Silent security downgrade on Windows: live session cookies stored as plaintext at rest, while the logs claim the keyring is in use.
- Affects the recommended auth path (browser session cookies), i.e. most Windows users.
Suggested fixes
- Make the availability probe size-aware so the "using keyring" decision reflects what will actually be stored (probe with a representative-size value, or catch the size failure at decision time). At minimum, make the fallback log unmistakable.
- Encrypt the file fallback at rest. On Windows, DPAPI (
win32crypt.CryptProtectData, already available via the pywin32 dependency) gives the same per-user protection Credential Manager uses, with no size cap. No-op on other platforms.
I'll open a PR implementing (2), with transparent migration of existing plaintext files.
Summary
On Windows,
_keyring_available()reports the keyring as usable, the server logsπ Using system keyring for token storage, and then the actual session save silently falls back to a plaintext file β because a real cookie-mode session is too large for Windows Credential Manager. Users who believe their credentials live in the encrypted OS vault instead have their live Monarch session cookies sitting in a plaintext file, with no clear signal that this happened.Root cause
_keyring_available()(insrc/monarch_mcp_server/secure_session.py) probes the backend by round-tripping a 1-byte sentinel ("1"):That always succeeds on Windows. But Windows Credential Manager (
keyring.backends.Windows.WinVaultKeyring) has a hard cap on the credential blob size. A cookie-mode session blob β{"auth_mode": "cookie", "device_uuid": ..., "cookies": {session_id, csrftoken, cf_clearance, __cf_bm, ...}}β is ~950 chars, which exceeds the cap. Sosave_session_blob()throws inside thetry, hits theexcept, and quietly writes the plaintext file instead:The only trace is a single
WARNINGline that's easy to miss;check_auth_statusthen also reports "no token found in keyring" even though auth works via the file, which compounds the confusion.Reproduction / evidence
On Windows 11 (Credential Manager backend),
CredWritestarts failing well below the documented limit:The documented Windows limit is 2560 UTF-16 chars; some machines are stricter. Either way, a cookie session blob exceeds it. This is the same platform limitation OpenAI Codex hit for OAuth tokens on Windows: openai/codex#10353 (closed, no platform-level fix β the token simply cannot go in the vault).
Impact
Suggested fixes
win32crypt.CryptProtectData, already available via thepywin32dependency) gives the same per-user protection Credential Manager uses, with no size cap. No-op on other platforms.I'll open a PR implementing (2), with transparent migration of existing plaintext files.