Skip to content

identity: enrollment, pairing, and revocation — finalize + docs#13

Merged
xeri merged 5 commits into
masterfrom
feat/identity-phase9-finalize
Jul 16, 2026
Merged

identity: enrollment, pairing, and revocation — finalize + docs#13
xeri merged 5 commits into
masterfrom
feat/identity-phase9-finalize

Conversation

@xeri

@xeri xeri commented Jul 16, 2026

Copy link
Copy Markdown
Owner

The finished 5-commit identity chunk that the agent-management GUI work builds on. Green, and zero commits behind master — it had been sitting unmerged while the GUI work stacked on it.

  • ec16cb0 security: redact the pending enrollment ticket in diagnostics
  • 122a090 gateway: push only in-scope tunnels on reconnect so a narrowed scope shows no phantom
  • eb2a7e5 lint: clear the golangci gate before its first CI run
  • ef3072f ui: consume the pxf:// deep link into pairing; reusable codes no longer expire
  • 172aa36 docs: per-agent identity/enrollment/pairing/revocation; rewrite residual-risk

Merging this shrinks the in-flight agent-management branch from 9 commits to 4.

Merge #12 first

172aa36 claims agentID is "unforgeable" and that "enrolled agents are protected by their key and by revocation". Both were aspiration when written — a shared-token peer could name itself agt_<victim> and inherit the session, and 40 bits was searchable for ~$50. #12 is what makes those sentences true.

There will be one CLAUDE.md conflict with #12: both rewrote the same security bullets. That is the two descriptions of one model meeting, and the reconciliation is the accurate text.

xeri added 5 commits July 16, 2026 17:19
redactConfig masked the agent token, agentID, and cert fingerprint but not
Agent.EnrollTicket, so a pending single-use tkt_ credential rode verbatim into
config.redacted.toml — precisely the failing-to-pair window when a user grabs a
bundle. Mask it and add it to the leak test's secret list (the test passed blind
because it never set the field).

Claude-Session: https://claude.ai/code/session_014EY1emYd9vPqNhwW4mb4Jy
…shows no phantom

pushConfigOnConnect reconciled the scope-filtered subset (never a bind bypass) but
hashed and pushed the full stored set. If a scope was narrowed after a tunnel was
adopted, the agent was handed a tunnel the gateway won't bind — a phantom "live"
tunnel with no listener, against the "config drift never appears" promise. Push
validSpecs for both the hash and the payload; in the common (unchanged-scope) case
valid == stored, so nothing changes. Covered by TestGatewayConfigScopeNarrowingHidesTunnel.

Claude-Session: https://claude.ai/code/session_014EY1emYd9vPqNhwW4mb4Jy
Three pre-existing findings would fail the gate on first push (it has never run):
- unused: (*actor).session(agentID) had no callers (RevokeAgent inlines the lookup
  to avoid nesting a.do); remove the dead accessor.
- SA4000: the HashTunnels determinism check compared identical expressions
  (HashTunnels(a) != HashTunnels(a)); store the two calls before comparing, same intent.
- misspell: "alltime" in proxyforward_alltime_bytes_total is a Prometheus metric-name
  token (hyphens are invalid there); add a documented ignore-rule.

Claude-Session: https://claude.ai/code/session_014EY1emYd9vPqNhwW4mb4Jy
…er expire

The backend parsed, forwarded, stashed, and emitted a clicked pxf:// link, but no
React code drained it — the link surfaced the window and died there. App now takes
the pending deep link on mount and subscribes to pxf:deeplink, opening the wizard
straight onto the agent paste step with the code prefilled (confirm-to-connect,
never auto). Also: the wizard passed ttl 600 even for a reusable code, so a code
labelled "until you revoke it" silently expired in 10 minutes — reusable now maps
to ttl 0. devmock gains a &deeplink=1 axis for headless verification.

Claude-Session: https://claude.ai/code/session_014EY1emYd9vPqNhwW4mb4Jy
…ual-risk

Phase 9 of the identity work. architecture.md gains identity/enrollment/pairing rows
in "The numbers" and a deep-dive section (Ed25519 identity, proof-of-possession auth,
enrollment tickets, AgentStore, validators, revocation, scope, the pxf:// scheme +
click-to-pair, the version axes, and an honest "deferred" list). CLAUDE.md's stale
residual-risk paragraph is rewritten — per-agent identity + revocation shipped; the
shared token is now only a migration fallback with a narrowed, path-scoped risk — and
"revoked" joins the fatal-auth list. polish-backlog records the backend-ahead-of-GUI
agent-management surfaces. Also drops a stale (CapEnroll) comment (identity is
field-driven, not a capability) and adds gateway_agents.json to doccheck's runtime-file
allowlist. Citations land green.

Claude-Session: https://claude.ai/code/session_014EY1emYd9vPqNhwW4mb4Jy
@xeri
xeri merged commit 9173a2f into master Jul 16, 2026
21 checks passed
xeri added a commit that referenced this pull request Jul 16, 2026
The security bullets described a shared-token world and promised a bundle
redaction that log shipping quietly undid. Bring them level with the code:

- the residual risk of the legacy path is now confined to other shared-token
  agents, because the agt_ namespace is reserved;
- agentID's width is a security parameter, and a scope change evicts;
- bundles ship logs, so logs are scrubbed — and a leak test must seed every
  channel it ships or it passes vacuously.

Note for whoever merges the agent-management GUI branch: its CLAUDE.md
(inherited from the identity-finalize work, #13) already
claims "enrolled agents are protected by their key and by revocation" and that
agentID is "unforgeable". Both were aspiration when written — a shared-token peer
could name itself agt_<victim>, and 40 bits was searchable. This branch is what
makes them true, so it should land first.
@xeri
xeri deleted the feat/identity-phase9-finalize branch July 16, 2026 12:48
xeri added a commit that referenced this pull request Jul 16, 2026
The security bullets described a shared-token world and promised a bundle
redaction that log shipping quietly undid. Bring them level with the code:

- the residual risk of the legacy path is now confined to other shared-token
  agents, because the agt_ namespace is reserved;
- agentID's width is a security parameter, and a scope change evicts;
- bundles ship logs, so logs are scrubbed — and a leak test must seed every
  channel it ships or it passes vacuously.

Note for whoever merges the agent-management GUI branch: its CLAUDE.md
(inherited from the identity-finalize work, #13) already
claims "enrolled agents are protected by their key and by revocation" and that
agentID is "unforgeable". Both were aspiration when written — a shared-token peer
could name itself agt_<victim>, and 40 bits was searchable. This branch is what
makes them true, so it should land first.
@xeri xeri changed the title identity phase 9: enrollment/pairing/revocation finalize + docs identity: enrollment, pairing, and revocation — finalize + docs Jul 16, 2026
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