decern decern

Authorization kernel · Pure Rust


decern

Decide what an agent, a person or a service is allowed to do. Keep proof of every answer.

Two things set it apart. A solver checks the safety rules against every possible input, instead of the handful a test file covers. And every answer goes into a signed, linked log, so anyone can check later that nothing was changed or removed — including people who don't trust whoever ran the server.

Contribute on GitHub Where to start Install and try it

The problem


Everyone agrees how to write permissions down. Nobody guarantees they hold.

Tokens and delegation formats settle how permission is described. Whether it holds — that passed-on authority can only get narrower, that nothing vanished from the log, that an answer stayed inside its mandate — is left to whoever builds the system.

decern fills that gap. People, AI agents and services are one kind of principal here, answered by the same proven function.

What makes it different


Prove the rules. Record the answer.

01 · Proven, not tested

A solver checks every case, not a few

An answer depends on four things only: who is asking, what authority exists, the policy, and the time. Because it is that contained, cvc5 can search every possible input for one that breaks a rule. Nine rules are checked this way. Green means it found nothing.


decern-proof · runs in decern prove and CI only, never on the request path

02 · Recorded, not trusted

A log that shows if it was touched

Each answer is added to the end of the log, signed, and linked to the one before it. The signature shows a record is genuine; the links show none went missing. A log torn by a crash repairs itself; history cut short is caught. You check the file yourself, offline.


decern-ledger · Merkle · JCS canonicalisation · anchors

Open call


The core stays small. Everything around it is open work.

A proven core is only useful once real systems can reach it — clients, gateways, log backends, policy packs. Those sit outside the kernel on purpose, and they are open.

People and agents are both welcome, on the same terms. Every change goes through one gate: ./scripts/verify.sh.

Area 01

A new client SDK

Mirror the Python or TypeScript client against the AuthZEN surface in decern-server. Any language.

Area 02

An enforcement adapter

Put decern behind an HTTP or gRPC gateway — a thin client over the server.

Area 03

Authority-graph tooling

Traversal, revocation blast-radius and graph export over the kernel's directory.

Area 04

A ledger backend

Implement the head-store trait in decern-store for your substrate.

Area 05

A new proven property

Add the invariant to decern-proof — and the negative control that proves it load-bearing.

Area 06

Model and policy packs

Cedar policy, schema and entities for a domain, contributed to crates/decern-kernel/model/.

Getting a change in

Open an issue first for anything big or design-changing — a short conversation saves a wasted PR. For a small, obvious fix, just send the PR. Agent-written PRs are welcome on the same terms; agents start at AGENTS.md, with the working method and standards in .agent/.

The one gate

build · test · cvc5 proofs
clippy · fmt · cargo-deny
standards guard


Run it before you open a PR and review holds no surprises.

Rules


Nine rules, checked against every possible input.

Each line says exactly what the solver checked — never more.

01 money-gate No privileged money action without explicit approval.
02 isolation No decision ever crosses a tenant boundary.
03 decay No decision once authority has expired.
04 attenuation-edge No access without ownership, a delegation ancestor, or an explicit grant.
05 scope-gate Bounded actions require their named scope.
06 revocation-gate A revoked principal is allowed nothing.
07 residency-gate Residency-labelled resources need matching clearance.
08 role-gate Role-required resources need the role.
09 consent-gate Non-owned PII needs explicit consent.

Negative controls

Every rule also ships with its opposite: a test that takes the guarantee out of the policy and insists cvc5 does find a counterexample. If that test ever passed quietly, the proof was proving nothing.

What it does not prove

cvc5 proves the kernel's rules. It cannot tell whether your policy says what you meant, and it is never a claim that a deployment is secure — the HTTP endpoints are unauthenticated by design, meant to run behind an authenticating proxy. Where a rule leans on a derived value, that part is ordinary tested Rust and is written up as such. The full boundary lives in the repository, under known limitations ↗.

One request, start to finish


Decide. Record. Or refuse.

The answer is handed back only after its record is safely written. If it cannot be recorded you get 503 — never a bare allow.

