Relationships
↑ child of #662#667 serve-auth: access bounded context — Grant, Group, and shared value objects
Opened by stack72 · 6/17/2026· Shipped 6/17/2026
Parent
Sub-issue of #662 (serve authentication & authorization). Layer 1, items 1 + 2.
Summary
Create the domain/access/ bounded context with the Grant and Group aggregates, their built-in models, and the shared value objects they depend on. This is the foundation of the authorization system — all other access-related work (CEL environment, decision service, CLI, enforcement) builds on top of it.
What to build
Shared value objects
These are used by both Grant and Group and should be established first:
Principal— an authenticated identity:{ kind: user | worker, id: string }. Used as grant subjects, group members, and the acting identity in access decisions.Subject— what a grant targets:{ kind: user | group | idp-group, name: string }. Distinct from Principal because grants can target IdP-asserted groups that aren't locally-managed principals.ResourceSelector— what a grant covers:{ kind: workflow | model | data | access, pattern: string }. Patterns use name-globs over scoped names (@collective/*).Action— what a grant permits:run | read | write | admin.Effect—allow | deny.GrantSource— how a grant was created:method | file | config | extension:<name>.
Grant aggregate (aggregate root, built-in model)
Schema:
id— unique identifiersubject—Subjecteffect—Effectactions—Action[]resource—ResourceSelectorcondition— optional CEL expression string (stored as string; validation against the CEL environment happens in a sibling issue)state—active | revokedsource—GrantSourcecreatedBy— principal who created the grantcreatedAt— timestamp
Model methods:
create— create a new grant, validates subject/resource shapesrevoke— transitionactive → revoked(state change, never a delete, history retained)list— query active grants, filterable by subject and resource
Group aggregate (aggregate root, built-in model)
Schema:
name— unique group identifiermembers— array of principal references (user:<sub>)createdBy— principal who created the groupcreatedAt— timestamp
Model methods:
create— create a new named groupadd-member— add a principal to the groupremove-member— remove a principal from the grouplist— list all groupsmembers— list members of a specific group
Key invariants
- Revocation is a state transition, never a delete — history is the audit trail
- Generic data writes must not create/alter access records — the built-in models own their data as
model-method(the existingOwnershipValidationErrorpattern). For access types this is security-critical and must be pinned by tests. - Resource selectors reuse the
@collective/namescoping convention from extension manifests - Groups are locally-managed — membership is stored in the model. This is distinct from IdP-asserted groups (
idp-group:<name>) whose membership comes from the provider's userinfo response and is never stored locally. The two must not be confused. - The
conditionfield is stored as a string — validation against the CEL environment happens in a separate issue (layer 1, item 3)
Scope
- Pure domain code in
src/domain/access/— no enforcement wiring, no CLI commands - No dependency on OAuth, TLS, or other auth machinery
- Fully testable in isolation
Out of scope
- AccessDecisionService (layer 1, item 4 — depends on this + CEL environment)
- Sealed CEL grant environment (layer 1, item 3)
- CLI commands (layer 2)
- Enforcement wiring (layer 5)
References
- Enrollment token model for the built-in model pattern:
src/domain/models/worker/enrollment_token_model.ts - Worker model:
src/domain/models/worker/worker_model.ts - Step lease model:
src/domain/models/worker/step_lease_model.ts - Extension collective validator for scoping patterns:
src/domain/extensions/extension_collective_validator.ts
Shipped
Click a lifecycle step above to view its details.
stack72 commented 6/17/2026, 6:30:38 PM
Merged. Access bounded context landed — Grant and Group aggregates, shared value objects (Principal, Subject, ResourceSelector, Action, Effect, GrantSource), ownership validation tests. All 52 tests passing, no regressions.
Sign in to post a ripple.