Skip to content

Getting Started with NaZelo

This takes you from a fresh install to running a command in the sandbox, collecting its evidence, and verifying a signed capability token. About five minutes. For the full technical reference, see REFERENCE.md.

1. Install

pip install nazelo

Requires Python 3.12. Runtime dependencies: blake3 and click.

2. Generate a signing key

NaZelo signs its capability tokens with a keyed BLAKE3 MAC. Generate one 32-byte key and export it. Store it like any other secret.

# 64 hex chars = 32 bytes
export NAZELO_SIGNING_KEY="$(python3 -c 'import os; print(os.urandom(32).hex())')"

3. Run your first command

nazelo run --preset dev "echo hello from the sandbox"

You will see:

hello from the sandbox

nazelo run streams the sandboxed command's stdout and stderr through to your terminal, then exits with the same exit code as the sandboxed process. The dev preset uses TL1 (bwrap), the fastest tier.

4. See the structured result

Add --json when you want to consume the result programmatically:

nazelo run --preset dev --json "python3 -c 'print(42)'"
{
  "run_id": "…",
  "exit_code": 0,
  "stdout": "42\n",
  "stderr": "",
  "backend": "sandbox_tl1",
  "verified": true
}

verified: true means verification was requested and the command exited 0. run_id is the correlation key stamped into every evidence event for this run.

5. Check which backends are available

nazelo backends
TL   Label        Backend                      Tool           Status
----------------------------------------------------------------------
1    LOCAL        sandbox_tl1                  bwrap          OK
2    CONTAINED    sandbox_tl2                  unshare        OK
3    ISOLATED     sandbox_tl3                  runsc          --
4    HARDENED     sandbox_tl4                  firecracker    --

OK means the backend's probe succeeded; -- means the required tool is not on PATH. Install the missing tool (bubblewrap, util-linux, gVisor, Firecracker) to light up higher tiers — see REFERENCE.md.

6. Collect the evidence

Every sandbox lifecycle event — create, exec, destroy — is recorded through the EvidenceRecorder protocol. Provide your own recorder to capture the audit trail:

from datetime import datetime
from pathlib import Path
from nazelo import (
    VerificationResult, SandboxProfile, TrustLevel2Backend,
    ExecutionRequest, issue_token, new_execution_profile,
)

class MyRecorder:
    def __init__(self):
        self.audit_log = []

    def record_sandbox_event(self, event: str, details: dict) -> VerificationResult:
        self.audit_log.append({"event": event, **details})
        return VerificationResult(
            verified=True, evidence_type=event,
            details=details, timestamp=datetime.now(),
        )

profile = new_execution_profile(SandboxProfile.for_ci(owner="ci-bot"))
token = issue_token(profile)
recorder = MyRecorder()
backend = TrustLevel2Backend(
    workspace_path=Path.cwd(),
    token=token,
    profile=profile,
    evidence_recorder=recorder,
)
backend.execute(ExecutionRequest(command="echo audited", timeout=30))

for entry in recorder.audit_log:
    print(entry["event"])     # sandbox.create, sandbox.exec, sandbox.destroy

Each entry is a structured record you can feed to a compliance layer. See the full event list for what NaZelo emits.

7. Verify a capability token

Tokens are signed and time-bounded. Issue one, then verify its signature and TTL:

nazelo token issue --owner ci-bot -t 2 -c exec
# prints the signature, sandbox id, ttl, and issued-at time

nazelo token verify <SIGNATURE> -t 2 --owner ci-bot \
    --sandbox-id <ID> --ttl <SECS> -c exec --issued-at <ISO>

A tampered or expired token fails verification. Every field in the command above is part of the signed payload, so changing any one of them breaks the signature.

Next steps

  • Trust levels, network rules, L7 stream-proxy enforcement, seccomp process governance, the full CLI and Python API → REFERENCE.md
  • Building the TL4 Firecracker kernel and rootfs → firecracker/README.md