Graduated from Discussion #15958, frozen at Fold 16.6.1. This is the ship-now authentication leaf under epic #15798. #15807 / PR #15983 supplies the parity-CI overlay this leaf must adapt; #15805 consumes the completed local HTTP profile at the seat-config boundary.
A post-graduation source step-back split the original 16-AC bundle without weakening the security baseline:
this ticket owns the minimum activation foundation plus a usable rosterless local provider-PAT profile;
sibling #15992 owns the non-blocking completion of authentication ownership, hybrid/ingress hardening, and the full plane-admission matrix.
#15992 is blocked by this ticket. It does not block #15805 or the pilot #15806. Epic #15798 remains the sole coordination parent; neither leaf is a nested epic.
At exact origin/devcdc2a73204b0f144aa5c8d7abdeb83ad6128e1b7:
AuthService.setup() implements local-bearer, seat-token, gitlab-pat, github-pat, and OIDC.
TransportService.setup() invokes it only for OIDC host/issuer, gitlab-pat, or local-bearer; the two later modes both missed activation.
A normal github-pat Streamable-HTTP boot accepted bearer-less MCP initialize, returned HTTP 200, and created a session.
auth.mode always defaults to oidc, so neither “mode exists” nor unconditional delegation without state discrimination is safe: no OIDC endpoint currently falls through to host.includes(...).
Custom middleware is mounted after the current conditional AuthService call; proxy-only identity is resolved later inside Transport.
The safe baseline therefore cannot be a two-string predicate patch. It must remove the legal-mode activation list while preserving custom/proxy states and making truly unconfigured HTTP fail closed.
Problem
Canonical dev parity currently declares no authentication mode. Memory Core and Knowledge Base can therefore serve HTTP without any authentication middleware even though provider-PAT validation already exists.
The first working local profile also cannot assume a hand-maintained roster:
generated managed seats can derive one provider subject from their canonical identity later in #15805;
ordinary forks and the direct Compose profile have no generator-owned roster;
current empty allowedUsers means “admit any validated provider user,” so roster absence cannot silently stand in for one-human admission;
github-pat is excluded from default AgentIdentity auto-provision, which leaves a rosterless fork authenticated but graph-gated.
The local profile needs an explicit bounded alternative: literal host-loopback publication plus a process-lifetime first-provider-subject policy bootstrapped from the operator/seat PAT before the listener opens. AuthService accepts exactly one direct-value or file carrier. Canonical Compose uses one environment-backed Docker secret mounted only into MC/KB; bootstrap and health read the same file. The later health probe therefore uses that same PAT and never introduces a synthetic second subject.
Architectural Reality
ADR 0019 keeps ConfigProvider leaves as runtime source of truth. The activation foundation removes Transport's built-in legal-mode predicate; it does not add an exported mode registry.
A first-subject policy is explicit config, not a reinterpretation of empty allowedUsers. Generic PAT profiles keep their shipped allowlist semantics unless they opt in.
Provider authentication, local-profile admission, AgentIdentity provisioning, and downstream authorization remain separate boundaries.
Docker host publication and the container listener are different surfaces. The host ports bind literal 127.0.0.1; the container listener must remain reachable through Docker networking.
Both MC and KB use the same operator/seat PAT as bootstrap and authenticated-readiness credential. Canonical Compose mounts one environment-backed secret file only into those services and passes only its path through config. AuthService validates it and pins the resolved provider login before app.listen; the later health probe proves the already-pinned subject. It is not a synthetic health identity.
Restart resets the process-lifetime pin and re-validates the configured bootstrap PAT before either listener opens. Rotation therefore means updating that secret reference and restarting; no persistent pin/reset file is added.
Local GitHub AgentIdentity auto-provision runs only after the admission decision passes. A denied login must not materialize a graph node.
#15992 owns full custom/proxy/guard ownership migration. This baseline must name any surviving Transport compatibility branch as temporary and must not claim the final single-owner state early.
Secret-carrier correction
Implementation preflight falsified the first direct-interpolation shape: mapping the host PAT into service environment values makes docker compose config render the credential. The corrected contract uses top-level Compose secrets.<name>.environment, mounts one neutral secret only into MC/KB, and gives AuthService plus the healthcheck only the shared file path. A dummy-value render proved the value absent while both services retained the same file reference and the orchestrator retained neither mount nor path.
Important boundary: an absent environment-backed secret does not make docker compose config --quiet fail. The negative proof belongs at container creation/boot; this ticket must not claim config-time fail-closed semantics.
Fix (one PR)
Remove Transport's hand-maintained built-in activation subset and invoke AuthService.setup() for every Streamable-HTTP boot.
Make AuthService discriminate custom middleware, the five built-ins, configured OIDC, proxy-only compatibility, and truly unconfigured HTTP before any endpoint dereference. Custom/proxy behavior stays green; no-installer boot fails with a named remediation.
Add an explicit ConfigProvider-owned first-provider-subject policy for the canonical rosterless local profile. It is off by default, cannot be combined ambiguously with a non-empty allowedUsers, and requires exactly one direct-value or file-reference provider PAT carrier. AuthService reads/validates it to establish the pin before opening the listener; unreadable/empty files and both/neither carrier states fail named and redacted.
Declare canonical dev parity as github-pat, proxy trust off, first-subject policy on, GitHub AgentIdentity auto-provision on, and one environment-backed Docker secret mounted only into MC/KB. Pre-listen subject bootstrap and authenticated health consume the same file path; rendered Compose carries the env name/path, never the PAT.
Bind the published host ports to literal IPv4 loopback (127.0.0.1:3100:3000 and 127.0.0.1:3101:3001); do not bind the in-container listener to loopback.
Adapt the landed #15807 / PR #15983 overlay in the same diff to an explicit provider-independent local-bearer fixture state. It reuses the mounted file carrier with one valid non-secret fixture and performs no live provider/PAT call.
Prove the baseline through consumed HTTP, Compose rendering, authenticated readiness, and one post-merge seat smoke.
Contract Ledger
Target surface
Source of authority
Required behavior
Fallback / failure
Docs
Evidence
Streamable-HTTP activation
D#15958 + source step-back
unconditional AuthService invocation; no built-in legal-mode predicate in Transport
no installer fails boot
AuthService + TransportService JSDoc
source census + setup matrix
Custom/proxy compatibility
current shipped behavior
unconditional invocation does not crash or double-mount; existing paths stay green
named boot error, never gateless HTTP
AuthService + TransportService JSDoc
focused regressions
auth.pinFirstProviderSubject + bootstrap carriers
rosterless local-profile decision
explicit opt-in; exactly one of providerBootstrapPat / providerBootstrapPatFile; configured operator/seat PAT pins before listen
both/neither, unreadable/empty file, invalid bootstrap, or later different login denied
ConfigProvider + AuthService JSDoc
setup + verifier + consumed HTTP
GitHub local identity
auth.autoProvisionIdentitySources
bounded local profile opts github-pat into AgentIdentity auto-provision after admission
explicit local-bearer fixture override reusing the neutral mounted carrier
no real PAT/provider egress
Parity-overlay contract comments
mandatory parity lane
Secret boundary
config/Compose contract
direct carrier remains optional for non-Compose runtimes; canonical Compose uses one environment-backed file carrier
no raw PAT in committed/rendered artifacts, logs, args, or diagnostics; orchestrator gets no mount/path
ConfigProvider JSDoc + Compose comments
negative sweep + actual Compose render
Decision Record
Decision Record: REQUIRED — aligned with ADR 0019; no amendment. The new first-subject policy is a ConfigProvider-owned leaf with explicit legal combinations; no consumer re-reads env and no second mode registry is introduced.
Credential posture aligns with ADR 0020. Provider login remains the canonical operational identity; credentials remain Brain-side. The local profile's loopback + first-subject policy is admission scope, not an alternate identity authority.
Discussion Criteria Mapping
A2 provider-PAT baseline: canonical local parity uses a dedicated provider PAT for MC + KB.
G2 activation: the ship-now slice removes the built-in legal-mode activation subset and makes every HTTP boot pass through AuthService state discrimination.
Rosterless correction: a generator-derived single subject is used where a generator exists; the direct local profile uses explicit loopback + first-subject admission.
Readiness authority: the explicit local-fork branch chooses the operator/seat PAT for pre-listen subject bootstrap and the existing authenticated /mcp health probe. It does not create a synthetic health subject or a new unauthenticated liveness route.
GitLab negative: the local GitHub policy does not become a mode-general roster/pin boot gate.
Retirement edge:#15992 removes the remaining compatibility ownership/guard residue after this baseline.
Acceptance Criteria
TransportService invokes AuthService.setup() for every Streamable-HTTP boot and carries no hand-maintained built-in legal-mode activation predicate.
AuthService.setup() safely distinguishes custom middleware, oidc, gitlab-pat, github-pat, local-bearer, seat-token, configured OIDC endpoint state, proxy-only compatibility, and truly unconfigured HTTP. No state reaches null.includes; no-installer HTTP fails boot with a named remediation.
Existing custom middleware and proxy-only regressions remain green, with no double mount. Any Transport-owned compatibility logic that survives this baseline is named in source/tests as the #15992 retirement target; this PR does not claim final single ownership.
Unauthenticated initialize under github-pat and seat-token returns the mode-appropriate bearer challenge and no MCP session id.
ConfigProvider exposes auth.pinFirstProviderSubject, auth.providerBootstrapPat, and auth.providerBootstrapPatFile through their NEO_AUTH_* leaves, all default-off/empty. Enabling the pin with a non-empty allowedUsers, a non-PAT mode, both/neither bootstrap carriers, an unreadable/empty file, or another ambiguous admission state fails named and redacted.
With first-subject policy enabled and allowedUsers empty, AuthService reads exactly one configured direct/file operator bootstrap PAT, validates it, and pins its resolved provider login before the MCP listener opens. The same login continues to succeed and any later different valid login is denied before MCP dispatch/session creation.
Restart semantics are explicit and tested: the pin is not persisted; every restart re-validates the current bootstrap PAT and establishes the process pin before listen. Rotation requires updating the secret reference and restarting.
Canonical dev parity renders NEO_AUTH_MODE=github-pat, proxy trust off, first-subject policy on, github-pat in NEO_AUTH_AUTO_PROVISION_IDENTITY_SOURCES, and one environment-backed Docker secret mounted only into MC + KB. Bootstrap and health resolve the same file path; the orchestrator receives neither mount nor path; rendered config contains no PAT. No roster or synthetic health identity is required.
Canonical host publication is literal loopback for both ports (127.0.0.1:3100:3000, 127.0.0.1:3101:3001); the in-container listener remains Docker-reachable.
The later health probe presents the same bootstrap PAT, observes the same pinned provider login on MC + KB, and returns the expected served-plane identity before either service becomes healthy.
AgentIdentity auto-provision runs only after the pin/allowlist admission decision passes. An invalid PAT or second different valid provider login creates no graph node, fails before MCP dispatch, and produces no MCP session id.
gitlab-pat compatibility is preserved: with first-subject policy off and absent/empty allowedUsers, the server boots and a valid GitLab PAT authenticates exactly as today.
The landed #15807 / PR #15983 overlay explicitly overrides the dev auth inputs to a provider-independent local-bearer state and passes one valid non-secret fixture into every Compose invocation. The base secret mount remains usable; CI makes no provider call and does not weaken canonical dev.
Committed/rendered artifacts, logs, process arguments, and public diagnostics carry secret references only—never a raw PAT. A real docker compose config render with a dummy token proves the value absent.
Existing stdio paths remain green.
[L4-deferred — operator handoff needed] Post-merge: one canonical local seat uses the real provider-PAT profile for MC + KB and authenticated readiness without exposing the credential.
A new unauthenticated loopback liveness route (D#15958 Option I); the local-fork profile explicitly chooses the operator/seat PAT as bootstrap + health credential. #13435 remains the revalidation route if provider-coupled readiness produces measured friction.
Generated per-seat subject/secret wiring; #15805 owns the generator and may derive a single provider login instead of relying on the fallback pin.
The #15806 cloned-snapshot/write-disposition pilot.
Seat-token retirement; it remains optional pending a post-pilot zero-consumer census.
General Declared-Domains/G2b machinery or an ADR-0019 amendment.
Avoided Traps
Two-string Transport patch. It recreates the legal-mode drift that caused the defect.
Unsafe unconditional delegation. Default oidc without an endpoint, custom middleware, and proxy-only state must be classified before endpoint use.
Implicit roster semantics. Empty allowedUsers keeps its shipped meaning; first-subject admission is explicit.
Container-loopback confusion. Bind Docker's host publication, not the container listener.
Synthetic health-subject collision. The local profile uses the operator/seat PAT for pre-listen bootstrap and health; no second subject can steal the pin.
Direct Compose interpolation. Equal environment aliases still render the raw credential. Canonical Compose shares a mounted secret file path instead.
Per-process subject divergence or first-request race. Both services validate the same bootstrap PAT before opening their listeners.
Authentication without graph identity. The bounded local profile explicitly opts GitHub identity into auto-provision, but only after admission.
Mode-general GitLab breakage. Local GitHub policy is not inherited by GitLab-PAT.
Making hardening a pilot blocker.#15992 follows this leaf and does not gate #15805/#15806.
Nested epic.#15798 already owns coordination; #15990 and #15992 are one-PR sibling leaves.
D#15958 graduated at frozen Fold 16.6.1 with GPT author approval, unconditional non-author Claude approval, Kimi additive approval, and Emmy's 8/8 neutral STEP_BACK.
Vega's post-graduation scope objection was accepted: baseline and hardening are separate close-targets.
Emmy's source falsifier was accepted: the baseline absorbs the minimum activation foundation and is not a two-string patch.
An independent source/DAG audit confirmed the sibling-leaf graph and the explicit rosterless-policy requirements.
Phoebe's non-GPT falsification made the pin and admission-before-auto-provision order load-bearing. Her synthetic-health collision is discharged by explicitly choosing her priced option 3: the operator/seat PAT bootstraps the pin before listen and is reused by health. Option I stays trigger-bound rather than expanding this baseline.
Unresolved Liveness
#15807 / PR #15983 remains the prerequisite overlay surface; this ticket is blocked until it lands.
The latest 20 open issues sorted by creation time, the latest 30 all-state A2A messages, Memory Core prior art, Knowledge Base/source surfaces, and the local issue mirror were checked during the split. No equivalent baseline or hardening ticket and no competing earlier claim exists.
Origin Session ID: 0b48c54a-3c2e-451c-a511-78169493b39c
Retrieval Hint: query_raw_memories("D#15958 #15990 provider PAT rosterless first subject activation foundation")
Context
Graduated from Discussion #15958, frozen at Fold 16.6.1. This is the ship-now authentication leaf under epic #15798. #15807 / PR #15983 supplies the parity-CI overlay this leaf must adapt; #15805 consumes the completed local HTTP profile at the seat-config boundary.
A post-graduation source step-back split the original 16-AC bundle without weakening the security baseline:
#15992 is blocked by this ticket. It does not block #15805 or the pilot #15806. Epic #15798 remains the sole coordination parent; neither leaf is a nested epic.
At exact
origin/devcdc2a73204b0f144aa5c8d7abdeb83ad6128e1b7:AuthService.setup()implementslocal-bearer,seat-token,gitlab-pat,github-pat, and OIDC.TransportService.setup()invokes it only for OIDC host/issuer,gitlab-pat, orlocal-bearer; the two later modes both missed activation.github-patStreamable-HTTP boot accepted bearer-less MCPinitialize, returned HTTP 200, and created a session.auth.modealways defaults tooidc, so neither “mode exists” nor unconditional delegation without state discrimination is safe: no OIDC endpoint currently falls through tohost.includes(...).The safe baseline therefore cannot be a two-string predicate patch. It must remove the legal-mode activation list while preserving custom/proxy states and making truly unconfigured HTTP fail closed.
Problem
Canonical dev parity currently declares no authentication mode. Memory Core and Knowledge Base can therefore serve HTTP without any authentication middleware even though provider-PAT validation already exists.
The first working local profile also cannot assume a hand-maintained roster:
allowedUsersmeans “admit any validated provider user,” so roster absence cannot silently stand in for one-human admission;github-patis excluded from default AgentIdentity auto-provision, which leaves a rosterless fork authenticated but graph-gated.The local profile needs an explicit bounded alternative: literal host-loopback publication plus a process-lifetime first-provider-subject policy bootstrapped from the operator/seat PAT before the listener opens. AuthService accepts exactly one direct-value or file carrier. Canonical Compose uses one environment-backed Docker secret mounted only into MC/KB; bootstrap and health read the same file. The later health probe therefore uses that same PAT and never introduces a synthetic second subject.
Architectural Reality
allowedUsers. Generic PAT profiles keep their shipped allowlist semantics unless they opt in.127.0.0.1; the container listener must remain reachable through Docker networking.app.listen; the later health probe proves the already-pinned subject. It is not a synthetic health identity.Secret-carrier correction
Implementation preflight falsified the first direct-interpolation shape: mapping the host PAT into service environment values makes
docker compose configrender the credential. The corrected contract uses top-level Composesecrets.<name>.environment, mounts one neutral secret only into MC/KB, and gives AuthService plus the healthcheck only the shared file path. A dummy-value render proved the value absent while both services retained the same file reference and the orchestrator retained neither mount nor path.Important boundary: an absent environment-backed secret does not make
docker compose config --quietfail. The negative proof belongs at container creation/boot; this ticket must not claim config-time fail-closed semantics.Fix (one PR)
AuthService.setup()for every Streamable-HTTP boot.allowedUsers, and requires exactly one direct-value or file-reference provider PAT carrier. AuthService reads/validates it to establish the pin before opening the listener; unreadable/empty files and both/neither carrier states fail named and redacted.github-pat, proxy trust off, first-subject policy on, GitHub AgentIdentity auto-provision on, and one environment-backed Docker secret mounted only into MC/KB. Pre-listen subject bootstrap and authenticated health consume the same file path; rendered Compose carries the env name/path, never the PAT.127.0.0.1:3100:3000and127.0.0.1:3101:3001); do not bind the in-container listener to loopback.local-bearerfixture state. It reuses the mounted file carrier with one valid non-secret fixture and performs no live provider/PAT call.Contract Ledger
auth.pinFirstProviderSubject+ bootstrap carriersproviderBootstrapPat/providerBootstrapPatFile; configured operator/seat PAT pins before listenauth.autoProvisionIdentitySourcesgithub-patinto AgentIdentity auto-provision after admissiongithub-pat, proxy trust off, first-subject on, one environment-backed secret file shared by bootstrap/health, host ports loopbackmcpHealthcheck.mjspath/mcphealth probeallowedUsersand pin policy off remain validlocal-bearerfixture override reusing the neutral mounted carrierDecision Record
Decision Record: REQUIRED — aligned with ADR 0019; no amendment. The new first-subject policy is a ConfigProvider-owned leaf with explicit legal combinations; no consumer re-reads env and no second mode registry is introduced.
Credential posture aligns with ADR 0020. Provider login remains the canonical operational identity; credentials remain Brain-side. The local profile's loopback + first-subject policy is admission scope, not an alternate identity authority.
Discussion Criteria Mapping
/mcphealth probe. It does not create a synthetic health subject or a new unauthenticated liveness route.Acceptance Criteria
TransportServiceinvokesAuthService.setup()for every Streamable-HTTP boot and carries no hand-maintained built-in legal-mode activation predicate.AuthService.setup()safely distinguishes custom middleware,oidc,gitlab-pat,github-pat,local-bearer,seat-token, configured OIDC endpoint state, proxy-only compatibility, and truly unconfigured HTTP. No state reachesnull.includes; no-installer HTTP fails boot with a named remediation.initializeundergithub-patandseat-tokenreturns the mode-appropriate bearer challenge and no MCP session id.auth.pinFirstProviderSubject,auth.providerBootstrapPat, andauth.providerBootstrapPatFilethrough theirNEO_AUTH_*leaves, all default-off/empty. Enabling the pin with a non-emptyallowedUsers, a non-PAT mode, both/neither bootstrap carriers, an unreadable/empty file, or another ambiguous admission state fails named and redacted.allowedUsersempty, AuthService reads exactly one configured direct/file operator bootstrap PAT, validates it, and pins its resolved provider login before the MCP listener opens. The same login continues to succeed and any later different valid login is denied before MCP dispatch/session creation.NEO_AUTH_MODE=github-pat, proxy trust off, first-subject policy on,github-patinNEO_AUTH_AUTO_PROVISION_IDENTITY_SOURCES, and one environment-backed Docker secret mounted only into MC + KB. Bootstrap and health resolve the same file path; the orchestrator receives neither mount nor path; rendered config contains no PAT. No roster or synthetic health identity is required.127.0.0.1:3100:3000,127.0.0.1:3101:3001); the in-container listener remains Docker-reachable.gitlab-patcompatibility is preserved: with first-subject policy off and absent/emptyallowedUsers, the server boots and a valid GitLab PAT authenticates exactly as today.local-bearerstate and passes one valid non-secret fixture into every Compose invocation. The base secret mount remains usable; CI makes no provider call and does not weaken canonical dev.docker compose configrender with a dummy token proves the value absent.Evidence Ladder
Out of Scope
Avoided Traps
oidcwithout an endpoint, custom middleware, and proxy-only state must be classified before endpoint use.allowedUserskeeps its shipped meaning; first-subject admission is explicit.Related
Parent epic #15798 · hardening successor #15992 · consumer #15805 · pilot #15806 · dependency #15807 / PR #15983 · provider-PAT lineage #15598 / PR #15601 · optional seat-token lineage #15801 / PR #15832 · source D#15958 · ADR 0019 · ADR 0020.
Signal and Step-Back Record
Unresolved Liveness
Creation Freshness
The latest 20 open issues sorted by creation time, the latest 30 all-state A2A messages, Memory Core prior art, Knowledge Base/source surfaces, and the local issue mirror were checked during the split. No equivalent baseline or hardening ticket and no competing earlier claim exists.
Origin Session ID: 0b48c54a-3c2e-451c-a511-78169493b39c Retrieval Hint:
query_raw_memories("D#15958 #15990 provider PAT rosterless first subject activation foundation")