메뉴
HN
Hacker News • 15일 전

클로드 웹의 마이크로VM 리버스 엔지니어링: 앤스로픽의 숨겨진 '앤트스페이스' 플랫폼 발견

IMP
7/10
핵심 요약

개발자가 호기심으로 시작한 strace 조사가 Claude Code Web의 Go 바이너리 리버스 엔지니어링으로 발전해, 앤스로픽이 미공개한 애플리케이션 호스팅 플랫폼의 아키텍처가 드러났습니다. 세션은 Firecracker 스냅샷에서 복원되어 부팅 없이 즉시 시작되며, AWS Lambda/Fargate와 동일한 기술 위에 극도로 최소화된 init 환경으로 구동됩니다. 보안 강화(페이지 초기화, CRNG 재시드, capability 제거)도 스냅샷 재사용에 맞춰 정교하게 설계되어 있어, AI 네이티브 PaaS 설계의 실제 사례로서 중요합니다.

번역된 본문

Claude Code Web 안에는 무엇이 들어 있을까: 심볼이 제거되지 않은 Go 바이너리, 앤스로픽의 비공개 배포 플랫폼, 그리고 AI 네이티브 PaaS의 아키텍처

시작점

우리는 Railway와 E2B와 유사한 포지션의 데스크톱부터 플랫폼까지 아우르는 풀스택 플랫폼인 ArcBox를 개발 중이다. 우리의 핵심 철학은 로컬-클라우드 일관성이며, OrbStack을 완전한 오픈소스인 ArcBox Desktop으로 대체해 로컬에서 샌드박스 기능을 제공한다. 최근 점점 더 많은 코딩 에이전트 플랫폼이 웹 기반 진입점을 출시하고 있으며, 놀랍게도 거의 모두가 내부적으로 Firecracker를 선택했다. Claude Code도 예외는 아니었다. 같은 분야의 실무자로서 그 런타임 환경에 대한 호기심에 약간의 조사가 시작되었다. 가볍게 시작한 strace -p 1이 결국 앤스로픽의 미출시 인프라 — 전혀 문서화되지 않은 애플리케이션 호스팅 플랫폼까지 — 를 밝혀내는 본격적인 리버스 엔지니어링 세션으로 이어졌다. 여기서 설명하는 모든 것은 Claude Code 세션 안에서 표준 리눅스 도구(strace, strings, objdump, go tool objdump)로 발견되었다. 익스플로잇도, 권한 상승도, 네트워크 공격도 없었다. 바이너리는 디버그 심볼이 완전히 담긴 채, 심볼이 제거되지 않은 상태로 그대로 그 자리에 있었다.

레이어 1: Firecracker 마이크로VM이다

첫 번째 질문: 이 환경은 정확히 무엇인가? ACPI 테이블이 OEM ID FIRECK와 creator ID FCAT로 서명되어 있으며, 둘 다 Firecracker 소스 코드에 하드코딩된 값이다. 이는 AWS Lambda와 Fargate을 구동하는 것과 동일한 마이크로VM 기술이다. 사양은 다음과 같다: vCPU 4개(인텔 Xeon Cascade Lake @ 2.80GHz), RAM 16GB, 디스크 252GB, Linux 6.18.5. Firecracker가 의도적으로 게스트에서 vmx/svm 플래그를 제거하기 때문에 중첩 가상화는 불가능하다.

프로세스 트리는 놀라울 정도로 최소화되어 있다: systemd 없음. sshd 없음. cron 없음. 로깅 데몬 없음. PID 1은 init이자 WebSocket API 게이트웨이 역할을 모두 하는 커스텀 바이너리다. 커널 커맨드 라인이 이를 확인해 준다. PID 1에 strace를 걸면 epoll 이벤트 루프를 실행하며 주기적으로 /proc//children과 /proc//status를 확인해 자식 프로세스를 모니터링한다. 본질적으로 최소화된 init 슈퍼바이저로, 포트 2024(WebSocket API)와 포트 2025(보조 엔드포인트)에서 대기한다.

스냅샷 아키텍처

세션은 처음부터 부팅되지 않고, 동결된 VM 스냅샷에서 복원된다. dmesg 출력은 템플릿 생성과 세션 복원 사이에 48.5시간의 간격이 있었음을 보여준다. 복원 시 Firecracker 호스트는 블록 디바이스를 핫스왑한다:

디바이스 | 템플릿 | 복원 후 내용 vda | 플레이스홀더 | 256 GiB ext4 — 세션 루트 파일시스템(Ubuntu 24.04) vdb | 플레이스홀더 | 63.7 MB squashfs — /opt/claude-code vdc | 플레이스홀더 | 12.1 MB squashfs — /opt/env-runner

initramfs는 의도적으로 최소화되어 있다: /process_api만 담긴 3.1MB cpio 아카이브다. 실제 Ubuntu 루트 파일시스템은 ext4 블록 디바이스(vda)에 있으며, 복원 시점에 주입된다. ext4의 마운트 횟수는 11로, 해당 이미지가 11개 세션에 걸쳐 재사용되었음을 나타낸다.

Snapstart: 지연 마운트 패턴

템플릿 생성 단계:

  • Firecracker 부팅: 커널 + 3.1MB initramfs
  • process_api가 최소 init 수행: /proc, /sys, /dev, cgroups 마운트; 네트워크 구성(IP=192.0.2.2/24, GW=192.0.2.1, MTU=1400)
  • 호스트에 SNAPSTART_READY 신호 전송
  • 호스트가 PUT /snapshot/create 호출 → 전체 VM 상태 저장

세션 복원 단계:

  • 호스트가 세션 전용 블록 디바이스(vda/vdb/vdc) 준비
  • 호스트가 새 디바이스 백엔드와 함께 PUT /snapshot/load 호출
  • VM 재개 — 커널이 디바이스 변경을 감지하고 CRNG 재시드
  • process_api가 복원을 감지하고 다음 수행:
    • 페이지 캐시 삭제 — 오래된 템플릿 캐시는 쓰레기 데이터를 반환할 수 있음
    • devtmpfs 재마운트 — 디바이스 노드 갱신
    • ext4 마운트 → 새 루트 파일시스템으로 pivot_root
    • squashfs 오버레이 마운트(claude-code, env-runner)
    • clock_setime()으로 벽시계 시간 보정 — 그렇지 않으면 템플릿 생성 시각에 멈춰 있음
    • CAP_SYS_RESOURCE 제거 — 보안 강화
    • 연결 수락 — WebSocket 서버 준비 완료

보안 조치

조치 | 목적 init_on_free=1 | 세션 간 해제된 페이지를 0으로 초기화 CAP_SYS_RESOURCE 제거 | init 완료 후 PID 1의 capability 제한 CRNG 재시드 | 스냅샷 포크 간 암호학적 예측 가능성 방지

원문 보기
원문 보기 (영어)
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? 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: 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: 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: 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: I/O uses a two-phase binary protocol: Stdin : ExpectStdIn (text) → binary frame Stdout/Stderr : ExpectStdOut / ExpectStdErr (text) → binary frame → StdOutEOF / StdErrEOF 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 ): 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: 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: Extracting the associated strings from the binary revealed a complete deployment protocol: Phase 1: Create Deployment Phase 2: Upload Build Artifact Phase 3: Stream Deployment Status 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 Vercel API token + Team ID Bearer token + dynamic control plane URL Public API Yes, documented No, completely internal The fact that Anthropic built a full deployme