{"_id":"@braedonsaunders/appkit-process-sandbox","name":"@braedonsaunders/appkit-process-sandbox","dist-tags":{"latest":"0.3.0"},"versions":{"0.3.0":{"name":"@braedonsaunders/appkit-process-sandbox","version":"0.3.0","description":"Fail-closed Linux process isolation plus supervised execution, bounded replay, reattachment, cancellation, network policy, and resource ceilings.","license":"AGPL-3.0-or-later","type":"module","exports":{".":{"types":"./index.d.ts","import":"./index.js","default":"./index.js"},"./package.json":"./package.json"},"main":"./index.js","types":"./index.d.ts","author":{"name":"Braedon Saunders"},"repository":{"type":"git","url":"git+https://github.com/braedonsaunders/appkit.git","directory":"packages/process-sandbox"},"homepage":"https://github.com/braedonsaunders/appkit/tree/main/packages/process-sandbox#readme","bugs":{"url":"https://github.com/braedonsaunders/appkit/issues"},"engines":{"node":">=22"},"keywords":["agents","appkit","application-framework","bubblewrap","isolation","sandbox","typescript"],"_id":"@braedonsaunders/appkit-process-sandbox@0.3.0","_integrity":"sha512-lXzNikNucocZjlbvI7Kn3mabPH4QYtPuMBDfQpCvaOD5sHDdpUtuK0f2dHEPN0hvfCY7fiTYTbIKWvcKAh38EA==","_resolved":"/tmp/3ae452908213169579ac524e1040b959/braedonsaunders-appkit-process-sandbox-0.3.0.tgz","_from":"file:braedonsaunders-appkit-process-sandbox-0.3.0.tgz","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-lXzNikNucocZjlbvI7Kn3mabPH4QYtPuMBDfQpCvaOD5sHDdpUtuK0f2dHEPN0hvfCY7fiTYTbIKWvcKAh38EA==","shasum":"d8ed89ee2fa8f0d6169511ec7251f6fe841873c5","tarball":"https://registry.npmjs.org/@braedonsaunders/appkit-process-sandbox/-/appkit-process-sandbox-0.3.0.tgz","fileCount":11,"unpackedSize":144166,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@braedonsaunders%2fappkit-process-sandbox@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCNUgmMk0n1/TD8VLMTHb6lwiICXz1TCH62YagvWsI1LwIgNiGirjcak1+fnRVgnGZ33fQBA2AzDui255BCBUH615s="}]},"_npmUser":{"name":"braedonsaunders","email":"bsaunders@rassaun.com"},"directories":{},"maintainers":[{"name":"braedonsaunders","email":"bsaunders@rassaun.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/appkit-process-sandbox_0.3.0_1787095182580_0.4264720706230738"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T23:19:42.423Z","0.3.0":"2026-08-18T23:19:42.745Z","modified":"2026-08-18T23:19:43.250Z"},"maintainers":[{"name":"braedonsaunders","email":"bsaunders@rassaun.com"}],"description":"Fail-closed Linux process isolation plus supervised execution, bounded replay, reattachment, cancellation, network policy, and resource ceilings.","homepage":"https://github.com/braedonsaunders/appkit/tree/main/packages/process-sandbox#readme","keywords":["agents","appkit","application-framework","bubblewrap","isolation","sandbox","typescript"],"repository":{"type":"git","url":"git+https://github.com/braedonsaunders/appkit.git","directory":"packages/process-sandbox"},"author":{"name":"Braedon Saunders"},"bugs":{"url":"https://github.com/braedonsaunders/appkit/issues"},"license":"AGPL-3.0-or-later","readme":"# @braedonsaunders/appkit-process-sandbox\n\nFail-closed Linux process isolation for workspace-bound coding agents and other\ntrusted application workers.\n\nThe package owns the reusable bubblewrap policy: user, process, IPC, UTS and\ncgroup namespaces; capability dropping; an explicitly rebuilt environment;\nread-only system roots; masked host data; a private PID namespace; fresh\n`/dev`, `/tmp`, and `/run`; an explicit set of writable binds; optional network\nnamespace isolation; and optional kernel resource ceilings. Secret environment\nvalues are passed in the sanitized child environment rather than serialized\ninto process arguments. The consuming application still owns authentication,\ntenant-to-workspace resolution, and the command being launched.\n\n## Supervised executions\n\n`ProcessSandboxSupervisor` decouples a command from the request that started\nit. A caller can safely retry a start with the same execution ID, poll from the\nlast event sequence after a disconnect, cancel it, or retrieve its bounded\nterminal result until retention expires. Stream replay and final output are\nbounded separately, so slow or disconnected consumers cannot create an\nunbounded host-memory log.\n\n```ts\nconst supervisor = new ProcessSandboxSupervisor({\n  defaultTimeoutMs: 120_000,\n  defaultRetentionMs: 15 * 60_000,\n  maxOutputBytes: 64 * 1024,\n})\n\nconst started = supervisor.start({\n  executionId: request.idempotencyKey,\n  maxOutputBytes: request.outputLimit,\n  process: {\n    command: '/usr/bin/sh',\n    args: ['-lc', request.command],\n    cwd: workspacePath,\n    writablePaths: [workspacePath],\n    network: 'none',\n  },\n})\n\nlet sequence = started.latestSequence\nwhile (true) {\n  const update = await supervisor.waitForUpdate(started.executionId, sequence)\n  if (!update) throw new Error('Execution expired')\n  sequence = update.latestSequence\n  if (update.result) break\n}\n```\n\nThe built-in launcher remains `spawnBubblewrappedProcess`. An adapter for a\ndifferent isolation backend may be supplied through `launcher`, but the adapter\nis responsible for providing equivalent confinement and a normal Node\n`ChildProcess` lifecycle. The supervisor does not weaken or replace the\nbubblewrap boundary.\n\n## Network policy\n\n`network` defaults to `'host'`, which is the behavior of every release before\nnetwork policy existed — package installs and agents that call an API keep\nworking across an upgrade. Pass `network: 'none'` for commands that have no\nbusiness reaching out; the child then has only a loopback interface, with no\nDNS and no access to services the application container can reach.\n\n```ts\nconst child = spawnBubblewrappedProcess({\n  command: '/opt/tools/ripgrep/rg',\n  args: ['--json', pattern],\n  cwd: workspacePath,\n  writablePaths: [workspacePath],\n  network: 'none',\n  limits: { cpuSeconds: 30, addressSpaceBytes: 1_073_741_824, processes: 64 },\n})\n```\n\n## Resource limits\n\n`limits` maps to `prlimit(1)` running inside the namespace, so the ceilings\napply to the command and everything it forks. Soft and hard values are set\ntogether, which means the child cannot raise them. Requesting a limit on a host\nwithout util-linux throws rather than running the command unbounded — silently\ndropping a ceiling would fail open on exactly the hosts the caller was trying\nto protect. `verifyProcessSandbox()` reports `resourceLimitsSupported` and\n`networkIsolationSupported` so a readiness screen can show what this host can\nactually enforce.\n\nProcfs is unavailable by default for compatibility with container hosts that\nprohibit nested proc mounts. A consumer can set `mountProc: true` to mount a\nfresh procfs scoped to the sandbox's private PID namespace. For native runtimes\nthat only require `current_exe()`, prefer `syntheticSelfExecutable`: AppKit\ncreates only `/proc/self/exe` as a symlink to that explicitly approved absolute\npath, exposing no process metadata. The two modes are mutually exclusive.\n\nThe working directory may be a writable bind for build/edit agents or a\nread-only bind for question-and-answer and inspection workers.\n\n```ts\nimport { spawnBubblewrappedProcess } from '@braedonsaunders/appkit-process-sandbox'\n\nconst child = spawnBubblewrappedProcess({\n  command: '/usr/local/bin/codex',\n  args: ['exec', '--json', prompt],\n  cwd: workspacePath,\n  writablePaths: [workspacePath, agentHomePath],\n  environment: {\n    PATH: '/usr/local/bin:/usr/bin:/bin',\n    CODEX_HOME: `${agentHomePath}/.codex`,\n  },\n  launcherIdentity: { uid: 1000, gid: 1000 },\n  syntheticSelfExecutable: '/usr/local/bin/codex',\n})\n```\n\nWhen the parent service runs as root inside a container, configure an\nunprivileged `launcherIdentity` and install bubblewrap setuid-root. AppKit\ninvokes only the bubblewrap executable as that identity; the setuid helper\ncreates the namespaces and drops all capabilities before the agent command is\nexecuted. This avoids granting `CAP_SYS_ADMIN` to the application container.\nWritable bind paths must be writable by the launcher identity.\n\nDo not fall back to an unsandboxed child process when this package reports an\nunsupported platform or missing bubblewrap binary. Desktop/single-user local\nexecution is a separate, explicit deployment mode.\n\nCall `verifyProcessSandbox({ launcherIdentity })` during a server's startup\nwith the same launcher identity used for real agent processes. It runs a\nminimal command inside the real sandbox so blocked namespace or mount syscalls\nare detected before the service accepts agent work.\n","readmeFilename":"README.md","_rev":"1-46e24d813fbbe4a6d5c726d869b71979"}