Claude Web의 MicroVM 역공학 분석: Anthropic의 숨겨진 Antspace를 밝히다
요약
본 글은 Claude Code Web의 런타임 환경을 역공학 분석하여 Anthropic이 사용하는 내부 플랫폼 구조를 밝혀냈습니다. 분석 결과, 해당 환경은 AWS Lambda나 Fargate와 유사한 MicroVM 기술인 Firecracker 기반으로 운영되고 있음이 확인되었습니다. 이는 단순 코딩 에디터를 넘어선 완전한 AI 네이티브 PaaS 아키텍처임을 시사합니다.
핵심 포인트
- Claude Code Web은 Firecracker 기반의 MicroVM 환경을 사용함.
- Anthropic은 자체적인 'Antspace'라는 플랫폼을 운영 중임.
- 시스템d, sshd 등 일반 데몬 없이 커스텀 바이너리로 구동됨.
- 로컬-클라우드 일관성을 추구하는 PaaS 아키텍처가 핵심 철학임.
Claude Code Web 내부에는 무엇이 있을까: 미처리된 Go 바이너리, Anthropic의 비밀 배포 플랫폼, 그리고 AI 네이티브 PaaS의 아키텍처
저희는 Railway나 E2B와 유사한 포지셔닝을 가진 데스크톱부터 플랫폼까지 풀스택 플랫폼인 ArcBox를 구축하고 있습니다. 저희의 핵심 철학은 로컬-클라우드 일관성(local-cloud consistency)이며, OrbStack을 완전히 오픈 소스로 제공하는 ArcBox Desktop으로 대체하여 로컬에서 샌드박스 기능을 제공합니다. 최근 들어 코딩 에이전트 플랫폼들이 웹 기반 진입점을 많이 출시하고 있다는 것을 알게 되었고, 놀랍게도 거의 모든 플랫폼이 내부적으로 Firecracker를 사용한다는 것을 발견했습니다. Claude Code 역시 예외가 아니었습니다. 같은 분야의 실무자로서, 그 런타임 환경에 대한 호기심은 몇 가지 탐색으로 이어졌습니다. 단순한 strace -p 1로 시작했던 것이
전혀 공개되지 않은 Anthropic 인프라를 포함하여 완전히 문서화되지 않은 애플리케이션 호스팅 플랫폼의 역공학 세션 전체로 발전했습니다.
여기에 설명된 모든 내용은 표준 Linux 도구(strace, strings, objdump, go tool objdump)를 Claude Code 세션 내부에서 실행하여 발견되었습니다. 익스플로잇도, 권한 상승도, 네트워크 공격도 없었습니다. 바이너리는 그저 거기에 있었고, 미처리된 상태에 완전한 디버그 심볼이 포함되어 있었습니다.
첫 번째 질문: 이 환경은 정확히 무엇일까요?

$ dmesg | grep FIRECK
ACPI: RSDP 0x00000000000E0000 000024 (v02 FIRECK)
ACPI: XSDT ... (v01 FIRECK FCMVXSDT ... FCAT 20240119)
...
ACPI 테이블은 OEM ID FIRECK와 생성자 ID FCAT로 서명되어 있으며, 이 둘 모두 Firecracker의 소스 코드에 하드코딩되어 있습니다. 이것은 AWS Lambda와 Fargate를 구동하는 것과 동일한 MicroVM 기술입니다.


