{"_id":"@ambitresearch/paperclip-plugin-coder-sandbox","name":"@ambitresearch/paperclip-plugin-coder-sandbox","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@ambitresearch/paperclip-plugin-coder-sandbox","version":"0.2.0","description":"Paperclip sandbox provider plugin for Coder-backed workspaces","type":"module","author":{"name":"Ambit Research Labs"},"license":"MIT","packageManager":"npm@10.9.8","engines":{"node":">=22"},"repository":{"type":"git","url":"git+https://github.com/ambitresearch/paperclip-plugin-coder-sandbox.git"},"publishConfig":{"access":"public"},"paperclipPlugin":{"manifest":"./dist/manifest.js","worker":"./dist/worker.js"},"scripts":{"build":"rm -rf dist && tsc","benchmark":"node ./scripts/benchmark.mjs","check":"npm run check:secrets && npm run check:version && npm test && npm run typecheck && npm run build && npm audit --omit=dev","check:secrets":"node ./scripts/check-secrets.mjs","check:version":"node ./scripts/check-version.mjs","clean":"rm -rf dist","prepack":"npm run build","test":"npm run build --silent && node --test test/**/*.test.js","typecheck":"tsc --noEmit"},"dependencies":{"@paperclipai/plugin-sdk":"2026.722.0"},"devDependencies":{"@types/node":"^26.2.0","typescript":"^7.0.2"},"_id":"@ambitresearch/paperclip-plugin-coder-sandbox@0.2.0","bugs":{"url":"https://github.com/ambitresearch/paperclip-plugin-coder-sandbox/issues"},"homepage":"https://github.com/ambitresearch/paperclip-plugin-coder-sandbox#readme","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-N6KGjNB+/S2yX7XOS9zXk5PV9ZvU7j50rOanT6/9xIwztz9yFLD1vbPwKvKMPMaaN+TuWStXwmVmurcPUGSvSw==","shasum":"7c948bd801a61e2eb31115a8668d75ca741e8686","tarball":"https://registry.npmjs.org/@ambitresearch/paperclip-plugin-coder-sandbox/-/paperclip-plugin-coder-sandbox-0.2.0.tgz","fileCount":30,"unpackedSize":190036,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBaXIyx1O9736Lf+vIUHGsrtjFiv06Z454yuEFl7bFIDAiEA4TRmiDVNx7rmnfeBvqIeVeJAdHHI1sQKeoRoslhvaj4="}]},"_npmUser":{"name":"roshang","email":"contact@roshangautam.net"},"directories":{},"maintainers":[{"name":"roshang","email":"contact@roshangautam.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/paperclip-plugin-coder-sandbox_0.2.0_1787011049728_0.05520415125080658"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-17T23:57:29.554Z","0.2.0":"2026-08-17T23:57:29.881Z","modified":"2026-08-17T23:57:30.112Z"},"maintainers":[{"name":"roshang","email":"contact@roshangautam.net"}],"description":"Paperclip sandbox provider plugin for Coder-backed workspaces","homepage":"https://github.com/ambitresearch/paperclip-plugin-coder-sandbox#readme","repository":{"type":"git","url":"git+https://github.com/ambitresearch/paperclip-plugin-coder-sandbox.git"},"author":{"name":"Ambit Research Labs"},"bugs":{"url":"https://github.com/ambitresearch/paperclip-plugin-coder-sandbox/issues"},"license":"MIT","readme":"# Paperclip Plugin Coder Sandbox\n\nPaperclip Plugin Coder Sandbox is an Ambit Research Labs TypeScript plugin that provides a sandbox provider for Paperclip workflows backed by Coder workspaces.\n\n## Status\n\n- Current release track: `0.2.0`\n- Repository: `ambitresearch/paperclip-plugin-coder-sandbox`\n- Planned npm package: `@ambitresearch/paperclip-plugin-coder-sandbox` (not yet published)\n\n## Prerequisites\n\n- Node.js 22 or newer\n- npm 10.9.8 (declared in `packageManager`)\n\n## Installation\n\n```bash\nnpm ci\nnpm run check\nnpm pack\n```\n\nThe package is not published yet. The resulting `.tgz` is the reviewable installation artifact for\nPaperclip's plugin manager; do not treat a local pack as a release.\n\n## Development Commands\n\n```bash\nnpm run build\nnpm test\nnpm run typecheck\nnpm run check:secrets\nnpm run check:version\nnpm run check\n```\n\n`npm run check` runs local repository safety checks plus test/type/build/audit gates.\n\n## Operator Setup\n\n### Coder prerequisites\n\nThe Paperclip host needs network access to the Coder deployment and a Coder CLI at `coderBin`. The\nconfigured Coder template must:\n\n- create an unprivileged workspace with no Docker socket;\n- give that workspace user the dedicated `/home/coder` home: `remoteCwd` must be a direct child of\n  that home, and\n  no unrelated workload may share that home or user;\n- run only trusted template and runner processes as the workspace user before initial realization;\n- treat workspace-user-owned entries in `/tmp`, `/var/tmp`, and `/dev/shm` as disposable;\n- define Coder's idle stop behavior (the default plugin setting requests eight hours);\n- expose an authenticated Coder app named `paperclip-runner` from the `main` agent for the default\n  `app-proxy` transport;\n- accept any explicit entries in `workspaceParameters` during create and start.\n\nThe runner app must accept `POST /run` with `{ script, stdin, timeoutMs, env, envMode }`, require\n`envMode: \"replace\"`, and execute `script` as `/bin/bash --noprofile --norc -c <script>` with `env`\nreplacing its process environment rather than merging into it. It returns\n`{ exitCode, signal, timedOut, stdout, stderr }`. Coder's app proxy authenticates the request with\nthe `coder_session_token` cookie and strips that cookie before forwarding to the runner. `rpty` and\n`ssh` are supported fallbacks through the local Coder CLI. The legacy unauthenticated\n`direct-runner` transport is disabled.\n\n`CODER_HEADER` and `CODER_HEADER_COMMAND` apply only to direct Coder control-plane requests. They\nare intentionally not forwarded through the app proxy to workspace runners. On Windows,\n`CODER_HEADER_COMMAND` is rejected because descendant process cleanup cannot be guaranteed; use\n`CODER_HEADER` instead.\n\n`workerImage`, `agentWorkspaceImages`, and `adapterWorkspaceImages` are compatibility metadata.\nThey do not guess a Coder template parameter name. To select an image, configure the parameter your\ntemplate actually declares, for example `\"workspaceParameters\": { \"container_image\":\n\"example/agent-runtime:1.2.3\" }`.\n\n### Credentials\n\n- Keep secrets in local environment configuration that is not committed.\n- Resolve `apiToken` and the compatibility `gitAuthToken` through Paperclip secrets; a UUID-looking\n  unresolved `apiToken` produces a validation warning. If `gitAuthToken` is used, scope it to one\n  repository and rotate it frequently.\n- Prefer host-side GitHub App credentials: `GITHUB_APP_ID`, `GITHUB_INSTALLATION_ID` (or\n  `GITHUB_APP_INSTALLATION_ID`), and either `GITHUB_APP_PRIVATE_KEY` or\n  `GITHUB_APP_PRIVATE_KEY_FILE`. Keep the private key on the Paperclip/plugin host. The plugin never\n  forwards App IDs or private-key material to Coder or agent commands.\n- For an exact credential-free `https://github.com/owner/repository` URL, the plugin mints a\n  short-lived installation token restricted to that repository. Only the transient token reaches\n  the authenticated `app-proxy` runner; command output and errors redact both the token and its Git\n  Basic-header representation.\n- Agent-propagated GitHub App credentials can authenticate the initial private clone only when the\n  Paperclip host forwards the resolved per-run adapter environment to `environmentRealizeWorkspace`.\n  Hosts whose realization contract omits that environment cannot authenticate the initial clone;\n  later command execution deliberately strips GitHub credentials rather than retaining them.\n  Provider-configured `agentWorkspaceEnv` remains a compatibility path, not an Agent Identities\n  source-of-truth integration.\n- GitHub authentication is realization-only: the initial private clone/fetch is authenticated during\n  workspace realization, and subsequent agent command execution runs without those credentials.\n- Do not put credentials, signed query parameters, or fragments in repository URLs.\n- Do not commit generated artifacts or recovery/provenance records.\n- Run `npm run check:secrets` before pushing.\n\n### Minimal environment-driver configuration\n\n```json\n{\n  \"driverKey\": \"coder\",\n  \"coderUrl\": \"https://coder.example.com\",\n  \"apiToken\": \"paperclip-secret-reference\",\n  \"gitAuthToken\": \"paperclip-secret-reference\",\n  \"template\": \"docker\",\n  \"workspaceParameters\": {},\n  \"remoteCwd\": \"/home/coder/workspace\",\n  \"commandTransport\": \"app-proxy\",\n  \"runnerAppSlug\": \"paperclip-runner\",\n  \"reuseLease\": false,\n  \"stopAfterHours\": 8\n}\n```\n\nVersion `0.1.0` is deliberately cold-only. Every acquisition generates a unique workspace and lease;\n`workspaceName`, when set, is only a prefix. `reuseLease: true` is rejected, and the manifest reports\nthat reusable leases are unsupported. A newly created workspace is deleted if readiness or marker\nclaim fails.\n\nRelease and destroy authorize the persisted lease locally, take its workspace lock, and ask the\nCoder control plane to delete the persisted immutable workspace UUID directly. They do not start the\nworkspace, wait for its runner, or execute cleanup code inside it. A repeated cleanup succeeds when\nCoder's authenticated API confirms that the same UUID's latest build is deleted. Legacy\n`stopOnRelease` and `deleteOnDestroy` values are parsed for compatibility but do not change this\nbehavior. Legacy reusable lease records that contain the immutable workspace UUID and complete\nownership metadata can be released or destroyed, but cannot be resumed, realized, or used to\nexecute commands. Older records without those fields fail closed and require manual workspace\ndeletion.\n\nThe environment-driver form exposes these transport settings:\n\n- `commandTransport` defaults to `app-proxy`, which authenticates through Coder's\n  `coder_session_token` cookie; Coder strips it before forwarding. `rpty` and `ssh` use the local\n  Coder CLI.\n- `runnerAppSlug` selects the Coder workspace app used by `app-proxy` and defaults to\n  `paperclip-runner`.\n- `directRunnerUrl` is retained only to parse legacy configurations; direct execution is refused.\n\n## Benchmarking\n\nThe benchmark harness runs operator-supplied probes and emits raw redacted JSON samples. Each\nsuccessful probe must exit zero and print non-empty evidence; missing commands, exit 127, empty\noutput, and unconfigured probes are `UNVERIFIED`, never `PASS`.\n\nThe report schema retains the historical `warmAcquisition` probe for comparison. It is not evidence\nthat reusable workspaces are supported by `0.1.0`.\n\nCreate a local command file that is not committed:\n\n```json\n{\n  \"warmAcquisition\": \"./ops/probe-warm-acquire.sh\",\n  \"coldAcquisition\": \"./ops/probe-cold-acquire.sh\",\n  \"coldFirstCommand\": \"./ops/probe-cold-first-command.sh\",\n  \"steadyCommand\": \"./ops/probe-steady-command.sh\",\n  \"cleanup\": \"./ops/probe-cleanup.sh\"\n}\n```\n\nRun the frozen SLO suite (100 samples per metric by default):\n\n```bash\nnpm run benchmark -- \\\n  --config ./benchmark-commands.json \\\n  --samples 100 \\\n  --output ./benchmark-results.json\n```\n\nThe output file is forced to mode `0600`. Exit status is `0` for `PASS`, `1` for `FAIL`, and `2`\nfor `UNVERIFIED`.\n\n| Metric | Frozen target |\n| --- | --- |\n| Warm acquisition | p50 <= 1.5s, p95 <= 3s, success >= 98% |\n| Cold acquisition | p50 <= 20s, p95 <= 45s, success >= 98% |\n| Cold first command | p50 <= 15s, success >= 99% |\n| Steady command | p50 <= 1s, p95 <= 2s, success >= 99% |\n| Cleanup | p50 <= 5s, p95 <= 15s |\n\nThe report recommends the Kubernetes fallback after two consecutive cold-acquisition failures or a\ncold-acquisition p95 breach. It records the decision only; it does not mutate provider settings.\n\n## Architecture (Public-Level)\n\nThe plugin is organized into focused modules:\n\n- `src/manifest.ts` and `src/plugin.ts` define plugin integration points.\n- `src/worker.ts` and `src/process.ts` coordinate workspace actions.\n- `src/config.ts`, `src/types.ts`, and `src/index.ts` provide shared configuration/types and exports.\n\nBuild output is generated into `dist/` from TypeScript sources in `src/`.\nCommands use Coder's authenticated app proxy by default; RPTY and SSH remain opt-in transports.\n\n## Security Note\n\nIf you discover a security issue, follow `SECURITY.md` for reporting guidance through GitHub Security Advisories.\n","readmeFilename":"README.md","_rev":"1-43e1b4cea4a34aed3f0963204348d032"}