Nono’s trust system verifies the provenance and integrity of files before they are consumed. Any file you choose to protect — instruction files, configs, scripts, templates — can be signed and verified. At sandbox startup, nono scans for files matching the trust policy and verifies their signatures. Unsigned or tampered files are hard-denied.Documentation Index
Fetch the complete documentation index at: https://nono.sh/docs/llms.txt
Use this file to discover all available pages before exploring further.
Quick Start
1. Generate a signing key
--keyref file:// to store the key as a file instead:
2. Create and sign a trust policy
A trust policy declares which files must be verified and who is allowed to sign them. It lives in the project root astrust-policy.json — if a project you’re working on already has one, skip this step.
trust-policy.json with the specified include patterns and your signing key’s public key as a publisher. The second signs it — an unsigned policy could be modified by an attacker.
Patterns use glob syntax and can be specific files or wildcards — SKILLS* matches SKILLS.md, SKILLS-ops.md, etc. Be specific with patterns: any file that matches includes must be signed or verification will fail.
3. Sign files
.bundle sidecar (e.g., SKILLS.md.bundle). Use --multi-subject to produce a single .nono-trust.bundle instead. Commit bundles alongside the signed files.
Signing works on any file — no trust policy is required for explicit file arguments. --all uses the policy to discover which files to sign.
4. Verify
nono run, the pre-exec scan automatically verifies all files matching the policy before the agent launches.
Headless Quick Start (file-backed keys)
On headless Linux, containers, or SSH-only hosts where the system keystore is unavailable, use--keyref file:// throughout:
--keyref — it uses the public key embedded in trust-policy.json by init.
The trust policy records the full file path as the signer identity (e.g.
file:///home/user/.config/nono/trust/default.pem). If you move the key to a different path, re-run nono trust init with the new --keyref to update the policy. The same key material at a different path is treated as a different signer. Use a stable, conventional path (e.g. $HOME/.config/nono/trust/default.pem) to avoid this.CLI Commands
nono trust init
Create atrust-policy.json in the current directory.
sign --all/verify --all do not respect .gitignore — adding a file to .gitignore cannot bypass trust verification.
nono trust sign
Sign files. Any file can be signed — no trust policy is required for explicit file arguments.Keyless signing requires a CI environment with ambient OIDC tokens, such as GitHub Actions with
permissions: id-token: write. Interactive browser-based keyless signing is not yet supported. Use keyed signing for local development.nono trust verify
nono trust list
List all files matching the policy and their verification status.nono trust keygen
Generate an ECDSA P-256 signing key pair.--keyref flag accepts keystore://name (explicit keystore reference) or file:///path (file-backed). When using file://, the private key is stored at the specified path and the public key at <path>.pub, both with owner-only permissions (0600).
nono trust export-key
Export the public key in base64 DER format for use in trust policypublic_key fields.
nono trust sign-policy
Sign the trust policy file. Required after every modification totrust-policy.json.
Development Override
For development and testing, disable attestation enforcement:NONO_TRUST_OVERRIDE=1 environment variable is also supported.
Trust Policy Reference
Trust policy is defined intrust-policy.json, separate from the sandbox policy (policy.json).
includes
Glob patterns identifying files under attestation. Any file matching these patterns is subject to verification.publishers
Trusted publisher identities. A file’s signature must match at least one publisher. Keyed publishers match by key ID and verify with the embedded public key:public_key field contains the base64-encoded DER SPKI public key.
Keyless (OIDC) publishers match by identity claims from the Fulcio certificate:
| Field | Description | Wildcards |
|---|---|---|
name | Human-readable label for this publisher (not matched against anything) | No |
issuer | The OIDC provider URL. For GitHub Actions this is always https://token.actions.githubusercontent.com | No |
repository | The owner/repo that ran the signing workflow (maps to the GitHub OIDC repository claim). Use org/* to trust all repos in an org | Yes (org/*) |
workflow | Path to the workflow file that performed the signing, relative to the repo root | Yes (*) |
ref_pattern | The git ref that triggered the workflow (e.g., refs/heads/main, refs/tags/v1.0). Use wildcards to trust a range of refs | Yes (refs/tags/v*) |
blocklist
Known-malicious file digests. Checked before any other verification. A file matching a blocklist digest is hard-denied regardless of signature validity.warn or audit enforcement modes.
enforcement
| Mode | Behavior |
|---|---|
deny | Hard deny unsigned/invalid/untrusted files. The agent does not start. |
warn | Log warnings but allow the agent to proceed. |
audit | Allow access, log verification results for post-hoc review. |
GitHub Actions Integration
Keyless signing integrates with GitHub Actions OIDC for automated CI/CD signing.Signing Workflow
Trust Policy for CI-Signed Files
my-org/my-repo, from the main branch.
Signing Modes
There are two signing modes, with two key storage backends for keyed signing: Keyed signing (keystore) — A private key stored in the system keystore (macOS Keychain / Linux Secret Service). The default for desktop environments. Use--key <name> or --keyref keystore://<name>.
Keyed signing (file) — A private key stored as a local file with owner-only permissions (0600). Designed for headless Linux, containers, SSH-only hosts, and environments where the system keystore is unavailable. Use --keyref file:///path/to/key.pem.
Keyless signing — Ephemeral key + OIDC identity + Fulcio certificate + Rekor transparency log. Suitable for CI/CD pipelines with ambient OIDC tokens (e.g., GitHub Actions with permissions: id-token: write). The signer’s identity is cryptographically bound to the signature via the OIDC token.
Both modes produce Sigstore bundle v0.3 files (.bundle extension).
What Signing Proves (and What It Does Not)
Signing a file proves two things:- Provenance — The file was signed by a specific identity (a key you control, or a CI/CD pipeline you trust).
- Integrity — The file has not been modified since it was signed. Any tampering breaks the signature.
publishers), and the cryptography guarantees only those signers can produce files that pass verification.
Signature Storage
Per-file bundles (default):<filename>.bundle
--multi-subject): .nono-trust.bundle
Recommended Project Structure
For most projects, use a single.nono-trust.bundle via --multi-subject rather than per-file sidecars. This keeps the tree clean and avoids bundle proliferation as the number of signed files grows.
trust-policy.jsonlives at the project roottrust-policy.json.bundleis its signature sidecar (always per-file).nono-trust.bundlecontains attestations for all files signed with--multi-subject- Bundle files for signed files in subdirectories (e.g.,
src/config.py) are also stored at the depth of the file when using per-file signing (src/config.py.bundle)
trust-policy.json, trust-policy.json.bundle, and .nono-trust.bundle alongside your source files.
Policy Composition
Trust policies compose across three levels:| Level | Location | Purpose |
|---|---|---|
| Embedded | Built into nono binary | Baseline include patterns |
| User | $XDG_CONFIG_HOME/nono/trust-policy.json | Personal trusted publishers |
| Project | <project-root>/trust-policy.json | Project-specific publishers |
- Publishers: union (all levels combined)
- Blocklist digests: union (all levels combined)
- Include patterns: union (all levels combined)
- Enforcement: strictest wins (
deny>warn>audit)
User-Level Policy as Trust Anchor
The user-level policy (~/.config/nono/trust-policy.json) defines who you trust — your publishers, enforcement floor, and blocklist. Project-level policies define what gets verified (include patterns). This separation means a malicious project can’t weaken your trust decisions.
When only a project-level policy exists with no user-level policy, nono warns:
~/.config/nono/trust-policy.json with empty includes and your signing key as a publisher. Edit it to add your trusted OIDC publishers:
includes list is empty — the project-level policy declares which files to verify. The user-level policy establishes who you trust: in this example, any GitHub Actions workflow in your organization signing from main. Projects can add additional publishers but can’t remove yours, and enforcement of deny can’t be downgraded.
You can also add keyed publishers for local development keys, though in practice your own key is already trusted implicitly — the user-level policy is more useful for declaring trust in CI/CD identities and other team members.
Pre-exec Scanning
Whennono run launches a command, it scans the working directory for files matching includes before applying the sandbox. File discovery does not respect .gitignore — this prevents hiding files from verification.
To keep startup latency bounded in large repositories, the scan prunes a small built-in set of regenerable heavy directories such as .git, node_modules, target, dist, and common cache directories. Hidden directories are otherwise still scanned. You can extend the skip set for a session with --skip-dir <name> or persist it in a profile with skipdirs.
Runtime Interception
The pre-exec scan catches files present at startup. Runtime behavior differs by platform.Linux (seccomp-notify)
In Supervised mode, the seccomp-notify supervisor traps everyopenat() syscall. When the target path matches an include pattern, the supervisor verifies the file’s bundle before allowing the read. Unsigned, tampered, or blocklisted files are denied with EPERM.
The verification result is cached by (path, inode, mtime, size). If any metadata changes, the cache entry is invalidated.
Files that appear mid-session (e.g., via curl or git pull) are verified on first open. Linux provides true runtime interception.
macOS (Seatbelt)
On macOS, trust verification is startup-only. For each verified file, a literal(deny file-write-data ...) rule is emitted in the Seatbelt profile, making the file structurally immutable for the session.
macOS does not have syscall-level file-open interception equivalent to Linux’s seccomp-notify. A file matching includes that appears after the sandbox is applied will be readable if its parent directory has read access granted.
The macOS enforcement model provides:
- Integrity of verified files — kernel-level write-protection prevents tampering
- Startup gating — if any existing file fails verification, the sandbox refuses to start
- No runtime interception — file existence and verification are evaluated once at startup
Why Glob Patterns?
Pattern matching keeps the system practical: it avoids requiring every file in the working directory to be signed, and lets you define the verification boundary to match your needs. Common use cases include agent instruction files (SKILLS*, CLAUDE*), configuration files (*.cfg, *.ini), scripts (*.py, *.sh), or any other files your workflow depends on.