Skip to main content

Installation

npm install nono-ts

Runnable Examples

Use the repository examples for end-to-end runnable flows:
  • /examples/js/01-support-check.js
  • /examples/js/02-build-capabilities.js
  • /examples/js/03-query-policy.js
  • /examples/js/04-state-roundtrip.js
  • /examples/js/05-safe-apply-pattern.js
  • /examples/ts/01-support-check.ts
  • /examples/ts/02-build-capabilities.ts
  • /examples/ts/03-query-policy.ts
  • /examples/ts/04-state-roundtrip.ts
  • /examples/ts/05-safe-apply-pattern.ts
npm run examples:list
npm run example:all
For the guarded apply demos, set NONO_APPLY=1.

Basic Usage

1. Check Platform Support

Before applying a sandbox, verify that the current platform supports it:
import { isSupported, supportInfo } from 'nono-ts';

if (!isSupported()) {
  const info = supportInfo();
  console.error(`Sandboxing not available: ${info.details}`);
  process.exit(1);
}

2. Define Capabilities

Create a CapabilitySet and add the permissions your application needs:
import { CapabilitySet, AccessMode } from 'nono-ts';

const caps = new CapabilitySet();

// Allow read/write access to a directory
caps.allowPath('/var/data', AccessMode.ReadWrite);

// Allow read-only access to system libraries
caps.allowPath('/usr/lib', AccessMode.Read);
caps.allowPath('/lib', AccessMode.Read);

// Allow access to a specific file
caps.allowFile('/etc/ssl/certs/ca-certificates.crt', AccessMode.Read);

3. Apply the Sandbox

Calling apply() is irreversible. Once applied, the sandbox cannot be weakened or removed for the lifetime of the process.
import { apply } from 'nono-ts';

// Apply the sandbox
apply(caps);

// From this point forward, the process can only access granted resources

Complete Example

Here’s a complete example of a sandboxed file processor:
import { CapabilitySet, AccessMode, apply, isSupported } from 'nono-ts';
import * as fs from 'fs';
import * as path from 'path';

const INPUT_DIR = '/var/input';
const OUTPUT_DIR = '/var/output';

function setupSandbox() {
  if (!isSupported()) {
    throw new Error('Sandboxing not supported on this platform');
  }

  const caps = new CapabilitySet();

  // Read from input directory
  caps.allowPath(INPUT_DIR, AccessMode.Read);

  // Write to output directory
  caps.allowPath(OUTPUT_DIR, AccessMode.ReadWrite);

  // Node.js runtime needs
  caps.allowPath('/usr/lib', AccessMode.Read);
  caps.allowPath('/lib', AccessMode.Read);

  // Block network access
  caps.blockNetwork();

  // Apply sandbox
  apply(caps);

  console.log('Sandbox applied successfully');
}

function processFiles() {
  const files = fs.readdirSync(INPUT_DIR);

  for (const file of files) {
    const input = path.join(INPUT_DIR, file);
    const output = path.join(OUTPUT_DIR, `processed_${file}`);

    const content = fs.readFileSync(input, 'utf-8');
    const processed = content.toUpperCase(); // Example transformation
    fs.writeFileSync(output, processed);

    console.log(`Processed: ${file}`);
  }
}

// Setup sandbox first
setupSandbox();

// Now process files with restricted permissions
processFiles();

Testing Before Applying

Use QueryContext to test whether operations would be permitted before applying the sandbox:
import { CapabilitySet, QueryContext, AccessMode } from 'nono-ts';

const caps = new CapabilitySet();
caps.allowPath('/tmp', AccessMode.ReadWrite);
caps.blockNetwork();

const query = new QueryContext(caps);

// Test file access
const fileResult = query.queryPath('/tmp/test.txt', AccessMode.Write);
console.log(`Write to /tmp/test.txt: ${fileResult.status}`);
// Output: Write to /tmp/test.txt: allowed

const homeResult = query.queryPath('/home/user/secret.txt', AccessMode.Read);
console.log(`Read /home/user/secret.txt: ${homeResult.status}`);
// Output: Read /home/user/secret.txt: denied

// Test network access
const netResult = query.queryNetwork();
console.log(`Network access: ${netResult.status}`);
// Output: Network access: denied

Error Handling

The SDK throws errors for invalid operations:
import { CapabilitySet, AccessMode, apply } from 'nono-ts';

const caps = new CapabilitySet();

try {
  // This will throw if /nonexistent doesn't exist
  caps.allowPath('/nonexistent', AccessMode.Read);
} catch (error) {
  console.error('Failed to add capability:', error.message);
}

try {
  apply(caps);
} catch (error) {
  console.error('Failed to apply sandbox:', error.message);
}

Next Steps