Retrieved article excerpt
Open article Β· Retrieved 2026-09-11T04:23:20.267649+00:00
What's inside Claude Code Web: an unstripped Go binary, Anthropic's secret deployment platform, and the architecture of an AI-native PaaS The Starting Point We are building ArcBox, a full-stack platform from Desktop to Platform, similar to Railway and E2B in positioning. Our core philosophy is local-cloud consistency: replacing OrbStack with a fully open-source ArcBox Desktop that provides Sandbox capabilities locally. Recently, we noticed more and more Coding Agent platforms launching web-based entry points, and remarkably, nearly all of them chose Firecracker under the hood. Claude Code is no exception. As practitioners in the same space, curiosity about its runtime environment led to some digging. What began as a casual strace -p 1 turned into a full reverse-engineering session that uncovered unreleased Anthropic infrastructure, including an entirely undocumented application hosting platform. Everything described here was discovered through standard Linux tooling ( strace , strings , objdump , go tool objdump ) running inside a Claude Code session. No exploits, no privilege escalation, no network attacks. The binary was sitting right there, unstripped, with full debug symbols. Layer 1: It's a Firecracker MicroVM The first question: what exactly is this environment? $ dmesg | grep FIRECK ACPI: RSDP 0x00000000000E0000 000024 (v02 FIRECK) ACPI: XSDT ... (v01 FIRECK FCMVXSDT ... FCAT 20240119) ACPI: FACP ... (v06 FIRECK FCVMFADT ... FCAT 20240119) ACPI: DSDT ... (v02 FIRECK FCVMDSDT ... FCAT 20240119) The ACPI tables are signed with OEM ID FIRECK and creator ID FCAT , both hardcoded in Firecracker's source code . This is the same MicroVM technology that powers AWS Lambda and Fargate. The specs: 4 vCPUs (Intel Xeon Cascade Lake @ 2.80GHz), 16GB RAM, 252GB disk, Linux 6.18.5. No nested virtualization since Firecracker intentionally strips vmx / svm flags from guests. The process tree is absurdly minimal: PID 1: /process_api --firecracker-init --addr 0.0.0.0:2024 ... ββ PID 517: /usr/local/bin/environment-manager task-run --session cse_... ββ PID 532: claude (the CLI itself) No systemd. No sshd. No cron. No logging daemon. PID 1 is a custom binary that acts as both init and a WebSocket API gateway. The kernel command line confirms it: rdinit=/process_api init_on_free=1 -- --firecracker-init reboot=k panic=1 nomodule strace on PID 1 shows it running an epoll event loop, periodically checking /proc/*/children and /proc/*/status to monitor child processes. Essentially a minimal init supervisor, listening on port 2024 (WebSocket API) and port 2025 (secondary endpoint). The Snapshot Architecture Sessions don't boot from scratch β they're restored from frozen VM snapshots. The dmesg output reveals a 48.5-hour gap between template creation and session restore: [ 30.731516] Run /process_api as init process β Template: 2026-03-16 13:53 UTC ~~~ 48.5 HOUR GAP β VM WAS FROZEN AS SNAPSHOT ~~~ [174695.927758] virtio_blk: [vdc] new size: ... β Restored: 2026-03-18 14:24 UTC [174695.953952] random: crng reseeded due to virtual machine fork [174695.980760] tokio-runtime-w: drop_caches: 3 [174695.993628] EXT4-fs (vda): mounted filesystem r/w without journal During restore, the Firecracker host hot-swaps block devices : Device Template After Restore Content vda placeholder 256 GiB ext4 Session rootfs (Ubuntu 24.04) vdb placeholder 63.7 MB squashfs /opt/claude-code vdc placeholder 12.1 MB squashfs /opt/env-runner The initramfs is deliberately minimal: a 3.1MB cpio archive containing only /process_api . The actual Ubuntu rootfs is on the ext4 block device (vda), injected at restore time. The ext4 has mount count=11, indicating the image has been reused across 11 sessions. Snapstart: The Deferred Mount Pattern Template creation phase: Firecracker boots: kernel + 3.1MB initramfs process_api performs minimal init: mount /proc , /sys , /dev , cgroups; configure networking (IP=192.0.2.2/24, GW=192.0.2.1, MTU=1400) Signals SNAPSTART_READY to the host Host calls PUT /snapshot/create β saves entire VM state Session restore phase: Host prepares session-specific block devices (vda/vdb/vdc) Host calls PUT /snapshot/load with new device backends VM resumes β kernel detects device changes, reseeds CRNG process_api detects restore and runs: Drop page caches β stale template cache would return garbage Remount devtmpfs β refresh device nodes Mount ext4 β pivot_root to new rootfs Mount squashfs overlays (claude-code, env-runner) Fix wall clock via clock_settime() β otherwise stuck at template epoch Drop CAP_SYS_RESOURCE β security hardening Accept connections β WebSocket server ready Security Measures Measure Purpose init_on_free=1 Zero freed pages between sessions CAP_SYS_RESOURCE drop Limit PID 1's capabilities post-init CRNG reseed Prevent crypto predictability across snapshot forks --block-local-connections Block localhost WebSocket access JWT auth WebSocket connection verification Token scrubbing Remove secrets from configs after use process_api: The Wire Protocol PID 1 exposes two network interfaces β a WebSocket API for process management and an HTTP API for container control. Unlike typical init systems, process_api is a Rust/tokio binary that implements a full remote process supervisor. WebSocket API (port 2024) Connection handshake: optional JWT β ProcessConnection JSON β process creation or reattach. Process creation accepts a CreateProcess struct: { "cmd" : "/bin/bash" , "args" : [ "-l" ], "env" : { "KEY" : "VALUE" }, "cwd" : "/home/user" , "rows" : 24 , "cols" : 80 , "timeout" : 300 , "memory_limit_bytes" : 1073741824 , "uid" : 1000 , "gid" : 1000 , "allow_process_id_reuse" : false } I/O uses a two-phase binary protocol: Stdin : ExpectStdIn (text) β binary frame Stdout/Stderr : ExpectStdOut / ExpectStdErr (text) β binary frame β StdOutEOF / StdErrEOF Client Server |--- WS Connect ------------------->| |--- JWT (optional) --------------->| |--- ProcessConnection JSON ------->| |<-- ProcessCreated ----------------| | | |<-- ExpectStdOut ------------------| |<-- [binary: stdout data] ---------| |--- ExpectStdIn ------------------>| |--- [binary: stdin data] --------->| | | |--- SendSignal ------------------->| SIGTERM, etc. |--- Resize ----------------------->| PTY resize |--- Detach ----------------------->| process keeps running |--- KeepAlive -------------------->| heartbeat | | |<-- ProcessExited -----------------| |<-- StdOutEOF --------------------| Process termination reasons include: normal exit, signal, per-process OOM, container-level OOM, timeout, and server shutdown. Internally, process_api tracks per-process cgroups (v1 at /sys/fs/cgroup/memory/process_api/ , v2 at /sys/fs/cgroup/process_api/ ), implements orphan adoption (reparenting to PID 1), and runs a configurable OOM polling loop. HTTP Control API (port 2025) Six endpoints manage container lifecycle: Endpoint Purpose GET /status Health check POST /fs_sync Flush filesystem buffers POST /shutdown Graceful shutdown with page cache drop POST /auth_public_key Set JWT verification key POST /mount_root Mount rootfs (snapstart restore) POST /container_name Set container identity The /mount_root endpoint accepts a MountRootConfig with network config ( etc_hosts , resolv_conf ), CA certs, squashfs mounts, FUSE mounts (with VFS cache config), and the wall clock timestamp β everything needed to initialize a session from a blank snapshot. During mount, root is frozen via FIFREEZE / FITHAW ioctls. Layer 2: The Unstripped Go Binary The real discovery was /usr/local/bin/environment-runner (symlinked as environment-manager ): $ file /usr/local/bin/environment-runner ELF 64-bit LSB executable, x86-64, dynamically linked, Go BuildID=..., with debug_info, not stripped $ go version -m /usr/local/bin/environment-runner go1.25.7 path github.com/anthropics/anthropic/api-go/environment-manager mod github.com/anthropics/anthropic/api-go (devel) build -ldflags=-X main.Version=staging-68f0dff496 A 27MB Go binary. Not stripped. Full debug info. Full symbol table. Built from Anthropic's private monorepo at github.com/anthropics/anthropic/api-go/environment-manager/ . Using go tool objdump and strings , the complete internal package structure can be extracted: internal/ βββ api/ # API client (session ingress, work polling, retry) βββ auth/ # GitHub app token provider βββ claude/ # Claude Code install, upgrade, execution βββ config/ # Session modes (new/resume/resume-cached/setup-only) βββ envtype/ β βββ anthropic/ # Anthropic-hosted environment β βββ byoc/ # Bring Your Own Cloud environment βββ gitproxy/ # Git credential proxy server βββ input/ # Stdin parser + secret handling βββ manager/ # Session manager, MCP config, skill extraction βββ mcp/ β βββ servers/ β βββ codesign/ # Code signing MCP server β βββ supabase/ # Supabase integration MCP server βββ orchestrator/ # Poll loop, hooks, whoami βββ podmonitor/ # Kubernetes lease manager βββ process/ # Process exec + script runner βββ sandbox/ # Sandbox runtime config βββ session/ # Activity recorder βββ sources/ # Git clone + source classification βββ tunnel/ # WebSocket tunnel + action handlers β βββ actions/ β βββ deploy/ # β THIS IS WHERE IT GETS INTERESTING β βββ snapshot/ # File snapshots β βββ status/ # Status reporting βββ util/ # Git helpers, retry, stream tailer Key dependencies extracted from the binary: Dependency Purpose github.com/anthropics/anthropic/api-go Internal Anthropic Go SDK github.com/gorilla/websocket WebSocket tunnel to API github.com/mark3labs/mcp-go v0.37.0 Model Context Protocol github.com/DataDog/datadog-go v5 Metrics reporting go.opentelemetry.io/otel v1.39.0 Distributed tracing google.golang.org/grpc v1.79.0 gRPC (session routing) github.com/spf13/cobra CLI framework Layer 3: Antspace, Anthropic's Hidden PaaS Inside the tunnel/actions/deploy/ package, there are function symbols for two deployment clients: VercelClient , the expected one: CreateDeployment β POST /v13/deployments UploadFile β PUT /v2/files with x-vercel-digest header WaitForReady β Poll until readyState == "READY" And then, AntspaceClient , the unexpected one: deploy.(*AntspaceClient).Deploy deploy.(*AntspaceClient).createDeployment deploy.(*AntspaceClient).uploadTarball deploy.(*AntspaceClient).streamStatus Extracting the associated strings from the binary revealed a complete deployment protocol: Phase 1: Create Deployment POST to antspaceControlPlaneURL Content-Type: application/json Authorization: Bearer {antspaceAuthToken} Body: { app name, metadata } Phase 2: Upload Build Artifact POST multipart/form-data File: dist.tar.gz (the built application) Size limit enforced: "project exceeds %dMB limit" Phase 3: Stream Deployment Status Response: application/x-ndjson (streaming) Status progression: packaging β uploading β building β deploying β deployed Error: "Streaming unsupported" if client can't handle NDJSON A search for "Antspace" across the entire public internet turned up nothing: Anthropic's website, GitHub, blog, documentation, LinkedIn, job postings, conference talks, patent filings. Zero results. This platform has never been publicly mentioned anywhere. The name likely derives from "Ant" (reportedly an internal nickname for Anthropic employees) + "Space" (hosting space), following the same naming pattern as platforms like Heroku or Vercel. Antspace vs. Vercel: Architectural Differences Aspect Vercel Antspace File upload SHA-based dedup, per-file Single tar.gz archive Build Remote (Vercel builds it) Local npm run build , upload output Status Polling-based Streaming NDJSON Auth Verce