Clean README, add LICENSE + NOTICE, tidy docs

This commit is contained in:
Your Name
2026-09-04 00:45:18 -07:00
parent b2fd3575ed
commit ef86224b2a
7 changed files with 325 additions and 87 deletions
+25
View File
@@ -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.
+30
View File
@@ -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.
+110 -34
View File
@@ -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.
+34 -13
View File
@@ -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). ~2540 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
View File
@@ -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, RAM3.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
View File
@@ -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
View File
@@ -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 ~56 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.