01

Ask

A request arrives at decern-serve.

02

Evaluate

The proven kernel answers, using the authority graph. The clock comes from the server, never the request.

03

Record

The answer, plus who is accountable for it, is appended and signed.

04

Serve or 503

Sent back only if that record was written. Otherwise refused.

05

Verify

Anyone can re-check the links and signatures later, offline.

decern decision flow: a request is evaluated by the proven kernel, recorded to the tamper-evident ledger, and served with 200 only when the record was written, else 503

Who is answerable


A record names an accountable owner: the top of the asker's delegation chain, worked out by the server from its own directory — never taken from the request. A stand-in's record names whoever is finally answerable for it; a top-level principal vouches for itself; a caller the directory does not know gets none.

It is there for accountability, not as a gate. It never changes the answer.

The ledger


Change one byte and the log says so.

Alter record 02 below to see what decern verify reports.

/tmp/decern.jsonl
Seq Decision Recorded Prev Chain
0001 allow corp · Read · claim1 genesis intact
0002 deny agent-7 · Transfer · account9 9f3c…a1 intact
0002 allow agent-7 · Transfer · account9 9f3c…a1 mismatch
0003 allow corp · Read · claim2 41ba…7d intact orphaned

$ decern verify --ledger /tmp/decern.jsonl
chain intact · 3 records · 3/3 signatures valid · exit 0

$ decern verify --ledger /tmp/decern.jsonl
chain broken at seq 0002 · hash mismatch · later records orphaned · exit 1

Illustration rendered in the browser — hashes abbreviated, no signing key involved. examples/quickstart.sh runs the real loop: prove, serve, decide, verify, and tamper-is-rejected.

An actual record seq 0 · Ed25519-signed · hash-chained · from a live run
{
  "entry": {
    "seq": 0, "ts_ms": 1785682110000,
    "subject_type": "Principal", "subject_id": "corp",
    "action": "Read",
    "resource_type": "Resource", "resource_id": "claim1",
    "context": { "now": 1785682110 },
    "decision": true, "reasons": ["policy0"],
    "sponsor": { "kind": "Principal", "id": "corp" }
  },
  "prev":    "0000000000000000000000000000000000000000000000000000000000000000",
  "hash":    "8f658ccb5595b7e85a9f020f6a128985929865558c642505e206134337e40e41",
  "sig_b64": "I0DGsmUoYdsk58/yWZCoRJh0zij7pvcUbb9TsJSF8UOfnxmQqWRI+6ultRAXeNBv2Bbz8upBalMg7VwSsTPOAQ==",
  "kid":     "d9396c76113e7aa7126b8358063331f9749ece673ddfdbe8b29661bf03714372"
}

$ decern verify --ledger /tmp/decern.jsonl --pubkey d9396c76…
OK · 3 entries · root 0b8a4b21…7718 · signatures verified

Missions


These tools, until this time — and never wider than the approver.

Someone with authority hands an agent a narrow slice of it: named tools, a deadline. Ask for a tool the approver does not hold, or a deadline past their own, and the Mission is refused — with nothing recorded.

Every accepted step is written to the same log before it is reported as done, and a Mission that has ended never comes back. Its reference is derived from the approval itself, so re-approving an identical dead grant is refused — a new one has to differ in at least one field.

Registry is durable and local — --missions <PATH>, default decern-missions.json beside the ledger. Consulted in-perimeter, no phone-home.

Mission lifecycle

POST /mission/v1/approve
{approver, agent, description, approved_tools, capabilities?, expiry}
→ {approver, s256, reference}
GET /mission/v1/{s256}
→ {reference, state: active | terminated, expiry}
POST /mission/v1/{s256}/terminate
→ {reference, state: terminated}

What it is made of


Two binaries over seven small crates.

The default build pulls no TLS stack, OpenSSL or cmake — though cedar's stacker/psm compiles one small assembly routine via cc. The Postgres head store adds a TLS provider, so it sits behind --features postgres, and is off by default.

