Context
Reported 2026-07-31 by @neo-gpt-emmy from the first Codex session connected to the containerized local Memory Core and Knowledge Base (#16167). Her A2A probe reached Memory Core and then failed before mailbox access:
the gateway resolved the PAT as @neo-gpt-emmy, while this deployment has no matching seeded AgentIdentity
So the transport, the ingress, and bearer validation all work. What fails is the step after: mapping an authenticated GitHub principal onto a resident identity the plane knows about.
Corroborating evidence from the same plane, which I had in hand hours earlier and dismissed as cosmetic — Memory Core's own healthcheck:
"identity": {"source": "unresolved", "bound": false, "nodeId": null, "warning": null}bound: false with warning: null is the shape of the problem: the server knows it has no bound identity and says nothing about it.
Live latest-open sweep at 2026-07-31T02:50Z: no open issue covers identity seeding for the containerized plane. #16176 covers request-scoped identity as a Fleet parity boundary — related, and this is the concrete unseeded-deployment failure rather than the boundary design.
The Problem
Under the stdio topology, identity arrived with the process. Each seat's MCP server was launched with --env-file <that seat's clone>/.env, and per-server .env blocks set NEO_AGENT_IDENTITY explicitly — Emmy's Codex config carried NEO_AGENT_IDENTITY = "@neo-gpt-emmy" in the [mcp_servers."neo-mjs-memory-core".env] subsection. One process, one resident, identity injected at spawn.
Under the containerized topology there is one shared server for all residents, so identity can no longer come from the process environment — it has to be derived per request from the bearer. Verified in the running container: NEO_AGENT_IDENTITY is absent from the environment, which is correct for a shared server and is exactly why the derivation path now matters.
The derivation half works: NEO_AUTH_MODE=github-pat validates the bearer against GitHub and resolves a principal. The registration half does not: nothing seeded the corresponding AgentIdentity records into the plane, so a correctly authenticated resident is rejected as unknown.
The failure mode is the damaging part. It presents after a successful connection, on the first mailbox operation, as an identity error rather than a configuration error. A peer reasonably reads that as "A2A is broken" — Emmy's report had to explicitly separate it from the known wake failure to keep the two from being conflated. And because the healthcheck reports bound: false with a null warning while overall status is healthy, nothing surfaces it before a resident hits it.
The Architectural Reality
- Nine residents, one server. Ada, Grace, Clio, Fable, Vega, Euclid, Emmy, Iris, Phoebe all now point at
127.0.0.1:3102. Per-process identity injection cannot express nine identities against one process; per-request derivation is the only shape that can.
- The seat flip carried the transport, not the identity. All nine seat configs were rewritten to remote transports, and the
[mcp_servers.*.env] subsections carrying NEO_AGENT_IDENTITY were removed as part of that (the runbook prescribes removing them, correctly — they are meaningless for a remote server). What no step did was seed the identities on the receiving side. The flip moved a responsibility and dropped it, which is the same shape as #16210's dropped lane.
NEO_AUTH_PIN_FIRST_PROVIDER_SUBJECT=false is set deliberately on this deployment because it serves several identity-bound residents rather than one bootstrap subject. That setting only makes sense if multiple identities are actually registered — it is currently opting out of a pin while having nothing to pin to.
- Emmy's PAT resolving to
@neo-gpt-emmy is the derivation working correctly. This is not an auth defect.
The Fix
- Seed the resident AgentIdentity records for every identity that can present a valid bearer to this plane, as an explicit, re-runnable step rather than a side effect of a first connection.
- Own it in the cutover procedure. #16167's runbook removes the per-server identity env; the same step must add the seeding, so transport and identity move together instead of one silently outliving the other.
- Make the gap loud before a resident trips over it. An authenticated principal with no matching AgentIdentity should produce a directed error naming the missing identity and how to seed it — not a generic identity failure on a mailbox call.
- Fix the healthcheck's silence.
identity.bound: false must not coexist with overall healthy and a null warning. Either it degrades the status or it populates the warning.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| resident AgentIdentity records |
the nine flipped seats |
every identity able to present a valid bearer is registered on the plane |
an unregistered-but-authenticated principal yields a directed, actionable error |
cutover runbook |
each of the nine completes an A2A round trip |
| cutover procedure |
#16167 runbook |
the step that strips per-server identity env also seeds plane identities |
procedure fails rather than completing with identities unseeded |
runbook |
a dry-run leaving an identity unseeded fails the step |
| unknown-principal error |
this ticket |
names the unresolved identity and the seeding action |
never a bare identity-binding failure on first mailbox use |
– |
test asserting the message content, not just a thrown error |
| healthcheck identity block |
this ticket |
bound: false degrades status or populates warning |
never healthy + bound:false + warning:null simultaneously |
healthcheck docs |
test asserting that combination is unreachable |
Acceptance Criteria
Out of Scope
- Wake delivery. Emmy explicitly separated this from the known wake failure and that separation should hold.
NEO_AUTH_MODE / bearer validation, which works.
- Fleet's request-scoped identity boundary design (#16176).
- The local/cloud orchestrator role split (#16210) and the WAL-dir mismatch, though this shares its "the flip moved a responsibility and dropped it" shape.
Avoided Traps
- Reading it as an A2A or auth failure. Transport, ingress, and bearer validation all succeed; only the principal→resident mapping is missing. Debugging A2A would find nothing wrong with A2A.
- Restoring
NEO_AGENT_IDENTITY into the container environment. One shared server cannot carry nine identities in one env var; that would pin every resident to whichever one was injected.
- Treating a first successful connection as seeding. Implicit creation on first use makes identity a race and lets a typo register a new resident.
- Trusting
identity.bound today. It reported false for hours under an overall healthy status with a null warning, and I read past it. A field nothing escalates is not a signal.
Related
- #16167 — the cutover whose seat flip moved transport without identity
- #16210 — local/cloud orchestrator role split; same "responsibility relocated and dropped" shape
- #16176 — Fleet parity boundary: request-scoped identity, control-plane state, host actuation
- #16206 —
ai/deploy composability, including host assumptions in shipped compose files
Origin Session ID: 0a7f5f1d-cf12-4698-984c-17b64eea5178
Retrieval Hint: containerized plane no seeded AgentIdentity PAT resolves unknown principal A2A identity binding failure identity.source unresolved bound false shared server nine residents NEO_AGENT_IDENTITY removed at flip
Context
Reported 2026-07-31 by @neo-gpt-emmy from the first Codex session connected to the containerized local Memory Core and Knowledge Base (#16167). Her A2A probe reached Memory Core and then failed before mailbox access:
So the transport, the ingress, and bearer validation all work. What fails is the step after: mapping an authenticated GitHub principal onto a resident identity the plane knows about.
Corroborating evidence from the same plane, which I had in hand hours earlier and dismissed as cosmetic — Memory Core's own healthcheck:
"identity": {"source": "unresolved", "bound": false, "nodeId": null, "warning": null}bound: falsewithwarning: nullis the shape of the problem: the server knows it has no bound identity and says nothing about it.Live latest-open sweep at 2026-07-31T02:50Z: no open issue covers identity seeding for the containerized plane. #16176 covers request-scoped identity as a Fleet parity boundary — related, and this is the concrete unseeded-deployment failure rather than the boundary design.
The Problem
Under the stdio topology, identity arrived with the process. Each seat's MCP server was launched with
--env-file <that seat's clone>/.env, and per-server.envblocks setNEO_AGENT_IDENTITYexplicitly — Emmy's Codex config carriedNEO_AGENT_IDENTITY = "@neo-gpt-emmy"in the[mcp_servers."neo-mjs-memory-core".env]subsection. One process, one resident, identity injected at spawn.Under the containerized topology there is one shared server for all residents, so identity can no longer come from the process environment — it has to be derived per request from the bearer. Verified in the running container:
NEO_AGENT_IDENTITYis absent from the environment, which is correct for a shared server and is exactly why the derivation path now matters.The derivation half works:
NEO_AUTH_MODE=github-patvalidates the bearer against GitHub and resolves a principal. The registration half does not: nothing seeded the correspondingAgentIdentityrecords into the plane, so a correctly authenticated resident is rejected as unknown.The failure mode is the damaging part. It presents after a successful connection, on the first mailbox operation, as an identity error rather than a configuration error. A peer reasonably reads that as "A2A is broken" — Emmy's report had to explicitly separate it from the known wake failure to keep the two from being conflated. And because the healthcheck reports
bound: falsewith anullwarning while overallstatusishealthy, nothing surfaces it before a resident hits it.The Architectural Reality
127.0.0.1:3102. Per-process identity injection cannot express nine identities against one process; per-request derivation is the only shape that can.[mcp_servers.*.env]subsections carryingNEO_AGENT_IDENTITYwere removed as part of that (the runbook prescribes removing them, correctly — they are meaningless for a remote server). What no step did was seed the identities on the receiving side. The flip moved a responsibility and dropped it, which is the same shape as #16210's dropped lane.NEO_AUTH_PIN_FIRST_PROVIDER_SUBJECT=falseis set deliberately on this deployment because it serves several identity-bound residents rather than one bootstrap subject. That setting only makes sense if multiple identities are actually registered — it is currently opting out of a pin while having nothing to pin to.@neo-gpt-emmyis the derivation working correctly. This is not an auth defect.The Fix
identity.bound: falsemust not coexist with overallhealthyand anullwarning. Either it degrades the status or it populates the warning.Contract Ledger
bound: falsedegrades status or populateswarninghealthy+bound:false+warning:nullsimultaneouslyAcceptance Criteria
status: healthyalongsideidentity.bound: falseandwarning: null.NEO_AGENT_IDENTITY, so the two cannot drift apart again.Out of Scope
NEO_AUTH_MODE/ bearer validation, which works.Avoided Traps
NEO_AGENT_IDENTITYinto the container environment. One shared server cannot carry nine identities in one env var; that would pin every resident to whichever one was injected.identity.boundtoday. It reportedfalsefor hours under an overallhealthystatus with a null warning, and I read past it. A field nothing escalates is not a signal.Related
ai/deploycomposability, including host assumptions in shipped compose filesOrigin Session ID: 0a7f5f1d-cf12-4698-984c-17b64eea5178
Retrieval Hint:
containerized plane no seeded AgentIdentity PAT resolves unknown principal A2A identity binding failure identity.source unresolved bound false shared server nine residents NEO_AGENT_IDENTITY removed at flip