README.md
June 23, 2026 ยท View on GitHub
Installation
npm install nono-ts
Usage
import { CapabilitySet, AccessMode, apply, isSupported, supportInfo } from 'nono-ts';
// Check platform support
if (!isSupported()) {
console.log('Sandboxing not supported on this platform');
process.exit(1);
}
// Build capabilities
const caps = new CapabilitySet();
caps.allowPath('/tmp', AccessMode.ReadWrite);
caps.allowPath('/usr', AccessMode.Read);
caps.allowFile('/etc/hosts', AccessMode.Read);
caps.blockNetwork();
// Apply sandbox (irreversible)
apply(caps);
// From this point, the process can only access granted resources
Examples
Runnable examples live in examples/ with both JavaScript and TypeScript variants:
Build the local native addon first:
npm run build:debug
01-support-check: detect platform support02-build-capabilities: build and inspect aCapabilitySet03-query-policy: dry-run policy decisions withQueryContext04-state-roundtrip: serialize/restore withSandboxState05-safe-apply-pattern: guardedapply()flow (NONO_APPLY=1)06-minimal-safe-cli: minimal wrapper pattern for safe sandboxed transforms07-agent-workspace-pattern: least-privilege input/output workflow for agent-like tasks08-failure-diagnostics: preflight + runtime denial diagnostics09-config-roundtrip: config-driven policy build and state roundtrip10-subprocess-inheritance: opt-inapply()with child-process inheritance checks
npm run examples:list
npm run example:all
Apply examples require explicit opt-in:
NONO_APPLY=1 npm run example:js:05-safe-apply-pattern
NONO_APPLY=1 npm run example:ts:05-safe-apply-pattern
NONO_APPLY=1 npm run example:js:10-subprocess-inheritance
NONO_APPLY=1 npm run example:ts:10-subprocess-inheritance
For 10-subprocess-inheritance, denied-read results are reported as:
BLOCKED(EACCES/EPERM) expectedMISSING(ENOENT) target not presentALLOWEDunexpected
Demonstrator
An end-to-end demonstrator is available at demo/sandboxed-file-transformer/.
npm run demo:dry-run
npm run demo
npm run demo:attack-test
NONO_DEMO_KEEP_TMP=1 npm run demo:dry-run
See examples/README.md for all commands.
API
CapabilitySet
Build a set of capabilities to grant the sandboxed process.
const caps = new CapabilitySet();
// Directory access
caps.allowPath('/data', AccessMode.Read);
caps.allowPath('/tmp', AccessMode.ReadWrite);
// Single file access
caps.allowFile('/etc/passwd', AccessMode.Read);
// Network
caps.blockNetwork();
// Commands
caps.allowCommand('git');
caps.blockCommand('curl');
// Platform-specific rules (macOS Seatbelt)
caps.platformRule('(allow file-read* (subpath "/opt"))');
// Utilities
caps.deduplicate(); // Remove duplicate capabilities
caps.pathCovered('/tmp/foo'); // Check if path is covered
caps.fsCapabilities(); // List all filesystem capabilities
caps.summary(); // Human-readable summary
AccessMode
enum AccessMode {
Read,
Write,
ReadWrite
}
QueryContext
Query whether operations would be permitted without applying the sandbox.
const caps = new CapabilitySet();
caps.allowPath('/tmp', AccessMode.ReadWrite);
caps.blockNetwork();
const query = new QueryContext(caps);
const result = query.queryPath('/tmp/test.txt', AccessMode.Write);
// { status: 'allowed', reason: 'granted_path', grantedPath: '/private/tmp', access: 'read+write' }
const netResult = query.queryNetwork();
// { status: 'denied', reason: 'network_blocked' }
SandboxState
Serialize and deserialize sandbox state for process inheritance.
// Serialize
const state = SandboxState.fromCaps(caps);
const json = state.toJson();
// Deserialize
const restored = SandboxState.fromJson(json);
const restoredCaps = restored.toCaps();
Functions
// Apply sandbox with capabilities (irreversible)
apply(caps: CapabilitySet): void
// Check if sandboxing is supported
isSupported(): boolean
// Get detailed support information
supportInfo(): SupportInfoResult
// Returns: { isSupported: boolean, platform: string, details: string }
Platform Support
| Platform | Backend | Status |
|---|---|---|
| Linux 5.13+ | Landlock | Supported |
| macOS 10.5+ | Seatbelt | Supported |
| Windows | - | Not supported |
License
Apache-2.0