Context
A GitLab-PAT authenticated Memory Core cloud deployment can validate a bearer token and stamp raw memory writes with the resolved provider username, but graph-gated Agent OS tools still fail if no matching AgentIdentity node already exists. This breaks first-use cloud deployments where agents authenticate with GitLab PATs instead of pre-seeded Neo maintainer identities.
Verified current behavior:
AuthService.createGitlabPatVerifier() validates Authorization: Bearer against {gitlabApiBaseUrl}/api/v4/user and returns userId: user.username, username: user.name || user.username, and source: 'gitlab-pat'.
memory-core Server.buildRequestContext() calls bindAgentIdentity(reqAuth.userId).
bindAgentIdentity() only looks up @${userId} and returns null on miss.
RequestContextService.unboundIdentityError() then causes Mailbox and Permission tools to reject the authenticated caller.
- Observed cloud behavior:
add_memory can persist under the authenticated GitLab username, while graph-gated tools such as list_messages and list_permissions reject because the caller has no matching AgentIdentity node.
The Problem
GitLab-PAT authentication proves a valid deployment principal. That principal is not necessarily pre-known to Neo's checked-in identityRoots.mjs, and cloud operators should not need to modify Neo source or run a Neo-maintainer seed workflow for every deployment user or agent account.
The current behavior conflates:
- authentication success and tenant identity via
RequestContextService.getUserId(), and
- pre-existing AgentIdentity graph-node binding via
getAgentIdentityNodeId().
This leaves authenticated cloud agents with partial functionality: raw memory writes work, but A2A mailbox, broadcast participation, permissions, turn presence, and related graph-backed Agent OS surfaces can fail before first use.
The Architectural Reality
ai/mcp/server/shared/services/AuthService.mjs owns GitLab-PAT bearer validation and already returns provider-derived user identity.
ai/mcp/server/memory-core/Server.mjs owns Memory Core request-context enrichment and AgentIdentity binding.
ai/mcp/server/shared/services/RequestContextService.mjs carries both userId and agentIdentityNodeId; graph-gated tools consume the latter.
ai/services/memory-core/MailboxService.mjs and PermissionService.mjs require a bound graph identity.
ai/services/memory-core/MailboxService.mjs treats accountType: 'agent' as the operational Agent OS routing role for AGENT:* broadcasts.
ai/graph/identityRoots.mjs is appropriate for Neo's built-in seeded identities, but it should not be the only way external cloud deployments get graph principals.
The Fix
Add an idempotent Memory Core auto-provision path for authenticated HTTP/SSE identities, starting with gitlab-pat.
Recommended implementation:
- Introduce a small graph helper, for example
GraphService.ensureAgentIdentityForAuthContext() or a focused identity service, that receives { userId, username, source, provider metadata }.
- On successful
gitlab-pat request-context build:
- resolve canonical node id
@${userId} or the existing normal form used by Memory Core
- if the node exists as
AgentIdentity, preserve it and update only safe last-auth metadata
- if the node is missing, create a durable
AgentIdentity
- if an existing non-
AgentIdentity node collides, fail closed with an actionable identity-collision error
- Auto-provisioned nodes use
accountType: 'agent' because the authenticated principal is an Agent OS actor and must participate in A2A and broadcasts like any seeded agent.
- Do not infer provider-account nature. Do not write fields such as
humanBacked, bot, or equivalent by default. PAT auth only proves a validated provider principal.
- Use provider-neutral metadata; do not require
githubLogin for GitLab-only deployments:
authProvider: 'gitlab'
authSource: 'gitlab-pat'
providerBaseUrl
providerUserId when available from /api/v4/user
providerUsername
providerDisplayName
autoProvisioned: true
createdAt
lastAuthenticatedAt
- Trust must be deployment-authenticated, not unclassified. Under the current taxonomy use
internal-authored, or introduce/configure a clearer deployment-trusted / tenant-trusted tier if that taxonomy change is chosen during implementation.
- Keep local stdio behavior deliberate: do not silently mask stale
NEO_AGENT_IDENTITY typos unless the implementation explicitly opts local stdio into the same provisioning policy.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| GitLab-PAT request context |
AuthService.createGitlabPatVerifier() |
Valid bearer auth yields provider principal metadata usable for graph binding |
Invalid bearer remains an auth failure |
Cloud deployment auth docs |
Unit test for request-context payload |
| AgentIdentity node binding |
Server.buildRequestContext() plus graph identity helper |
Missing authenticated GitLab-PAT principal is idempotently created as AgentIdentity |
Non-AgentIdentity id collision fails closed |
Memory Core identity docs |
Unit tests for create/reuse/collision |
| A2A/mailbox/permission tools |
MailboxService, PermissionService, RequestContextService |
Graph-gated tools receive a non-null agentIdentityNodeId after provisioning |
Actionable unbound-identity error if provisioning fails |
MCP tool auth docs |
Unit tests for mailbox and permissions |
| Provider metadata |
AgentIdentity graph metadata |
Store provider-neutral fields such as authProvider, providerUsername, and providerBaseUrl |
Preserve existing seeded metadata when identity already exists |
Identity metadata docs |
Metadata assertion test |
| Trust classification |
Existing trust-tier taxonomy or new deployment/tenant tier |
Authenticated PAT identities are not unclassified |
Use internal-authored until a better deployment tier exists |
Deployment identity docs |
Trust-tier assertion test |
Decision Record impact
none. This aligns with the existing Memory Core auth/request-context split and AgentIdentity graph substrate; it does not supersede an ADR.
Acceptance Criteria
Out of Scope
- OIDC-specific provisioning beyond keeping the design compatible.
- Dedicated provider-account classification such as human vs bot vs service account.
- Tenant membership or per-repo KB visibility changes.
- Deployment image/tool-surface freshness diagnostics.
- WAL, embedding, and session projection timing; delayed memory embedding remains expected behavior.
Avoided Traps / Gold Standards Rejected
- Requiring every cloud deployment to edit Neo's
identityRoots.mjs: rejected; that is Neo-team seed substrate, not tenant onboarding.
- Falling back to raw
userId without a graph node: rejected; graph-backed A2A, mailbox, and permission surfaces need durable node endpoints.
- Creating reduced-rights "human" principals: rejected; authenticated PAT callers are operational Agent OS agents and must receive normal A2A and broadcast behavior.
- GitHub-specific metadata: rejected; GitLab cloud deployments need provider-neutral identity fields.
Related
- #13493 - related mode-aware unbound identity diagnostics; not a duplicate.
- #13571 - related RequestContext/AgentIdentity/userId boundary work; not a duplicate.
- #13431 / #12990 - related GitLab-PAT deployment-healthcheck seams; not duplicates.
- #13041 - related AgentIdentity metadata evolution; not a duplicate.
- #14145 - related Discussion about PAT-authenticated wake stream channels; not a duplicate.
Origin Session ID: 4cea162d-b6d0-4f80-9048-ae43a82508de
Handoff Retrieval Hints: GitLab PAT AgentIdentity auto-provision cloud user mailbox permissions, RequestContext agentIdentityNodeId gitlab-pat bindAgentIdentity unbound, provider-neutral AgentIdentity metadata authProvider providerUsername
Live latest-open sweep: checked latest 20 open issues on 2026-07-01; no equivalent found.
A2A in-flight sweep: checked latest 30 A2A messages immediately before creation on 2026-07-01; no overlapping claim found.
Context
A GitLab-PAT authenticated Memory Core cloud deployment can validate a bearer token and stamp raw memory writes with the resolved provider username, but graph-gated Agent OS tools still fail if no matching
AgentIdentitynode already exists. This breaks first-use cloud deployments where agents authenticate with GitLab PATs instead of pre-seeded Neo maintainer identities.Verified current behavior:
AuthService.createGitlabPatVerifier()validatesAuthorization: Beareragainst{gitlabApiBaseUrl}/api/v4/userand returnsuserId: user.username,username: user.name || user.username, andsource: 'gitlab-pat'.memory-core Server.buildRequestContext()callsbindAgentIdentity(reqAuth.userId).bindAgentIdentity()only looks up@${userId}and returnsnullon miss.RequestContextService.unboundIdentityError()then causes Mailbox and Permission tools to reject the authenticated caller.add_memorycan persist under the authenticated GitLab username, while graph-gated tools such aslist_messagesandlist_permissionsreject because the caller has no matchingAgentIdentitynode.The Problem
GitLab-PAT authentication proves a valid deployment principal. That principal is not necessarily pre-known to Neo's checked-in
identityRoots.mjs, and cloud operators should not need to modify Neo source or run a Neo-maintainer seed workflow for every deployment user or agent account.The current behavior conflates:
RequestContextService.getUserId(), andgetAgentIdentityNodeId().This leaves authenticated cloud agents with partial functionality: raw memory writes work, but A2A mailbox, broadcast participation, permissions, turn presence, and related graph-backed Agent OS surfaces can fail before first use.
The Architectural Reality
ai/mcp/server/shared/services/AuthService.mjsowns GitLab-PAT bearer validation and already returns provider-derived user identity.ai/mcp/server/memory-core/Server.mjsowns Memory Core request-context enrichment andAgentIdentitybinding.ai/mcp/server/shared/services/RequestContextService.mjscarries bothuserIdandagentIdentityNodeId; graph-gated tools consume the latter.ai/services/memory-core/MailboxService.mjsandPermissionService.mjsrequire a bound graph identity.ai/services/memory-core/MailboxService.mjstreatsaccountType: 'agent'as the operational Agent OS routing role forAGENT:*broadcasts.ai/graph/identityRoots.mjsis appropriate for Neo's built-in seeded identities, but it should not be the only way external cloud deployments get graph principals.The Fix
Add an idempotent Memory Core auto-provision path for authenticated HTTP/SSE identities, starting with
gitlab-pat.Recommended implementation:
GraphService.ensureAgentIdentityForAuthContext()or a focused identity service, that receives{ userId, username, source, provider metadata }.gitlab-patrequest-context build:@${userId}or the existing normal form used by Memory CoreAgentIdentity, preserve it and update only safe last-auth metadataAgentIdentityAgentIdentitynode collides, fail closed with an actionable identity-collision erroraccountType: 'agent'because the authenticated principal is an Agent OS actor and must participate in A2A and broadcasts like any seeded agent.humanBacked,bot, or equivalent by default. PAT auth only proves a validated provider principal.githubLoginfor GitLab-only deployments:authProvider: 'gitlab'authSource: 'gitlab-pat'providerBaseUrlproviderUserIdwhen available from/api/v4/userproviderUsernameproviderDisplayNameautoProvisioned: truecreatedAtlastAuthenticatedAtinternal-authored, or introduce/configure a clearerdeployment-trusted/tenant-trustedtier if that taxonomy change is chosen during implementation.NEO_AGENT_IDENTITYtypos unless the implementation explicitly opts local stdio into the same provisioning policy.Contract Ledger Matrix
AuthService.createGitlabPatVerifier()Server.buildRequestContext()plus graph identity helperAgentIdentityAgentIdentityid collision fails closedMailboxService,PermissionService,RequestContextServiceagentIdentityNodeIdafter provisioningauthProvider,providerUsername, andproviderBaseUrlunclassifiedinternal-authoreduntil a better deployment tier existsDecision Record impact
none. This aligns with the existing Memory Core auth/request-context split and AgentIdentity graph substrate; it does not supersede an ADR.
Acceptance Criteria
gitlab-patauthenticated Memory Core request auto-provisions a missingAgentIdentitynode for the resolved provider principal before graph-gated tools need it.accountType: 'agent'and participates in existing A2A, broadcast, mailbox, and permission flows without requiring source edits toai/graph/identityRoots.mjs.githubLoginfield is required for GitLab-only identities.humanBackedandbotare not written by default.unclassified; implementation usesinternal-authoredor introduces/configures a deployment/tenant-trusted tier with tests and docs.AgentIdentitynodes are preserved and not overwritten except for safe last-auth metadata if applicable.AgentIdentitynode collision at the target id fails closed with an actionable error.identityRoots.mjsfor first-use identities.Out of Scope
Avoided Traps / Gold Standards Rejected
identityRoots.mjs: rejected; that is Neo-team seed substrate, not tenant onboarding.userIdwithout a graph node: rejected; graph-backed A2A, mailbox, and permission surfaces need durable node endpoints.Related
Origin Session ID: 4cea162d-b6d0-4f80-9048-ae43a82508de
Handoff Retrieval Hints:
GitLab PAT AgentIdentity auto-provision cloud user mailbox permissions,RequestContext agentIdentityNodeId gitlab-pat bindAgentIdentity unbound,provider-neutral AgentIdentity metadata authProvider providerUsernameLive latest-open sweep: checked latest 20 open issues on 2026-07-01; no equivalent found.
A2A in-flight sweep: checked latest 30 A2A messages immediately before creation on 2026-07-01; no overlapping claim found.