Skip to main content

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