Clean README, add LICENSE + NOTICE, tidy docs
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Kendall Booker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
This license covers the build recipe, packaging, scripts, and documentation in
|
||||
this repository. Anthropic-provided content (the skill files under mnt-skills/
|
||||
and the prebuilt Anthropic binaries under _context/bin/) is covered by NOTICE.
|
||||
@@ -0,0 +1,30 @@
|
||||
Third-party content in this repository
|
||||
|
||||
1. Anthropic skill files (mnt-skills/**, including every SKILL.md and each
|
||||
skill's own LICENSE.txt)
|
||||
|
||||
(c) Anthropic, PBC. Each skill carries its own LICENSE.txt. Verbatim, it
|
||||
grants use under Anthropic's service terms and adds restrictions, including
|
||||
that users may not "extract these materials from the Services or retain
|
||||
copies of these materials outside the Services", may not "reproduce or copy
|
||||
these materials, except for temporary copies created automatically during
|
||||
authorized use of the Services", and may not create derivative works.
|
||||
|
||||
In plain terms: these files are Anthropic's, were captured from a running
|
||||
sandbox for research and interoperability, and their own license does not
|
||||
clearly grant public redistribution. Redistribute them at your own reading
|
||||
of those terms, or remove mnt-skills/ and re-obtain them from your own
|
||||
Claude session (any user can ask Claude to show them).
|
||||
|
||||
2. Prebuilt Anthropic binaries (_context/bin/): extract-text, magika,
|
||||
rclone-filestore. Anthropic builds. magika runs a public Google model;
|
||||
extract-text and rclone-filestore are Anthropic-compiled. Included for
|
||||
faithful reproduction and analysis.
|
||||
|
||||
3. Everything else in this repo (the Dockerfile, scripts, docs, manifests):
|
||||
MIT, see LICENSE.
|
||||
|
||||
4. The base system is Ubuntu 24.04 under its own licenses.
|
||||
|
||||
No claim of ownership is made over Anthropic or Google content. "wiggle" is an
|
||||
internal Anthropic codename, used descriptively. Not affiliated with Anthropic.
|
||||
@@ -1,51 +1,127 @@
|
||||
# wiggle — the claude.ai code-execution sandbox, reproduced
|
||||
# Wiggle
|
||||
|
||||
A faithful, rebuilt copy of **the VM that runs Claude's code execution** (claude.ai chat/cowork tool sandbox, internally codenamed `wiggle`): Ubuntu 24.04 in a Firecracker microVM with Anthropic's exact package set, tool stack, directory layout, and exec-environment contract.
|
||||
A working copy of the sandbox that Claude (claude.ai) runs code in.
|
||||
|
||||
Built from a complete filesystem export of a live sandbox (178,151 files, swept end-to-end) plus its recovered build recipe. **Sanitized: zero credentials, keys, cookies, or shell history inside.**
|
||||
When Claude executes a command, reads a file, converts a document, or takes a
|
||||
screenshot, it is not running on a web server. It is running inside a small
|
||||
virtual machine, one per conversation. This repository is that machine: the
|
||||
same files, tools, packages, and instructions, packaged so you can run it
|
||||
yourself on Docker, e2b, Modal, or any Linux host.
|
||||
|
||||
## What's in the box
|
||||
Codename `wiggle` is what Anthropic calls this VM internally.
|
||||
|
||||
- Ubuntu 24.04.2, 866 apt packages (exact versions pinned in `meta/dpkg-manifest.txt`)
|
||||
- Python: 3.12 + uv stack — 114 pinned distributions (`meta/uv-manifest.txt`) — numpy/pandas/scikit-learn/matplotlib/scipy/jupyter, Pillow 12.1.1, opencv, playwright 1.56
|
||||
- Node 22.22.2 + 21 pinned npm globals incl. `@anthropic-ai/mcpb`-era toolchain, sharp, puppeteer 23.11 + Chrome-headless-shell 131, playwright chromium-1194 (`/opt/pw-browsers`)
|
||||
- Document stack: LibreOffice 24.2, TeX Live 2023, poppler 24.02, qpdf 11.9, ImageMagick 6, tesseract (eng+osd, public models byte-identical), ffmpeg, pandoc, wkhtmltopdf
|
||||
- **40 Anthropic skills** at `/mnt/skills` (verbatim — the instruction playbooks Claude follows: docx/pptx/xlsx/pdf, deep-research, morning, skill-creator, mcp-builder, …)
|
||||
- Anthropic's custom binaries: `extract-text` (Rust, document→text) and `magika` (public build) — the mount daemon `rclone-filestore` interface is documented in `meta/`
|
||||
- Exact `/mnt` layout: `user-data/{uploads,outputs}`, `transcripts`, `tool_results`; exact exec-env contract (18 vars incl. `IS_SANDBOX=yes`) — see `meta/env-contract.md`
|
||||
## Quick start
|
||||
|
||||
Known authentic quirks preserved: pandoc→PDF needs `fonts-lmodern` + `poppler-data` (missing in the original image too — that's a finding, not a bug), `LANG` unset, npm offline cache deliberately cleaned at build, two `rclone-filestore` builds coexist on the PATH.
|
||||
|
||||
## Use it
|
||||
|
||||
**Plain Docker (works immediately):**
|
||||
```bash
|
||||
docker build -t wiggle . # uses Dockerfile.recovered (verified to build green)
|
||||
docker build -t wiggle .
|
||||
docker run -it wiggle bash
|
||||
|
||||
# inside:
|
||||
soffice --version # LibreOffice 24.2
|
||||
python3 -c "import pandas" # full data science stack
|
||||
ls /mnt/skills # the 40 playbooks Claude follows
|
||||
```
|
||||
**Prebuilt rootfs (fastest):** release asset `wiggle-rootfs-sanitized.tar.zst` (2.53 GB, sha256 in `meta/rootfs-sha256.txt`):
|
||||
|
||||
Want the exact bytes instead of a rebuild? The GitHub release carries the
|
||||
full filesystem of a live session (8.9 GB, sanitized):
|
||||
|
||||
```bash
|
||||
skopeo/umoci or simply: mkdir rootfs && tar --zstd -xf wiggle-rootfs-sanitized.tar.zst -C rootfs
|
||||
# or import straight into Docker:
|
||||
cat wiggle-rootfs-sanitized.tar.zst | zstd -d | docker import - wiggle:live
|
||||
cat wiggle-part-aa wiggle-part-ab > wiggle.tar.zst
|
||||
zstd -d -c wiggle.tar.zst | docker import - wiggle:live
|
||||
```
|
||||
|
||||
**e2b (same architecture as the original — Firecracker microVM + in-VM daemon):**
|
||||
```bash
|
||||
e2b template build -n wiggle -d Dockerfile.recovered # tested with e2b SDK 1.7.0
|
||||
## How it works
|
||||
|
||||
- Claude decides to run something and sends the command over a private
|
||||
channel to a supervisor process inside the VM.
|
||||
- The supervisor starts the command with a fixed environment (see
|
||||
`meta/env-contract.md`) and streams the output back.
|
||||
- The command runs as root with no seccomp and no user sandbox. Inside this
|
||||
VM, root is normal. The isolation that matters is the VM boundary itself.
|
||||
- User files arrive as mounted folders under `/mnt/user-data`. In production
|
||||
those are remote storage; here they are plain directories.
|
||||
|
||||
Machine spec to match if you care about parity: 1 vCPU, 3.9 GiB RAM, no
|
||||
swap. Details in `meta/box-spec.md`.
|
||||
|
||||
## What is inside
|
||||
|
||||
| Capability | What gives you that |
|
||||
|---|---|
|
||||
| Documents (docx, xlsx, pptx, pdf) | LibreOffice 24.2, pandoc, python-docx, openpyxl |
|
||||
| OCR (make scans searchable) | tesseract 5 with English models, byte-identical to the public Google release |
|
||||
| File-type detection | magika (Google model, public build) |
|
||||
| Web automation and screenshots | Playwright 1.56 with a pinned Chromium at `/opt/pw-browsers`, plus puppeteer with Chrome-headless-shell |
|
||||
| Diagrams and charts | mermaid-cli, matplotlib, graphviz |
|
||||
| Numbers and data | pandas, numpy, scipy, scikit-learn, Jupyter |
|
||||
| Typesetting and PDFs | TeX Live 2023, poppler, qpdf, ImageMagick, wkhtmltopdf |
|
||||
| Media | ffmpeg |
|
||||
| **The playbooks Claude follows** | `/mnt/skills`, 40 skills, verbatim |
|
||||
|
||||
The skills are the interesting part. They are plain Markdown instruction
|
||||
files that Claude reads before doing certain jobs: how to fill a PDF form,
|
||||
how to run deep research (including the sub-agent prompts), how to drive the
|
||||
desktop with computer use, how to build a skill. Public ones cover office
|
||||
documents and file reading; example ones cover deep-research, morning
|
||||
briefings, painting, MCP server building, and more.
|
||||
|
||||
Two custom Anthropic binaries are included: `extract-text` (turns uploaded
|
||||
documents into text, Rust) and the mount daemon interface (`rclone-filestore`,
|
||||
Go, documented in `meta/filestore-api.md`).
|
||||
|
||||
## What is not included
|
||||
|
||||
Four things live outside the filesystem, so no dump could contain them:
|
||||
|
||||
- The supervisor binary itself (runs from RAM, never from disk).
|
||||
- The model, which is remote by definition.
|
||||
- Anthropic's egress firewall and its CA roots.
|
||||
- The remote storage service behind `/mnt/user-data` (contract documented).
|
||||
|
||||
For e2b users: e2b's own daemon takes the supervisor's role, which is why
|
||||
this image drops straight into an e2b template.
|
||||
|
||||
## Using it with your own agent
|
||||
|
||||
Any agent that can shell into a container can use this box exactly the way
|
||||
Claude does: read the relevant `/mnt/skills/*/SKILL.md`, then run the tools
|
||||
it names. Inject the environment from `meta/env-contract.md` and the
|
||||
behavior matches production, quirks included.
|
||||
|
||||
## Known quirks (present in the original, kept on purpose)
|
||||
|
||||
- `pandoc x.md -o x.pdf` fails until you add `fonts-lmodern` or use the
|
||||
Chromium fallback. The real sandbox has the same hole.
|
||||
- `pip install` is blocked by PEP 668. Use `uv`. The offline wheel cache is
|
||||
included.
|
||||
- ImageMagick has no SVG coder and ignores `-quality` for webp. Use
|
||||
Pillow or sharp.
|
||||
- Tool exit codes lie often. Verify outputs (`test -s out && file out`)
|
||||
instead of trusting success.
|
||||
- `extract-text` segfaults under x86 emulation on ARM Macs. It is fine on
|
||||
native x86-64 hosts.
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
Dockerfile recovered build recipe (verified to build green)
|
||||
_context/ build inputs: pinned manifests + the Anthropic binaries
|
||||
mnt-skills/ the 40 skills, copied to /mnt/skills
|
||||
meta/ manifests, env contract, filestore API, machine spec
|
||||
ROOTFS.md how to get the exact live filesystem dump
|
||||
```
|
||||
then `Sandbox(template="wiggle")` — your agent sees Claude's sandbox: same binaries, same paths, same skills, same quirks. (The original's PID 1 `process_api` is replaced by e2b's `envd` — role-equivalent.)
|
||||
|
||||
**Modal / any Firecracker or container platform:** the Dockerfile is plain `ubuntu:24.04` + steps; nothing Anthropic-proprietary required.
|
||||
## License
|
||||
|
||||
## Notes
|
||||
The build recipe, scripts, and documentation in this repo are MIT (`LICENSE`).
|
||||
|
||||
- The original mounts a per-conversation remote filesystem (custom rclone backend over Anthropic's Filestore API). Here `/mnt/user-data/*` are local dirs; the RPC contract is documented in `meta/filestore-api.md`.
|
||||
- Not included (Anthropic-side by design): the in-VM supervisor binary, MITM egress CAs, the telemetry collector. Egress in production is an allowlist (PyPI/npm/GitHub/Ubuntu/Anthropic API) — configure in your platform.
|
||||
- Verified (parity smoke-test of the imported live rootfs, Docker on Apple Silicon): 866/866 packages, LibreOffice 24.2.7.2, pandoc 3.1.3, magika 1.0.1, node 22.22.2, python 3.12.3 + full 114-dist stack (pandas/numpy/pdfplumber/pypdfium2/playwright/cv2 import OK), 21 npm globals, skills present, env contract intact. Two notes: `extract-text` SIGSEGVs only under Rosetta/qemu x86 emulation (ARM hosts) — fine on native x86-64; `/mnt/user-data/outputs` + `tool_results` are mount points that exist only when the (Anthropic-side) mounts are attached — `mkdir` them or use the Dockerfile, which creates them.
|
||||
Everything Anthropic-made is theirs, not MIT:
|
||||
|
||||
<!-- parity verified 2026-09-04: docker import of live rootfs -> 866 pkgs, all stacks OK -->
|
||||
- The 40 skill files in `mnt-skills/` and the Anthropic binaries each carry
|
||||
Anthropic's own license (`NOTICE` and each `LICENSE.txt`), which does not
|
||||
clearly allow redistribution. They are here as captured research artifacts.
|
||||
- If you would rather not ship them, delete `mnt-skills/` and remove the one
|
||||
`COPY mnt-skills/` line from the Dockerfile. The box still builds and runs;
|
||||
any user can re-obtain the skills by asking Claude to show them.
|
||||
|
||||
- Everything here was reconstructed from the container itself + public CVE data; see the write-up: [link to your post].
|
||||
|
||||
*Not affiliated with Anthropic. Reproduce freely.*
|
||||
Not affiliated with Anthropic.
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
# Getting the rootfs
|
||||
|
||||
Two ways, from fastest to most authentic:
|
||||
Two ways, from fastest to most authentic.
|
||||
|
||||
## 1. Rebuilt image (recommended)
|
||||
|
||||
## 1. Rebuilt image (recommended for most users)
|
||||
```bash
|
||||
docker build -t wiggle .
|
||||
```
|
||||
The `Dockerfile` is the build recipe recovered from the container's own package timestamps + logs — it replays Anthropic's exact build (verified green). ~25–40 min, produces an image functionally equal to the live sandbox (minus Anthropic-only daemons). On e2b: `e2b template build -n wiggle` (needs the e2b CLI + `E2B_API_KEY`).
|
||||
|
||||
## 2. Exact live rootfs (byte-level, session export)
|
||||
The GitHub release `v1.0-live-rootfs` carries the **complete filesystem of a live session**, split in two (GitHub's 2 GiB asset cap):
|
||||
The `Dockerfile` is the build recipe recovered from the container's own
|
||||
package timestamps and logs. It replays the exact build (verified green).
|
||||
Takes about 25 to 40 minutes and produces an image functionally equal to the
|
||||
live sandbox, minus the Anthropic-only daemons. On e2b:
|
||||
`e2b template build -n wiggle` (needs the e2b CLI and `E2B_API_KEY`).
|
||||
|
||||
## 2. Exact live rootfs (byte-level session export)
|
||||
|
||||
The GitHub release `v1.0-live-rootfs` carries the complete filesystem of a
|
||||
live session, split in two for GitHub's 2 GiB asset limit:
|
||||
|
||||
```bash
|
||||
# download wiggle-part-aa and wiggle-part-ab from the release, then:
|
||||
cat wiggle-part-aa wiggle-part-ab > wiggle-rootfs-sanitized.tar.zst
|
||||
@@ -23,11 +32,23 @@ zstd -d -c wiggle-rootfs-sanitized.tar.zst | docker import - wiggle:live
|
||||
docker run --rm wiggle:live /usr/bin/soffice --version # LibreOffice 24.2.7.2
|
||||
```
|
||||
|
||||
## Sanitization (what was changed from the raw export — nothing else was touched)
|
||||
- `claude_chat_01EavkjP4hzEzMFe3KCQAVip` → `claude_chat_REDACTED` (paths + 5 files)
|
||||
- cgroup session hashes `732b735e…` / `6c2653aa…` → `cgroup_session_REDACTED`
|
||||
- `container_01QJ16vmsgBTwzVoBf5YMxqf--wiggle--<hex>` → `container_REDACTED--wiggle--REDACT`
|
||||
- removed: root-level `container_info.json` (session id)
|
||||
- deleted: session-runtime strays only if you chose (current tarball **keeps** `home/claude/{bg-test,zip*.log,cp*.log,virtual-fs-snapshot/}`, `req/`, `inspect/` — they're part of the session story)
|
||||
- tar packaged with `--owner=0 --group=0 --numeric-owner --no-xattrs` (macOS metadata/username stripped)
|
||||
- **Independent verification: zero private keys, zero credentials, zero cookies, zero shell history, zero user files besides `mnt/user-data/uploads/pdfcrowd.pdf` (the researcher's own test upload — kept intentionally)**
|
||||
## Sanitization
|
||||
|
||||
What was changed from the raw export. Nothing else was touched.
|
||||
|
||||
- `claude_chat_01EavkjP4hzEzMFe3KCQAVip` set to `claude_chat_REDACTED`
|
||||
(directory names and 5 files)
|
||||
- cgroup session hashes `732b735e...` and `6c2653aa...` set to
|
||||
`cgroup_session_REDACTED`
|
||||
- `container_01QJ16vmsgBTwzVoBf5YMxqf--wiggle--<hex>` set to
|
||||
`container_REDACTED--wiggle--REDACT`
|
||||
- removed the root-level `container_info.json` (it held the session id)
|
||||
- tar packaged with `--owner=0 --group=0 --numeric-owner --no-xattrs`, so
|
||||
macOS metadata and the local username are stripped
|
||||
- verified clean: zero private keys, zero credentials, zero cookies, zero
|
||||
shell history, and no user files except `mnt/user-data/uploads/pdfcrowd.pdf`
|
||||
(the researcher's own test upload, kept on purpose)
|
||||
|
||||
The session-runtime strays (`home/claude/zip.log` and friends, `req/`,
|
||||
`inspect/`, the `virtual-fs-snapshot/` folder) are kept. They are part of the
|
||||
session, and they show what a live box actually accumulates.
|
||||
|
||||
+32
-22
@@ -1,18 +1,19 @@
|
||||
# Machine spec — what to configure when you run Wiggle
|
||||
# Machine spec
|
||||
|
||||
The image is the disk. To match Anthropic's sandbox, configure the VM around it like this:
|
||||
The image is the disk. To match Anthropic's sandbox, configure the VM around
|
||||
it like this:
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| vCPU | **1** |
|
||||
| RAM | **3.9 GiB** (4,093,820 kB), no swap |
|
||||
| `/dev/shm` | = full RAM (their images depend on it; see OOM caveat below) |
|
||||
| Disk | 256 GiB rw ext4 (+ read-only layers for skills if you want the same layout) |
|
||||
| Privilege | agent runs as **root**, 40 capabilities (`−CAP_SYS_RESOURCE`), no seccomp, no LSM, no PID namespace |
|
||||
| Cgroups | all **unlimited** (they account per-session but enforce nothing) |
|
||||
| Per-call budget | ~300 s |
|
||||
| Kernel | any 6.x; they ship a custom 6.18 Firecracker build (not required) |
|
||||
| Network | egress allowlist: PyPI, npm, GitHub, Ubuntu archives, Anthropic API (swap in your own LLM API host); they also TLS-inspect outbound — optional |
|
||||
| vCPU | 1 |
|
||||
| RAM | 3.9 GiB (4,093,820 kB), no swap |
|
||||
| `/dev/shm` | their images assume it equals full RAM; see the OOM note below |
|
||||
| Disk | 256 GiB rw ext4, plus read-only layers for skills if you want the layout |
|
||||
| Privilege | commands run as root, 40 capabilities (`-CAP_SYS_RESOURCE`), no seccomp, no LSM, no PID namespace |
|
||||
| Cgroups | all unlimited. They account per session but enforce nothing |
|
||||
| Per-call budget | about 300 seconds |
|
||||
| Kernel | any 6.x. They ship a custom 6.18 Firecracker build; not required |
|
||||
| Network | egress allowlist: PyPI, npm, GitHub, Ubuntu archives, Anthropic API (swap in your own LLM host). They also TLS-inspect outbound; optional |
|
||||
|
||||
## Run it
|
||||
|
||||
@@ -20,24 +21,33 @@ The image is the disk. To match Anthropic's sandbox, configure the VM around it
|
||||
# Docker
|
||||
docker run -it --memory=3900m --memory-swap=3900m --cpus=1 --shm-size=3900m wiggle bash
|
||||
|
||||
# e2b / Modal / Firecracker hosts: vCPU=1, RAM≈3.9GiB, no swap
|
||||
# e2b / Modal / Firecracker hosts: vCPU=1, RAM about 3.9 GiB, no swap
|
||||
```
|
||||
|
||||
## Exec environment (inject these — this is what Claude's commands actually see)
|
||||
## Exec environment
|
||||
|
||||
Inject these. This is what Claude's commands actually see.
|
||||
|
||||
```
|
||||
IS_SANDBOX=yes HOME=/root PYTHONUNBUFFERED=1 DEBIAN_FRONTEND=noninteractive
|
||||
PATH=/home/claude/.npm-global/bin:/usr/local/bin:... # note: HOME=/root but PATH leads /home/claude — intentional
|
||||
PATH=/home/claude/.npm-global/bin:/usr/local/bin:...
|
||||
PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers
|
||||
NODE_PATH=/usr/local/lib/node_modules_global
|
||||
PIP_ROOT_USER_ACTION=ignore
|
||||
# LANG intentionally unset; CA bundle vars point at the system store
|
||||
```
|
||||
Full list + rationale: `meta/env-contract.md`
|
||||
|
||||
## Known gotchas (all authentic — present in the original)
|
||||
- `pandoc x.md -o x.pdf` fails out of the box (needs `lmodern.sty` / a fallback engine) — see README
|
||||
- ImageMagick: no SVG coder, webp ignores `-quality` — use Pillow/sharp
|
||||
- `pip install` blocked by PEP 668 — use `uv` (offline cache included)
|
||||
- Never trust tool exit codes in this image — verify artifacts (`test -s out && file out`); full rule list: `meta/` + the 38-rule playbook in the accompanying writeup
|
||||
- Big writes to `/dev/shm`-adjacent paths can OOM a 3.9 GiB box if you also set shm to full RAM (Anthropic ships exactly this hazard; lower `--shm-size` to ~1 GiB if you don't need it)
|
||||
Two intentional oddities: `HOME` is `/root` but `PATH` leads with
|
||||
`/home/claude`, and `LANG` is left unset. Full list with rationale:
|
||||
`meta/env-contract.md`.
|
||||
|
||||
## Gotchas (all present in the original)
|
||||
|
||||
- `pandoc x.md -o x.pdf` fails until you add `fonts-lmodern` or use a
|
||||
fallback engine. See the README.
|
||||
- ImageMagick has no SVG coder and ignores `-quality` on webp. Use Pillow or
|
||||
sharp.
|
||||
- `pip install` is blocked by PEP 668. Use `uv`. The offline cache is included.
|
||||
- Do not trust tool exit codes. Verify the artifact (`test -s out && file out`).
|
||||
- `/dev/shm` at full RAM is a hazard on a 3.9 GiB box under heavy writes.
|
||||
Anthropic ships it that way. Drop `--shm-size` to about 1 GiB if you do not
|
||||
need the big one.
|
||||
|
||||
+30
-11
@@ -1,27 +1,46 @@
|
||||
# The exec-environment contract (what every command Claude runs gets)
|
||||
# The exec-environment contract
|
||||
|
||||
Injected by the supervisor at exec handoff. Reproduced here for reference / replication:
|
||||
What every command Claude runs receives, injected by the supervisor at
|
||||
handoff. Reproduce it and behavior matches production.
|
||||
|
||||
```
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
HOME=/root # note: PATH leads /home/claude/* — the HOME/PATH split is original
|
||||
HOME=/root
|
||||
IS_SANDBOX=yes
|
||||
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
|
||||
LANG=<absent> # deliberate: causes documented ASCII behaviors
|
||||
LANG=<absent on purpose>
|
||||
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
|
||||
NODE_PATH=/usr/local/lib/node_modules_global
|
||||
NPM_CONFIG_USERCONFIG=/home/claude/.npmrc # file exists but is NOT read (HOME=/root) — original quirk
|
||||
NPM_CONFIG_USERCONFIG=/home/claude/.npmrc
|
||||
PATH=/home/claude/.npm-global/bin:/usr/local/bin:...
|
||||
PIP_CACHE_DIR=... PIP_CONFIG_FILE=... PIP_ROOT_USER_ACTION=ignore
|
||||
PIP_CACHE_DIR=<set>
|
||||
PIP_CONFIG_FILE=<set>
|
||||
PIP_ROOT_USER_ACTION=ignore
|
||||
PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers
|
||||
PYTHONUNBUFFERED=1
|
||||
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
|
||||
SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
TERM=<set>
|
||||
SBX_TELEMETRY_SOCKET=<set on current builds> # telemetry ingest door (see filestore-api.md sibling note)
|
||||
SBX_TELEMETRY_SOCKET=<set on current builds>
|
||||
```
|
||||
|
||||
- The CA trio point at a 150-cert bundle = 146 stock + **4 Anthropic egress-inspection roots** (2 generations × prod/staging) — in this reproduction they simply trust the normal store.
|
||||
- Privilege: commands run **uid 0**, `CapEff = all − CAP_SYS_RESOURCE`, `Seccomp: 0`, shared PID namespace, no LSM (by design, per Anthropic triage: root is confined to the single-tenant sandbox).
|
||||
- Resources: 1 vCPU @2.1GHz, 3.9 GiB RAM (host balloon = real memory limiter), no swap, no `pids.max`; per-tool-call budget ~300 s.
|
||||
- `/etc/sandbox-release` here carries the `IS_SANDBOX` marker for detection.
|
||||
Three things worth knowing, all authentic:
|
||||
|
||||
- `HOME` is `/root` but `PATH` leads with `/home/claude`. The mismatch is
|
||||
real and causes the Playwright/browser path quirks people hit.
|
||||
- `NPM_CONFIG_USERCONFIG` points at a file that is never read, because
|
||||
`HOME` is `/root`. Original quirk, kept.
|
||||
- `LANG` is deliberately unset, which is why some tools behave as pure ASCII.
|
||||
|
||||
Notes:
|
||||
|
||||
- The three CA variables point at a 150-cert bundle: 146 stock plus 4
|
||||
Anthropic egress-inspection roots (two generations, prod and staging). In
|
||||
this reproduction they just trust the normal system store.
|
||||
- Privilege: commands run as uid 0, `CapEff` is all capabilities minus
|
||||
`CAP_SYS_RESOURCE`, `Seccomp` is 0, shared PID namespace, no LSM. This is
|
||||
by design; Anthropic treats root here as confined to the single-tenant VM.
|
||||
- Resources: 1 vCPU, 3.9 GiB RAM with a host balloon as the real limiter, no
|
||||
swap, no `pids.max`, and a per-call budget of about 300 seconds.
|
||||
- `/etc/sandbox-release` in this image carries the `IS_SANDBOX` marker so a
|
||||
script can detect it is in the sandbox.
|
||||
|
||||
+64
-7
@@ -1,9 +1,66 @@
|
||||
# Filestore API (what the mount daemon speaks)
|
||||
# Filestore API
|
||||
|
||||
Recovered from the `rclone-filestore` binary (Go, custom build; symbols recovered via pclntab parse):
|
||||
What the mount daemon (`rclone-filestore`, a custom Go build) speaks.
|
||||
Recovered from the binary's symbols. Included so you can build a stand-in
|
||||
backend if you want the mounts to behave like production.
|
||||
|
||||
- Backend: `https://api.anthropic.com` — custom RPC paths under `v1/filestore/fs/*`:
|
||||
`listDirectory`, `createFile`, `readMetadata`, `readFile`, `writeFile`, `deleteFile`, `moveFile` (per-mount JWT via `Authorization: Bearer`, injected out-of-band — **never present in the on-disk mount JSON**)
|
||||
- Mount config: `/tmp/rclone-mount-config.json` — `{mounts[]{source,destination,filesystem_id,readonly,file_perms,dir_perms,uid:999,gid:1000,vfs_cache_mode:"full",vfs_cache_max_size:"1G",cache_duration_s:1|3|10|3600}, service_url, state_dir, ready_file}` (session ids redacted in this copy)
|
||||
- Semantics that differ from POSIX (verified): `fsync` = no network flush (upload deferred ~5–6 s); `chmod`/`xattr`/symlinks = no-ops; locks never reach the server; every write uploads `overwriteExisting:true`; dir-cache TTL per mount (3600 s on outputs).
|
||||
- Reproduction note: local dirs stand in for the mounts; to fake the backend, the RPC set above + JSON responses is a ~200-line stub server.
|
||||
## Transport
|
||||
|
||||
- Backend base: an API host (production uses `https://api.anthropic.com`)
|
||||
- Auth: per-mount JWT as `Authorization: Bearer <token>`
|
||||
- The token is injected out of band. It is never present in the on-disk
|
||||
mount config.
|
||||
|
||||
## RPC surface (custom JSON paths under `v1/filestore/fs/`)
|
||||
|
||||
| Call | Purpose |
|
||||
|---|---|
|
||||
| `listDirectory` | list a folder |
|
||||
| `createFile` | start/register an upload |
|
||||
| `readMetadata` | stat a file |
|
||||
| `readFile` | download |
|
||||
| `writeFile` | upload content |
|
||||
| `deleteFile` | remove |
|
||||
| `moveFile` | move or rename |
|
||||
|
||||
## Mount config shape (`/tmp/rclone-mount-config.json`)
|
||||
|
||||
```json
|
||||
{
|
||||
"service_url": "https://api.anthropic.com",
|
||||
"state_dir": "/tmp/rclone-mounts",
|
||||
"ready_file": "/tmp/rclone-mounts/ready",
|
||||
"mounts": [
|
||||
{
|
||||
"source": "/outputs",
|
||||
"destination": "/mnt/user-data/outputs",
|
||||
"filesystem_id": "claude_chat_<id>",
|
||||
"readonly": false,
|
||||
"file_perms": "0644", "dir_perms": "0755",
|
||||
"uid": 999, "gid": 1000,
|
||||
"vfs_cache_mode": "full", "vfs_cache_max_size": "1G",
|
||||
"cache_duration_s": 3600
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Four mounts ship: `uploads` (ro, 1 s cache), `tool_results` (ro, 3 s),
|
||||
`transcripts` (ro, 10 s), `outputs` (rw, 3600 s).
|
||||
|
||||
## Semantics that differ from POSIX (all verified)
|
||||
|
||||
These are the surprising ones, and the reason a stub backend should mimic
|
||||
them if you want true parity:
|
||||
|
||||
- `fsync` performs no network flush. Uploads are deferred about 5 to 6
|
||||
seconds. A kill inside that window loses the file.
|
||||
- `chmod`, extended attributes, and symlinks are accepted but are no-ops.
|
||||
- Locks never reach the server. Two VMs on one filesystem id can overwrite
|
||||
each other silently.
|
||||
- Every write uploads with `overwriteExisting: true`.
|
||||
- Reported file permissions are synthetic (constant per config), not real.
|
||||
|
||||
A faithful stand-in backend is roughly a small HTTP server implementing the
|
||||
seven calls above plus the deferred-upload behavior. The read-only mounts can
|
||||
also just be plain local directories for most agent use cases.
|
||||
|
||||
Reference in New Issue
Block a user