identity: enrollment, pairing, and revocation — finalize + docs#13
Merged
Conversation
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
added a commit
that referenced
this pull request
Jul 16, 2026
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
added a commit
that referenced
this pull request
Jul 16, 2026
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
ec16cb0security: redact the pending enrollment ticket in diagnostics122a090gateway: push only in-scope tunnels on reconnect so a narrowed scope shows no phantomeb2a7e5lint: clear the golangci gate before its first CI runef3072fui: consume the pxf:// deep link into pairing; reusable codes no longer expire172aa36docs: per-agent identity/enrollment/pairing/revocation; rewrite residual-riskMerging this shrinks the in-flight agent-management branch from 9 commits to 4.
Merge #12 first
172aa36claims 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 itselfagt_<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.