Context
who_is_online (WakeSubscriptionService) reports the online roster by reading ALL AgentIdentity nodes (_listAgentIdentityNodes, ai/services/memory-core/WakeSubscriptionService.mjs:595) plus each rostered agent's most-recent AGENT_MEMORY activity (_readActivityRecency:687). #13517/#13527 bound RequestContextService for the wake-subscription path but deferred the roster's tenant-scoping — documented inline as "tracked separately" (WakeSubscriptionService.mjs:673-674, :698-699) — without filing a tracking ticket. This files it (closing that loop).
The Problem
_listAgentIdentityNodes runs a raw SELECT data FROM Nodes WHERE json_extract(data,'$.label')='AgentIdentity' with no tenant-scoping. In a MULTI-TENANT cloud (multiple tenants sharing one graph), a caller in tenant A gets tenant B's agent identities + their turn-activity (online status) — a cross-tenant identity + activity leak. NOT live in the current single-deployment swarm, where all rostered agents are same-tenant, so this is a pre-multi-tenant-cloud hardening item, not a live incident.
The nuance — why the naive per-caller RLS fix is WRONG
A per-caller user_id RLS filter at the activity read is explicitly the wrong layer (documented at :669-672): each agent's raw AGENT_MEMORY is tagged with its OWN per-agent userId, so a per-caller filter would hide same-deployment teammates from each other — the roster IS the intended visibility boundary within a tenant. The correct fix is TENANT-scoping at the roster level: _listAgentIdentityNodes scopes to the requester's tenant, so a tenant sees its own full roster but not other tenants'.
Acceptance Criteria
Out of Scope
- Single-deployment swarm behavior (all same-tenant) — unchanged.
- Per-caller RLS on the activity read — the explicitly-wrong layer (breaks same-tenant roster visibility).
Evidence
WakeSubscriptionService.mjs:595 (unscoped roster SELECT), :669-676 + :691-699 (the documented deferral). Surfaced by a memory-core graph-read RLS audit (2026-06-20); the rest of the tool-exposed graph-read surface was confirmed RLS-sound in the same pass — transitionTask is gated by an originator/assignee authz check at MailboxService.mjs:1340 before its state read, and the other raw-.prepare() reads (#exportGraph, syncWorkspace, the @protected subscription loaders) are maintenance/internal, not caller-reachable cross-tenant.
Release classification
post-release / pre-multi-tenant-cloud security hardening. Boardless.
Origin Session ID: abe80be3-6235-4a9e-99bc-b14659ba806a
Context
who_is_online(WakeSubscriptionService) reports the online roster by reading ALL AgentIdentity nodes (_listAgentIdentityNodes,ai/services/memory-core/WakeSubscriptionService.mjs:595) plus each rostered agent's most-recent AGENT_MEMORY activity (_readActivityRecency:687). #13517/#13527 boundRequestContextServicefor the wake-subscription path but deferred the roster's tenant-scoping — documented inline as "tracked separately" (WakeSubscriptionService.mjs:673-674,:698-699) — without filing a tracking ticket. This files it (closing that loop).The Problem
_listAgentIdentityNodesruns a rawSELECT data FROM Nodes WHERE json_extract(data,'$.label')='AgentIdentity'with no tenant-scoping. In a MULTI-TENANT cloud (multiple tenants sharing one graph), a caller in tenant A gets tenant B's agent identities + their turn-activity (online status) — a cross-tenant identity + activity leak. NOT live in the current single-deployment swarm, where all rostered agents are same-tenant, so this is a pre-multi-tenant-cloud hardening item, not a live incident.The nuance — why the naive per-caller RLS fix is WRONG
A per-caller
user_idRLS filter at the activity read is explicitly the wrong layer (documented at:669-672): each agent's raw AGENT_MEMORY is tagged with its OWN per-agentuserId, so a per-caller filter would hide same-deployment teammates from each other — the roster IS the intended visibility boundary within a tenant. The correct fix is TENANT-scoping at the roster level:_listAgentIdentityNodesscopes to the requester's tenant, so a tenant sees its own full roster but not other tenants'.Acceptance Criteria
_listAgentIdentityNodesscopes the AgentIdentity roster to the requester's tenant boundary (not per-caller userId).who_is_onlinedoes NOT return tenant B's AgentIdentity nodes / activity; same-tenant teammates still appear.:673-674,:698-699) updated to cite this ticket.Out of Scope
Evidence
WakeSubscriptionService.mjs:595(unscoped roster SELECT),:669-676+:691-699(the documented deferral). Surfaced by a memory-core graph-read RLS audit (2026-06-20); the rest of the tool-exposed graph-read surface was confirmed RLS-sound in the same pass —transitionTaskis gated by an originator/assignee authz check atMailboxService.mjs:1340before its state read, and the other raw-.prepare()reads (#exportGraph,syncWorkspace, the@protectedsubscription loaders) are maintenance/internal, not caller-reachable cross-tenant.Release classification
post-release / pre-multi-tenant-cloud security hardening. Boardless.
Origin Session ID: abe80be3-6235-4a9e-99bc-b14659ba806a