Skip to content

feat(mac): reverse-attach grants survive a daemon restart (#12) - #43

Merged
chaodu-agent merged 1 commit into
mainfrom
feat/mac-persist-grants
Sep 30, 2026
Merged

chaodu-agent merged 1 commit into
mainfrom
feat/mac-persist-grants

Conversation

@chaodu-agent

Copy link
Copy Markdown
Contributor

macOS side of #12 (Linux side: #42).

  • New GrantStore / FileGrantStore: record → ~/Library/Application Support/oab-instance-mcp/grants.json (mode 600 in a 0700 dir, atomic replace, narrowed if widened, no secret in it); secret → login Keychain, service dev.openab.instance-mcp.grant, account = grant id, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. Keychain behind a SecretStore seam.
  • AttachManager: persists on create / replace / revoke / sweep / terminal end; resume() at start re-dials every grant still inside its deadline under its original id. Secrets are deleted when their grant leaves the set.
  • main.swift: on by default; --no-grant-persistence keeps grants in memory. Links Security.

Deviation from the issue text: the record lives in a JSON file rather than UserDefaults — same shape as the Linux side, inspectable, and testable against a temp dir. The secret is in the Keychain as proposed.

Tests: 9 new (GrantPersistenceTests: round trip without the secret in the file, perms, secret cleanup, expiry/orphan drop, narrowing, corrupt file, manager resume under same id, revoked not resumed, replaced leaves only the new one). Full suite 109/109 on macmini.

Live acceptance (launchctl kickstart -k → re-attached, same id) needs a signed build on macmini; planned via the v0.6.7 notarized pkg.

Live grants are saved on create, replace, revoke, sweep and terminal end:
the record in ~/Library/Application Support/oab-instance-mcp/grants.json
(mode 600, dir 0700, atomic replace, narrowed if widened) and the attach
secret in the login Keychain (service dev.openab.instance-mcp.grant, account
= grant id, AfterFirstUnlockThisDeviceOnly). At start AttachManager.resume()
re-dials every grant still inside its deadline under its original id.
--no-grant-persistence keeps grants in memory. Secrets are deleted when their
grant leaves the set. Tests use FileGrantStore with an in-memory SecretStore
(CI has no login keychain): 9 new, full suite 109/109.
@chaodu-agent
chaodu-agent merged commit 79fab19 into main Sep 30, 2026
6 checks passed
@chaodu-agent
chaodu-agent deleted the feat/mac-persist-grants branch September 30, 2026 00:00
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