Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@ KVM_DEVICE_PATH=/dev/kvm
SANDBOX_USE_CGROUPV2=true
SANDBOX_LOG_LEVEL=DEBUG
SANDBOX_MAX_PROCESS_COUNT=100
SANDBOX_RUN_CPU_TIME=10000
SANDBOX_RUN_TIMEOUT=15000
SANDBOX_OUTPUT_MAX_SIZE=65536

# Execution limits passed through by docker-compose.yaml (Compose defaults shown).
# SANDBOX_RUN_TIMEOUT and SANDBOX_RUN_CPU_TIME bound one sandbox run (ms).
# JOB_TIMEOUT bounds the whole job, including file transfer, in the API, worker
# and egress gateway (ms); raise it together with SANDBOX_RUN_TIMEOUT.
# SANDBOX_OUTPUT_MAX_SIZE caps each output stream (bytes). MAX_FILE_SIZE caps
# each uploaded file (bytes).
# SANDBOX_RUN_TIMEOUT=300000
# SANDBOX_RUN_CPU_TIME=300000
# SANDBOX_OUTPUT_MAX_SIZE=65536
# JOB_TIMEOUT=300000
# MAX_FILE_SIZE=26214400

# Docker Compose keeps hardened mode enabled. Its API exposes bridge routes even
# with the default HTTP sandbox backend, so configure a private enrollment token
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ the resolver handoff and rootfs assembly without requiring KVM.
Setting `KVM_ENABLED=false` still selects the directory-root target and the
host package mount automatically for direct NsJail development.

Compose reads the execution limits from `.env`: `SANDBOX_RUN_TIMEOUT` and
`SANDBOX_RUN_CPU_TIME` (default `300000` ms each), `SANDBOX_OUTPUT_MAX_SIZE`
(default `65536` bytes per stream), `JOB_TIMEOUT` (default `300000` ms, applied
to the API, worker and egress gateway) and `MAX_FILE_SIZE` (default `26214400`
bytes per uploaded file). Raise `JOB_TIMEOUT` together with
`SANDBOX_RUN_TIMEOUT`; the job deadline also covers file transfer.