사양: 4 vCPU (Intel Xeon Cascade Lake @ 2.80GHz), RAM 16GB, 디스크 252GB, Linux 6.18.5. Firecracker가 게스트로부터 vmx/svm 플래그를 의도적으로 제거하기 때문에 중첩 가상화(nested virtualization)는 없습니다.
프로세스 트리는 터무니없이 최소한입니다:
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)
시스템d(systemd)도, sshd도, cron도, 로깅 데몬도 없습니다. PID 1은 init 역할과 WebSocket API 게이트웨이 역할을 동시에 수행하는 커스텀 바이너리입니다. 커널 명령줄에서 이를 확인할 수 있습니다:
rdinit=/process_api init_on_free=1 -- --firecracker-init
reboot=k panic=1 nomodule
strace를 PID 1에 사용하면 epoll 이벤트 루프가 실행되는 것을 볼 수 있으며, 주기적으로 /proc/*/children와 /proc/*/status를 확인하여 자식 프로세스를 모니터링합니다. 본질적으로 최소한의 init 슈퍼바이저 역할을 하며, 포트 2024(WebSocket API)와 포트 2025(보조 엔드포인트)에서 리스닝합니다.
세션은 처음부터 부팅되는 것이 아니라, 동결된 VM 스냅샷으로부터 복원됩니다. dmesg 출력에는 템플릿 생성과 세션 복원 사이에 48.5시간의 간격이 드러납니다:
[ 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
...
복원 과정에서 Firecracker 호스트는 **블록 장치를 핫 스왑(hot-swaps)**합니다:
| Device | Template | After Restore | Content |
|---|---|---|---|
| vda | placeholder | 256 GiB ext4 | 세션 루트 파일 시스템 (Ubuntu 24.04) |
| ... |
initramfs는 의도적으로 최소화되어 있습니다: /process_api만을 포함하는 3.1MB cpio 아카이브입니다. 실제 Ubuntu 루트 파일 시스템은 ext4 블록 장치(vda)에 있으며, 복원 시점에 주입됩니다. 이 ext4는 mount count=11을 가지고 있어, 해당 이미지가 11개의 세션에 걸쳐 재사용되었음을 나타냅니다.
템플릿 생성 단계:
- 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를 리시딩(reseeds)합니다.
process_api
detects restore and runs:Drop page caches— stale template cache would return garbageRemount devtmpfs— refresh device nodesMount ext4→pivot_root
to new rootfsMount squashfsoverlays (claude-code, env-runner)Fix wall clockviaclock_settime()
— 그렇지 않으면 템플릿 에포크에 갇힙니다Drop CAP_SYS_RESOURCE— 보안 강화Accept connections— WebSocket 서버 준비
| 측정 항목 | 목적 |
|---|---|
init_on_free=1 | 세션 간 해제된 페이지 제로화 (Zero freed pages between sessions) |
| CAP_SYS_RESOURCE drop | init 이후 PID 1의 권한 제한 (Limit PID 1's capabilities post-init) |
| CRNG reseed | 스냅샷 포크 전반에 걸친 암호학적 예측 가능성 방지 (Prevent crypto predictability across snapshot forks) |
--block-local-connections | localhost WebSocket 접근 차단 (Block localhost WebSocket access) |
| JWT auth | WebSocket 연결 검증 (WebSocket connection verification) |
| Token scrubbing | 사용 후 설정에서 비밀 정보 제거 (Remove secrets from configs after use) |
PID 1은 두 개의 네트워크 인터페이스를 노출합니다. 하나는 프로세스 관리를 위한 WebSocket API이고 다른 하나는 컨테이너 제어를 위한 HTTP API입니다. 일반적인 init 시스템과 달리, process_api는 전체 원격 프로세스 감독자(remote process supervisor)를 구현하는 Rust/tokio 바이너리입니다.
연결 핸드셰이크: 선택적 JWT → ProcessConnection JSON → 프로세스 생성 또는 재첨부.
프로세스 생성은 CreateProcess 구조체를 수락합니다:
{
"cmd": "/bin/bash",
"args": ["-l"],
...
}
I/O는 2단계 바이너리 프로토콜을 사용합니다:
Stdin: ExpectStdIn (텍스트) → 바이너리 프레임
Stdout/Stderr: ExpectStdOut/ExpectStdErr (텍스트) → 바이너리 프레임 → StdOutEOF/StdErrEOF
Client Server
|--- WS Connect ------------------->|
|--- JWT (선택적) --------------->|
...
프로세스 종료 사유에는 정상 종료, 시그널, 프로세스별 OOM(Out Of Memory), 컨테이너 수준 OOM, 타임아웃 및 서버 종료가 포함됩니다.
내부적으로 process_api는 프로세스별 cgroups를 추적합니다 (v1은 /sys/fs/cgroup/memory/process_api/, v2는 /sys/fs/cgroup/process_api/).
)), 고아 채택(orphan adoption, PID 1로 재부모화)을 구현하고 구성 가능한 OOM 폴링 루프를 실행합니다.
컨테이너 라이프사이클을 관리하는 여섯 개의 엔드포인트가 있습니다:
| Endpoint | 목적 |
|---|---|
GET /status | 상태 확인 (Health check) |
POST /fs_sync | 파일 시스템 버퍼 비우기 (Flush filesystem buffers) |
POST /shutdown | 페이지 캐시를 해제하며 정상 종료 (Graceful shutdown with page cache drop) |
POST /auth_public_key | JWT 검증 키 설정 (Set JWT verification key) |
POST /mount_root | 루트 파일 시스템 마운트 (snapstart 복원) (Mount rootfs (snapstart restore)) |
POST /container_name | 컨테이너 식별자 설정 (Set container identity) |
/mount_root 엔드포인트는 네트워크 구성(etc_hosts, resolv_conf), CA 인증서, squashfs 마운트, FUSE 마운트(VFS 캐시 구성 포함), 그리고 벽 시계 타임스탬프 — 즉, 빈 스냅샷에서 세션을 초기화하는 데 필요한 모든 것을 담은 MountRootConfig를 허용합니다. 마운트 중에는 FIFREEZE/FITHAW ioctl을 통해 루트가 동결됩니다.
진짜 발견은 /usr/local/bin/environment-runner (별칭으로 environment-manager)였습니다:

$ file /usr/local/bin/environment-runner
ELF 64-bit LSB executable, x86-64, dynamically linked,
Go BuildID=..., with debug_info, not stripped
...
27MB 크기의 Go 바이너리입니다. 스트립되지 않았습니다. 전체 디버그 정보가 있습니다. 전체 심볼 테이블이 있습니다. Anthropic의 사설 모노레포인 github.com/anthropics/anthropic/api-go/environment-manager/에서 빌드되었습니다.
go tool objdump와 strings를 사용하면 완전한 내부 패키지 구조를 추출할 수 있었습니다:
internal/
├── api/ # API 클라이언트 (세션 진입, 작업 폴링, 재시도)
├── auth/ # GitHub 앱 토큰 제공자
...
바이너리에서 추출된 주요 의존성:
| Dependency | 목적 |
|---|---|
github.com/anthropics/anthropic/api-go | 내부 Anthropic Go SDK |
github.com/gorilla/websocket | API로의 WebSocket 터널 |
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 (세션 라우팅) |
github.com/spf13/cobra | CLI 프레임워크 |
tunnel/actions/deploy/ 패키지에는 두 가지 배포 클라이언트의 함수 심볼이 있습니다:

예상되는 VercelClient:
CreateDeployment
→ POST/v13/deployments
UploadFile
→ PUT/v2/files
(x-vercel-digest 헤더 사용)
WaitForReady
→ readyState == "READY"가 될 때까지 폴링(Poll)합니다.
그리고 예상치 못한 AntspaceClient:
deploy.(*AntspaceClient).Deploy
deploy.(*AntspaceClient).createDeployment
deploy.(*AntspaceClient).uploadTarball
...
바이너리에서 추출된 관련 문자열을 분석한 결과, 완전한 배포 프로토콜이 밝혀졌습니다:

1단계: 배포 생성 (Create Deployment)
POST to antspaceControlPlaneURL
Content-Type: application/json
Authorization: Bearer {antspaceAuthToken}
...
2단계: 빌드 아티팩트 업로드 (Upload Build Artifact)
POST multipart/form-data
File: dist.tar.gz (빌드된 애플리케이션)
적용되는 크기 제한: "프로젝트가 %dMB 제한을 초과했습니다"
3단계: 배포 상태 스트리밍 (Stream Deployment Status)
응답: application/x-ndjson (스트리밍)
상태 진행 과정: packaging → uploading → building → deploying → deployed
오류: 클라이언트가 NDJSON을 처리할 수 없는 경우 "Streaming unsupported"
전체 공개 인터넷에서 "Antspace"를 검색했지만 아무것도 찾을 수 없었습니다. Anthropic의 웹사이트, GitHub, 블로그, 문서, LinkedIn, 채용 공고, 컨퍼런스 발표 자료, 특허 출원 등 어디에서도 결과가 0건입니다. 이 플랫폼은 그 어느 곳에서도 공개적으로 언급된 적이 없습니다.
이 이름은 아마도 "Ant" (보고된 바에 따르면 Anthropic 직원의 내부 별명) + "Space" (호스팅 공간)에서 파생되었으며, Heroku나 Vercel 같은 다른 플랫폼들과 동일한 명명 패턴을 따르는 것으로 보입니다.
| 측면 | Vercel | Antspace |
|---|---|---|
| 파일 업로드 | SHA 기반 중복 제거(dedup), 파일별 | 단일 tar.gz 아카이브 |
| ... |
Anthropic이 단순히 Vercel의 API를 래핑하는 대신, 처음부터 완전한 배포 프로토콜을 구축했다는 사실은 이것이 빠르고 임시적인 통합이 아니라 전략적인 플랫폼 투자임을 시사합니다.
"Baku"는 claude.ai의 웹 앱 빌더 경험을 위한 내부 코드명입니다. 사용자가 웹에서 Claude에게 웹 애플리케이션 생성을 요청하면, Baku 환경이 실행됩니다.
바이너리에서 추출된 임베디드 리소스를 통해 확인한 내용은 다음과 같습니다:
프로젝트 템플릿:
-
소스:
/opt/baku-templates/vite-template -
스택: Vite + React + TypeScript
-
supervisord를 통한 자동 관리 개발 서버, 로그는
/tmp/vite-dev.log에 기록됨
Supabase 자동 프로비저닝:
총 6개의 MCP(Microservice Component) 도구가 자동으로 사용 가능합니다:
provision_database: 온디맨드(on demand)로 Supabase 프로젝트 생성
execute_query: SQL 쿼리 실행
apply_migration: 자동 타입 생성을 포함한 버전 관리 스키마 변경 적용
list_migrations: 적용된 마이그레이션 목록 확인
generate_types: DB 스키마로부터 TypeScript 타입 재생성
deploy_function: Supabase Edge Functions 배포
환경 변수는 .env.local에 자동 작성됩니다:
SUPABASE_URL, SUPABASE_ANON_KEY,
VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY
정지 훅(Stop Hooks) (임베디드 쉘 스크립트):
Baku 환경에는 다음과 같은 경우 세션이 종료되는 것을 방지하는 사전 정지 훅(pre-stop hook)이 있습니다:
- 커밋되지 않았거나 푸시되지 않은 git 변경 사항이 있을 때
- Vite 개발 서버 로그에 오류가 포함되어 있을 때 (
tsc --noEmit실행 시 TypeScript 타입 오류 보고)
기본 배포 대상: Antspace, Vercel이 아닙니다. Vercel은 대안으로 존재하지만, Baku의 네이티브 배포 경로는 Anthropic 자체 플랫폼을 거칩니다.
내부 구성:
- 초안(Drafts)은
.baku/drafts/에 저장됨 - 탐색(Explorations)은
.baku/explorations/에 저장됨 - Git 커밋은 작성자로
[email protected]을 사용하며, git 원격 저장소는 구성되지 않음 (로컬 전용 버전 관리)
envtype/ 패키지에는 두 가지 환경 구현이 포함되어 있습니다:
: Anthropic 호스팅 (Firecracker MicroVMs) (anthropic)
: Bring Your Own Cloud (byoc)
BYOC는 기업 고객이 세션은 Anthropic의 API가 오케스트레이션하는 동안, environment-runner를 자체 인프라에서 실행할 수 있도록 합니다. 주요 특징:
- 기본 세션 모드:
resume-cached
(가장 빠른 재시작, 기존 상태 재사용) - 사용자 정의 인증 (Custom auth):
containProvideAuthRoundTripper
컨테이너 수준 자격 증명 주입 - 스마트 git 처리: fetch 전에 작업 브랜치가 원격에 존재하는지 확인
- 하위 유형:
antspace
(Anthropic 내부) 및 baku
(Vite 프로젝트 빌더) - Kubernetes 통합:
podmonitor
AI 자동 생성 콘텐츠
본 콘텐츠는 HN Code Generation의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기