Skip to main content

Runtime API

Complete reference for the Ninka runtime — the API your application uses to load compiled policies and make authorization decisions. Decisions run in-process via WASM: no network, no opa binary on the request path, no cloud.

Import

import { Ninka, type AuthzInput, type DecisionLogEntry } from "ninka-authz/runtime";

Exported from ninka-authz/runtime: the class Ninka and the interfaces AuthzInput, InputValidationError, DecisionLogEntry, DecisionLogOptions, NinkaLoadOptions.

The sole runtime dependency is @open-policy-agent/opa-wasm.


class Ninka

The constructor is private — construct an instance with Ninka.load.

static load(dir?, options?)

static async load(dir = "authz/out", options: NinkaLoadOptions = {}): Promise<Ninka>

Loads every *.build.json in dir and returns a ready Ninka instance.

Fail-closed load-time integrity. load throws if:

  • dir is missing,
  • there are no policies, or
  • a WASM's SHA-256 does not match its build.json (wasm_sha256) or the manifest's rego_sha256 lineage.

Error messages point to npx ninka build. A tampered or stale authz/out/ fails to load rather than being used.

check(policy, input)

Call check through the generated lib/authz.ts boundary, passing a typed policy reference from the generated policies table — never a bare string:

import { check, policies } from "@/lib/authz";

const allowed = await check(policies.invoiceAccess, {
subject: { properties: { roles: ["accountant"], user_id: "user-123" } },
action: { name: "view" },
resource: { type: "invoice", properties: { amount: 1200, submitted_by: "user-123" } },
});

The boundary is typed as:

check<In extends AuthzInput>(policy: PolicyRef<string, In>, input: In): Promise<boolean>
  • policies.invoiceAccess (from types.gen.ts) is a generated, typed PolicyRef.
  • Its type carries the policy's required input shape (InvoiceAccessInput).
  • TypeScript checks the second argument against that type — a wrong shape or an unknown action is a compile-time error.
  • The underlying runtime id is a string, but callers pass policies.<id>, never an arbitrary string.

Returns true (allow) or false (deny). An unknown policy throws (fail-closed); deny-overrides is resolved inside the compiled policy; a DENY caused partly by missing required input fires onInputError without changing the decision; one decision-log entry is emitted when logging is on.

On the Ninka instance itself the method is check(policyId: string, input: AuthzInput): boolean (synchronous). The generated boundary adds the PolicyRef typing and awaits the memoized load, so application code sees Promise<boolean>.

get policyIds()

get policyIds(): string[]

Runtime introspection — the string ids currently loaded. This is not the type-safe application API: to make a decision, pass a typed PolicyRef from the generated policies table (see check), not a string taken from policyIds.


interface AuthzInput

The input to check.

export interface AuthzInput {
subject: { properties: Record<string, unknown> };
action: { name: string };
resource: { type: string; properties?: Record<string, unknown> };
context?: Record<string, unknown>;
}

Tegata attribute keys map onto this shape as follows:

Tegata keyInput location
actionaction.name
resource_typeresource.type
subject.*subject.properties.*
resource.*resource.properties.*
environment.*context.*
custom.*context.custom.*

The generated types.gen.ts gives you a policy-specific type that pins this shape exactly — see Generated Files and Tegata Schema.


interface NinkaLoadOptions

export interface NinkaLoadOptions {
onInputError?: (error: InputValidationError) => void; // { policyId, missing: string[] }
decisionLog?: DecisionLogOptions; // { enable?: boolean; sink?: (e) => void }
}

onInputError

Called when a DENY was caused, in part, by missing required input — this distinguishes data-caused denies from judgment-caused ones. It never changes the decision. Default: one line on stderr.

decisionLog

Off by default. When enabled, check emits OPA-Decision-Log-compatible entries.

The logged input is masked. Only attributes the vocabulary marks audit: true appear raw; every other value becomes "***". The default sink writes one JSON line to stdout.

export interface DecisionLogOptions {
enable?: boolean;
sink?: (entry: DecisionLogEntry) => void;
}

interface DecisionLogEntry

An OPA-compatible decision-log entry, with Ninka's own data under ninka.

FieldValue
decision_idUnique id for the decision.
timestampWhen the decision was made.
path"ninka/result".
resultboolean — the decision.
inputThe masked input (only audit: true attributes appear raw).
bundlesPolicy → { revision }, where revision is the tegata_hash.
labelsLog labels.
ninka{ schema: 1, policies: [{ id, verdict, missing? }] }.

interface InputValidationError

export interface InputValidationError {
policyId: string;
missing: string[];
}

Passed to onInputError: which policy denied, and which required input keys were missing.


See also