nono is a capability-based sandboxing system for running untrusted AI agents with OS-enforced isolation. Contributions are welcome from anyone who understands what they are submitting.
If anything here is unclear, ask in Discord before writing code. A five-minute conversation saves a rejected PR.
For anything beyond a typo fix, open an issue first or find an existing one. PRs without a linked issue will not be reviewed.
This is especially true for changes that touch the Landlock enforcement path, the sandbox policy model, or the credential proxy. Those areas have security constraints that are not obvious from reading the code.
For scoped, well-defined starting points, see the good-first-issue label.
Before touching any code, read CLAUDE.md. It is the authoritative source for this project's coding standards, security requirements, error handling conventions, and platform-specific constraints. The PR checklist references it directly.
A few things from CLAUDE.md that affect every contribution:
Error handling. Use NonoError for all errors. Propagate with ?.
Never use .unwrap() or .expect(). This is enforced by Clippy and will
block merge.
Path handling. Validate and canonicalize all paths before applying
capabilities. Use Path::starts_with() for path comparisons, not string
starts_with(). String comparison on paths is a security vulnerability:
"/home".starts_with("/home") also matches "/homeevil".
Unsafe code. Restrict to FFI. Every unsafe block requires a
// SAFETY: comment explaining why it is sound.
Tests. Write unit tests for all new capability types and sandbox logic. If you modify environment variables in tests, save and restore the original value. Rust runs unit tests in parallel; an unrestored env var causes flaky failures in unrelated tests.
Security posture. On any error, deny access. Never silently degrade to a less secure state. No escape hatch: once a sandbox is applied, there is no API to expand permissions.
Requirements:
- Rust 1.95 or later (
rustup update stable) - Linux for Landlock-dependent paths (WSL2 works)
- macOS for Seatbelt-dependent paths
make(standard on Linux and macOS)
Clone and build:
git clone https://github.com/nolabs-ai/nono.git
cd nono
make buildmake build builds the core library and CLI. It is the right starting point
for most contributors.
Run tests:
make testRun the full CI check locally:
make ciRun make ci before opening a PR. It runs Clippy, format check, all tests,
and a dependency audit in one command. A PR that fails make ci will not be
merged.
Individual commands if you need them:
make clippy # Lint check (strict: -D warnings -D clippy::unwrap_used)
make fmt-check # Format check
make fmt # Auto-format (run this, then commit the result)
make audit # Dependency audit
make test-lib # Library tests only
make test-cli # CLI tests onlyWorkspace structure:
crates/nono Core library. Pure sandbox primitive, no built-in policy.
crates/nono-cli CLI binary. Owns all security policy, profiles, and UX.
crates/nono-proxy Proxy for network filtering and credential injection.
bindings/c C FFI bindings (package name: nono-ffi).
The library/CLI boundary matters: the library applies only what clients
explicitly add to CapabilitySet. All policy lives in the CLI. Do not add
policy to the library.
If you change bindings/c, regenerate the FFI header after your changes:
cargo build -p nono-ffiCommit the updated bindings/c/include/nono.h alongside your code change.
CI verifies the header is up to date and fails with an explicit error if it
is not.
This project uses conventional commits. Commit messages must follow this format:
<type>(<optional scope>): <short description>
Valid types: feat, fix, docs, perf, refactor, test, ci,
build, chore.
Examples:
feat(proxy): add credential injection for GitHub token
fix(sandbox): canonicalize paths before applying capabilities
docs(contributing): add conventional commit format
The changelog is auto-generated from commit messages at release time.
Use the right type. A fix that is labeled chore will not appear in the
release notes under Bug Fixes.
1. Open or find an issue.
Every PR must reference an existing issue. Open one before writing code. PRs without a linked issue will not be reviewed.
2. Fork the repo and create a branch.
git checkout -b type/short-descriptionMatch the branch name to your commit type. fix/proxy-rotation is good.
patch-1 is not.
3. Write the code.
Follow CLAUDE.md for all coding standards. Key requirements repeated here for visibility:
- Use
NonoErrorfor all errors. Propagate with?. - No
.unwrap()or.expect(). Clippy will catch this and fail CI. - Use
Path::starts_with()for path comparisons, not stringstarts_with(). unsafeblocks require// SAFETY:comments.- New functionality needs unit tests.
- New public API needs doc comments.
- Do not add policy to the library. Policy belongs in the CLI.
4. Sign off your commits (DCO).
nono uses the Developer Certificate of Origin. Every commit must include a
Signed-off-by line with your name and email.
The simplest way:
git commit -s -m "fix(proxy): your commit message here"The -s flag adds the signoff automatically.
If you forgot to sign off on commits already made:
git commit --amend --signoff --no-edit
git push --force-with-leaseDCO signoff is verified by the PR checklist. A PR without signed commits will be asked to amend before review begins.
5. Open a pull request against main.
The PR template will prompt you for:
- A linked issue (
Closes #NNN) - A summary of what the PR does and why
- A test plan describing how you verified the change
- A checklist including DCO signoff confirmation
Fill these in honestly. A PR description that says "integration tests are missing, here is why and what the plan is" moves faster than one that pretends coverage is complete.
If your PR was generated or assisted by an AI tool, complete the Agent Disclosure and Agent Compliance Check sections in the PR template. See CLAUDE.md for the full agent contribution policy, including hard stop conditions that prohibit certain automated contributions.
6. Review.
The maintainer council reviews PRs. See MAINTAINERS.md for the current list. First response within 5 business days. If that window passes with no response, ping in Discord with a link to the PR.
nono applies OS-enforced capability restrictions to sandbox AI agents and the tools they call. Once a sandbox is applied, there is no API to expand permissions. The policy lives in the profile, not in the prompt.
nono is in alpha. Security guarantees are not yet stable. A third-party security audit is planned prior to v1.0. Do not overstate what the current implementation guarantees.
Sustained contributors can move from Contributor to Reviewer to Maintainer. The criteria, nomination process, and expectations for each role are in GOVERNANCE.md.
Do not open public issues for security vulnerabilities.
Report privately via GitHub Security Advisories: https://github.com/nolabs-ai/nono/security/advisories/new
See SECURITY.md for the full disclosure policy, including guidance on LLM-generated findings.
See MAINTAINERS.md for the current maintainer council and contact information.
By contributing, you agree your contributions are licensed under Apache-2.0.