LearnNewsExamplesServices
Frontmatter
id13065
titleFleet Manager Bridge-token credential class: registry-side mint / verify
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-grace
createdAtJun 13, 2026, 8:02 AM
updatedAtJun 21, 2026, 2:17 AM
githubUrlhttps://github.com/neomjs/neo/issues/13065
authorneo-opus-grace
commentsCount1
parentIssue13015
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 13, 2026, 8:16 PM

Fleet Manager Bridge-token credential class: registry-side mint / verify

Closed v13.1.0/archive-v13-1-0-chunk-2 enhancementaiarchitecture
neo-opus-grace
neo-opus-grace commented on Jun 13, 2026, 8:02 AM

Context

A second credential class for the Fleet Manager registry (#13037), needed by the Neural Link Bridge handshake (#13056 — extended-NL coordination; the Bridge currently has ZERO auth, role=query-param). Routed to me by the #13056 steward (@neo-opus-ada, 2026-06-13); the design was shaped in the #13056 design exchange. The registry (#13031, merged) + instance lifecycle (#13049, merged) are the substrate this extends.

The Problem

The FM registry stores a GitHub PAT per agent — reversibly encrypted (AES-256-GCM) because the spawner must inject the real token into the harness env (#13049). That is the wrong primitive for agent↔Bridge transport auth: GitHub PATs are GitHub-scoped and never leave the Brain side (ADR 0020 §3). The Bridge handshake needs a separate credential class — a registry-minted, short-lived, hash-stored token that is verify-only (no reversible secret, no plaintext egress).

The Architectural Reality

  • Extends the FM registry substrate (ai/services/fleet/, FleetRegistryService from #13031). Additive methods on the registry or a thin FleetCredentialStore sibling — decided at structural-pre-flight (Stage-1 fast-path; the domain exists).
  • Two credential classes, separated at the method/file level (not only conceptually — peer-role tightening, @neo-gpt): the existing registry helpers (storeCredential / readCredentials / writeCredentials / resolveCredential, file credentials.enc) are all PAT-shaped and reversibly-encrypted. The Bridge token MUST NOT route through them — a casual reuse would preserve the "encrypted-at-rest" phrase while accidentally creating a second reversible secret store. So: a distinct store path/namespace (bridgeTokens.enc) or a FleetCredentialStore sibling with its own readBridgeTokens / writeBridgeTokens.
  • #12984-INDEPENDENT: the Bridge token is a registry-minted local token with no session-id dependency, so this leaf is not gated by the #12984 session-id canonicalization (unlike the identity-env provisioning leaf).

The Fix

Add to the FM registry substrate, in a distinct store from the PAT (separate file + separate read/write methods, never the PAT helpers):

  • mintBridgeToken(id) → {token, expiresAt} — generate a random token, return it once to the caller, persist only a record shaped {hash, expiresAt} (+ createdAt / hash-metadata if useful) to the Bridge store. The raw token is absent from storage after mint returns. Encrypting the Bridge record at rest is defense-in-depth over a hash, not permission to persist the raw token reversibly.
  • verifyBridgeToken(id, presented) → boolean — constant-time hash-compare against the stored hash; rejects expired tokens (expiresAt); fails closed on a malformed store (bad JSON / bad hash encoding / missing expiresAtfalse, no throw), matching the registry's existing fail-closed posture. Short-lived → rotation falls out of expiresAt.

Contract Ledger

Field Value
Target Surface ai/services/fleet/mintBridgeToken / verifyBridgeToken over a distinct Bridge store (bridgeTokens.enc or a FleetCredentialStore sibling with own readBridgeTokens/writeBridgeTokens), NOT the PAT helpers/credentials.enc
Source of Authority #13056 (Bridge handshake auth requirement); #13037 (the registry it extends); ADR 0020 §3 (PAT stays Brain-side, GitHub-scoped — don't reuse as transport cred)
Proposed Behavior Registry-minted, short-lived, hash-stored Bridge session tokens in a store separate from the PAT; mint returns the token once + persists only {hash, expiresAt}; verify is constant-time hash-compare, fails closed (incl. malformed store); the raw token is never returned/surfaced/persisted after mint
Fallback Unknown id / expired / absent / unreadable or malformed store (bad JSON / bad hash / missing expiresAt) → verify returns false (fail-closed, no throw, no leak)
Docs Service JSDoc (Anchor & Echo)
Evidence Unit tests: mint→verify round-trip; the Bridge store payload (JSON.stringify) contains no raw token (only its hash); wrong / unknown / expired / corrupt-store → false; the PAT resolveCredential still round-trips unaffected

Decision Record impact

aligned-with ADR 0020 (two-hemisphere; PAT stays Brain-side). depends-on #13037 (registry — merged). A distinct credential class from the PAT — additive sibling with its own store + methods, NOT a mutation of, or a reuse of, the {agentId: pat} store. NOT #12984-gated.

Acceptance Criteria

  • mintBridgeToken(id) → {token, expiresAt} — returns the token once, persists only {hash, expiresAt} to a distinct Bridge store.
  • The Bridge store is separate from the PAT at the file/method level (bridgeTokens.enc / FleetCredentialStore sibling with own read/write); Bridge tokens are never routed through storeCredential / resolveCredential / credentials.enc.
  • verifyBridgeToken(id, presented) → boolean — constant-time hash-compare, rejects expired, fails closed on unknown / expired / absent / malformed store (bad JSON / bad hash / missing expiresAtfalse, no throw).
  • The raw Bridge token is never persisted or returned after mint (only its hash) — asserted by a unit test inspecting the store payload.
  • The existing PAT class/store is unaffected — asserted by a resolveCredential round-trip test alongside the Bridge tests.
  • Unit tests: mint→verify round-trip + wrong/unknown/expired/corrupt-store → false + raw-token-never-persisted + PAT-unaffected.
  • Linked as a sub-issue of #13015; Related: #13056.

Out of Scope

  • The Bridge handshake itself (the Neural Link Bridge calling verifyBridgeToken) — #13056's domain (@neo-opus-ada steward).
  • Binding the verified token to a specific runtime / session / topological target — a #13056 decision (residual named by @neo-gpt); kept OUT of this leaf so the storage primitive stays narrow, unless the steward pulls it forward.
  • Spawn-time injection of the Bridge token alongside the PAT — a #13049-lifecycle follow-up; the mint/verify primitive stands alone here.
  • The PAT credential class — unchanged.

Avoided Traps

  • Reusing the PAT as a transport cred — rejected: GitHub-scoped, ADR 0020 §3 (Brain-side only).
  • Reusing the PAT store path/helpers for Bridge tokens — rejected (peer-role, @neo-gpt): routing through storeCredential/credentials.enc would silently create a second reversible secret store under the "encrypted-at-rest" phrasing. Distinct store + methods, enforced.
  • Reversibly-encrypting the Bridge token — rejected: it's verify-only; hash-storage means no decryptable secret at all (a stronger boundary than the PAT's reversible store, which is reversible only because the spawner must inject it).
  • Mutating the existing {agentId: pat} store — rejected: additive sibling class, no migration.
  • Gating on #12984 — rejected: the Bridge token is a registry-minted local token, session-id-independent.

Evolution

Tightened pre-implementation after @neo-gpt's (Euclid) peer-role review (V-B-A'd against FleetRegistryService.mjs + the spec + ADR 0020 §3):

  • Separate the Bridge store at the method/file level, not just conceptually — the existing helpers are PAT-shaped + reversible; a distinct store path/methods prevents accidentally creating a second reversible secret store.
  • verify fails closed on a malformed store (bad JSON / bad hash / missing expiresAt), matching the registry posture.
  • Tests assert the store payload has no raw token + the PAT class is unaffected (no regression to resolveCredential).

Related

Parent: #13015 (Fleet Manager MVP). Consumed by: #13056 (Bridge auth — @neo-opus-ada steward). Extends: #13037 (registry — merged). #12984 (NOT a gate — independent). Design shaped in the #13056 exchange; tightened by @neo-gpt peer-role.

Live latest-open sweep: checked #13015 subs (#13031/#13049 closed, #13058 open) + searched bridge token / credential / mint / verify (none) + latest open (#13064#13039) at 2026-06-13 ~05:42Z — no equivalent leaf. A2A claim sweep: routed to me by the #13056 steward (@neo-opus-ada); no competing claim.

Release classification: post-release (FM MVP / Bridge-auth product line; nothing v13.x-blocking).

Origin Session ID: db407b1c-07ea-4a6d-95a3-810b195dc899 Retrieval Hint: "fleet manager bridge token credential class mint verify hash-stored verify-only FleetCredentialStore bridge handshake auth #13056"

Authored by Claude Opus 4.8 (Claude Code, @neo-opus-grace / Grace).

tobiu referenced in commit fd82eed - "feat(ai): mint/verify Fleet Manager Bridge session tokens (#13065) (#13108) on Jun 13, 2026, 8:16 PM
tobiu closed this issue on Jun 13, 2026, 8:16 PM