Cross-Forge Authorization Conventions¶
SystemForge is the authorization substrate for every Forge application
(ActionForge, AgentForge, DashForge, and future apps). This document is the
normative contract an application implements so that a composed
deployment has one identity, one role model, one token-scope convention, and
one ReBAC schema. The programmatic form of this contract is
authz.AppVocabulary; registering a vocabulary validates it against these
rules (authz.ValidateVocabulary) and is what makes an app administrable in
the IAM console.
The two-gate model¶
Every API operation is guarded by two independent checks, and both must pass:
- Role gate — the principal's role in the organization must grant the operation's permission: may this person ever do this?
- Scope gate — the presenting credential's OAuth scopes must include the operation's scope: may this credential do it right now?
Effective access is the intersection (the standard OAuth resource-server
model): an org admin using a token scoped to actionforge:runs:read is
read-only on that token.
Naming¶
| Surface | Convention | Example |
|---|---|---|
| App identifier | short lowercase, ^[a-z][a-z0-9]{1,31}$ |
actionforge |
| Resource type | {app}.{resource} |
actionforge.run |
| Permission | dotted {resource}.{verb} |
run.approve |
| OAuth scope | {app}:{resource}:{verb} |
actionforge:runs:approve |
| Blanket scope | {app}:admin implies all app scopes |
dashforge:admin |
| SpiceDB definition | {app}_{name} |
actionforge_workflow |
Scopes are namespaced per app so a host SaaS can issue a token for exactly the embedded capability it exposes. A scope never crosses apps; grant multiple scopes instead.
Roles¶
The platform ladder is owner(100) / admin(80) / editor(60) / member(40) /
viewer(20) — reuse it wherever it fits. Apps may add domain roles where
the domain demands them, slotted into the hierarchy; every role must appear
in both Roles() and Hierarchy().
Domain roles exist for separation of duties, and each app must document its pairs. The canonical example is ActionForge's approval gate:
editorcan change and trigger workflows but cannot approve gates;approver(50)can approve gates but cannot edit or trigger;- only
admin/ownerhold both sides.
The same separation applies at the scope level: actionforge:runs:write
deliberately does not imply actionforge:runs:approve, so a CI bot can
trigger pipelines but can never release a production gate.
SpiceDB: base + facets¶
SystemForge owns the base definitions (authz.BaseSpiceDBSchema):
principal, organization (membership ladder: manage/edit/contribute/
view plus org administration), and platform. Apps never redefine
these.
An app attaches through a facet: an {app}_org definition that references
the shared organization and adds the app's domain-role relations:
definition actionforge_org {
relation org: organization
relation approver: principal
relation developer: principal
permission workflow_read = org->view
permission workflow_write = org->edit
permission workflow_trigger = org->edit + developer
permission run_approve = org->manage + approver
}
definition actionforge_workflow {
relation app_org: actionforge_org
permission read = app_org->workflow_read
permission write = app_org->workflow_write
permission trigger = app_org->workflow_trigger
}
Generic membership flows through the arrows (org->view); domain roles are
relations on the facet. Every definition an app contributes must carry the
{app}_ prefix — validated at registration, and re-checked by
VocabularyRegistry.ComposeSpiceDBSchema, which assembles base + all
registered fragments into the deployment schema.
An app that only supports the simple role-hierarchy provider returns an
empty SpiceDBSchema(); both providers must express the same grants, and
each app should carry a consistency test asserting so.
Conformance checklist (per app)¶
- Implement
authz.AppVocabulary; register it at startup. - Scopes pass
authz.ValidateScope; schema passesauthz.ValidateAppSchema(enforced byRegister). - Simple-provider grants and the SpiceDB fragment express identical rules (app-local consistency test).
- Separation-of-duties pairs documented in the app's authz package.
- API routes declare (permission, scope) pairs and enforce the two-gate model.
Adoption status: ActionForge (actionforge/authz, aligning to the facet
pattern), DashForge (schema re-namespace pending), AgentForge (vocabulary
pending).