Skip to content

v1.1.17

@burrellka burrellka tagged this 01 Sep 14:06
Fixes the outer-leg failure exposed right after the 1.1.16 redeploy:
KAIROS/claude.ai got 'invalid_grant: refresh token does not exist' on
reconnect. Distinct from 1.1.16 (inner-leg service token) — this is the
OAuth session between the external client and the MCP.

Cause (predates 1.1.16; any MCP restart triggered it): the provider
extends the upstream InMemoryOAuthProvider, which holds all issued access
+ refresh tokens in memory. URSA persisted the OAuth CLIENT (1.1.5) but
not the issued TOKENS, so a container restart wiped them and a client
holding a pre-restart refresh token could never refresh -> forced full
re-authorization on every redeploy.

Fix: persist the token tables to /data/mcp_oauth_tokens.json (atomic,
0600, same /data trust boundary) and restore on boot. Hooked into the
three token-state mutations (exchange_authorization_code /
exchange_refresh_token / revoke_token). Restore applies three filters:
drop expired; rebuild map entries only when both ends survive (1.1.6
refresh-outlives-access); and drop tokens whose client isn't currently
loaded — preserving the 1.1.9 guarantee that open-window DCR-client
tokens don't revive across a restart. Load runs after the pre-registered
client is wired. Only OAuth-minted tokens persist; static-bearer and
operator-JWT tokens never touch disk.

Also (1b): moved MCP logging.basicConfig ahead of the import-time
build_auth_provider so the restore is observable — otherwise the
'Restored ... token(s)' line was dropped like the 1.1.16 API logs.

Slices: 306e0e1 (persistence + tests) + cbce041 (docs + version) +
5977f94 (log-order fix).

Images: brain40/ursa-oscar-{api,mcp,web,watcher}:1.1.17 (all four pushed).
Tests: full MCP suite 60 passed (8 new persistence tests incl. the 1.1.9
unknown-client-drop security case). Verified live in the built MCP image:
the restore log prints and a direct in-container load restores the seeded
access + refresh tokens with the map intact.

Operator: MCP /data mount must be :rw. One-time full re-add of each
connector mints tokens that then persist across future redeploys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assets 2
Loading