Skip to main content

Security model

How the Code Interpreter isolates untrusted code — hardened sandbox mode, Ed25519 JWT verification, signed execution manifests, network-egress control, environment scrubbing, and the secrets the stack auto-provisions.

The Code Interpreter runs untrusted, model-generated code, so its security model is layered: every request is authenticated, every job is cryptographically authorized, the sandbox holds no secrets, and outbound network access is brokered and logged. This page explains each layer.

The master switch: hardened sandbox mode

CODEAPI_HARDENED_SANDBOX_MODE (default true) enables the full security posture. When on:

  • The sandbox-runner scrubs forbidden environment variables before executing any job (see Environment scrubbing).
  • Jobs require a signed execution manifest — unsigned requests are rejected.
  • The sandbox has no direct network; only the egress-gateway is reachable, and only with a valid egress grant.

Set it the same everywhere

This flag must match on the api, service-worker, egress-gateway, and sandbox-runner. A mismatch causes jobs to be rejected (one side signs/scrubs, the other expects the opposite).

Request authentication (JWT)

The api is the only externally-reachable service, and it authenticates every request with an Ed25519 (EdDSA) JWT issued by the IntelliAsk app.

Claim / settingVariableDefault
Issuer (iss)CODEAPI_JWT_ISSUERintelliask
Audience (aud)CODEAPI_JWT_AUDIENCEcodeapi
Allowed algorithmsCODEAPI_JWT_ALLOWED_ALGSEdDSA,RS256
Key ID hintCODEAPI_JWT_KIDintelliask-codeapi
Verification keyCODEAPI_JWT_PUBLIC_KEY(auto-provisioned)

The app signs with the private half of the keypair; the api verifies with CODEAPI_JWT_PUBLIC_KEY. Setting CODEAPI_AUTH_PROVIDER=none (or LOCAL_MODE=true) disables this — development only.

Job authorization (signed execution manifests)

Passing the api's JWT check is not enough to run code. The service-worker signs an execution manifest for each job with its Ed25519 private key (CODEAPI_EXECUTION_MANIFEST_PRIVATE_KEY), and the sandbox-runner verifies it with the matching public key (SANDBOX_EXECUTION_MANIFEST_PUBLIC_KEY) before executing. This ensures a job reaching the sandbox genuinely came from the worker and wasn't tampered with.

SANDBOX_REQUIRE_EGRESS_MANIFEST=true enforces that no unsigned job runs.

Environment scrubbing

Before running untrusted code, the sandbox-runner strips sensitive environment variables from the job's environment so leaked code can't read them. In hardened mode it removes anything matching these patterns:

PatternExamples
CODEAPI_*CODEAPI_INTERNAL_SERVICE_TOKEN, CODEAPI_JWT_PUBLIC_KEY
REDIS_*REDIS_PASSWORD
AWS_*, S3_*, MINIO_*MINIO_SECRET_KEY, AWS_SECRET_ACCESS_KEY
*SECRET*, *TOKEN*, *PASSWORD*, *PRIVATE_KEY*any secret-shaped variable

This is why the sandbox-runner itself holds no long-lived secrets it doesn't strictly need — the credentials live in the unprivileged services.

Network egress control

The sandbox is network-isolated (SANDBOX_DISABLE_NETWORKING=true). Its only route out is the egress-gateway, reachable on the single allowed local port (SANDBOX_ALLOWED_LOCAL_NETWORK_PORT=3190).

  • The egress-gateway signs and verifies egress grants with CODEAPI_EGRESS_GRANT_SECRET (HMAC). With CODEAPI_EGRESS_LEDGER_REQUIRED=true, every proxied request must carry a valid grant.
  • Payloads are capped by EGRESS_GATEWAY_MAX_TOOL_CALL_BYTES (1 MiB default).
  • File I/O and tool calls are routed to the file-server and tool-call-server through the gateway, so the sandbox never talks to them directly.
flowchart LR
    SB[sandbox-runner<br/>no direct network] -->|only :3190 + grant| EG[egress-gateway]
    EG -->|verify grant<br/>HMAC| EG
    EG --> FS[file-server]
    EG --> TC[tool-call-server]
    EG -.->|allow-listed| NET[(external network)]

Service-to-service authentication

Internal calls between the codeapi services are authenticated with a shared HMAC token, CODEAPI_INTERNAL_SERVICE_TOKEN (identical across all services). This prevents a foothold in one service from freely impersonating another.

Tenancy

The Code Interpreter supports single- or multi-tenant operation:

VariablePurposeDefault
CODEAPI_JWT_SINGLE_TENANT_IDTenant all jobs run under in single-tenant modelegacy
CODEAPI_TENANT_ISOLATION_STRICTReject requests without an explicit X-CodeAPI-Tenant headerfalse

The Compose stack runs single-tenant by default.

The auto-provisioned secrets

None of the following should be set by hand — intelliask secrets gen and the Garage bootstrap create them:

SecretUsed byPurpose
CODEAPI_JWT_PUBLIC_KEYapiVerify app-issued request JWTs
CODEAPI_EXECUTION_MANIFEST_PRIVATE_KEYservice-workerSign execution manifests
SANDBOX_EXECUTION_MANIFEST_PUBLIC_KEYsandbox-runnerVerify execution manifests
CODEAPI_INTERNAL_SERVICE_TOKENall servicesService-to-service HMAC auth
CODEAPI_EGRESS_GRANT_SECRETegress-gatewaySign/verify egress grants
GARAGE_RPC_SECRET, GARAGE_ADMIN_TOKENGarageCluster + admin auth
MINIO_ACCESS_KEY, MINIO_SECRET_KEYfile-server, GarageS3 credentials

To rotate these, use intelliask secrets rotate and re-run the Garage bootstrap where S3 keys change, then recreate the codeapi services with intelliask up.

Last updated on