LearnNewsExamplesServices
Frontmatter
id10146
titleCross-tenant permission edges + multi-tenant validation test suite
stateClosed
labels
enhancementaitestingarchitecturecore
assigneesneo-gemini-pro
createdAtApr 21, 2026, 11:29 AM
updatedAtMay 20, 2026, 11:01 PM
githubUrlhttps://github.com/neomjs/neo/issues/10146
authortobiu
commentsCount0
parentIssue10016
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[x] 10145 OAuth2 authentication layer for Memory Core MCP connections, [x] 10144 AgentIdentity node type + GitHub account binding for model identities
blocking[ ] 10139 Extend Memory Core with Explicit A2A Primitive
closedAtApr 21, 2026, 11:25 PM

Cross-tenant permission edges + multi-tenant validation test suite

Closed v13.0.0/archive-v13-0-0-chunk-5 enhancementaitestingarchitecturecore
tobiu
tobiu commented on Apr 21, 2026, 11:29 AM

Cross-tenant permission edges + multi-tenant validation test suite

Context

Once OAuth2 MC auth lands and AgentIdentity nodes populate, multi-tenant boundaries become enforceable. This ticket adds the cross-tenant permission edge type + the test suite validating isolation + reach. The test suite is load-bearing for #9999 — production-shape validation, with mailbox as workload.

Scope-rebalance note (2026-04-21, session d69ac7a0-9fe8-4416-b766-cd9edb8bee71): relocated AC #5 from #10147 ("Permission check integration: caller-identity = SENT_BY on write; read scoped to tenant + permission edges") into this ticket. Rationale: #10147 ships the Message schema + 4 core tools with server-stamped SENT_BY but without cross-tenant read-scoping via permission edges. This ticket adds the CAN_READ_* / CAN_REPLY_TO edge types and wires them into mailbox read paths atop the already-landed primitives. Splitting this way avoids circular PR dependency and addresses the Stage 3 scope-overlap concern I flagged in my epic-review on #10016 ("#10146 bundles permission-edge implementation and cross-cutting test suite — bundling risks weak coverage for whichever half loses the scope-contest"). The split now makes the two concerns symmetrical: #10147 = mailbox primitives, #10146 = tenant-boundary enforcement on those primitives.

The Problem