Local Docker Compose files set `CODEAPI_INTERNAL_SERVICE_TOKEN` to a shared
development value by default. Production deployments must override it with a
strong secret; when it is unset, file object routes and Tool Call Server
Expand Down
10 changes: 7 additions & 3 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ services:
- EXECUTION_MANIFEST_MAX_OUTPUT_FILES=${EXECUTION_MANIFEST_MAX_OUTPUT_FILES:-}
- EXECUTION_MANIFEST_MAX_REQUESTS=${EXECUTION_MANIFEST_MAX_REQUESTS:-}
- CODEAPI_HTTP_JSON_LIMIT=${CODEAPI_HTTP_JSON_LIMIT:-50mb}
- MAX_FILE_SIZE=${MAX_FILE_SIZE:-26214400}
- JOB_TIMEOUT=${JOB_TIMEOUT:-300000}
- SERVICE_PORT=3112
- SANDBOX_ENDPOINT=http://sandbox-runner:2000/api/v2
- EGRESS_GATEWAY_URL=http://egress_gateway:3190
Expand Down Expand Up @@ -95,6 +97,7 @@ services:
- REDIS_PASSWORD=localdev
- PYTHON_CONCURRENCY=${PYTHON_CONCURRENCY:-1}
- OTHER_CONCURRENCY=${OTHER_CONCURRENCY:-8}
- JOB_TIMEOUT=${JOB_TIMEOUT:-300000}
- WORKER_HEALTH_PORT=3113
depends_on:
redis:
Expand All @@ -118,6 +121,7 @@ services:
- EGRESS_GATEWAY_FILE_SERVER_URL=http://file_server:3000
- EGRESS_GATEWAY_TOOL_CALL_SERVER_URL=http://tool_call_server:3033
- EGRESS_GATEWAY_MAX_TOOL_CALL_BYTES=1048576
- JOB_TIMEOUT=${JOB_TIMEOUT:-300000}
- CODEAPI_EGRESS_GRANT_SECRET=${CODEAPI_EGRESS_GRANT_SECRET:-localdev-egress-grant-secret-change-me-32b}
- CODEAPI_INTERNAL_SERVICE_TOKEN=${CODEAPI_INTERNAL_SERVICE_TOKEN:-localdev-internal-service-token}
- REDIS_HOST=redis
Expand Down Expand Up @@ -169,15 +173,15 @@ services:
- SANDBOX_EXECUTE_BODY_LIMIT=${SANDBOX_EXECUTE_BODY_LIMIT:-50mb}
- SANDBOX_PACKAGES_DIRECTORY=/pkgs
- SANDBOX_LOG_LEVEL=DEBUG
- SANDBOX_OUTPUT_MAX_SIZE=65536
- SANDBOX_OUTPUT_MAX_SIZE=${SANDBOX_OUTPUT_MAX_SIZE:-65536}
- SANDBOX_MAX_CONCURRENT_JOBS=${SANDBOX_MAX_CONCURRENT_JOBS:-8}
- SANDBOX_PER_JOB_UIDS=${SANDBOX_PER_JOB_UIDS:-true}
- SANDBOX_JOB_UID_BASE=${SANDBOX_JOB_UID_BASE:-200000}
- SANDBOX_JOB_GID_BASE=${SANDBOX_JOB_GID_BASE:-200000}
- SANDBOX_JOB_UID_COUNT=${SANDBOX_JOB_UID_COUNT:-}
- SANDBOX_WORKSPACE_REAPER_MAX_AGE_SECONDS=${SANDBOX_WORKSPACE_REAPER_MAX_AGE_SECONDS:-3600}
- SANDBOX_RUN_TIMEOUT=300000
- SANDBOX_RUN_CPU_TIME=300000
- SANDBOX_RUN_TIMEOUT=${SANDBOX_RUN_TIMEOUT:-300000}
- SANDBOX_RUN_CPU_TIME=${SANDBOX_RUN_CPU_TIME:-300000}
- SANDBOX_COMPILE_TIMEOUT=30000
- LAUNCHER_VCPUS=2
- LAUNCHER_RAM_MIB=2048
Expand Down
37 changes: 36 additions & 1 deletion tests/compose-bridge-config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ function render(overrides) {
CODEAPI_BRIDGE_RECOVERY_MAX_CHALLENGES_PER_MINUTE: '',
CODEAPI_BRIDGE_RECOVERY_MAX_ATTEMPTS_PER_MINUTE: '',
CODEAPI_BRIDGE_RECOVERY_MAX_UNTRUSTED_PER_MINUTE: '',
SANDBOX_RUN_TIMEOUT: '',
SANDBOX_RUN_CPU_TIME: '',
SANDBOX_OUTPUT_MAX_SIZE: '',
JOB_TIMEOUT: '',
MAX_FILE_SIZE: '',
...overrides,
},
}));
Expand Down Expand Up @@ -76,4 +81,34 @@ for (const overrides of [
assert.ok(config.services.redis.volumes.some(volume => volume.target === '/data' && volume.type === 'volume'));
}
assert.equal(render({}).services.api.environment.CODEAPI_BRIDGE_TOKEN, '');
console.log('Compose bridge configuration passed (pairing, opt-in recovery, durable Redis).');

const limitServices = {
SANDBOX_RUN_TIMEOUT: ['sandbox-runner'],
SANDBOX_RUN_CPU_TIME: ['sandbox-runner'],
SANDBOX_OUTPUT_MAX_SIZE: ['sandbox-runner'],
JOB_TIMEOUT: ['api', 'service-worker', 'egress_gateway'],
MAX_FILE_SIZE: ['api'],
};
const limitDefaults = {
SANDBOX_RUN_TIMEOUT: '300000',
SANDBOX_RUN_CPU_TIME: '300000',
SANDBOX_OUTPUT_MAX_SIZE: '65536',
JOB_TIMEOUT: '300000',
MAX_FILE_SIZE: '26214400',
};
const limitOverrides = {
SANDBOX_RUN_TIMEOUT: '600000',
SANDBOX_RUN_CPU_TIME: '600000',
SANDBOX_OUTPUT_MAX_SIZE: '16384',
JOB_TIMEOUT: '615000',
MAX_FILE_SIZE: '104857600',
};
for (const [overrides, expected] of [[{}, limitDefaults], [limitOverrides, limitOverrides]]) {
const config = render(overrides);
for (const [name, services] of Object.entries(limitServices)) {
for (const service of services) {
assert.equal(config.services[service].environment[name], expected[name], `${service} ${name}`);
}
}
}
console.log('Compose bridge configuration passed (pairing, opt-in recovery, durable Redis, execution limits).');