Manifests
Write zaccess.yaml, the product manifest and the resource manifests, and keep the generated files in step.
A product has one zaccess.yaml, one product manifest and one resource manifest per area. zaccess gen compiles them, together with the built-in access.* manifest, into everything the runtime needs. Every field is in the manifest reference.
zaccess.yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/zynactra/access/main/spec/zaccess.schema.json
product: ./access/access.yaml # exactly one product manifest
resources:
- ./access/plugins/*/access.yaml # globs; one manifest per area
orgRules:
- ./access/orgs/*.yaml # optional; may match nothing
out:
policies: ./access/generated/policies # what Cerbos serves
manifest: ./access/generated/access.manifest.json
ts: ./src/access/generated.ts # optional: P constants and the manifest for TS
go: { file: ./access/generated/accessgen/access.go, package: accessgen } # optional
tests: ./policy-tests # Cerbos test suitesThe yaml-language-server comment gives editors completion and validation from spec/zaccess.schema.json (and spec/manifest.schema.json for manifests). zaccess check is the source of truth.
The product manifest
Scope types and system roles: the parts every area shares.
product: leave-demo
scopeTypes:
team:
kind: prefix
principalAttr: reportingPath
resourceAttr: ownerPath
excludeSelf: true
org_unit:
kind: set
resourceAttr: orgUnitId
expand: true
systemRoles:
employee:
name: Employee
description: Every member of staff.
permissions:
leave.request:create: self
leave.request:read: self
line_manager:
name: Line manager
description: Assign with scope team.
permissions:
leave.request:read: inherit
leave.request:approve: inherit
hr_officer:
name: HR officer
description: Assign with scope org_unit.
permissions:
leave.request:read: inherit
leave.request:approve: inherit
access.assignment:read: global
access.assignment:manage: globalName roles after jobs people recognise, and say in the description which scope to assign them with. A system role may include access.* permissions, as HR does above, so delegated administration needs no custom roles.
Resource manifests
One per area of the product, owned by the team that owns the area:
source: leave-demo/leave
resources:
leave.request:
attributes:
ownerId: string
ownerPath: string
orgUnitId: string
status: { enum: [pending, approved, rejected] }
days: number
actions:
create: { scopes: [self, global], description: Request leave }
read: { scopes: [self, team, org_unit, global], description: View leave requests }
approve:
scopes: [team, org_unit, global]
description: Approve or reject leave
condition: R.attr.status == "pending" && R.attr.ownerId != P.id
read:reason:
scopes: [self, team, global]
description: See the reason given for leave
sensitive: trueDescriptions are what admins read in the role editor; write them for people. Permissions covers how to model attributes, actions and conditions.
What zaccess check enforces
- Every action has a
descriptionand at least one scope; the scopes exist; a scope's resource attribute is declared on the kind;selfandexcludeSelfneedownerId. - Attribute types are
string,numberorboolean;enumonly on strings. - Conditions use only
R.attr.<declared>,P.id,P.attr.orgIdand scope principal attributes, neverP.attr.perms, with balanced parentheses. - Kinds are unique across manifests and never
access.*; system role keys are unique;access_admincan't be redeclared. - Every role permission exists, and its mode is one of the scopes the action allows.
What gets generated
| Output | Used by |
|---|---|
policies/leave_request.yaml: one resource policy per kind | Cerbos |
policies/orgs/<org>/employee.yaml: org rules as scoped policies | Cerbos |
policies/_schemas/: a JSON schema per kind and for the principal | Cerbos (enforced with reject) |
access.manifest.json | both SDKs, at startup |
ts / go constants and the embedded manifest | product code |
<tests>/<kind>_test.yaml, once per new kind | you: the scaffold is yours to fill in |
Generated files are committed, never edited. CI runs zaccess check -verify, which fails when they're stale.
The change workflow
- Edit the manifest.
zaccess gen.- Update the policy tests,
zaccess test. - Update the code: the three checks for new actions, the resource function for new attributes, the SQL column map for list filters.
- Deploy. Startup
SyncCatalog(orzaccess syncin the deploy) brings the catalog and system roles in line: new permissions appear in the role editor, removed ones are deprecated, a removed system role becomes an ordinary role admins can delete.
Deploy the generated policies with the code that needs them: a service that checks a permission Cerbos doesn't know is denied.