Author a Policy
This guide adds authorization for a new resource in four steps: define the Tegata, declare any new project vocabulary, compile, and use the generated policy reference.
1. Create the Tegata
Create ninka/document-access.tegata.json:
{
"tegata": "0.1",
"policy": { "id": "document-access" },
"attributes": {
"subject.user_id": "string",
"resource.owner_id": "string"
},
"rules": [
{
"id": "allow-member-access-own-document",
"effect": "allow",
"subject": { "roles": ["member"] },
"actions": ["view", "edit"],
"resource": { "type": "document" },
"relationships": [
{
"label": "ownership",
"subject_attribute": "user_id",
"op": "eq",
"resource_attribute": "owner_id"
}
]
}
]
}
The relationship compares subject.user_id with resource.owner_id. Because a relationship does not contain a literal value from which Ninka can infer a type, the example declares both attribute types explicitly.
See Tegata Schema for the complete document shape and evaluation rules.
2. Update the vocabulary when the project uses one
If ninka/vocabulary.json exists, the declared vocabulary is closed-world. A role, action, resource type, or governed attribute that is not declared there is rejected instead of silently becoming a new project term.
For this example:
{
"roles": ["member"],
"actions": ["view", "edit"],
"resource_types": ["document"],
"attributes": {
"subject.user_id": "string",
"resource.owner_id": "string"
},
"decision_log": { "unmasked": [] }
}
This makes additions such as a new role or resource type visible as an explicit vocabulary diff. See Vocabulary for the complete contract, including attribute types and Decision Log disclosure settings.
3. Compile
npx ninka-authz compile
After schema and Sekisho checks succeed, Ninka writes the source-derived artifacts under ninka/out/. When the OPA toolchain is available, it also builds the execution WASM, runs the generated validation against that build, and writes the Consumer Projection. The new policy then appears in the generated policies map.
If OPA cannot be resolved, plain compile still writes the outputs that do not require WASM and reports that the bundle build was skipped. Use ninka-authz build in environments where producing the execution bundle is mandatory.
See Generated Files for the exact outputs.
You can also run:
npx ninka-authz docs
to inspect the current policy in the read-only Authorization Reference.
4. Use the generated policy reference
No separate policy registration step is required. Once the Consumer Projection is generated, the policy is available from policies under a name derived from the id the Tegata declares — hyphens removed, camelCase:
authz.check(policies.documentAccess, input);
The generated reference binds the policy id to that policy's input contract. In TypeScript, misspelling the id or passing another policy's generated input type is caught by the generated API at compile time rather than being deferred to authorization logic you write by hand.
Your application still owns creation of the Ninka instance, construction of the concrete input, placement of the authorization check, and enforcement of the returned decision. See Runtime API.
See also
- Tegata Schema — fields and evaluation semantics.
- Vocabulary — project vocabulary and Decision Log disclosure policy.
- Review Authorization — what a human reviews before accepting the change.