Docs

Verification ladder

"It compiles" and "it is secure" are different claims. The ladder makes the difference legible: five levels, each earned, so a project can state what it proves instead of what it hopes.

The five levels

Levels are cumulative. A level only counts when every level below it holds, so achieved is the highest rung with unbroken support underneath, and it is -1 when even L0 fails.

  • L0 typed contract - nifra check passes: the frontend and backend cannot have silently diverged.
  • L1 route assurance - every route is classified, and the enforcement a rule requires is actually installed.
  • L2 capability lockfile - declared effects match a reviewed lockfile, so a route that starts writing to the database shows up in review.
  • L3 route manifest - the committed trust artifact still matches the routes, evidence, effects, and response classification it was emitted from.
  • L4 invariant-tested - contract-derived invariants ran against an isolated executor with nothing skipped.

Run it

Shell
# What does this project prove right now?
nifra levels

# Fail CI below a level the project already reached.
nifra levels --min 2

# The same report an agent reads over MCP (nifra_levels).
nifra levels --json
TEXT
L0  typed contract        ok
L1  route assurance       ok
L2  capability lockfile   ok
L3  route manifest        FAIL  manifest drifted from routes: run `nifra manifest emit`
L4  invariant-tested      FAIL  no isolated invariant executor configured

achieved: 2

Every level that does not hold explains itself, so the output is a work list rather than a verdict. --min <n> is the CI floor: pin it to the level you have reached and the build fails when a change quietly costs you a rung.

Wiring the levels

L0 needs nothing. L1 through L4 hang off nifra.assurance.ts, and each one you configure is a level you can start claiming.

TS
// nifra.assurance.ts
// doc-check: skip - policy rules and the isolated executor are app-specific
import { defineAssuranceConfig } from "@nifrajs/core/assurance"
import { backend } from "./backend"

export default defineAssuranceConfig({
  source: backend,
  policy: { rules: [/* ... */] },
  capabilities: { lockfile: "capabilities.lock.json", policy: { /* ... */ } },
  manifest: { path: "nifra.manifest.json" },
  invariants: {
    // L4 fuzzes the contract, so it needs an app you are willing to throw hostile
    // input at. Point this at an isolated instance, never a live one.
    executor: (request) => backend.fetch(request),
  },
})

L4 requires an explicit executor. Invariant runs send deliberately hostile input, so Nifra will not guess which app to point them at; a level that fuzzed your live server by default would be a footgun, and a skipped route is reported rather than passed over in silence.

For coding agents

The nifra_levels MCP tool returns the same { achieved, levels[] } report. An agent can read what its change proved, and a level that regressed comes back with the reason it broke.