
컨테이너 없이 OS 수준에서 임의의 프로세스에 대한 파일시스템 및 네트워크 제한을 적용하는 경량 샌드박싱 도구입니다.
# Anthropic Sandbox Runtime (srt)
컨테이너 없이 OS 수준에서 임의의 프로세스에 파일 시스템 및 네트워크 제한을 적용하기 위한 경량 샌드박싱 도구입니다.
`srt`는 네이티브 OS 샌드박싱 프리미티브(macOS에서는 `sandbox-exec`, Linux에서는 `bubblewrap`)와 프록시 기반 네트워크 필터링을 사용합니다. 에이전트, 로컬 MCP 서버, bash 명령 및 임의의 프로세스의 동작을 샌드박싱하는 데 사용할 수 있습니다.
> **베타 리서치 프리뷰**
>
> Sandbox Runtime은 더 안전한 AI 에이전트를 구현하기 위해 [Claude Code](https://www.claude.com/product/claude-code)용으로 개발된 리서치 프리뷰입니다. 더 광범위한 생태계가 보다 안전한 에이전틱 시스템을 구축할 수 있도록 돕기 위해 초기 오픈 소스 프리뷰로 공개되었습니다. 초기 리서치 프리뷰인 만큼 API와 구성 형식은 변경될 수 있습니다. AI 에이전트를 기본적으로 더 안전하게 만들기 위한 피드백과 기여를 환영합니다!
## 설치
```bash
npm install -g @anthropic-ai/sandbox-runtime
```
## 기본 사용법
```bash
# Network restrictions
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html> # Request succeeds
$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist # Request blocked
# Filesystem restrictions
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb... # Current directory access allowed
$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted # Specific file blocked
```
## 개요
이 패키지는 CLI 도구와 라이브러리로 모두 사용할 수 있는 독립형 샌드박스 구현을 제공합니다. 일반적인 개발자 사용 사례에 맞춘 **기본적으로 안전한(secure-by-default)** 철학을 바탕으로 설계되었습니다. 프로세스는 최소한의 접근 권한으로 시작하며, 필요한 부분만 명시적으로 열어줍니다.
**주요 기능:**
- **네트워크 제한**: HTTP/HTTPS 및 기타 프로토콜을 통해 접근할 수 있는 호스트/도메인 제어
- **파일시스템 제한**: 읽기/쓰기가 가능한 파일/디렉터리 제어
- **Unix 소켓 제한**: 로컬 IPC 소켓에 대한 접근 제어
- **위반 모니터링**: macOS에서 시스템의 샌드박스 위반 로그 저장소에 연결하여 실시간 알림 제공
### 사용 사례 예시: MCP 서버 샌드박싱
주요 사용 사례 중 하나는 Model Context Protocol(MCP) 서버를 샌드박싱하여 해당 서버의 기능을 제한하는 것입니다. 예를 들어, 파일시스템 MCP 서버를 샌드박싱하려면 다음과 같이 합니다:
**샌드박싱 없이** (`.mcp.json`):
```json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
```
**샌드박싱 사용 시** (`.mcp.json`):
```json
{
"mcpServers": {
"filesystem": {
"command": "srt",
"args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
```
그런 다음 `~/.srt-settings.json`에서 제한 사항을 구성합니다:
```json
{
"filesystem": {
"denyRead": [],
"allowWrite": ["."],
"denyWrite": ["~/sensitive-folder"]
},
"network": {
"allowedDomains": [],
"deniedDomains": []
}
}
```
이제 MCP 서버는 거부된 경로에 쓰기가 차단됩니다:
```
> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'
```
## 작동 방식
샌드박스는 OS 수준의 기본 요소를 사용하여 전체 프로세스 트리에 적용되는 제한을 시행합니다:
- **macOS**: 동적으로 생성된 [Seatbelt 프로필](https://reverse.put.as/wp-content/uploads/2011/09/Apple-Sandbox-Guide-v1.0.pdf)과 함께 `sandbox-exec`를 사용합니다
- **Linux**: 네트워크 네임스페이스 격리를 갖춘 컨테이너화를 위해 [bubblewrap](https://github.com/containers/bubblewrap)을 사용합니다
- **Windows**: 전용 `srt-sandbox` 로컬 사용자 계정으로 샌드박스된 프로세스를 실행하며, 해당 계정의 SID를 키로 하는 [Windows Filtering Platform](https://learn.microsoft.com/en-us/windows/win32/fwp/windows-filtering-platform-start-page) 이그레스 펜스, 쓰기 제한 토큰, 그리고 작업 트리에 대한 명시적 ACE를 사용합니다
0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305
### 이중 격리 모델
효과적인 샌드박싱을 위해서는 파일 시스템 격리와 네트워크 격리 모두 필요합니다. 파일 격리가 없으면 침해된 프로세스가 SSH 키나 기타 민감한 파일을 유출할 수 있습니다. 네트워크 격리가 없으면 프로세스가 샌드박스를 탈출하여 무제한 네트워크 액세스를 얻을 수 있습니다.
**파일 시스템 격리**는 읽기 및 쓰기 제한을 시행합니다:
- **읽기** (거부 후 허용 패턴): 기본적으로 읽기 액세스는 모든 곳에서 허용됩니다. 넓은 영역(예: `/Users`)을 거부한 다음 그 안의 특정 경로(예: `.`)를 다시 허용할 수 있습니다. `allowRead`는 `denyRead`보다 우선합니다 — 이는 `denyWrite`가 `allowWrite`보다 우선하는 쓰기와는 반대입니다. `allowRead` 영역 내부에 있으면서 그보다 더 구체적인 `denyRead` 항목(예: `allowRead: ["."]`와 함께 `denyRead: ["**/.env"]` 또는 `["./secrets"]`)은 여전히 거부된 상태로 유지됩니다.
- **쓰기** (허용 전용 패턴): 기본적으로 쓰기 액세스는 모든 곳에서 거부됩니다. 경로(예: `.`, `/tmp`)를 명시적으로 허용해야 합니다. 빈 허용 목록은 쓰기 액세스가 없음을 의미합니다.
**네트워크 격리** (허용 전용 패턴): 기본적으로 모든 네트워크 액세스는 거부됩니다. 도메인을 명시적으로 허용해야 합니다. 빈 allowedDomains 목록은 네트워크 액세스가 없음을 의미합니다. 네트워크 트래픽은 호스트에서 실행되는 프록시 서버를 통해 라우팅됩니다:
- **Linux**: 요청은 Unix 도메인 소켓을 통해 파일 시스템을 경유하여 라우팅됩니다. 샌드박스된 프로세스의 네트워크 네임스페이스가 완전히 제거되므로, 모든 네트워크 트래픽은 호스트에서 실행되는 프록시(샌드박스에 바인드 마운트된 Unix 소켓에서 수신)를 통해야 합니다
- **macOS**: Seatbelt 프로필은 특정 localhost 포트로의 통신만 허용합니다. 프록시가 이 포트에서 수신하여 모든 네트워크 액세스를 위한 제어된 채널을 만듭니다
- **Windows**: 머신 전체 WFP 필터 세트가 `srt-sandbox` 계정으로 실행되는 프로세스가 만드는 아웃바운드 연결을 차단하며, 프록시 포트 범위로의 루프백은 예외입니다. 프록시는 해당 범위 내에서 수신하여 모든 네트워크 액세스를 위한 제어된 채널을 만듭니다. 이 필터는 소켓을 생성하기 전에 스레드에 다른 토큰을 설정하는 샌드박스된 프로세스를 따르지 않으며, 시스템 서비스가 대신 전달하는 트래픽도 따르지 않습니다. Windows [알려진 제한 사항](#known-limitations)을 참조하십시오
HTTP/HTTPS(HTTP 프록시 경유)와 기타 TCP 트래픽(SOCKS5 프록시 경유) 모두 이러한 프록시에 의해 중개되며, 프록시는 도메인 허용 목록과 거부 목록을 시행합니다.
Claude Code의 샌드박싱에 대한 자세한 내용은 다음을 참조하십시오:
- [Claude Code 샌드박싱 문서](https://docs.claude.com/en/docs/claude-code/sandboxing)
- [권한 프롬프트를 넘어서: Claude Code를 더 안전하고 자율적으로 만들기](https://www.anthropic.com/engineering/claude-code-sandboxing)
## 아키텍처
```
src/
├── index.ts # Library exports
├── cli.ts # CLI entrypoint (srt command)
├── utils/ # Shared utilities
│ ├── debug.ts # Debug logging
│ ├── settings.ts # Settings reader (permissions + sandbox config)
│ ├── platform.ts # Platform detection
│ └── exec.ts # Command execution utilities
└── sandbox/ # Sandbox implementation
├── sandbox-manager.ts # Main sandbox manager
├── sandbox-schemas.ts # Zod schemas for validation
├── sandbox-violation-store.ts # Violation tracking
├── sandbox-utils.ts # Shared sandbox utilities
├── http-proxy.ts # HTTP/HTTPS proxy for network filtering
├── socks-proxy.ts # SOCKS5 proxy for network filtering
├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing
├── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing
└── windows-sandbox-utils.ts # Windows srt-win sandboxing
```
## 사용법
### CLI 도구로 사용하기
`srt` 명령(Anthropic Sandbox Runtime)은 모든 명령을 보안 경계로 감쌉니다:
```bash
# Run a command in the sandbox
srt echo "hello world"
# With debug logging
srt --debug curl https://example.com
# Specify custom settings file
srt --settings /path/to/srt-settings.json npm install
```
설정 파일은 선택 사항입니다 — `~/.srt-settings.json`에 파일이 없으면 `srt`는 내장 기본값으로 실행됩니다: 네트워크 접근 없음, 기본 쓰기 경로 외부에 쓰기 없음, 읽기는 제한 없음. 파일이 _있지만_ 비어 있거나, 읽을 수 없거나, 검증을 통과하지 못하면 오류입니다: `srt`는 그렇게 알리고 기본값으로 폴백하지 않고 종료합니다. 기본값은 더 약한 설정이 아니라 다른 설정이기 때문입니다 — 폴백하면 파일의 `denyRead`, `allowRead` 및 자격 증명 규칙을 비롯해 파일이 지정한 모든 것이 사라집니다. `--settings`로 지정한 파일도 마찬가지이며, 이 파일 역시 존재해야 합니다.
#### 명령 실행 중 설정 업데이트: `--control-fd`
`--control-fd <fd>`는 호출자가 이미 열어 둔 디스크립터에서 설정 업데이트를 읽으며, 설정 파일과 같은 형태의 JSON 객체를 한 줄에 하나씩 읽습니다. 각 줄은 전체 설정을 대체하며 동일한 스키마로 검증되므로, 한 줄은 패치가 아니라 완전한 설정입니다: `network`와 `filesystem`은 필수이며, 줄에서 생략된 키는 기본값으로 돌아갑니다.
```bash
# fd 3 is the read end of a pipe the caller writes lines to
srt --control-fd 3 -- npm test
```
실행 중인 명령에 대해 허용된 라인이 적용되면 바뀌는 사항:
- 네트워크 목록 — `network.allowedDomains`, `deniedDomains`(`deniedDomainReasons` 포함) 및 `deniedResolvedAddresses` — 은 모든 플랫폼에서 다음 연결 시점에 적용됩니다. 프록시는 요청마다 이 목록을 참조하며, 확인된 주소 검사도 같은 라인에서 다시 구성됩니다. 요청마다 읽는 `network.mitmProxy`와 `network.tlsTerminate.excludeDomains`도 마찬가지입니다.
- `credentials.sigv4`는 srt가 `credentials` 블록과 함께 시작된 경우 다음 요청 시점에 적용됩니다. 서명 훅은 그때만 설치되며, 정책을 캡처하지 않고 요청마다 읽습니다.
- 그 외에는 아무것도 바뀌지 않습니다. 파일시스템 규칙은 이미 seatbelt 프로필, bubblewrap argv 또는 Windows ACE에 반영되어 있고, 자격 증명 마스크는 명령이 래핑될 때 만들어졌으며, 실행 중인 프록시 서버는 시작 시점에 `network.parentProxy`와 TLS 종료 여부를 캡처했습니다.
- Windows에서 srt는 파일 접근 집합(`filesystem.*`과 `credentials.files`를 합친 것)이 시작 시 적용된 집합과 다른 라인을 감지하고, `SRT_DEBUG`에서만 이를 알립니다. ACL 부여는 세션 전체에 적용되므로 시작 시 적용된 집합이 계속 유효합니다. 라인의 나머지 부분은 위와 같이 적용됩니다.
srt가 채널을 읽는 방식과 거부하는 대상:
- 디스크립터는 **3 이상**의 정수여야 하며 읽을 수 있어야 합니다. `0`-`2`는 표준 스트림입니다. srt는 주어진 디스크립터를 읽을 수 없으면 명령을 실행하는 대신 오류와 함께 종료하므로, 죽은 채널이 살아 있는 채널로 통과되는 일은 없습니다. 단 하나의 업데이트도 전달하기 전에 죽은 채널은 명령도 함께 종료시킵니다. 업데이트를 전달한 뒤 죽은 채널은 그 사실을 알리고 마지막으로 적용된 구성으로 명령을 계속 실행합니다.
- 유효한 구성이 아닌 라인은 stderr에 보고되고 버려지며, 이전 구성이 계속 유효합니다. 라인 자체는 터미널이 아닌 디버그 로그로 전달되고, 빈 라인은 조용히 무시됩니다.
- srt는 샌드박스가 올라온 뒤에야 읽기를 시작하므로, 그 전에 작성된 업데이트는 유실되지 않고 채널에서 대기합니다. 또한 샌드박스가 시작될 때의 구성으로 덮어쓸 수도 없습니다. 이러한 라인은 명령이 아직 래핑되는 중에 읽힐 수 있으며, 그 경우 파일시스템 규칙을 포함하여 해당 라인이 래핑에 사용되는 구성이 됩니다. 그렇게 일찍 읽히는지는 래핑에 걸리는 시간에 따라 달라지므로(Linux에서는 큰 트리에 대한 deny glob이 충분히 길 수 있음), 명령이 시작 시 반드시 적용받아야 하는 규칙은 설정 파일에 두어야 합니다.
- srt는 **래핑된 명령과 함께 종료**되며, 작성자가 디스크립터를 닫을 때까지 기다리지 않습니다. 입력의 끝도 오류가 아닙니다. 명령은 마지막으로 적용된 구성으로 계속 실행됩니다.
- srt에는 **전용의 읽기 전용 끝**을 제공하십시오. srt는 파이프나 소켓을 비차단 모드로 전환하며, 이 플래그는 열린 파일 설명에 존재하므로, 같은 설명을 보유한 다른 대상(셸의 `exec 3<fifo`, 부모가 계속 사용하는 디스크립터의 `pass_fds`)은 그때부터 자체 차단 읽기에서 `EAGAIN`을 받게 됩니다.
- macOS와 Linux에서 샌드박스된 명령은 디스크립터를 받지 않습니다. srt는 명령에 대해 해당 슬롯을 `/dev/null`로 지정하므로, 샌드박스 내부의 어떤 것도 업데이트를 읽거나 자체 구성을 작성할 수 없습니다. Windows에서는 명령이 세 개의 표준 스트림만으로 생성되므로, 제어 디스크립터는 명령에 전달되는 디스크립터에 포함되지 않습니다.
### 외부 결정자와 함께하는 독립 실행형 프록시: `srt proxy`
`srt proxy`(또는 `bun run build:srt-proxy`로 빌드되는 단일 파일 `srt-proxy` 실행 파일)는 워크로드를 다른 곳에서 실행하는 호스트를 위해 샌드박스된 자식 프로세스 없이 SRT의 HTTP 프록시만 실행합니다. 호스트가 넘겨준 리스닝 소켓에서 연결을 수락하고, 모든 CONNECT를 프로세스 내에서 종료하며, 또 다른 상속된 디스크립터를 통해 연결되는 별도의 결정자 프로세스가 허용할 때만 요청을 전달합니다. 명령줄, 결정자 프로토콜 및 보안 모델은 [docs/srt-proxy.md](https://github.com/anthropic-experimental/sandbox-runtime/blob/main/docs/srt-proxy.md)를 참조하십시오.
### 라이브러리로 사용하기
```typescript
import {
SandboxManager,
type SandboxRuntimeConfig,
} from '@anthropic-ai/sandbox-runtime'
import { spawn } from 'child_process'
// Define your sandbox configuration
const config: SandboxRuntimeConfig = {
network: {
allowedDomains: ['example.com', 'api.github.com'],
deniedDomains: [],
},
filesystem: {
denyRead: ['~/.ssh'],
allowWrite: ['.', '/tmp'],
denyWrite: ['.env'],
},
}
// Initialize the sandbox (starts proxy servers, etc.)
await SandboxManager.initialize(config)
// Wrap a command with sandbox restrictions
const sandboxedCommand = await SandboxManager.wrapWithSandbox(
'curl https://example.com',
)
// Execute the sandboxed command
const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })
// Handle exit and cleanup after child process completes
child.on('exit', async code => {
console.log(`Command exited with code ${code}`)
// Cleanup when done (optional, happens automatically on process exit)
await SandboxManager.reset()
})
```
**위반 귀속(`commandId` / `commandText`).** 래핑된 명령이 실행되는 동안 관찰된 위반(seatbelt 로그 라인, seccomp 이벤트, 프록시 거부)은 귀속 키 아래에 저장되며, `annotateStderrWithSandboxFailures(key, stderr)` / `getViolationsForCommand(key)`는 동일한 키로 이를 조회합니다. 기본적으로 키는 래핑된 문자열 자체입니다. 대신 불투명한 호출별 `commandId`(예: 도구 사용 id)를 전달하여 이를 키로 사용하세요 — 권장 사항: 키는 처음 100자로 비교되므로, 접두사를 공유하는 긴 명령은 그렇지 않으면 교차 귀속될 수 있고, 동일한 텍스트를 재실행하면 이전 실행의 이벤트를 상속하게 됩니다. _실행하는_ 문자열이 호출이 _나타내는_ 명령과 다른 경우(예: 조립된 `source <snapshot> && eval '<cmd>'`를 래핑하는 경우), `commandText: '<cmd>'`도 전달하세요: 이것이 `ignoreViolations` 명령 패턴이 대조되는 대상이며 각 위반이 `command`로 보고하는 값입니다. `wrapWithSandbox`에 전달하는 `commandId`는 이후 `annotateStderrWithSandboxFailures` / `getViolationsForCommand`에 전달하는 것과 동일한 비어 있지 않은 문자열이어야 합니다. 빈 문자열은 `commandId`가 전혀 없는 것으로 취급되므로 키는 명령이 됩니다.
키만 100자로 잘립니다. v0.0.76 기준으로, 보고되는 `command` — 그리고 `ignoreViolations` 명령 패턴이 대조되는 텍스트 — 는 `commandId` 없이 래핑된 호출의 경우 처음 100자가 아니라 전체 명령입니다. 따라서 패턴은 이전보다 더 많이 억제할 수만 있고, 결코 더 적게 억제할 수는 없습니다. 이 프로세스의 어떤 호출도 등록하지 않은 귀속 키(캐리어는 샌드박스 내부에서 쓰기 가능함)는 정리(sanitize)되어 동일한 키 길이로 잘려 보고됩니다.
```typescript
const wrapped = await SandboxManager.wrapWithSandbox(
assembledCommand, // what actually runs
undefined,
undefined,
undefined,
{ commandId: invocationId, commandText: rawCommand },
)
// ... run it ...
const annotated = SandboxManager.annotateStderrWithSandboxFailures(
invocationId,
stderr,
)
```
**어떤 규칙도 결정하지 못한 호스트에 대해 묻기(ask 콜백).** `SandboxManager.initialize`의 두 번째 인자는 선택적인 `SandboxAskCallback`입니다. 이 콜백은 `network.deniedDomains`도, `network.allowedDomains`도, 명령별 허용 목록(다음 절)도 결정하지 못한 목적지에 대해 호출되며, `network.strictAllowlist` 아래에서는 절대 호출되지 않습니다. 콜백이 없으면 그러한 목적지는 거부됩니다. 콜백은 다음 중 하나로 귀결됩니다:
- `true` - 연결을 허용합니다. 오직 `true` 값만 허용합니다.
- `false` - 거부합니다. 위반 라인에는 일반적인 사유인 `user denied`가 보고됩니다.
- `{ allow: false, reason: '...' }` - 거부하며, 일반 텍스트 대신 `reason`을 위반 라인에 보고하여 `<sandbox_violations>` 블록을 읽는 사람(모델 포함)이 그 이유와 대신 무엇을 해야 하는지 알 수 있게 합니다. 샌드박스된 클라이언트도 이를 읽으며, 자신의 CONNECT에 응답하는 `403`의 상태 문구로 사용됩니다.
그 밖의 다른 응답은 참 같은 값이든 아니든 거부되며 일반적인 사유를 보고합니다. 여기에는 `allow: false`라고 밝히지 않고 `reason`만 담은 객체, 예컨대 `{ allow: true, reason: '...' }`도 포함되며, 그 reason은 보고되지 않습니다.
reason은 위반 라인의 나머지 부분과 마찬가지로 저장되기 전에 정제됩니다. 제어 문자(줄바꿈과 탭 포함)나 보이지 않는 문자(제로 폭 문자, 그중 조이너, 그리고 양방향 제어 문자)의 연속은 각각 하나의 공백이 됩니다. `<`와 `>`는 제거되므로 `re-run with <host> listed`는 `re-run with host listed`로 저장됩니다. 양끝은 잘라냅니다. 결과는 `String.prototype.length`가 세는 방식대로 UTF-16 코드 단위로 계산하여 500자로 잘립니다. 그 후 아무것도 남지 않은 reason은 `user denied`로 대체됩니다. 한 줄의 일반 텍스트로 작성하십시오.
**0.0.79 이후 첫 릴리스에서의 동작 변경:** 이전에는 필터가 참 같은 응답이면 무엇이든 허용했으므로, `true`가 아닌 참 같은 값(`1`, 문자열, 임의의 객체)으로 귀결되는 콜백은 연결을 허용했습니다. 이제는 거부합니다. 같은 이유로, `{ allow: false, reason }`을 이전 릴리스로 되돌리면 객체는 참 같은 값이므로 그곳에서는 허용으로 읽힙니다. `SandboxManager.askCallbackDenyReason`은 객체를 이해하는 릴리스에서 `true`입니다. 객체를 반환하기 전에 이를 확인하고, 존재하지 않는 곳에서는 일반 `false`로 거부하십시오.
```typescript
const deny = (reason: string) =>
SandboxManager.askCallbackDenyReason
? { allow: false as const, reason }
: false
await SandboxManager.initialize(config, async ({ host, port }) => {
if (await userApproves(host, port)) return true
return deny(`${host} was not approved for this session`)
})
```
**명령별 네트워크 허용 목록(`registerCommandNetworkLists`).** 하나의 호출은 실행되는 동안 구성된 목록에 더해 자체 허용 목록을 함께 가질 수 있습니다:
```typescript
import { randomBytes } from 'node:crypto'
// 128 bits of randomness, 22 characters. See below for why nothing less will do.
const commandId = randomBytes(16).toString('base64url')
const wrapped = await SandboxManager.wrapWithSandbox(
command,
undefined,
undefined,
undefined,
{ commandId },
)
SandboxManager.registerCommandNetworkLists(commandId, {
allowedDomains: ['registry.example.org', '*.cdn.example.org:443'],
})
const child = spawn(wrapped, { shell: true })
const unregister = () => SandboxManager.unregisterCommandNetworkLists(commandId)
child.once('exit', unregister)
child.once('error', unregister) // the child never started
```
항목은 `network.allowedDomains`의 문법과 매처를 사용하며 동일한 검증을 받습니다. `registerCommandNetworkLists`는 유효하지 않은 항목에 대해, 그리고 `commandId`가 22자 미만인 경우(짝이 없는 서로게이트는 포함되지 않음) 예외를 발생시킵니다. id를 다시 등록하면 해당 목록이 대체됩니다. `unregisterCommandNetworkLists`는 목록이 없는 id에 대해서는 아무 동작도 하지 않습니다. `reset()`은 모든 등록을 제거합니다.
연결에 대한 평가 순서는 다음과 같습니다:
1. `network.deniedDomains` 일치 시 거부;
2. `network.allowedDomains` 일치 시 허용;
3. `network.strictAllowlist`인 경우 거부;
4. 연결이 제시하는 id에 대해 등록된 목록에서 일치 시 허용;
5. ask 콜백이 있으면 그것이 결정하고, 그렇지 않으면 연결은 거부됩니다.
따라서 명령별 항목은 구성된 `deniedDomains` 항목을 절대 재정의하지 않으며, `strictAllowlist` 하에서는 완전히 무시됩니다. 또한 `network.*`에 절대 포함되지 않습니다: `getConfig()`와 `getNetworkRestrictionConfig()`는 이를 표시하지 않으며, 마스킹된 자격 증명의 기본 `injectHosts` 범위인 `network.allowedDomains`도 확장되지 않습니다. 이 방식으로 허용된 호스트는 다이얼될 때 여전히 확인된 주소 검사를 거치며, 명령별 목록의 IP 리터럴은 여기에 어떤 예외도 추가하지 않습니다.
id가 무엇이어야 하는지, 그리고 이 기능이 무엇이 아닌지: 프록시는 프록시 사용자 이름으로부터 연결이 어떤 호출에 속하는지 학습하며, 사용자 이름은 샌드박스 내부의 클라이언트가 제시합니다. 따라서 id는 연결을 허용 목록에 묶는 **유일한** 요소입니다. id는 **반드시** 추측 불가능해야 합니다: 최소 128비트의 무작위성, 절대 카운터, 타임스탬프 또는 명령에서 파생된 어떤 것도 안 됩니다. 다른 활성 호출의 id를 제시하는 샌드박스 프로세스는 해당 호출의 허용을 얻게 되므로, 이는 귀속(attribution)이지 한 세션의 동시 명령 간 경계가 아닙니다. 래핑된 명령을 스폰하기 직전에 등록하고, 자식이 종료되거나 시작되지 않았을 때 등록 해제하십시오: 명령보다 오래 살아남는 등록은 해당 id를 제시할 가치가 있는 창을 넓힙니다. 모든 귀속 키와 마찬가지로, id의 처음 100자만 참여합니다. id는 ASCII로 유지하십시오: 인코딩된 형태가 프록시 사용자 이름(255바이트)에 맞지 않는 비ASCII id의 목록은 등록되지만 절대 적용되지 않습니다.
`network.httpProxyPort`가 외부 프록시를 가리키는 동안에는 어떤 목록도 적용되지 않습니다: 래핑은 해당 프록시에 사용자 이름을 주지 않으므로 어떤 연결도 id를 전달하지 않습니다. `registerCommandNetworkLists`는 여전히 성공하며(`SRT_DEBUG` 하에서 한 줄로 그렇게 표시함), 모든 연결은 목록이 존재하지 않는 것처럼 결정됩니다.
#### 사용 가능한 내보내기
```typescript
// Main sandbox manager
export { SandboxManager } from '@anthropic-ai/sandbox-runtime'
// Violation tracking
export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'
// TypeScript types
export type {
SandboxRuntimeConfig,
NetworkConfig,
FilesystemConfig,
IgnoreViolationsConfig,
SandboxAskCallback,
FsReadRestrictionConfig,
FsWriteRestrictionConfig,
NetworkRestrictionConfig,
} from '@anthropic-ai/sandbox-runtime'
```
## 구성
### 설정 파일 위치
기본적으로 샌드박스 런타임은 `~/.srt-settings.json`에서 구성을 찾습니다. `--settings` 플래그를 사용하여 사용자 지정 경로를 지정할 수 있습니다:
```bash
srt --settings /path/to/srt-settings.json <command>
```
### 전체 구성 예시
```json
{
"network": {
"allowedDomains": [
"github.com",
"*.github.com",
"lfs.github.com",
"api.github.com",
"npmjs.org",
"*.npmjs.org"
],
"deniedDomains": ["malicious.com"],
"allowUnixSockets": ["/var/run/docker.sock"],
"allowLocalBinding": false
},
"filesystem": {
"denyRead": ["~/.ssh"],
"allowRead": [],
"allowWrite": [".", "src/", "test/", "/tmp"],
"denyWrite": [".env", "config/production.json"]
},
"ignoreViolations": {
"*": ["/usr/bin", "/System"],
"git push": ["/usr/bin/nc"],
"npm": ["/private/tmp"]
},
"enableWeakerNestedSandbox": false,
"allowNestedUserNamespaces": false,
"enableWeakerNetworkIsolation": false,
"allowAppleEvents": false
}
```
### 구성 옵션
#### 네트워크 구성
**허용 전용 패턴**을 사용합니다. 모든 네트워크 액세스는 기본적으로 거부됩니다.
- `network.allowedDomains` - 허용된 도메인 배열 (`*.example.com` 같은 와일드카드 지원). 빈 배열 = 네트워크 액세스 없음. 선택적 `:port` 접미사 (`api.example.com:443`, `*.example.com:8443`)는 해당 항목을 해당 대상 포트로 제한하며, 포트가 없는 항목은 모든 포트와 일치합니다.
- IPv6 리터럴은 RFC 3986 스타일로 대괄호로 묶어야 합니다: `[::1]`, `[2001:db8::1]:443`. 대괄호 없는 다중 콜론 항목은 모호하므로 거부됩니다 (`2001:db8::1:443` 자체는 유효한 주소입니다).
- `network.deniedDomains` - 거부된 도메인 배열 (먼저 확인되며 allowedDomains보다 우선함). 동일한 `:port` 접미사를 사용하며, 전체 거부를 위해 단독 `*` (또는 `*:22`)가 허용됩니다.
- `network.deniedDomainReasons` - `deniedDomains` 항목(정확한 문자열로 일치)에서 해당 항목이 연결을 거부할 때 `<sandbox_violations>` 줄에 나타나는 모델 대상 사유로의 선택적 매핑 — 무엇이 차단되었는지와 승인된 대안을 명시하세요 (예: `{"github.com:22": "SSH pushes to GitHub are blocked; use an https:// remote"}`). 사유가 없는 항목은 일반 사유를 보고합니다. SSH 대상(포트 22)의 경우 사유는 인밴드로도 전달됩니다: 인증 없는 SOCKS ProxyCommand(예: BSD `nc -X 5`)를 통해 터널링된 SSH 클라이언트는 사유를 설명으로 하는 키 교환 전 SSH 연결 해제를 수신하며, OpenSSH는 이를 그대로 출력합니다 — OpenSSH는 비ASCII 문자를 잘라내고 이스케이프하므로 이러한 사유는 약 400 ASCII 문자 이하로 유지하고 명령형을 먼저 두세요. srt가 주입하는 `GIT_SSH_COMMAND`처럼 실제로 인증하는 클라이언트는 CONNECT에 응답하는 `403`의 상태 구문으로 모든 포트에서 사유를 받으며, 이는 해당 명령과 많은 HTTP 클라이언트가 출력하는 것입니다. 여기서는 위반 줄에서처럼 정리되고 잘리며, 모든 비ASCII 문자는 `?`가 됩니다. 클라이언트마다 상태 줄에서 이를 읽는 방식이 다르기 때문입니다.
- `network.allowLocalBinding` - 샌드박스된 명령이 로컬 포트를 직접 사용하도록 허용합니다 (불리언, 기본값: false). macOS: 모든 로컬 포트에서 바인드 및 수락하고, 모든 포트에서 `localhost`에 연결합니다. Windows: 모든 TCP 또는 UDP 포트에서 루프백 주소에 연결합니다. 이는 머신 전체 설치의 일부입니다. [Windows의 로컬 서버](#local-servers-on-windows)를 참조하세요. Linux: 무시됨 — 샌드박스에는 자체 네트워크 네임스페이스가 있으며, 그 루프백은 항상 사용 가능하고 호스트의 것이 아닙니다.
**확인된 주소 검사.** 허용/거부 목록은 _이름_으로 일치하지만, 허용된 이름의 DNS(또는 허용된 와일드카드 아래의 모든 레이블)를 제어하는 사람은 그것이 확인되는 대상을 제어합니다. 따라서 허용된 **호스트 이름**에 직접 연결하기 전에 프록시는 이를 한 번 확인하고, 거부된 집합에 있는 주소는 버리고, 살아남은 주소에 연결합니다 (검사에 통과한 주소가 연결되는 주소이며, 두 번째 조회는 없습니다). 살아남는 것이 없으면 연결은 다른 정책 거부와 마찬가지로 거부됩니다: HTTP/CONNECT는 `403`을 받고 (`X-Proxy-Error: blocked-by-sandbox-runtime`, 사유는 본문에, CONNECT의 경우 상태 구문으로), SOCKS는 "connection not allowed by ruleset"을 받으며, `deny network-outbound host:port (resolved to a loopback address)` 줄 — 주소 자체가 아니라 주소 클래스(루프백, 링크 로컬, 이 호스트의 것, 클라우드 메타데이터, 거부 목록에 있음, 목록에 있음, …)를 명시하며, 주소 자체는 디버그 로그에만 기록됨 — 이 위반 저장소에 기록됩니다.
---
[Read more](https://github.com/anthropic-experimental/sandbox-runtime)