Browse pages
Access
Who may act in the product and how they reach it: actors, roles, permissions, the access each use case declares, entry points, and external systems. Actors and roles live in Intent as product decisions users see; permissions, entry points, and external systems live in Behavior as access design. The use-cases page states the use cases that carry access, and the model page states the shared record rules.
Stack: any Layer: records
Concepts#
The Permission Chain#
Access follows one chain from actor to use case: an actor holds a role at a scope, the role allows permissions, and the use case declares the grant it checks. The chain answers every access question in one place: which actor held which role at which scope, which permission the role allowed, and which use case declared it. The chain is role-based access control: permissions attach to roles, never to actors directly, and scopes bound each hold to one reach. The role lives in Intent and the permission lives in Behavior, so allows is a declared down-link from Intent to Behavior.
Actors, Roles, and Scopes#
An actor is one party that acts in the product. Its kind is person for a human acting directly, or organization for a company or group acting as one party. Software is no actor: an external system stands alone, and internal machinery is never an actor. Each actor states in belongsToTeam whether it belongs to the team building and running the product.
A role is one named set of permissions granted together. Its scope is a kebab-case level naming the reach of the grant, such as platform, tenant, or self. An actor records each grant in holds as the role with the scope at which it holds it, so the same role grants different reach in different places.
Permissions#
A permission is one allowed action. Each module keeps its permissions in modules/<m>/permissions.yaml, and each permission states in meaning what it allows in one sentence. Roles allow permissions from those lists, and use cases cite them in their grants, so the lists are the one vocabulary both sides share.
Each permission names in scope the reach at which a role grants it, using the same levels a role scope uses, such as platform, tenant, or self, or levels the project names itself. An optional group orders the permission beside its peers in role editors. The risk field marks a sensitive permission; it is the only risk field records carry. Whether a permission only reads follows from the kind of its leading verb in the use-case verb table on the use-cases page. A permission name may also open with manage, which allows every changing use case on what it names, so it never only reads.
The Access Field#
Each use case declares access with two parts: what grant the call checks and which tenant reach the call has. The door the call arrives through declares auth: how the caller proves identity. The door authenticates and the use case authorizes. The auth list on the door names one or more of session, token, device, link, app, or none. The grant on the use case is a permission such as permission/manage-copies, a self grant such as self(permission/read-loans), or the open grants anonymous and system. A self grant allows the permission only on records the caller owns. The tenant scope is own for work inside the caller tenant, cross for work reaching across tenants, public for work with no tenant boundary, or none for work that touches no tenant data. The use-cases page states the use case fields around access.
Generic Refusals#
Refusals that follow from access and input need no written result. A call without the grant ends in the derived forbidden result, and a call with input that breaks its rules ends in the derived invalid input result. Derive Generic Refusals Instead of Writing Them states the rule, and scenarios point at the derived results without anyone writing them.
Tenant Scope and Cross Tenant Access#
own keeps the call inside the caller tenant, public opens it beyond tenant boundaries, none states the call touches no tenant data, and cross reaches across tenants. A call that reads or writes another tenant carries the widest reach, and the pull request that changes it is its review.
Entitlements#
An entitlement is a grant that depends on what the caller bought or configured, such as a plan limit or a policy setting. The use case cites the setting or plan behind the grant, as Cite the Setting or Plan Behind Every Entitlement states, so a reviewer sees why one tenant is allowed what another is refused. The cited setting states its range and setter, as the use-cases page states for settings.
Hierarchies, Derived Trust, and Delegation#
Hierarchies, derived trust, and delegation are patterns built from the same access parts, not extra grant forms. A hierarchy nests roles so a senior role allows what its junior roles allow, derived trust lets one checked fact stand for another within one call, and delegation lets one actor act for another. Each pattern proves its allowed and refused calls with scenarios, as Prove Hierarchies Derived Trust and Delegation with Scenarios states. Grants that cross tenants through delegation use the tenancy extension, which states the declared grant each side carries.
Entry Points#
An entry point is one door from outside: a way people or systems reach the product. Give Each Entry Point a Kind states the kinds, and each door declares its auth. A served-content entry point carries path, contentType, cacheControl, versioning, and fulfills, so a static script or a legal text is served content rather than a use case. Use cases name the entry points that reach them in reachedThrough, so every operation shows how callers arrive. The protocol detail for devices lives in the devices extension.
External Systems#
An external system is one system outside the bounded context that the product exchanges messages with, such as a payment provider or a text gateway. It stands alone and names no actor. An integration that calls the product is an external system calling through an API entry point. It declares each operation the product may call in operations, with its direction, timeout, response, and cache behavior, as Describe Every External System Operation states, so every outside call is a declared edge. It declares in erasure how personal data it holds is erased, or why erasure there is impossible with the Intent decision that accepts the limit.
Homes and Summaries#
Each access record lives in one home, and the model page states the shared identifier and ownership rules.
| Type | Stored as | Home |
|---|---|---|
actor | page | The Intent actors home |
role | page | The Intent roles home |
permission | entry | The module permissions file |
entry-point | page | The Behavior entry points home |
external-system | page | The Behavior systems home, at product or module level |
An actor, role, or external system page carries a summary field stating what the record is for. Each module permissions file holds entries only.
Standards#
Keep Access Records in Their Homes#
ruleruleguidancelitespec/core/access#keep-access-records-in-their-homesAn access record MUST live in the home its type names.
Applies toartifact: record:actor, record:role, record:permission, record:entry-point, record:external-system
Why
One home per type tells a reader where to open a record and tells the book where to render it. A record kept anywhere else looks written while readers open the home and find nothing there.
Checka reviewer answers
Reviewed forWhich access records sit outside the homes their type names? Look at the actor, role, permission, entry point, and external system homes.
Marked guidance: a reviewer answers this question.
Name Every Actor with Its Kind#
rulerulecheckablelitespec/core/access#name-every-actor-with-its-kindAn actor MUST name its kind in kind.
Applies toartifact: record:actor
Why
The kind tells a reviewer whether a human or an organization stands behind the actor, which decides who answers for what it does. Without the kind, a reviewer assigns responsibility without knowing who stands behind the actor. Software stands behind no actor.
Example
An employee actor carries person, and the library as a lending party carries organization.
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for an actor without kind and field-invalid for a kind outside person and organization.
Marked checkable: the tool named here checks it.
Record Every Role Hold with Its Scope#
rulerulecheckablelitespec/core/access#record-every-role-hold-with-its-scopeAn actor that acts through a role MUST record the hold in holds.
Applies toartifact: record:actor
Why
The hold is the grant: without it, the actor and the role sit on separate pages with no stated connection, and no reviewer can tell what the actor is allowed to do. The scope bounds each grant, so one role grants platform reach to one actor and self reach to another.
Example
"holds": [{ "role": "role/employee", "scope": "self" }]Checkchecked by a tool
Checked bylitespec model check reports role-orphan for a role held by no actor.
Marked checkable: the tool named here checks it.
Scope Every Role#
rulerulecheckablelitespec/core/access#scope-every-roleA role MUST name its scope in scope.
Applies toartifact: record:role
Why
The scope bounds what the role reaches: the same permission allows platform-wide action in one grant and own-record action in another. Without a named scope, every grant reads as product-wide, and the narrowest grant gets the widest reach.
Example
A scope of team bounds the role to one team, while platform bounds it to the whole product.
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for a role without scope and field-invalid for a scope outside kebab-case form.
Marked checkable: the tool named here checks it.
Grant Permissions Through Roles#
rulerulecheckablelitespec/core/access#grant-permissions-through-rolesA role MUST name the permissions it allows in allows.
Applies toartifact: record:role
Why
The allows list is the middle of the chain: it joins the actors that hold the role to the use cases that cite its permissions in their grants. The role lives in Intent and the permission lives in Behavior, so allows is a declared down-link. A role that names no permission grants nothing, so every use case its holders run breaks the chain at the middle link.
Example
role/employee allows permission/submit-expense, which the submit use case cites in its grant.
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for a role with no allows entry, and permission-orphan for a permission allowed by no role.
Marked checkable: the tool named here checks it.
State Every Permission with Its Meaning#
rulerulecheckablelitespec/core/access#state-every-permission-with-its-meaningA permission MUST state what it allows in meaning.
Applies toartifact: record:permission
Why
The meaning is the one sentence a reviewer reads to decide whether the grant fits the role: a name alone carries no boundary. Two permissions with the same name pattern and no meanings drift into overlapping grants that no audit can separate.
Example
permission/submit-expense carries "Submit an expense for reimbursement."
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for a permission without meaning.
Marked checkable: the tool named here checks it.
State the Scope of Every Permission#
rulerulecheckablelitespec/core/access#state-the-scope-of-every-permissionEach permission MUST state the scope at which a role grants it.
Applies toartifact: record:permission
Why
A grant without a named reach is decided in code: each check looks up the reach beside the permission list, and code then holds a second list of permissions.
Example
permission/manage-copies carries scope: platform, the same level as the librarian role that grants it.
Checkchecked by a tool
Checked bylitespec model check fails a permission entry without scope.
Marked checkable: the tool named here checks it.
Declare Access on Every Use Case#
rulerulecheckablelitespec/core/access#declare-access-on-every-use-caseEach use case MUST declare its grant and its tenant scope in access.
Applies toartifact: record:use-case
Why
The two parts answer the access questions together: what grant the call checks and which tenant reach the call has. A use case without one part leaves that question to code, and two implementations of the same page enforce different boundaries. The door the call arrives through declares how the caller proved identity.
Example
"access": { "grant": "permission/manage-copies", "tenant": "own" }Checkchecked by a tool
Checked bylitespec model check reports record-invalid for a use case without access, grant, or tenant.
Marked checkable: the tool named here checks it.
Authenticate Every Caller Through a Declared Auth Kind#
rulerulecheckablelitespec/core/access#authenticate-every-caller-through-a-declared-auth-kindEach entry point MUST name its auth kinds from session, token, device, link, app, and none.
Applies toartifact: record:entry-point
Why
The auth kind tells a reviewer how the caller proves identity before the grant is checked: a session for a signed-in person, a device for a managed scanner, a link for a single-purpose link, an app for a calling application, a token for a bearer token, and none for open calls. Without a declared kind, open calls and authenticated calls look alike, and a reviewer cannot tell which identity the grant was checked against.
Example
An expense submission names session, a self-checkout kiosk names device, and a public catalog names none.
Checkchecked by a tool
Checked bylitespec model check reports field-invalid for an auth kind outside session, token, device, link, app, and none.
Marked checkable: the tool named here checks it.
Grant Every Use Case Through a Declared Grant#
rulerulecheckablelitespec/core/access#grant-every-use-case-through-a-declared-grantEach access declaration MUST name its grant as a permission, a self grant, anonymous, or system.
Applies toartifact: record:use-case
Why
The grant is the check the call passes: a permission for role-held action, a self grant for action on records the caller owns, anonymous for open calls, and system for work with no caller. A grant outside these four forms gives the checker no stated check to enforce, and each caller invents its own test.
Example
permission/manage-copies for staff work, self(permission/read-loans) for a reader reading their own loans, anonymous for a public catalog, and system for a self-checkout handler acting for the device.
Checkchecked by a tool
Checked bylitespec model check reports field-invalid for a grant outside permission, self, anonymous, and system, and reference-unknown for a permission no record defines.
Marked checkable: the tool named here checks it.
Remove a Target That Must Be the Caller#
ruleruleguidancelitespec/core/access#remove-a-target-that-must-be-the-callerA message whose target must be the caller MUST carry no target and act on the caller, or declare a self grant that the authorization guard enforces.
Applies toartifact: record:use-case
Why
A target that must equal the caller is a value the caller could forge: each handler would compare it with the verified identity, and each comparison is authorization logic in a handler. Carrying no target leaves nothing to forge, and the call acts on the caller from verified claims. Where the target must stay on the message, a self grant states the scope beside the use case, and the guard enforces it.
Example
"My loans" lists the signed-in reader's loans and takes no reader id.
Checka reviewer answers
Reviewed forWhich use cases with a self grant name a caller-owned target in their inputs without a self declaration the guard enforces, or compare the caller with a target in a handler? Look at the inputs and handlers beside the self grants.
Marked guidance: a reviewer answers this question.
Scope Every Grant to Its Tenant Reach#
rulerulecheckablelitespec/core/access#scope-every-grant-to-its-tenant-reachEach access declaration MUST name its tenant scope as own, cross, public, or none.
Applies toartifact: record:use-case
Why
The tenant scope bounds which tenant data the call may touch: own stays inside the caller tenant, cross reaches across tenants, public works beyond tenant boundaries, and none touches no tenant data. Without the scope, a read inside one tenant and a read across all tenants look the same, and the widest reach hides in the narrowest grant.
Example
An expense report names own, a staff search across tenants names cross, a public catalog names public, and a health check names none.
Checkchecked by a tool
Checked bylitespec model check reports field-invalid for a tenant scope outside own, cross, public, and none.
Marked checkable: the tool named here checks it.
Cite the Setting or Plan Behind Every Entitlement#
ruleruleguidancelitespec/core/access#cite-the-setting-or-plan-behind-every-entitlementA grant that depends on a setting or a plan MUST cite that setting or plan.
Applies toartifact: record:use-case
Why
An entitlement allows one tenant what another is refused, so the grant alone reads as unequal treatment. Citing the setting or plan shows the commercial or configured reason behind the difference, and gives the reviewer the range and setter to judge it against.
Example
A submit command cites setting/spending.receipt-threshold, so an expense above the line with no receipt is refused by the policy setting rather than by role.
Checka reviewer answers
Reviewed forWhich entitlement grants lack a cited setting or plan? Look at the grant beside the settings each use case cites.
Marked guidance: a reviewer answers this question.
Prove Hierarchies Derived Trust and Delegation with Scenarios#
ruleruleguidancelitespec/core/access#prove-hierarchies-derived-trust-and-delegation-with-scenariosA hierarchy, derived trust, or delegation pattern MUST prove its allowed and refused calls with scenarios.
Applies toartifact: record:use-case
Why
Patterns compose the same access parts into wider reach, so prose alone cannot show where the wider grant stops. Scenarios that walk an allowed call and a refused call pin the boundary as checked facts, and the checker confirms each one through the public boundary.
Example
A delegation story shows an assistant acting for a manager succeeding, and the same assistant acting without delegation ending in the derived forbidden result.
Checka reviewer answers
Reviewed forWhich hierarchy, trust, or delegation patterns lack an allowed and a refused scenario? Look at the scenarios on the use cases that carry the pattern.
Marked guidance: a reviewer answers this question.
Record a Delegated Grant Inside One Tenant as a Role Assignment#
ruleruleguidancelitespec/core/access#record-a-delegated-grant-inside-one-tenant-as-a-role-assignmentA delegated or temporary grant inside one tenant MUST be recorded as a role assignment with a validity window in validFrom and validUntil, held on an aggregate the access model reads and granted and revoked by its own use cases.
Applies toartifact: record:use-case, record:aggregate
Why
Inside one tenant, delegation changes who holds a role for a time, not which grant forms exist. Recording the assignment with its window on an aggregate the access model reads gives the authorization guard one stated source for the deputy, the role, and the dates, and the granting and revoking use cases make the start and the end reviewable acts. A use case acting under the assignment declares its permission as usual, so no new access form appears. The tenancy extension adds only the cross-tenant form of delegation; it adds no form inside one tenant.
Example
In Claimwick, a manager delegating team management to a deputy for 2026-12-20 to 2027-01-05 is recorded as an assignment of the manager role to the deputy with validFrom of 2026-12-20 and validUntil of 2027-01-05, granted by its own grant use case and ended by its own revoke use case. The manage use case acting under it still declares permission/manage-team.
Checka reviewer answers
Reviewed forWhich delegated or temporary grants inside one tenant lack a role assignment with validFrom and validUntil on an aggregate the access model reads, or lack their own granting and revoking use cases? Look at the assignments beside the actor holds and the use cases that act under them.
Marked guidance: a reviewer answers this question.
Grant Across Tenants Only Through Declared Delegation#
ruleruleguidancelitespec/core/access#grant-across-tenants-only-through-declared-delegationA grant that acts across tenants through delegation MUST declare its delegation grant on both sides.
Applies toartifact: any
Why
Delegation lets one actor act for another, so a grant on one side alone leaves the other side unreviewed. Declaring the grant on both sides shows who may delegate and who may be acted for, and the tenancy extension states the fields each side carries.
Example
An assistant acts for a manager only where the manager delegation grant and the assistant acting grant both name the pairing.
Checka reviewer answers
Reviewed forWhich cross tenant delegation grants lack a declared grant on both sides? Look at the delegation grants the tenancy extension declares.
Marked guidance: a reviewer answers this question.
Name Every Entry Point with Its Kind#
rulerulecheckablelitespec/core/access#name-every-entry-point-with-its-kindAn entry point MUST name its kind in kind.
Applies toartifact: record:entry-point
Why
The kind tells a reviewer how callers arrive: a browser serves people, an api serves callers over a boundary, a webhook receives inbound events, a device serves a managed scanner, a cli serves operators, an mcp channel serves a model, and served content serves a static file. Without the kind, a webhook and a page look alike.
Example
The library app carries browser, and a kiosk handshake carries device.
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for an entry point without kind and field-invalid for a kind outside browser, api, webhook, device, cli, mcp, and served-content.
Marked checkable: the tool named here checks it.
Describe Every Served Content Entry Point with Its Serving Fields#
rulerulecheckablelitespec/core/access#describe-every-served-content-entry-point-with-its-serving-fieldsA served content entry point MUST carry path, contentType, cacheControl, versioning, and fulfills.
Applies toartifact: record:entry-point
Why
Served content is a file the product hands out, not behavior it runs, so the serving fields are the whole contract: where it lives, what it is, how long it caches, how it versions, and which requirement it carries. Without them, a legal text or a static script ships as an unreviewed file with no stated requirement or freshness.
Example
A terms text carries its path, text/html, its cache window, its version rule, and the requirement it fulfills.
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for a served content entry point without path, contentType, cacheControl, versioning, or fulfills.
Marked checkable: the tool named here checks it.
Declare Every Operation on Every External System#
rulerulecheckablelitespec/core/access#declare-every-operation-on-every-external-systemAn external system MUST declare every operation the product may call in operations.
Applies toartifact: record:external-system
Why
Each operation is one edge to outside behavior the product does not control. Without the declared list, a new outside call ships as an unreviewed dependency with no stated edge to review. Each operation carries its direction, timeout, response, and cache, as Describe Every External System Operation states.
Example
A payout bank declares a settle-batch operation with its timeout, timeout handling, and response shape, which the payout effect cites as calls.
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for an external system without operations and reference-unknown for a calls edge naming no declared operation.
Marked checkable: the tool named here checks it.
Declare Erasure for Every External System#
rulerulecheckablelitespec/core/access#declare-erasure-for-every-external-systemAn external system MUST declare how personal data it holds is erased in erasure.
Applies toartifact: record:external-system
Why
Personal data sent outside stays outside unless some operation or event removes it, so the erasure declaration states the call, the integration event, or why removal is impossible. An impossible erasure cites the Intent decision that accepts the limit, so the gap reads as an accepted decision rather than a missed edge.
Example
A payout archive declares {"mode": "impossible", "reason": "settled payouts belong to the bank"} with the decision that accepts the limit, plus the sentence employees see.
Checkchecked by a tool
Checked bylitespec model check reports record-invalid for an external system without erasure and reference-unknown for an impossible erasure without an Intent decision.
Marked checkable: the tool named here checks it.
Write a Summary on Every Actor and Role Page#
rulerulecheckablelitespec/core/access#write-a-summary-on-every-actor-and-role-pageAn actor or role page MUST carry a summary field.
Applies toartifact: record:actor, record:role
Why
The summary states in plain sentences who the party is and what the grant is for, which the fields alone do not say. A reviewer meets the story first and the fields after, so the grant reads as a decision rather than a row.
Checkchecked by a tool
Checked bylitespec model check fails an actor or role page without summary.
Marked checkable: the tool named here checks it.
Write a Summary on Every External System Page#
rulerulecheckablelitespec/core/access#write-a-summary-on-every-external-system-pageAn external system page MUST carry a summary field.
Applies toartifact: record:external-system
Why
The summary states in plain sentences what the outside system does for the product, which the message lists alone do not say. A reviewer meets the relationship first and the messages after, so the dependency reads as a decision rather than a row.
Checkchecked by a tool
Checked bylitespec model check fails an external system page without summary.
Marked checkable: the tool named here checks it.
Keep Permission Lists to Entries#
ruleruleguidancelitespec/core/access#keep-permission-lists-to-entriesA module permissions file MUST hold entries only.
Applies toartifact: record:permission-list
Why
The file holds entries only: each permission is one row with its fields, and prose around them would restate the meanings the entries already carry. A file with entries only stays readable as a list even as entries grow.
Checka reviewer answers
Reviewed forWhich module permissions files carry an explanation file? Look beside each permissions file for a .md file with its name.
Marked guidance: a reviewer answers this question.
Conventions#
Use Three Conventional Scope Levels#
conventiondefaultguidancelitespec/core/access#use-three-conventional-scope-levelsUse platform, tenant, and self as role scopes.
ReplacementA project names its own scope levels in its role records.
Applies toartifact: record:role
Why
Three levels cover the grants most products need: product-wide administration, tenant-wide operation, and action on one's own records. A project with different boundaries names its own levels, and every role still carries exactly one scope.
Checka reviewer answers
Reviewed forWhich role scopes fall outside platform, tenant, and self without a recorded project convention? Look at scope on each role page.
Marked guidance: a reviewer answers this question.