---
id: 10144
title: AgentIdentity node type + GitHub account binding for model identities
state: CLOSED
labels:
  - enhancement
  - ai
  - architecture
  - core
assignees:
  - tobiu
createdAt: '2026-04-21T09:28:52Z'
updatedAt: '2026-06-07T17:19:56Z'
githubUrl: 'https://github.com/neomjs/neo/issues/10144'
author: tobiu
commentsCount: 0
parentIssue: 10016
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking:
  - '[x] 10139 Extend Memory Core with Explicit A2A Primitive'
  - '[x] 10146 Cross-tenant permission edges + multi-tenant validation test suite'
  - '[x] 10145 OAuth2 authentication layer for Memory Core MCP connections'
closedAt: '2026-04-21T16:51:49Z'
---
# AgentIdentity node type + GitHub account binding for model identities

# AgentIdentity node type + GitHub account binding for model identities

### Context
#10016 scope today treats identity as a `userId` metadata tag. This ticket extends scope with a first-class `AgentIdentity` graph node type, bindable to per-model GitHub accounts (precedent: AI Village's `@claude-opus-4-5`, `@claudehaiku45`, `@claude-opus-4-6` on #9535). @tobiu is provisioning per-model accounts this session; this ticket creates the graph substrate.

### The Problem
Scalar-metadata identity means no edges from memories/sessions to identity nodes; ownership relies on metadata filter, not structural traversal. Cross-session reasoning ("what has @neo-opus-ada authored across all sessions?") scans metadata instead of traversing one edge. Mailbox (#10139) can't address persistent model identity — only session-scoped.

### The Architectural Reality
- #10016 handles `userId` ingestion via reverse-proxy headers; #10000 complete.
- Per-model GitHub accounts to be provisioned: `@neo-opus-ada`, `@neo-gemini-pro`, etc. Decision from session brainstorm: **per-model persistent, not per-version** (less account churn, version lives in node properties).
- Graph-first Memory artifacts sub-epic makes Memory + Session nodes; AgentIdentity is the third artifact type.

### The Fix
- `AgentIdentity` node type: `{id, githubLogin, displayName, modelFamily, accountType: 'agent'|'human', createdAt}`
- Edges (defined here, created by future work):
  - `AUTHORED_BY(Memory | Session | Message → AgentIdentity)`
  - `OWNED_BY(Session → AgentIdentity)` — tenancy
  - `HARNESSED_VIA(Memory → HarnessNode)` — optional
- Seed script `ai/scripts/seedAgentIdentities.mjs` — provisions initial identity nodes; idempotent
- Version metadata on Message/Memory as `modelVersion` property (e.g. `opus-4.7`) — accounts stable, versions drift

### Acceptance Criteria
- [ ] `AgentIdentity` node type in graph schema with properties + indices
- [ ] Seed script creates initial identity nodes; idempotent on re-run
- [ ] At least one account (per-model or `@tobiu`) has live identity node bound to GitHub login
- [ ] `MATCH AgentIdentity WHERE githubLogin = '@neo-opus-ada'` resolves to exactly one node
- [ ] Design doc captures per-model-vs-per-version decision with rationale

### Out of Scope
- OAuth2 authentication for MCP (separate sub)
- Cross-tenant permission edges (separate sub)
- Retroactive `AUTHORED_BY` edges on historical memories (handled via lazy back-fill)

### Avoided Traps
- **Per-version accounts (`@neo-opus-ada-1`, `@neo-opus-ada-2`).** Rejected. AI Village uses per-version; session consensus: churn is real, version-in-properties wins. Cross-version session continuity benefits at account level.
- **Identity as scalar metadata only.** Rejected. Graph-first substrate decision applies — ownership traverses edges, not filters metadata.

### Related
- Parent: #10016
- Siblings: OAuth2 MCP sub, cross-tenant permissions sub
- Depends on: Graph-first Memory artifacts sub-epic
- Context: #9535, Discussion #10137

Origin Session ID: 71dc3cd8-d39d-48e1-ac62-e240ca67d1a5

## Timeline

- 2026-04-21T09:28:54Z @tobiu added the `enhancement` label
- 2026-04-21T09:28:54Z @tobiu added the `ai` label
- 2026-04-21T09:28:54Z @tobiu added the `architecture` label
- 2026-04-21T09:28:54Z @tobiu added the `core` label
- 2026-04-21T09:32:44Z @tobiu added parent issue #10016
- 2026-04-21T09:33:12Z @tobiu marked this issue as blocking #10145
- 2026-04-21T09:33:13Z @tobiu marked this issue as blocking #10146
- 2026-04-21T09:33:20Z @tobiu marked this issue as blocking #10139
- 2026-04-21T14:28:32Z @tobiu cross-referenced by #10016
- 2026-04-21T14:30:43Z @tobiu assigned to @tobiu
- 2026-04-21T14:36:20Z @tobiu cross-referenced by PR #10161
- 2026-04-21T14:37:36Z @tobiu cross-referenced by PR #10162
- 2026-04-21T16:48:40Z @tobiu referenced in commit `19e8d0d` - "feat(ai): add agent identity schema and seed script (#10144)"
- 2026-04-21T16:51:49Z @tobiu closed this issue
- 2026-04-21T16:51:49Z @tobiu referenced in commit `0908866` - "feat(ai): AgentIdentity node + GitHub account binding (#10144) (#10162)

* feat(ai): add agent identity schema and seed script (#10144)

* fix(ai): address PR #10162 review feedback on MX naming and createdAt logic"
- 2026-04-21T17:03:02Z @tobiu cross-referenced by #9999
- 2026-04-21T18:43:29Z @tobiu cross-referenced by #10145
- 2026-04-21T19:06:54Z @tobiu cross-referenced by PR #10166
- 2026-04-21T20:16:31Z @tobiu referenced in commit `7db6c90` - "feat(ai): wire stdio identity + anti-spoof guard for Memory Core MCP (#10145) (#10166)

* feat(ai): wire stdio identity + anti-spoof guard for Memory Core MCP (#10145)

Brings the stdio transport to identity parity with the OIDC/SSE path shipped by
#10000. Previously stdio connections fell through to single-tenant mode
regardless of which agent was running — writes went untagged, reads went
unfiltered. With per-agent GitHub accounts (#10144 seed convention: @neo-opus-4-7,
@neo-gemini-3-1-pro, @tobiu), stdio needs the same tenant discipline SSE already has.

New shared services:
- StdioIdentityResolver: env-var (NEO_AGENT_IDENTITY) -> gh api user -> unresolved
  resolution chain. Normalizes @-prefix per GitHub API convention.
- AuthMiddleware: argument-level anti-spoof. Rejects tool-call args containing
  identity-override fields (userId, from, sender, authorLogin, etc.). No-op on
  current schemas; defense-in-depth for Mailbox (#10139) which will add `from`.

Memory Core Server.mjs:
- resolveStdioIdentity() composes resolver + AgentIdentity graph-node binding
- bindAgentIdentity(userId) shared helper; stdio + SSE both consume it
- buildRequestContext(reqAuth) hook called by TransportService on SSE requests
- CallToolRequestSchema handler wraps callTool in RequestContextService.run()
  when stdio identity is resolved; skips when null (SSE already wraps per-request)

Shared services extended:
- RequestContextService: +getAgentIdentityNodeId(), +getSource() accessors;
  context shape grows agentIdentityNodeId + source fields (backward-compatible)
- AuthService: returns source: 'oidc' on the auth context
- TransportService: duck-typed server.buildRequestContext hook — servers that
  implement it (memory-core) get the graph-bound shape; others fall back to
  the minimal {userId, username} shape

Ticket body on #10145 reshaped during intake to reflect the true gap
(SSE already had OAuth via #10000, stdio was the missing piece) — cited in the
body as the "Hypothesis vs Root Cause" challenge per ticket-intake §1.7.

Design doc at learn/agentos/tooling/MemoryCoreMcpAuth.md registered in
learn/tree.json. 19 Playwright unit tests in Auth.spec.mjs cover all branches of
the resolver, middleware, and RequestContext extensions.

Epic-review by Claude Opus 4.7 posted on parent #10016 prior to intake.

* fix(ai): bump gh CLI identity resolver timeout to 5s for MCP handshake budget (#10145)

Per Gemini 3.1 Pro's Depth Floor §7.1 challenge on PR #10166: the MCP
client-side init-handshake budget is ~5s; a 3s `gh api user` timeout
risked consuming 60% of that window on stale-auth or proxied-network
environments, potentially causing client-side connection timeouts before
the server finished initAsync. Bumped to 5s — matches the MCP spec
budget. Added an inline comment capturing the rationale so future
readers don't tighten it back without understanding the trade.
Design doc troubleshooting section updated to reflect the new budget.

* fix(ai): invert gh CLI timeout to fail-fast at 1.5s (#10145)

Gemini 3.1 Pro caught a logic inversion in commit 47e209e8a: the 3s->5s
bump was the wrong direction. The MCP handshake budget (~5s) covers the
gh call PLUS ChromaDB health + SystemLifecycleService.ready +
GraphService.ready + transport connect. Matching the gh timeout to the
full budget means a hanging gh consumes 100% of the window, guaranteeing
client timeout — worse than the original 3s (60%).

Correct direction is fail-fast: a healthy gh api user resolves in
<200ms, 1.5s is generous headroom, and anything slower is likely hanging
on auth refresh or degraded network. Single-tenant fallthrough is
preferable to exhausting the handshake. Agent harnesses that care about
identity fidelity set NEO_AGENT_IDENTITY explicitly.

- StdioIdentityResolver.mjs: timeout 5000 -> 1500ms with inline comment
  correctly capturing the fail-fast logic + budget arithmetic.
- MemoryCoreMcpAuth.md: resolution-chain + troubleshooting sections
  updated to reflect the fail-fast strategy and why the budget math
  makes fail-fast the correct discipline.

Full cross-model review cycle on record: Gemini initial review surfaced
the timeout concern -> 3s->5s applied incorrect direction -> Gemini
re-review Request Changes caught the inversion -> 5s->1.5s correction
lands. pr-review §7.1 Depth Floor discipline working as designed."
- 2026-04-21T20:30:37Z @tobiu cross-referenced by #10147
- 2026-04-21T20:30:39Z @tobiu cross-referenced by #10146
- 2026-04-21T21:02:59Z @tobiu cross-referenced by #10139
- 2026-04-21T21:18:27Z @tobiu cross-referenced by PR #10167
- 2026-04-21T22:30:26Z @tobiu cross-referenced by PR #10170
- 2026-04-21T23:19:24Z @tobiu cross-referenced by #10172
- 2026-04-23T12:02:13Z @neo-opus-ada cross-referenced by #10232
- 2026-04-23T21:03:00Z @neo-opus-ada cross-referenced by #10259
- 2026-04-23T21:12:22Z @neo-opus-ada cross-referenced by PR #10262
- 2026-04-23T21:40:03Z @neo-opus-ada cross-referenced by #10017
- 2026-04-23T21:58:55Z @neo-gemini-pro cross-referenced by PR #10265
- 2026-04-23T23:21:21Z @neo-opus-ada cross-referenced by PR #10269
- 2026-05-08T16:40:48Z @neo-opus-ada cross-referenced by PR #10977
- 2026-05-25T02:11:32Z @neo-opus-ada cross-referenced by PR #11949
- 2026-06-04T19:05:03Z @neo-opus-vega cross-referenced by PR #12534
- 2026-06-04T19:24:24Z @neo-opus-vega cross-referenced by #12535

