Skip to content

feat(linux): reverse-attach grants survive a daemon restart (#12) - #42

Merged
chaodu-agent merged 2 commits into
mainfrom
feat/linux-persist-grants
Sep 29, 2026
Merged

chaodu-agent merged 2 commits into
mainfrom
feat/linux-persist-grants

Conversation

@chaodu-agent

@chaodu-agent chaodu-agent commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Linux side of #12: reverse-attach grants survive a daemon restart.

  • New attach/store.rs: live grants → MCP_GRANTS_FILE (default $XDG_STATE_HOME/oab-instance-mcp/grants.json; off disables). Mode 600 in a 0700 dir, atomic replace, a widened file is narrowed before being read, expired/malformed entries dropped on load.
  • Written on create, replace, DELETE and terminal end (set_ended), so revoked/ended grants are never resumed.
  • At startup every grant inside its deadline is re-dialled under its original id; a runtime that forgot it answers 401 and the grant ends normally.
  • The attach secret is stored (TTL-bounded, same trust level as the bearer token file) and never appears in GET /attach.

Tests: 4 unit tests (round trip + perms, expiry/malformed, narrowing, missing file). Smoke adds 8 checks: file 600, grant present, secret not in API, kill -9 → restart → redial on its own, same id, resume logged, DELETE removes it, revoked grant not resumed — 46/46 on amd64 + arm64.

Live: on black (CI-built binary), lent to kiro-1040 session mac, systemctl --user restart: runtime shows attached: true again on a new socket (peer :47434 → :47478) under the same grant id, no human action.

Swift side (Keychain + UserDefaults per the issue) is still to do; #12 stays open for it.

Live grants are written to a mode-600 JSON file (MCP_GRANTS_FILE, default
$XDG_STATE_HOME/oab-instance-mcp/grants.json, 'off' disables) on create,
replace, DELETE and terminal end. At startup every grant still inside its
deadline is re-dialled under its original id; a runtime that forgot it answers
401 and the grant ends through the normal disposition. Atomic replace, dir
0700, a widened file is narrowed before being read. The secret is stored at the
same trust level as the bearer token file and never appears in GET /attach.
@chaodu-agent
chaodu-agent marked this pull request as ready for review September 29, 2026 23:42
@chaodu-agent
chaodu-agent merged commit 5d1162e into main Sep 29, 2026
6 checks passed
@chaodu-agent
chaodu-agent deleted the feat/linux-persist-grants branch September 29, 2026 23:43
jinwei-pikmin added a commit to jinwei-pikmin/instance-mcp that referenced this pull request Sep 30, 2026
…e frames

Parity with Swift openabdev#43 and the upstream Linux PoC openabdev#42:

- attach/store.rs: live grants (not ended, not expired) in
  $XDG_STATE_HOME/oab-instance-mcp/grants.json — 0600 in a 0700 dir, narrowed
  again if widened, atomic replace, secret included (never in GET /attach).
  Saved on create, replace, revoke, sweep and when a dial loop ends for good
  (client exit hook), so revoked or runtime-ended grants are not resumed.
- AttachManager::resume() at start re-dials each persisted grant still inside
  its deadline under its original id, profile resolved again (a custom profile
  that is gone is skipped and logged) and the remaining TTL carried over; the
  file is rewritten without expired or unresumable entries.
- --no-grant-persistence keeps grants in memory only (Swift flag name).
- On a Close frame from the runtime, flush tungstenite's echoing Close before
  leaving, so the runtime sees a clean close handshake instead of a bare EOF
  (the mock runtime reported close_echo_missing; upstream fixed the same).

Tests: saved privately and dropped on revoke; a simulated restart redials the
same id with the saved secret (the fake runtime 401s any other); expired and
undefined-profile grants are not resumed; a runtime-revoked grant leaves the
file. 73 pass. Live with upstream's mock_runtime.py (CLOSES=4006): the same
grant id resumed across two real daemon restarts with the TTL carried over,
and close_echo_missing dropped to 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant