제공자
프로바이더는 격리 기본 요소를 소유합니다. 즉, 하네스가 실제로 실행되는 위치입니다. 모든
프로바이더는 동일한 SandboxProvider / SandboxHandle 계약을 구현하므로,
에이전트에 전달하는 workspace와 이를 보호하는 policy는
프로바이더와 무관합니다. 필요한 격리, 인증 및
스냅샷/재개 동작에 맞는 프로바이더를 선택하면 샌드박스 정의의 나머지 부분은
동일하게 유지됩니다.
프로바이더 네이티브 스냅샷과 재개는 프로바이더 상태를 유지하거나 다시 생성합니다. 부트스트랩 시간을 줄일 수 있습니다. Portable Snapshots는 재구성을 위해 완료된 workspace 데이터를 애플리케이션 영속성에 저장합니다.
프로바이더는 에이전트가 실행되는 _위치_입니다. 어떤 에이전트가 실행되는지(Grok Build, Claude Code, Codex, OpenCode 또는
acpCompatible를 통한 모든 ACP 에이전트)는 Harnesses를 참조하세요.
프로바이더 선택
| 프로바이더 | 패키지 | 격리 | 비고 |
|---|---|---|---|
| 로컬 프로세스 | @tanstack/ai-sandbox-local-process | 없음(호스트) | 빠른 Docker 없는 개발 루프입니다. 신뢰할 수 있는 개발 환경에서만 사용하세요. |
| Docker | @tanstack/ai-sandbox-docker | 컨테이너 | 실제 격리, 커밋 기반 스냅샷, 포크, ID로 재개를 제공합니다. |
Docker Sandboxes (sbx) | @tanstack/ai-sandbox-docker | microVM | 네이티브 Docker Sandboxes입니다. dockerSandbox()와 동일한 패키지이지만 내보내기가 다릅니다: sbxSandbox(). sbx CLI, 로그인 및 하이퍼바이저가 필요합니다. |
| Daytona | @tanstack/ai-sandbox-daytona | 클라우드 샌드박스 | 관리형 Daytona 샌드박스입니다. 설정 후 스냅샷, 포트 미리보기 링크, ID로 재개를 제공합니다. DAYTONA_API_KEY이 필요합니다. |
| Vercel | @tanstack/ai-sandbox-vercel | microVM | 관리형 Vercel Sandbox microVM입니다. 노출된 포트 도메인과 ID로 재개(영속)를 제공합니다. VERCEL_TOKEN 및 팀/프로젝트가 필요합니다. |
| Sprites | @tanstack/ai-sandbox-sprites | 상태 유지 샌드박스 | 관리형 Sprites(Fly.io) 샌드박스입니다. 영구 파일 시스템, 인플레이스 체크포인트, 단일 프록시 공개 URL 포트, ID로 재개를 제공합니다. SPRITES_API_KEY이 필요합니다. |
| Upstash Box | @tanstack/ai-sandbox-upstash-box | 클라우드 샌드박스 | 관리형 Upstash Box 샌드박스입니다. WebSocket 세션을 통한 대화형 프로세스(실제 pid, stdin, 신호), 네이티브 스냅샷, 미리보기 URL, ID로 재개를 제공합니다. UPSTASH_BOX_API_KEY이 필요합니다. |
대부분의 provider는 자체 패키지입니다. dockerSandbox() 및 sbxSandbox()은 모두 @tanstack/ai-sandbox-docker에서 제공됩니다. 이 둘의 차이는 constructor뿐입니다:
import { localProcessSandbox } from '@tanstack/ai-sandbox-local-process'
import { dockerSandbox, sbxSandbox } from '@tanstack/ai-sandbox-docker'
import { daytonaSandbox } from '@tanstack/ai-sandbox-daytona'
import { vercelSandbox } from '@tanstack/ai-sandbox-vercel'
import { upstashBoxSandbox } from '@tanstack/ai-sandbox-upstash-box'
const dev = localProcessSandbox() // runs on your host
const isolated = dockerSandbox({ image: 'node:22' }) // container
const microvm = sbxSandbox() // Docker Sandboxes microVM
const daytona = daytonaSandbox({ apiKey: process.env.DAYTONA_API_KEY }) // managed cloud sandbox
const vercel = vercelSandbox({ runtime: 'node24' }) // managed Vercel microVM
const box = upstashBoxSandbox({ apiKey: process.env.UPSTASH_BOX_API_KEY }) // managed Upstash Box
클라우드 provider(Daytona, Vercel, Upstash Box)는 원격 VM으로 실행됩니다. 이를 다음에서 제어하면 노트북에서 tools는
chat()에서 브리징되므로 컴퓨터의localhost, bridge tunnel이 필요합니다. 도구 가이드에서 ngrok 하위 경로를 확인하고, Cloudflare 가이드에서 엣지 네이티브 공동 배치 모델을 확인합니다.
로컬 프로세스
import { localProcessSandbox } from '@tanstack/ai-sandbox-local-process'
const dev = localProcessSandbox()
- 격리: 없음. 하네스는 호스트에서 직접 실행되며 호스트 환경을 상속합니다. 호스트 환경. 신뢰할 수 있는 작업이나 개발 작업에만 사용합니다. 다음과 같은 경계는 없습니다. 에이전트와 컴퓨터 사이에는 경계가 없습니다.
- 인증 / 환경: 호스트 환경을 상속합니다. 하네스에서
authMode를 설정합니다. ('host'또는'api-key'). 제공자는 이를 선택하지 않습니다. 다음을 참조합니다. Harness 인증. - 스냅샷 / 재개: 스냅샷이 없으며 영속적인 ID 기반 재개도 지원되지 않습니다. 각 실행은 동일한 ID로 다시 생성되고 부트스트랩됩니다. 스냅샷 단계는 자동으로 건너뜁니다(자세한 내용은 Capabilities 참조).
호스트 로그인과 API 키 비교 (scrubEnv)
provider는 에이전트가 실행되는 위치이며, 로그인 방식이 아닙니다. 기본값은
authMode이며 'api-key'입니다. 머신에 이미 CLI
로그인이 되어 있는 경우 'host'을 설정합니다. 로컬 프로세스 실행은 사용자의 노트북이나 GitHub runner에서 수행할 수 있습니다. 자세한 내용은
Harness 인증을 참조하세요.
localProcessSandbox는 해당 환경에 내보내진 모든 API 키를 포함하여 호스트 환경을 상속합니다. authMode: 'host'을 설정하는 경우, 해당 키가 CLI 로그인을 재정의하지 않도록 scrubEnv를 전달합니다:
import { localProcessSandbox } from '@tanstack/ai-sandbox-local-process'
const hostLogin = localProcessSandbox({
scrubEnv: ['XAI_API_KEY', 'GROK_API_KEY'],
})
동일한 로컬 프로세스 샌드박스가 CI 머신에서 실행되는 경우 authMode: 'api-key'를 설정합니다. 그런 다음 키를 워크스페이스 시크릿으로 주입합니다. 격리된 provider와 클라우드 provider에는 호스트 CLI 로그인이 없습니다. 해당 환경에서는 authMode: 'api-key'와 워크스페이스 시크릿을 사용합니다.
Windows 프로세스 정리 (logger)
생성된 프로세스를 종료한다는 것은 전체 트리를 종료한다는 의미이며, Windows에서는 다음 작업에
taskkill /T보다 더 오래 걸립니다. 명령은 git-bash sh를 통해 실행되며, MSYS의
fork 에뮬레이션은 다음과 같은 문 목록의 최종 명령을 실행합니다.
tail -f가 journal follow read 뒤에서 중간 셸을 통해 실행되며
해당 셸은 즉시 종료됩니다. Windows는 절대로 프로세스를 다시 부모에 연결하지 않으므로, 살아남은 프로세스는
종료된 부모를 가리키게 되고, 살아 있는 부모 링크만 순회하는 taskkill /T는 해당 프로세스에
0를 종료하는 중인 동안에는 도달할 수 없습니다. 그대로 두면 모든 follow read가
머신의 수명 동안 프로세스를 하나씩 누수시킵니다.
따라서 localProcessSandbox는 종료하기 전에 MSYS 자체의 프로세스 테이블을 확인합니다. 이 테이블은
논리적 부모 관계를 유지하므로, 이후 누락된 모든 자손 /T를 종료합니다.
정리는 설계상 완전합니다. 종료 작업에서 예외를 발생시키면 실행 중인 작업이 중간에 멈추기 때문에
절대로 예외를 발생시키지 않습니다. 즉, 실제로 완료할 수 없는 종료(보호된 프로세스, 액세스 거부)가
그 외에는 보이지 않으므로, 이를 확인하려면 logger를 전달합니다:
import { localProcessSandbox } from '@tanstack/ai-sandbox-local-process'
const dev = localProcessSandbox({
logger: {
warn: (message, meta) => console.warn(message, meta),
},
})
warn(message, meta?) 메서드가 있는 모든 객체가 작동하므로, 어댑터가 이미 받는 InternalLogger
을 그대로 전달할 수 있습니다. 이미 자체적으로 종료된 프로세스는 실패가 아니며 절대 보고되지
않습니다.
sh가 실제로 명령의 부모인 POSIX에서는 여기서 변경되는 사항이 없으며,
래퍼에 시그널을 보내는 것만으로 충분합니다.
Docker
import { dockerSandbox } from '@tanstack/ai-sandbox-docker'
const isolated = dockerSandbox({ image: 'node:22' })
- 격리: 에이전트와 호스트 사이에 실제 컨테이너 경계를 제공합니다.
- 인증 / 환경: 호스트 로그인이 필요하지 않습니다. 자격 증명을 워크스페이스 시크릿으로 제공하면
생성/재개 시 컨테이너 환경에 주입됩니다. 에이전트는
host.docker.internal을 통해 호스트 도구에 접근합니다(자세한 내용은 tools를 참조하세요). - 스냅샷 / 재개: 커밋 기반 전체 스냅샷,
fork, ID를 통한 재개를 지원합니다.setup이 완료된 후 부트스트랩 스냅샷을 생성하므로, 이후 실행에서는 설정을 다시 실행하지 않고 스냅샷에서 재개합니다.
Docker 샌드박스 (sbx)
import { sbxSandbox } from '@tanstack/ai-sandbox-docker'
import { defineSandbox, defineWorkspace, githubRepo } from '@tanstack/ai-sandbox'
const isolated = sbxSandbox({
allowNetwork: ['*.npmjs.org', 'registry.npmjs.org'],
})
const sandbox = defineSandbox({
id: 'repo-agent',
provider: isolated,
workspace: defineWorkspace({
source: githubRepo({ repo: 'owner/repo' }),
setup: ['pnpm install'],
}),
})
- 격리: 하이퍼바이저 마이크로VM을 사용합니다. 샌드박스에는 자체 커널과 자체 Docker 데몬이 있습니다. 이는 컨테이너를 시작하는
dockerSandbox()가 아닙니다. - 필요 사항:
sbx및PATH에 대한sbx login(또는 CI에서sbx login --password-stdin에 파이프된 PAT), 그리고 하이퍼바이저(Hyper-V, Virtualization.framework 또는 KVM)가 필요합니다. Docker 소켓만으로는 충분하지 않습니다. - Workspace:
sbx create --clone는 호스트 Git 저장소를 VM으로 복사합니다.workspaceDir를 전달하고, 여기에.git가 포함되어 있어야 하며, 또는workspace.source을 git URL로 설정합니다. Git 저장소가 없으면 create가 예외를 발생시킵니다. bind-mount 대체 경로는 없습니다. - 인증 / 환경 변수: API 키를 워크스페이스 시크릿으로 주입합니다. v1은
sbx secret을 호출하지 않습니다. - 스냅샷 / 재개: 스냅샷과 포크를 지원하지 않습니다. 재개하면 이름으로 다시 연결됩니다(
sbx ls). 중지된 샌드박스는 다음sbx exec에서 다시 시작됩니다. - 네트워크:
networkPolicy: true을(를) 지원하는 첫 번째 provider입니다. 정책을 참조하세요.
Daytona
import { daytonaSandbox } from '@tanstack/ai-sandbox-daytona'
const daytona = daytonaSandbox({
apiKey: process.env.DAYTONA_API_KEY,
snapshot: 'daytona-medium',
autoStopInterval: 0,
})
- 격리: 직접 실행하지 않는 원격 VM에서 관리되는 클라우드 샌드박스입니다.
- 인증 / 환경 변수:
DAYTONA_API_KEY가 필요합니다. 하네스 자격 증명을 워크스페이스 시크릿에 입력합니다. 해당 자격 증명은 실행 중인 샌드박스에 생성, 재개 및 복원 시 적용됩니다. Daytona 생성 레코드에는 저장되지 않으며 명령 기록에도 기록되지 않습니다. - 스냅샷 / 재개: 설정 후 특정 시점의 스냅샷을 생성합니다(기본값은
lifecycle.snapshot가'after-setup'인 경우).snapshot를 전달하여daytonaSandbox()에서 Daytona 이미지를 선택합니다(예:'daytona-medium'). 재개하면stopped또는archived샌드박스가 시작되고 핸들이 반환됩니다. - 유휴 중지: Daytona는 기본적으로 15분 후 유휴 샌드박스를 중지합니다.
분 단위로
autoStopInterval를 설정하여 이 시간을 변경합니다.0를 전달하면 자동 중지를 끄십시오. 중지될 때 샌드박스를 삭제하려면ephemeral: true를 설정합니다. - 네트워크:
policy.capabilities.network: 'deny'는 모든 아웃바운드 네트워크를 생성 시 차단합니다. - 작업 디렉터리: 이식 가능한 루트
/workspace는 기본적으로/home/daytona/workspace에 매핑됩니다. 다른 경로가 필요하면workdir를 사용하여daytonaSandbox()을 재정의합니다. - Stdin: 생성된 프로세스는 호스트 stdin(
writableStdin: true)을 허용합니다. - 권한: Daytona 사용자는 root가 아닙니다.
setup에 패키지를 설치하려면sudo -n를 사용해야 합니다(예:sudo -n apt-get install …). 다음을 입력하지 마십시오.sudo *를 이 provider의 policy deny 목록에 추가하지 마십시오. - 브리지: 샌드박스는 원격이므로 bridged tool 호출로는
노트북의
localhost에 접근할 수 없습니다. 로컬 개발에서는 브리지를 터널링합니다(참조 tools). 배포된 오케스트레이터는 터널 없이 연결할 수 있습니다.
Daytona의 기본 헤드리스 경로:
import { chat } from '@tanstack/ai'
import { grokBuildText } from '@tanstack/ai-grok-build'
import {
defineSandbox,
defineSandboxPolicy,
defineWorkspace,
gitSkill,
githubRepo,
withSandbox,
} from '@tanstack/ai-sandbox'
import { daytonaSandbox } from '@tanstack/ai-sandbox-daytona'
const sandbox = defineSandbox({
id: 'daytona-agent',
provider: daytonaSandbox({
apiKey: process.env.DAYTONA_API_KEY,
snapshot: 'daytona-medium',
}),
workspace: defineWorkspace({
source: githubRepo({ repo: 'owner/app' }),
skills: [gitSkill({ repo: 'owner/skills-pack' })],
}),
policy: defineSandboxPolicy({
default: 'allow',
}),
})
const stream = chat({
adapter: grokBuildText('grok-build'),
messages: [{ role: 'user', content: 'List the project files.' }],
middleware: [withSandbox(sandbox)],
})
헤드리스 Grok Build와 Codex는 default: 'allow'을 사용하여 자동 승인을 유지합니다.
격리는 Daytona VM에서 수행됩니다. 명령 수준의 거부가 필요하면 Claude Code를 사용합니다.
Vercel
import { vercelSandbox } from '@tanstack/ai-sandbox-vercel'
const vercel = vercelSandbox({ runtime: 'node24' })
- 격리: 관리형 마이크로VM(Vercel Sandbox)입니다.
- 인증 / 환경:
VERCEL_TOKEN과 팀/프로젝트가 필요합니다. Harness 자격 증명은 워크스페이스 시크릿으로 주입됩니다. - 스냅샷 / 재개: 내구성 있는 파일 시스템을 사용한 ID별 영속적 재개와 프리뷰를 위한 노출된 포트 도메인을 제공합니다.
- 브리지: Daytona와 마찬가지로 원격 VM이므로, 브리지된 도구를 사용하려면 로컬 개발 환경에서 터널이 필요합니다(see tools).
Sprites
import { spritesSandbox } from '@tanstack/ai-sandbox-sprites'
const sprites = spritesSandbox({ apiKey: process.env.SPRITES_API_KEY })
- 격리: 관리형 Sprites 상태 유지 샌드박스입니다 (Fly.io)의 원격 VM으로, 직접 실행하지 않습니다.
- 인증 / 환경:
SPRITES_API_KEY(토큰 형식이 필요합니다org/projectNumber/tokenId/secret); 제어 플레인 URL은 다음으로 재정의합니다apiUrl/SPRITES_API_URL. Harness 자격 증명은 워크스페이스 시크릿으로 주입됩니다. - 스냅샷 / 재개: ID로 재개하면 이름이 지정된 Sprite에 다시 연결됩니다(Sprite의
파일시스템은 유휴 일시 중지/재개 전반에서 영속적입니다).
snapshot()은 Sprite 체크포인트(쓰기 가능한 오버레이의 저장 지점)를 생성합니다. 복원은 동일한 Sprite에서 핸들의restoreCheckpoint()/listCheckpoints()을 통해 제자리에서 수행됩니다. 체크포인트는 Sprite 삭제 후 유지되지 않으므로, provider는 의도적으로 삭제 후 재구성restoreSnapshot을 구현하지 않습니다. Sprite가 사라지면 프레임워크는 새로운 생성으로 대신 저하됩니다. 복원은 환경을 다시 시작하며 몇 분이 걸릴 수 있습니다.restoreCheckpoint()은 다시 나열할 수 있을 때까지 workspace를 폴링한 후 리졸브합니다. 복원 직후 오버레이는 나열할 수 있지만 개별 파일 읽기는 환경이 안정화되는 동안 잠시 I/O 오류를 반환할 수 있으므로, 재시도 복원이 반환되는 즉시 파일시스템에서 작업한다면 읽기를 재시도합니다. - 포트: Sprite는 단일 내부 HTTP 포트(기본값
8080,httpPort를 통해 구성 가능)를 항상 활성화된 공개 URL로 프록시합니다.ports.connect(8080)URL을public인증으로 전환하고 반환합니다. 다른 포트는 노출되지 않습니다. - 브리지: Daytona 및 Vercel과 마찬가지로 원격 VM이므로, 브리지된 도구를 사용하려면 다음이 필요합니다. 로컬 개발 환경의 터널(see tools)입니다.
Upstash Box
import { upstashBoxSandbox } from '@tanstack/ai-sandbox-upstash-box'
const box = upstashBoxSandbox({ apiKey: process.env.UPSTASH_BOX_API_KEY })
- 격리: 관리형 Upstash Box 클라우드 샌드박스, 직접 실행하지 않는 원격 컨테이너입니다.
- 인증 / 환경 변수:
UPSTASH_BOX_API_KEY(또는apiKey)가 필요합니다. API 기본 경로는baseUrl/UPSTASH_BOX_BASE_URL로 재정의합니다. 이미지는 다음으로 선택하고 크기는runtime(기본값node) 및size로 선택합니다. - 경로: 일반적인
/workspace가상 루트는 box의 홈에 매핑됩니다, 핸들의/workspace/home인workspaceRoot입니다. - 프로세스:
spawn()는 WebSocket을 통해 실시간exec.session을 열므로, 백그라운드 프로세스에는 실제 인박스 pid, 쓰기 가능한 stdin, 분리된 stdout 및 stderr와 서버 측 시그널이 있습니다. 세션은 프로세스를 소유합니다. 연결을 끊으면 명령이 종료되고 세션을 다시 연결할 수 없으므로spawn()는 박스가 아닌 핸들의 수명으로 범위가 제한됩니다. 차단 중인exec()HTTP 경로에 머무르며cwd/env에 대해 셸로 래핑되고, 세션은 이를 기본적으로 처리합니다. - 스냅샷 / 재개:
snapshot()은(는)box.snapshot()을(를) 호출하고restoreSnapshot()은(는)Box.fromSnapshot()을(를) 통해 이를 기반으로 새 박스를 재구성하므로 스냅샷은 이를 만든 박스가 삭제되어도 유지됩니다. ID로 재개하면Box.get(ID 또는 이름)을(를) 사용하고getStatus을(를) 확인하므로, 삭제된 레코드도 다음과 같이 재개됩니다 삭제 표시 핸들 대신null를 사용합니다. - 포트:
ports.connect(port)는getPublicURL를 통해 미리보기 URL을 생성합니다. 전달하려면publicUrlAuth를 사용해 이를 제한하고,{ bearerToken: true }는 토큰과Authorization: Bearer헤더와{ basicAuth: true }는 Basic을 반환합니다 자격 증명입니다. 이것이 없으면 미리보기 URL은 인증되지 않습니다. - 네트워크:
policy.capabilities.network의'deny'는 Box의deny-all송신 모드입니다. 계약의 게이트가 포괄적이므로 Box의 도메인 및 CIDR 허용 목록은 이를 통해 접근할 수 없습니다. 이는 거부를 허용 목록으로 모델링하는 제공자보다 더 엄격합니다.deny-all은 모든 아웃바운드 연결을 차단하므로, 다음과 같은 허용 목록 방식의 거부 환경에서 작동하는 에이전트는 패키지 레지스트리나 모델 제공자 호스트에 접근할 수 없습니다. 에이전트에 다음 중 하나가 필요한 경우에는 이 기능을 설정하지 않은 상태로 두십시오. - 포크:
fork()샌드박스의 스냅샷을 생성하고 해당 스냅샷으로 새 샌드박스를 생성합니다, Docker의 commit 및 create와 동일한 형태입니다. 전체 스냅샷 왕복에 약 25초가 소요되며, Docker의 로컬 commit과는 다릅니다. - 브리지: Daytona 및 Vercel과 마찬가지로 원격 VM이므로 브리지된 도구에는 로컬 개발에서 터널이 필요합니다(참조: 도구).
기능
Provider는 capabilities()를 통해 지원하는 기능을 선언합니다. 플래그는 다음과 같습니다:
| 기능 | 의미 |
|---|---|
fs | 샌드박스 파일 시스템을 읽고 씁니다. |
exec | 명령을 실행합니다. |
env | 환경 변수를 주입합니다. |
ports | 포트를 노출하거나 전달합니다(미리보기 URL). |
backgroundProcesses | 호출 간에 장시간 실행되는 프로세스를 계속 유지합니다. |
writableStdin | 생성된 프로세스는 쓰기 가능한 호스트→프로세스 stdin을 노출합니다. true local-process, Docker 컨테이너, Daytona 및 Upstash Box에서 지원됩니다. false Docker Sandboxes(sbx), Vercel, Sprites 및 Cloudflare에서 지원됩니다. false인 경우 stdin 기반 하네스는 프롬프트를 파일에 쓰고 셸에서 해당 파일을 리디렉션합니다. |
killableProcesses | 생성된 프로세스는 SpawnHandle.kill() 를 통해 강제로 중지할 수 있으며, signal 가 spawn 에 전달되는 동안 중간에 중단될 수도 있습니다. |
snapshots | 시점별 스냅샷을 캡처하고 복원합니다. |
networkPolicy | 네트워크 허용/차단 규칙을 강제합니다. |
durableFilesystem | 리프레임을 통해 유지되는 디스크입니다. |
fork | 기존 샌드박스에서 새 샌드브로를 분기합니다. |
선택적 기능을 사용하는 코드는 먼저 플래그를 확인하고 우아하게 성능을 저하시킵니다. 예를 들어, snapshots 가 지원될 때만 bootstrap 은 스냅샷을 생성하며, localProcessSandbox 는 해당 단계를 건너뜁니다. 플래그를 확인하지 않고 직접 UnsupportedCapabilityError 를 호출하면 예외가 발생합니다:
import { localProcessSandbox } from '@tanstack/ai-sandbox-local-process'
const provider = localProcessSandbox()
const caps = provider.capabilities()
if (caps.snapshots) {
// safe to take a snapshot
} else {
// degrade gracefully, local-process has no snapshots
}
플래그를 사용하여 제공자 무관한 코드를 작성하세요: 구체적인 제공자가 아닌 기능을 기준으로 분기하고, 한 제공자를 다른 것으로 교체해도 샌드박스 정의는 계속 작동합니다.
killableProcesses 번들된 제공자 전반
이 플래그는 측정되며, 주장은 아닙니다. 잘못된 true 은 저널 리더에게
멈출 수 없는 tail -f 를 넘겨주고 실행마다 프로세스를 누출하므로 제공자는 단순히 선언만 합니다.
실제 샌드박스에서 관찰되어 작동하는 것으로 확인되기 전까지는 이를 한 번 죽이는 것입니다. 이 중 두 가지는
추론만으로 한때 true되었다고 선언되었지만, 두 가지 모두 조사 결과 거짓으로 드러났습니다
(Docker의 스트림 destroy는 컨테이너 측 프로세스를 ps 상태로 남겼습니다;
로컬 프로세스의 sh -c는 exec되지 않았으므로 셸만 종료하면 명령이
계속 실행됩니다). 아직 측정할 수 없는 것은 false 상태로 유지합니다. poll는
단지 더 느린 것이지만 잘못된 follow는 누수이기 때문입니다.
| 제공자 | killableProcesses | 이유 |
|---|---|---|
| 로컬 프로세스 | true | 측정됨. 래퍼가 아니라 프로세스 GROUP을 종료합니다: POSIX에서는 detached spawn과 process.kill(-pid, signal)을 사용합니다(sh만 종료하면 명령이 계속 실행되며, dash는 안정적으로 exec하지 않습니다). Windows에서는 taskkill /T과 검증된 sweep을 사용합니다. Windows teardown을 참조하세요. |
| Docker | true | 측정됨. 래퍼가 자체적으로 기록한 pid를 사용해 컨테이너 내부의 프로세스에 신호를 보냅니다. 먼저 프로세스 그룹을 처리한 다음 KILL로 escalation합니다. 탈취한 exec 스트림을 삭제하는 것만으로는 충분하지 않습니다. 이는 클라이언트의 연결만 분리합니다. |
Docker Sandboxes (sbx) | false | 측정되지 않음. sbx login 전까지 실시간 테스트는 named-skip됩니다. 핸들은 VM의 pid를 기록하지만, 해당 kill이 관찰될 때까지 killableProcesses는 false 상태로 유지됩니다. |
| Daytona | false | kill()만 클라이언트 측 poll loop를 중단하며 어떠한 종료도 기다리지 않습니다. 명령을 종료할 수 있는 deleteSession는 나중에 pump의 teardown에서 실행되며, 실패가 무시되고, 완료된 세션의 정리 작업으로 문서화되어 있습니다. 측정되지 않았으며 DAYTONA_API_KEY가 필요합니다. |
| Vercel | false | abort signal은 detached command를 START하는 HTTP 요청에만 도달하므로 기존 kill()는 no-op이었습니다. 이제 SDK의 서버 측 Command.kill를 실행하지만, fork된 자식 프로세스에 도달하는지는 측정되지 않았으며 Vercel 자격 증명이 필요합니다(follow command는 여러 문으로 구성된 셸이므로 tail -f는 항상 자식 프로세스입니다). |
| Sprites | true (검증되지 않음) | 클라이언트 측 detach가 아닙니다. kill()는 소켓을 닫기 전에 실제 서버 측 POST /exec/<sessionId>/kill를 실행합니다. 해당 엔드포인트가 무엇에 신호를 보내는지(프로세스 그룹 또는 pid)는 문서화되지 않았고 측정되지도 않았으며, SPRITES_API_KEY가 필요합니다. |
| Upstash Box | true | 측정됨. kill()는 허용 목록에 있는 신호(TERM/KILL/INT/HUP)를 보내며, box agent가 이를 서버 측 프로세스 TREE에 전달하므로 fork된 자식 프로세스에도 신호가 전달됩니다. 프로덕션 환경에서 검증했습니다. 생성된 sleep 5 && touch <marker>가 종료되었고 marker는 나타나지 않았습니다. UPSTASH_BOX_API_KEY가 필요합니다. |
| Cloudflare | false | kill()는 no-op이며, Workers RPC는 하나를 직렬화할 수 없기 때문에 호출자의 AbortSignal는 exec나 spawn에도 도달하지 않습니다. |
각 원격 제공자는 공유 journal conformance suite를 등록하므로, 주장이 단순히 제시되는 것이 아니라 반증 가능합니다. 자격 증명이 있으면 suite가 실제 샌드박스에서 실행되고, 없으면 조용히 통과하지 않고 이유를 담은 named skip을 보고합니다. Cloudflare의 gate는 자격 증명이 아니라 런타임입니다. 해당 제공자는 Sandbox Durable Object binding을 통해서만 샌드박스를 생성할 수 있는데, Node 테스트 프로세스에는 이것이 없으므로 Workers-runtime harness가 이를 측정할 수 있을 때까지 등록 결과는 정확히 그 이유를 설명하는 named skip이 됩니다.
이 플래그는 자체 제공자를 포함한 모든 제공자에서 필수입니다. 이를 생략한 제공자는 종료 가능하다고 간주되며, 이것이 위험한 기본값입니다. 그곳에서 시작된 follower 프로세스는 회수할 수 없고, 샌드박스가 유지되는 동안 계속 샌드박스 내부에서 실행되기 때문입니다.
이 플래그는 run journal가 실행 출력의 tail 방식을 결정할 때 읽는 값입니다. 종료 가능한 제공자는 스트리밍 tail -f를 받고, Cloudflare와 같은 제공자는 제한된 읽기 루프를 사용하며 각 읽기는 자체적으로 종료됩니다.
이 플래그는 cancel의 의미도 제한합니다. false 제공자에는 agent 프로세스로 연결되는 신호 경로가 없으므로, 실제로 agent를 중지하는 유일한 cancel은 샌드박스를 삭제하는 것이며 cancel 경로가 바로 이 작업을 수행합니다. kill할 수 없는 제공자에서 cancel이 의미하는 것을 참조하세요.