decern architecture: the decern and decern-serve binaries over the kernel, identity, proof, ledger, store, and crypto crates
Crate topology, as shipped in the repository.
decern-kernel lib The deterministic decision function — Cedar authority graph, the directory, the check itself. The security core.
decern-proof lib The SMT harness — compiles the Cedar model symbolically and discharges the nine invariants with cvc5, each with a negative control.
decern-ledger lib The tamper-evident record — append-only, Ed25519-signed, hash-chained; Merkle, JCS canonicalisation, anchors, single-file and sharded.
decern-store lib Persistence traits and reference implementations — the single-host file head store and the durable Mission registry.
decern-store-postgres lib · opt Multi-host head store over Postgres advisory locks — the same trait, behind a feature flag.
decern-identity lib The Mission core — approval-backed, fail-closed-attenuated authority.
decern-crypto lib Ed25519 and SHA-256 primitives shared by the ledger and identity.
decern-cli → decern bin prove · decide · verify.
decern-server → decern-serve bin The fail-closed PDP — evaluate, record, serve — and the Mission lifecycle over decern-identity.

HTTP surface

POST /access/v1/evaluation
POST /decide (alias)
GET  /pubkey
GET  /healthz

Request {subject, action:{name}, resource, context}; response {decision}, with reasons or errors under context.

Clients

Python and TypeScript SDKs ship in the repository, written against the AuthZEN surface. New SDKs and enforcement adapters are the easiest way in: mirror an existing client, or put a thin gateway in front of the server.


crates/decern-kernel/model/ holds the Cedar policy, schema and entities the kernel loads.

Start


Prove it, run it, verify it.

Two binaries: decern to prove and verify, decern-serve to answer requests. You only need cvc5 on PATH for the proofs — not to serve answers.

Prebuilt binaries

Prebuilt for Linux (x64/arm64), macOS (Apple Silicon) and Windows (x64) — grab the latest archive on GitHub, unpack, and put both binaries on your PATH.

Get it on GitHub ↗

From source

cargo install decern-cli decern-server

Or clone and build with the pinned toolchain from rust-toolchain.toml.

# 1 · prove all invariants hold over every input (cvc5)
decern prove
# 2 · run the PDP (writes a tamper-evident ledger)
decern-serve --ledger /tmp/decern.jsonl &
# 3 · decide over HTTP (AuthZEN-shaped) — corp reads a claim it owns
curl -s localhost:8080/access/v1/evaluation -H 'content-type: application/json' -d '{
  "subject":  {"type":"Principal","id":"corp"},
  "action":   {"name":"Read"},
  "resource": {"type":"Resource","id":"claim1"}
}'
# 4 · verify the ledger (hash chain + every signature)
decern verify --ledger /tmp/decern.jsonl \
  --pubkey "$(curl -s localhost:8080/pubkey | jq -r .kid)"

Sovereign · one process

--ledger <PATH>

One ledger file. Binds loopback by default; a non-loopback address logs a startup warning.

Hosted · one host

--sharded <DIR>

Several server processes share one ledger — one hash chain per tenant — coordinated by a file lock. Unix only.

Hosted · many hosts

--sharded postgres://…

Advisory-lock head store for replicas across hosts. Build with --features postgres; the URL is never logged.

--ledger and --sharded are mutually exclusive. Audit a sharded deployment with decern verify --sharded <dir> --pubkey <kid> — it checks every shard and exits non-zero if any fails.

Client SDKs

Call a running decern-serve from an application with a thin AuthZEN client — uv add decern (Python) or npm install decern (TypeScript).

Who runs this


Maintained in the open, with the roadmap in the open too.

What is next

Tie an answer to the Mission that justified it, so a recorded allow names the approved task. Check a log against an outside anchor from the CLI. Record who a decision was about, separately from who asked for it. Wider AuthZEN coverage.

ROADMAP.md

Maintained by

AA

Anivar A. Aravind

anivar.net ↗

Review and merge rules are written down in GOVERNANCE.md; disclosure in SECURITY.md.