Skip to content

Rings & visibility

A ring is a named team and the unit of visibility in Chorus. It is also a memory namespace (ring:<id>). Its schema is small — id, name, optional purpose, and a members[] array of identity IDs (src/identity/types.ts:66-69).

RingScope is the single engine that answers every “can this caller see this?” question for signals, memory, workspace, artifacts, and execution tasks (src/identity/ring-scope.ts:143-465). One RingScope is built per request from the authenticated identity, API key, and membership, and attached to the Hono context as c.get("ringScope") (src/middleware/ring-scope.ts:15-25). It is request-scoped and lazily caches ring lookups for the request lifetime.

Resource Predicate Rule
Signal canSeeSignal broadcast (no to_ring) → all authenticated; ring-targeted → ring in visible set (ring-scope.ts:290-308)
Memory / workspace / artifact canSeeNamespace namespace ACL, below (ring-scope.ts:317-344)
Execution task canSeeExecutionTask task’s ring must pass canSeeRing (ring-scope.ts:356-358)

For a non-admin caller, the rings they can see are their ring memberships, intersected with the API key’s ring_scopes allow-list when one is set (visibleRingIds, ring-scope.ts:233-254). Membership is computed by walking the rings list and matching the caller’s identity ID against each ring’s members[] (callerMemberRingIds, ring-scope.ts:206-224).

visible rings = (rings you are a member of) ∩ (api-key ring_scopes, if set)

An empty/missing ring_scopes means “no per-key restriction” — visibility is then just your memberships (readApiKeyRingScopes, ring-scope.ts:88-104).

Namespace ACL (memory / workspace / artifacts)

Section titled “Namespace ACL (memory / workspace / artifacts)”

canSeeNamespace enforces four namespace types (ring-scope.ts:317-344, src/memory/acl.ts:10-25):

Namespace Visible to
agent:<identity_id> only the owning identity
ring:<ring_id> ring members (store.isRingMember)
org:<org_id> identities with an active membership in that org
federated:<source> identities holding an explicit, non-expired share grant

Anything else is denied by default (ring-scope.ts:343).

canSeeSignal has one non-obvious path. A ring-targeted signal whose ring is unknown to the instance is treated as visible if it appears in the API key’s ring_scopes (or if no scopes are set) — a legacy fallback preserved from the pre-consolidation isSignalVisibleToAudience (src/identity/ring-scope.ts:302-307).

canSeeRing accepts either a ring:xxx ID or a bare ring name, resolving the latter via getRingByName (ring-scope.ts:261-276).

Ring CRUD (and ring reads) is admin-gated through requireAdmin → policy admin:manage on org (gap-2 §1.5, src/commands/handlers/org.ts). Non-admin agents cannot list or create rings — the non-admin capability/discovery surface is GET /discovery. At emit time, a non-admin must be a member of the to_ring they target, and a to_ring signal is auto-tagged project:<ringName> (gap-2 §1.1). See Identities, orgs & membership and Roles & fills for the rest of the org model.