Skip to content

Getting started with NaZelo Trust

A guided first success. You will install the stack, run one sealed command, then run a BIM-governed command that shows an ALLOW and a DENY, and finally verify the sealed chain offline.

Each step ends where nazelo-trust doctor says the next capability is READY. Full setup detail is in INSTALL.md.

1. Install

pip install nazelo-trust        # pulls in nazelo, pimatika, pyyaml, click
nazelo-trust doctor             # → Isolate READY

For sealing you also need the litatoli binary on PATH (or point $NAZELO_TRUST_LITATOLI_BINARY at it) and its keys:

litatoli keygen                 # writes ~/.config/litatoli/{signing.key,ed25519.key}
nazelo-trust doctor             # → Seal READY

doctor reports the capabilities in order — isolate → seal → egress → three bricks — and tells you exactly what each remaining one needs. For egress enforcement and the BIM path, follow the eBPF and pimatika-cli steps in INSTALL.md.

2. A sealed run

# Isolated + sealed, no network (the default policy is deny-all):
nazelo-trust run -- pytest -q
# run_id: 9f2c… · exit=0 · no egress (deny-all) · sealed → …/chain.jsonl

NaZelo isolates the command, Litatoli seals the run into a signed chain, and egress is deny-all by default. If the command passes but the evidence cannot be sealed, NaZelo Trust reports NOT SEALED — never a green pass.

To allow specific egress without a BIM, --allow rules are enforced by NaZelo's own eBPF filter at the IP/port level. This turns on eBPF enforcement and requires the eBPF tooling; NaZelo Trust refuses to run an egress-granting workload it cannot enforce:

nazelo-trust run --allow api.github.com --allow port:443 -- ./deploy.sh

3. A BIM-governed run — ALLOW and DENY

There are two ways to run.

  • Without a BIM, --allow rules are enforced by NaZelo's eBPF filter at the IP/port level — isolation and sealing, no policy engine.
  • With a signed BIM, PimaTika loads and verifies it and decides each egress semantically (target, purpose, data_class, method, payload), sealing an EGRESS_ALLOWED / EGRESS_DENIED with a reason the packet layer could never give (PURPOSE_MISMATCH, TARGET_NOT_IN_USAGE_CONTRACTS, …). The allowed targets are then projected into NaZelo's eBPF filter. PimaTika decides, NaZelo enforces, Litatoli seals — one run_id, one chain, one bim_digest.
nazelo-trust run --bim policy.signed.yaml --probe https://1.1.1.1 -- ./app
# PimaTika (bim blake3:7c39…) decided:
#   ALLOW  https://example.com
#   DENY TARGET_NOT_IN_USAGE_CONTRACTS  https://1.1.1.1
# run_id: … · exit=0 · PimaTika-decided, NaZelo-enforced · sealed → …/chain.jsonl

A committed, reproducible version of this run — with the signed BIM, the probe, the runner, and a captured sample chain — is in examples/conclusive/ and explained in CONCLUSIVE-TEST.md. Run it:

sudo env \
  LITATOLI_KEY_FILE=~/.config/litatoli/signing.key \
  LITATOLI_ED25519_KEY_FILE=~/.config/litatoli/ed25519.key \
  PATH="/path/to/litatoli/dir:$PATH" \
  examples/conclusive/run.sh

Why not just nazelo run?

nazelo run is the engine, à la carte: it can seal to Litatoli and enforce egress with its eBPF filter, but by default it seals nothing (a null recorder). NaZelo Trust is the opinionated integration: sealed by default, egress deny-all by default, and it refuses to report a run as passed when its evidence could not be sealed.

4. Verify the chain offline

The same run_id is stamped onto every PimaTika egress decision, every sandbox event, and every sealed chain entry (ADR-0005). Hand it to your dashboards, your CI, or Litatoli directly, and one query returns everything that happened in one run — the semantic decisions and the sandbox lifecycle, in one signed chain.

PUB=$(litatoli export-pubkey)
HEAD=$(python3 -c 'import json,sys;print(json.loads([l for l in open(sys.argv[1]) if l.strip()][-1])["blake3_signature"])' examples/conclusive/sample-chain.jsonl)
litatoli verify-chain --log-file examples/conclusive/sample-chain.jsonl --expected-pubkey "$PUB" --expected-head "$HEAD"
# → overall_ok: True · pinned-leaf · head pinned (end-truncation would fail)

Query the whole run by its run_id — a structured, signed, top-level field, not buried in the payload:

litatoli query --log-file examples/conclusive/sample-chain.jsonl --run-id RUN-CONCLUSIVE
# the PimaTika ALLOW and DENY, the NaZelo lifecycle, and the sealed output — one chain

Machine output and the Python API

Bring your own correlation id (for example your CI run) and get machine output:

nazelo-trust run --run-id "$CI_RUN_ID" --json -- cargo build

If you are in a Python codebase:

from nazelo_trust import TrustRun

result = TrustRun(preset="ci").run(["pytest", "-q"])
assert result.ok                 # command passed AND evidence sealed
print(result.run_id, result.evidence_log)

Next

  • Operating limits — what each control proves and where it stops. Read before trusting a result.
  • Disclosure policy — what leaves the machine. Read before enabling any export.
  • Integration contract — what the three engines guarantee together, and what they do not.