Tenancy without explicit grants: either strict isolation (no A2A cooperation) or implicit trust via shared tenancy. Both break MX at swarm scale — multi-agent A2A needs selective cross-tenant reach (Opus-4-7 reading Gemini-3-1-pro's mailbox) by explicit consent only.

The Architectural Reality

  • AgentIdentity nodes exist (#10144 ✓ shipped).
  • OAuth2 authenticates caller + stdio env-var identity flows via RequestContextService (#10145 ✓ shipped).
  • Mailbox primitives (Message node + 4 MCP tools) land in #10147 — the workload this ticket's permission-edge-check middleware protects.
  • Graph expresses permissions as first-class edges; no substrate change needed.
  • Mailbox (#10139) is the highest-volume workload exercising cross-tenant boundaries — validates the pattern in production shape.

The Fix

  • Edge types: CAN_READ_INBOX_OF, CAN_READ_MEMORIES_OF, CAN_READ_SESSIONS_OF, CAN_REPLY_TO — all AgentIdentity_A → AgentIdentity_B
  • Read operations check caller → target permission edges; reject when absent
  • Mailbox integration (relocated from #10147 AC #5):
    • listMessages / getMessage on inbox where SENT_TO = caller → always allowed (own data)
    • listMessages / getMessage on inbox where SENT_TO = other → requires CAN_READ_INBOX_OF edge from caller to target
    • addMessage with to = other-identity → requires CAN_REPLY_TO edge if the addressee is not already a reachable counterparty
  • Admin tools: grantPermission(from, to, scope), revokePermission, listPermissions(for: identity)
  • Playwright suite in test/playwright/unit/ai/mcp/server/memory-core/:
    • Tenant isolation: A can't see B's inbox without permission
    • Grant + reach: A grants B; B sees A's inbox
    • Revoke: A revokes B; reads return empty
    • Tenant-scoped search: listMessages({to: me}) scopes to caller
    • Spoof-rejection: call with from: B by identity A fails auth (already landed in #10145's AuthMiddleware; this suite confirms integration)

Acceptance Criteria

  • Permission edge types in graph schema (CAN_READ_INBOX_OF, CAN_READ_MEMORIES_OF, CAN_READ_SESSIONS_OF, CAN_REPLY_TO)
  • Permission-check middleware on MC MCP reads — integrates with the existing AuthMiddleware (#10145) + RequestContextService (#10000 + #10145) stack
  • Grant/revoke/list permission tools exposed via MCP
  • Permission-check integration on mailbox primitives (relocated from #10147 AC #5): caller-identity always matches SENT_BY on write; read paths scope to tenant + check permission edges for cross-tenant reach
  • Playwright suite covering the 5 scenarios above
  • Mailbox work (#10139 subs) integrates with permission checks as default read-path — exercised end-to-end via the suite
  • Design doc: default permission policy (strict-isolate default, opt-in visibility) made explicit; lives in learn/agentos/ or extends MemoryCoreMcpAuth.md

Out of Scope

  • UI for permission management
  • Automatic permission inference ("same-epic auto-grant") — rejected as implicit-trust reintroduction
  • Message schema + core mailbox tools — scope of #10147 (already shipped as prerequisite)

Avoided Traps

  • Synthetic multi-tenant test data. Rejected. Mailbox workload provides production-shape substrate; synthetic never exercises real OAuth + permission + cross-tenant paths under real shape.
  • Implicit tenant-join rules. Rejected. Permissions are explicit grants only.
  • Permission check at the service-method layer. Rejected — put it at the callTool dispatch path alongside AuthMiddleware (shipped in #10145) so the permission model and the anti-spoof model share a single integration point.

Related

  • Parent: #10016
  • Blocks: #10139 mailbox cross-tenant scenarios
  • Depends on: #10144 ✓ (AgentIdentity nodes), #10145 ✓ (OAuth/anti-spoof substrate), #10147 (mailbox primitives — Phase 3 sibling, lands before this)
  • Files: graph schema, MC MCP permission middleware, test/playwright/unit/ai/mcp/server/memory-core/

Origin Session ID: 71dc3cd8-d39d-48e1-ac62-e240ca67d1a5 (ticket creation) Reshape Session ID: d69ac7a0-9fe8-4416-b766-cd9edb8bee71 (scope rebalance 2026-04-21, Claude Opus 4.7 / Claude Code)

tobiu added the enhancement label on Apr 21, 2026, 11:29 AM
tobiu added the ai label on Apr 21, 2026, 11:29 AM
tobiu added the testing label on Apr 21, 2026, 11:29 AM
tobiu added the architecture label on Apr 21, 2026, 11:29 AM
tobiu added the core label on Apr 21, 2026, 11:29 AM
tobiu added parent issue #10016 on Apr 21, 2026, 11:32 AM
tobiu marked this issue as being blocked by #10144 on Apr 21, 2026, 11:33 AM
tobiu marked this issue as being blocked by #10145 on Apr 21, 2026, 11:33 AM
tobiu marked this issue as blocking #10139 on Apr 21, 2026, 11:33 AM
tobiu cross-referenced by #10016 on Apr 21, 2026, 4:28 PM
tobiu cross-referenced by PR #10162 on Apr 21, 2026, 4:54 PM
tobiu cross-referenced by #9999 on Apr 21, 2026, 7:03 PM
tobiu cross-referenced by #10145 on Apr 21, 2026, 8:43 PM
tobiu cross-referenced by PR #10166 on Apr 21, 2026, 9:06 PM
tobiu cross-referenced by #10139 on Apr 21, 2026, 10:27 PM
tobiu cross-referenced by #10147 on Apr 21, 2026, 10:30 PM
tobiu cross-referenced by PR #10167 on Apr 21, 2026, 10:55 PM
tobiu referenced in commit 5c5f6c8 - "docs: convert ascii architecture diagram to mermaid (#10146)" on Apr 21, 2026, 10:58 PM
tobiu cross-referenced by #10168 on Apr 21, 2026, 11:24 PM
tobiu cross-referenced by #10169 on Apr 21, 2026, 11:24 PM
tobiu referenced in commit 43d950e - "feat(ai): multi-tenant mailbox permissions and validation (#10146) (#10147) (#10167) on Apr 21, 2026, 11:25 PM
tobiu closed this issue on Apr 21, 2026, 11:25 PM
tobiu assigned to @neo-gemini-pro on May 20, 2026, 11:01 PM