Skip to main content
Use remote mode when you want centralized execution infrastructure and shared policy enforcement across multiple clients.

Diagram 1: Remote architecture overview

Start the server

isol8 serve \
  --key my-secret-key \
  --port 3000
If --key and ISOL8_API_KEY are both missing, startup fails. --key takes precedence when provided.
Port resolution order is --port > ISOL8_PORT > PORT > 3000. If the chosen port is unavailable, isol8 serve prompts for another port or can auto-select an open port.

Serve runtime behavior

isol8 serve has two startup paths:
  • Bun/dev path: runs server in-process.
  • Built CLI (Node) path: downloads/runs standalone isol8-server binary and can prompt for version update.
  • Both paths run graceful cleanup on SIGINT/SIGTERM (sessions, containers, and images).
Use isol8 serve --update to force binary re-download.

Authentication contract

isol8 supports two authentication modes that can work together.

Static key (default)

  • GET /health: no auth required.
  • all other endpoints: require Authorization: Bearer <api-key>.
StatusMeaning
401Authorization header missing
403token provided but invalid

DB-backed keys (optional)

When the server is started with --auth-db, API keys are stored in a database. Without a value, it defaults to SQLite at ~/.isol8/auth.db. You can also pass a postgres:// or mysql:// connection string for PostgreSQL or MySQL. The --key value becomes the master key with admin privileges:
  • Additional keys are created via POST /auth/keys (master key required)
  • DB keys authenticate execution, file, and session endpoints
  • DB keys cannot access admin routes (/auth/*) — only the master key can
  • Keys support configurable TTL, tenant scoping, and individual revocation
  • POST /auth/login exchanges the master key for a short-lived token

Login flow and credential storage

The CLI provides isol8 login to exchange the master key for a short-lived token:
isol8 login --host http://localhost:3000 --key my-master-key
This calls POST /auth/login, receives a token, and saves it to ~/.isol8/credentials.json (permissions 0600). After login, isol8 run --host <url> uses the stored token automatically. Key resolution priority (for isol8 run --host and RemoteIsol8):
  1. --key flag (explicit, always wins)
  2. ISOL8_API_KEY environment variable
  3. Stored credentials from ~/.isol8/credentials.json (host-matched, non-expired)
Use isol8 logout to remove stored credentials locally.

Library usage with DB-backed auth

When using RemoteIsol8 programmatically, pass the API key (master or DB-generated) directly:
import { RemoteIsol8 } from "@isol8/core";

const engine = new RemoteIsol8({
  host: "http://localhost:3000",
  apiKey: "isk_...",  // master key or DB-generated key
});
For server-side key management, use the createServer function with authDbPath. Pass a file path for SQLite, or a postgres:// / mysql:// URL for other backends:
import { createServer } from "@isol8/server";

// SQLite
const server = await createServer({
  port: 3000,
  apiKey: "my-master-key",
  authDbPath: "./isol8-auth.db",
});

// PostgreSQL
const server = await createServer({
  port: 3000,
  apiKey: "my-master-key",
  authDbPath: "postgres://user:pass@localhost:5432/isol8",
});

Where remote values are set

ConcernCLIConfigAPI bodyLibrary
Server listen/authisol8 serve --port --keyAPI key can come from env (ISOL8_API_KEY)n/an/a
Server defaultsn/adefaults.*, maxConcurrent, cleanup.*, security.*merged with request optionsn/a
Request overridesisol8 run --host --key ... flagsbaseline defaults onlyoptions in /execute bodynew RemoteIsol8(..., isol8Options)
Persistent sessionisol8 run --persistent --host ...cleanup policy affects idle sessionssessionIdRemoteIsol8Options.sessionId

Request envelope

Remote execution endpoints use this shape:
{
  "request": { "code": "print('ok')", "runtime": "python" },
  "options": { "timeoutMs": 30000 },
  "sessionId": "optional-session-id"
}
options are merged over server defaults for that request.

Execute remotely

curl -sS -X POST http://localhost:3000/execute \
  -H "Authorization: Bearer $ISOL8_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "request": {
      "code": "print(2 ** 10)",
      "runtime": "python"
    }
  }'

Persistent sessions

A stable sessionId reuses one container across calls.
curl -sS -X POST http://localhost:3000/execute \
  -H "Authorization: Bearer $ISOL8_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "session-123",
    "request": { "code": "x = 41", "runtime": "python" }
  }'
Destroy session when done:
curl -X DELETE http://localhost:3000/session/session-123 \
  -H "Authorization: Bearer $ISOL8_API_KEY"
Idle persistent sessions remain until explicit delete or cleanup pruning (cleanup.autoPrune, cleanup.maxContainerAgeMs).

Auto-pruning setup (idle session cleanup)

The server runs a periodic cleanup loop when cleanup.autoPrune is enabled.
  • sweep interval: every 60_000 ms (60s)
  • idle threshold: cleanup.maxContainerAgeMs
  • active sessions are skipped while executing (isActive === true)
  • lastAccessedAt is refreshed on execute and file upload/download calls

Defaults

  • cleanup.autoPrune: true
  • cleanup.maxContainerAgeMs: 3_600_000 (1 hour)

Configure in isol8.config.json

{
  "cleanup": {
    "autoPrune": true,
    "maxContainerAgeMs": 1800000
  }
}
The example above prunes idle sessions after 30 minutes.

Practical tuning guidance

  • shorter idle timeout (5m to 30m): lower container footprint, more frequent cold session starts
  • longer idle timeout (1h to 24h): better session reuse, higher idle resource usage
  • disable auto-prune only if you have explicit lifecycle management (for example always calling DELETE /session/:id)
If users report disappearing sessions, check cleanup.maxContainerAgeMs first before investigating execution errors.

Streaming behavior

RemoteIsol8.executeStream() uses a WebSocket-first transport strategy:
  1. First call attempts WebSocket connection to GET /execute/ws.
  2. If WebSocket succeeds, all subsequent calls use WebSocket.
  3. If WebSocket fails on the first attempt (server doesn’t support it, proxy blocks upgrade), the client falls back to SSE via POST /execute/stream.
  4. If WebSocket was previously available but later fails, the error is thrown (not silently retried as SSE).
Both transports emit the same StreamEvent objects (stdout, stderr, exit, error).
// executeStream() automatically picks the best transport
for await (const event of engine.executeStream({
  code: "for i in range(3): print(i)",
  runtime: "python",
})) {
  if (event.type === "stdout") process.stdout.write(event.data);
  if (event.type === "stderr") process.stderr.write(event.data);
  if (event.type === "exit") console.log(`Exited: ${event.data}`);
}
  • current server implementation always uses ephemeral mode for both streaming paths.
Sending sessionId to /execute/stream does not create persistent streaming sessions in current implementation. The WebSocket endpoint (/execute/ws) is ephemeral-only.

File APIs (persistent sessions only)

  • POST /file: upload base64 content to session container
  • GET /file?sessionId=...&path=...: download base64 content
curl -sS -X POST http://localhost:3000/file \
  -H "Authorization: Bearer $ISOL8_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "session-123",
    "path": "/sandbox/input.txt",
    "content": "aGVsbG8K"
  }'

Operations checklist

  • keep strict defaults in isol8.config.json (network: "none" by default)
  • size maxConcurrent to host capacity
  • configure cleanup pruning for session-heavy workloads
  • place TLS/rate limiting in front of the service
  • enable audit logging when provenance/compliance is required

FAQ

401 means missing Authorization header. 403 means token provided but it does not match server API key.
If a sessionId is configured, it calls DELETE /session/{id}. Without sessionId, there is no session to delete.
No. File upload/download are bound to a persistent session container.

Troubleshooting

  • Server start fails with API key error: pass --key or set ISOL8_API_KEY.
  • Session not found on file calls: create session first via /execute with the same sessionId.
  • Session state unexpectedly gone: check prune settings in cleanup.autoPrune and cleanup.maxContainerAgeMs.
  • Remote request hangs under load: inspect maxConcurrent queueing and host saturation.

Configuration reference

Defaults, cleanup policy, and concurrency settings.

Architecture

Internal engine/server/session architecture.

Library reference

Programmatic client usage for local and remote execution flows.

Troubleshooting

Diagnose session, network, and remote execution failures.