Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.copass.com/llms.txt

Use this file to discover all available pages before exploring further.

exec is for one-shot commands. When you spin up a long-running server inside the sandbox — an API, a notebook, a dev preview — you reach it through the Compute Gateway: a managed L7 reverse proxy on gateway.copass.id. Same API key, same auth model, no DNS to manage.
import { CopassClient } from '@copass/core';

const client = new CopassClient({
  auth: { type: 'api-key', key: process.env.COPASS_API_KEY! },
});
const sandboxId = process.env.COPASS_SANDBOX_ID!;

const session = await client.compute.createSession(sandboxId, {
  template: 'python-web',
  timeout_seconds: 600,
});

// Start a server inside the sandbox.
await client.compute.exec(sandboxId, session.session_id, {
  cmd: ['nohup', 'python', '-m', 'http.server', '3000', '&'],
});

// Reach it.
const res = await session.fetch(3000, '/health');
console.log(res.status, await res.text());

await client.compute.stopSession(sandboxId, session.session_id);
That’s the whole shape. Provision, start a server, fetch against the port. The gateway authorizes every request against your API key and routes only to sandboxes you own.

When to use what

You want to…Use
Run a one-off commandcompute.exec
Talk to a long-running HTTP serversession.fetch(port, path)
Open a WebSocket / SSE connectionsession.websocketUrl(port, path)
Hand a URL to a browser or third-partysession.proxyUrl(port, path)
exec and the gateway are independent. You can use both in the same session.

The session helpers

Every active session exposes three methods:
session.proxyUrl(port: number, path?: string): string
session.websocketUrl(port: number, path?: string): string
session.fetch(port: number, path: string, init?: RequestInit): Promise<Response>
// HTTP
const res = await session.fetch(3000, '/api/users', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ name: 'Ada' }),
});

// URL only — for embeds, browser hand-off, or a non-fetch HTTP client
const url = session.proxyUrl(3000, '/dashboard');

// WebSocket
import WebSocket from 'ws';
const ws = new WebSocket(session.websocketUrl(8080, '/stream'), {
  headers: { Authorization: `Bearer ${process.env.COPASS_API_KEY}` },
});
fetch is a passthrough — it sets Authorization: Bearer <api_key> and points at the gateway. Everything else (method, body, headers, signal, streaming) flows through to the underlying HTTP client. There are no retries, no JSON parsing, no body shaping. Treat it like globalThis.fetch with auth and URL injected.

URL shape

The gateway URL has one form:
https://gateway.copass.id/compute/{session_id}/p/{port}{path}
Same shape on staging — the SDK reads the host from the session response and never hardcodes it, so a staging API key talks to staging-gateway.copass.id automatically. You can construct URLs yourself if you have to (e.g. a non-SDK environment), but prefer the SDK helpers. The URL shape is documented for transparency, not stability — we may evolve it. SDK helpers are the contract.

Auth

Authorization: Bearer <api_key> on every request. Your API key, the same one you use elsewhere. The gateway authorizes against the (api_key, session_id) pair on every request:
  • The session must exist and be in a non-terminal state.
  • The session must belong to the API key’s owner.
Anything else — wrong owner, stopped session, unknown session — returns 404. The gateway deliberately collapses several authz failure modes into one response shape so it can’t be probed for what’s behind it. There is no IP allowlisting and no path-level ACL. Owner is the unit of authorization in v1.

WebSockets and SSE

Long-lived connections work the same way as HTTP. Pass Authorization in the headers on the upgrade request.
import WebSocket from 'ws';

const ws = new WebSocket(session.websocketUrl(8080, '/events'), {
  headers: { Authorization: `Bearer ${process.env.COPASS_API_KEY}` },
});
ws.on('open', () => ws.send('hello'));
The gateway forwards Upgrade and Connection headers, pins the connection to one upstream pod via stickiness, and holds the connection open for up to one hour. SSE works identically — start a text/event-stream server inside the sandbox, hit it with fetch, read the body as a stream. Browser-side WebSocket constructors don’t accept custom headers. Use Node ws (or any other WS client that supports headers) for direct connections; for browser embeds, route the request through your own backend.

Sandbox-side server

Your server inside the sandbox should:
  • Listen on 0.0.0.0:<port> (not 127.0.0.1). The gateway connects from outside the process.
  • Respond to whatever path the SDK sends. The gateway doesn’t rewrite paths.
  • Not assume a specific public hostname. Use relative URLs in HTML, or read the Host header.
The gateway strips upstream-reflected Access-Control-Allow-* headers and applies its own allowlist, so a misbehaving dev server can’t reflect * and bypass the platform’s CORS posture.

Errors

Same shape as the rest of the API — CopassApiError with a .status. The networking-specific cases:
StatusCauseRecovery
401Missing or malformed Authorization.Set the header.
404Session unknown, stopped, or not owned by this key.Provision a new session, or list yours and pick one.
502Upstream not listening, crashed mid-request, or returned an unparseable response.Check the server inside the sandbox is running. Retry.
503Compute kill-switch engaged for your account or globally.Surface to the user; retry later.
A response with a non-2xx status code is a normal Response object. The SDK does not throw on 502 from the upstream — that’s a real HTTP response from your server (or its absence). Inspect the status yourself.
const res = await session.fetch(3000, '/health');
if (res.status === 502) {
  // Server inside the sandbox isn't up yet. Wait and retry.
}
The Gateway is not configured error is a separate case — it throws before any network call. It means your control plane returned a session record without a gateway envelope, usually because it’s an older deployment or the gateway feature is disabled. Update the API host or upgrade the SDK.

Anatomy of a request

Useful when you’re debugging or evaluating the trust model.
  1. SDK substitutes session_id, port, and path into the gateway URL template returned on the session record.
  2. SDK calls the underlying HTTP client with Authorization: Bearer <api_key>.
  3. Gateway receives the request, authorizes against the control plane, and forwards to the sandbox upstream.
  4. Sandbox responds.
  5. Gateway streams the response back to the SDK, stripping any reflected CORS headers.
The gateway holds no per-request cache. Authorization happens on every request, so revoking or stopping a session takes effect immediately.

Limits

  • Session lifetime caps how long the URL works. timeout_seconds on createSession (default 300, max 3600).
  • Idle timeout on a single connection: 1 hour.
  • Request body: streamed; no fixed cap, but the session’s billable lifetime is the real ceiling.
  • Concurrent sessions per user are capped (see Errors → 409).

Next