Security

The isolation, credential and network model of Ciresk dev, written for a security reviewer: where a session runs, what it holds, how your machine reaches it, what may leave it and what is recorded. Where something is not available yet, the page says so.

Topology in three lines

An organization is pinned to one regional cell; the only cell today is eu1 in AWS eu-central-1. The dev control plane (API, relay, browser gateway, console) and the workers that run sessions are in that cell, and so is everything with your content: the session workspaces, checkpoints, the session journal and the logs. Identity (WorkOS), API key records and routing are the shared Ciresk control plane and hold no session content. An organization that runs its sessions on its own workers keeps the workspaces and checkpoints on its own hosts and storage; the control plane, the relay and the browser gateway stay in the cell (see hybrid.md).

Crosses regionsNever crosses
Identity, organization placement, API key records and their checksSource, workspaces, checkpoints, the session journal, service logs, the connection log, egress decisions, audit records

Isolation on a worker

ControlMechanism
WorkersA worker is enrolled into exactly one organization and only ever claims that organization's sessions; sessions on one worker share its kernel, never another organization's.
AdmissionBefore a session is placed, the control plane checks the memory, CPU, disk and session count each worker declared; a worker without room is not considered.
UsersThe worker runs as root to build namespaces and overlays; every container in a session runs as uid 1000 in a user namespace with a uid range of its own, with all capabilities dropped and no-new-privileges. Each session's uid range maps to a host uid range of its own, unused by any other session on the worker, and each session keeps its own directories, pid and IPC namespaces, private temporary directory, cgroup and disk quota; /proc hides other users' processes.
CPU, memory, processesEach session has its own cgroup slice with memory limits, a CPU weight and at most 4096 tasks; each container has memory, swap and process limits.
NetworkEach session's pod is created with no network: one loopback interface, no route to the host, the VPC or instance metadata. Its only exits are local ports the session's guest forwards to the worker: the session's own egress proxy and, when the template asks for it, a read-only package registry cache (GET and HEAD only).
Instance metadataUnreachable from the pod (no route); an iptables rule rejects metadata traffic from session users as well, and IMDSv2 has hop limit 1.
InboundNone. The worker's security group has no inbound rule and pods publish no port; everything reaches a session through the authenticated tunnel, which the worker checks per stream.
DiskEach session has a 0700 directory with a disk quota. On stop, the worker cuts egress, removes the pods, the workspace and the cgroup, and deletes the directory.
BrowserChromium runs inside the session's pod, from a pinned upstream Playwright image, with Chromium's sandbox on. It is driven over a pipe, so there is no DevTools port; its control server listens on the pod's loopback only, and the one way in is the browser stream of the tunnel, which the worker filters. Chromium's telemetry and crash reporting are not switched off by flag; the session's network gives them no route out.
Fair use within a sessionCommands from several people and clients are queued round robin, by person and then by client.

No reusable secret inside a session

A session holds no credential that works outside it. What is in it:

In the sessionScope
The session's epoch (CIRESK_SESSION_EPOCH)An identifier, not a credential; every ticket and login is checked against it.
A test-identity login key (CIRESK_LOGIN_KEY), only in the container that serves a template's login routeDrawn for this session only. The worker uses it to mint a one-time login token valid 60 s and bound to the epoch, logs the declared test identity in itself, and refuses a cookie that outlives the identity's cookie_max_secs, which may not exceed the session's idle timeout. The login route is filtered out of every tunnel stream. Identities logged in under an earlier epoch are revoked or left to expire before a new login.
Nothing elseThe session's egress proxy carries no secrets, so no outbound request gets a credential attached on its way out. A template cannot name a secret placeholder for a login. ciresk dev exec refuses environment variables whose names look like credentials, and ciresk dev ssh never forwards your SSH agent.

Agents work on your machine (Claude Code, Codex, Cursor) and reach the session through ciresk dev; their model and provider keys stay there. A session has no model access today: a model proxy that holds the provider credential outside the sandbox, and an MCP gateway for agents running inside a session, are not available yet.

A csk_ key acts for the person who owns it: sessions, tickets and audit records it creates belong to its owner, and the key's id is recorded as the requester.

The tunnel and your devices

ControlDesign
Device keyAn Ed25519 key the CLI generates on first use and keeps in the OS keychain (Windows Credential Manager on Windows). On a headless Linux host without a keychain it is a file readable only by you; the CLI refuses the file if anyone else can read it, on Windows if its ACL grants anyone beyond you, SYSTEM and Administrators.
Device grantThe device's public key is granted to one session when its owner or a member attaches from that device; ciresk dev attach --revoke revokes it.
TicketsA dvt_ ticket is signed by the dev API for one session, epoch, device grant and device, and lists the stream kinds the holder's role allows. The device signs every ticket request; tickets live 30 s and the CLI renews them every 20 s. Each renewal checks the credential, the device grant, the member's role and the organization's policy again; a connection whose ticket is not renewed closes when it expires.
Proof of possessionOn every connection the relay sends a random nonce; the device signs it together with the session, epoch, grant and ticket id under a fixed, versioned context. The relay still accepts the earlier proof (nonce and ticket id only) from older clients; it will refuse it no earlier than 2026-11-07.
The relayRuns in the cell. Your machine and the worker both dial out to it over TLS; the worker authenticates with a 30 s lease assertion the dev API signed, never with a reusable token. Pings every 10 s; a connection silent for 25 s is closed.
Encryption in the tunnelTLS to the cell on each leg. SSH is encrypted end to end between your SSH client and the worker; for every other stream (commands, logs, search, previews) the relay forwards readable bytes. End-to-end encryption of every other stream between your device and the worker is not available yet.
RevocationRevoking a device grant, removing a member, a kill, and a revocation reported by identity each write a relay event in the same transaction as the change, and the relay closes the affected connections at once. A narrower role ends the streams it no longer allows at the next renewal. If the relay loses its event feed for 30 s, it closes its client connections and accepts no new ones.
Local originsOn your machine each session is served at http://<label>.localhost:<port> by a local daemon that accepts only its exact host names (421 otherwise) and checks the origin of WebSocket upgrades.
SSH and editorsThe SSH server runs in the worker and accepts only streams whose ticket the worker verified; ciresk dev code and ciresk dev ssh use it.

Browser previews

The browser gateway serves session previews, the browser terminal and VS Code in the browser at *.eu1.ciresk.dev, a registrable domain of its own, separate from ciresk.com.

ControlDesign
HandoffA person (never an agent) asks for a dvh_ link: one-time, valid 60 s, stored only as a hash and spent atomically; at most 30 links a minute and 20 open previews per person.
Cookie__Host-ciresk_gw: host-only (no Domain), Secure, HttpOnly, SameSite=Lax, stripped before a request reaches your app. It lives as long as the preview: at most 8 hours (1 hour when your groups could not be read); a terminal or editor preview ends 1 hour after its last renewal and after 15 minutes idle, and opens only after a confirm page; any other preview ends after an hour unused.
RenewalThe gateway renews each preview with the dev API every 20 s, with the same checks as a ticket renewal.
What the gateway holdsOne credential, valid only to redeem and renew previews; no database credential and no signing key.
TransportHSTS with includeSubDomains and preload on every answer.
Who can use itOwners and members of the session, in every organization whose policy allows browser previews (the default). A preview link opened from the console opens at once; the same link opened anywhere else (a chat, a mail, another preview) first shows a page naming whom it was made for. Previews need a current browser (Chrome 76, Firefox 90, Safari 16.4 or newer).
Cookies across previewsEvery preview of a cell shares eu1.ciresk.dev, so one preview's script can set a cookie for the whole domain. Your app's HttpOnly cookies are kept in your browser as __Host- cookies of their own preview host, which no other preview can set or replace, and reach your app under their own names. Of your other cookies, a value your app's own server set wins over any other copy, a name sent twice that your server never set reaches your app not at all, and a name or value that frameworks read differently (a . or % in the name, quotes, commas or spaces in the value) is not forwarded; the answer deletes the stray copies. The gateway's own cookie is __Host- and HttpOnly, so no other preview can replace or read it. What remains: another organization's preview you opened can fill your browser's cookie store, or set a cookie your app's own script writes before your app has set it.
Your app's cookiesSet cookies from your server, and never set Domain from your page's script: a cookie set with Domain=eu1.ciresk.dev or Domain=ciresk.dev is sent to, and readable by, every preview of the cell, other organizations' included.

Egress: default deny

A session reaches a host only if every layer allows it. A missing or empty list allows nothing.

LayerWho sets itChecked
Vendor hostsCireskModel providers, AI APIs and hosted tool MCP servers are refused as rows, and so are IP literals and wildcards over cloud provider domains. A refusal answers 422 egress_denied at ciresk dev up.
Organization policyYour admins, ciresk dev policy setThe ceiling for every session of the organization, at ciresk dev up.
Agent policyYour adminsA narrower ceiling for sessions an agent starts, empty when unset.
The templateYour repositoryThe hosts the template's egress names.
The installation's egress.yamlThe operator of the cellApplied on the worker by the session's egress proxy; the only exit of the pod.

In eu1 the installation's file grants sessions no host today, so a session reaches only the read-only package registry cache; the worker's own image and toolchain fetches are separate rows that no session inherits. The proxy also refuses model vendors and private addresses on every connection, even when a row lists them. Each session has an egress decision log, with host, method, decision, rule and bytes, read with ciresk dev egress; each new denial is written to the audit log.

Data residency

DataWhere
Sessions, the session journal, members, device grants, the connection log, usage and audit recordsThe dev control plane database, a managed PostgreSQL (Neon) in AWS eu-central-1
WorkspacesThe worker's disk in eu-central-1, deleted when the session stops
Checkpoints and tree contentThe cell's object store in eu-central-1 under your organization's prefix, encrypted with the cell's KMS key; the worker can create objects there but not overwrite them; deleted after 30 days
Session imagesCiresk's registry in eu-central-1; upstream images are mirrored digest for digest, never pulled through
Ticket signing keysThe dev API's database, their private halves encrypted with a key-encryption key held in the cell's secret store; they sign for 6 weeks

A us cell is not available yet.

What leaves your machine and the installation

Ciresk programs on your machine reach only the hosts in ~/.ciresk/egress.yaml; a missing file allows nothing. The installer writes these rows once, and deleting a row turns that path off:

RowFor
api.eu.ciresk.com, api.us.ciresk.comciresk dev: the dev API and the relay
api.workos.comRefreshing your sign-in
get.ciresk.comciresk update, signed releases

ciresk dev sends no telemetry. From the cell, the dev API calls Ciresk's platform to check csk_ keys and resolve access, and WorkOS for directory reads when you share a session. Sessions themselves reach only what the egress layers above allow.

Accountability

RecordWhat
Audit logWritten in the same transaction as the change: session create, update, stop, kill, share, unshare, grant, revoke, exec, reset, login, SSH, transfer, CI record, web handoff and open, needs raised and resolved; tunnel connects, path reads and refusals, clipboard copies and denials; policy changes, template publish and promote, worker enrolment, registration and revocation. Holders of dev:audit.read read it on the console's Audit log page or through GET /v1/dev/audit/events, and every read is itself recorded (see admin.md).
Connection logciresk dev connections (scope dev:audit.read, people and services): one row per minute of verified tunnel streams, with session, worker, person, agent, device grant, device, client, stream kind, port, bytes each way and why streams closed.
Kill switchciresk dev kill (scope dev:sessions.kill, a person or a service, never an agent) stops one session, one owner's sessions or every session of the organization: the epoch moves, the relay closes connections at once, the worker stops the session and closes its egress; a worker cut off from the control plane closes egress 30 s after its last lease renewal.
Usage budgetsPer person per day, per organization per month and per group member a day or a month: ciresk dev up is refused with 403 budget_exceeded, and a running session over a hard budget is stopped. ciresk dev usage shows every window that applies to you.

Sharing: only a person shares a session (ciresk dev share), within the organization's sharing ceiling.

RoleMay
viewFollow along: the app's HTTP ports, logs, status, a read-only shell and editor, search
useAlso run commands, restart and reset, test logins, the browser, a read-write shell, and SSH, port forwarding and uploads where policy allows
ownerAlso sync files, from the device that created the session only

Policy switches for local sync, SSH, port forwarding, downloads, uploads, clipboard and the browser editor are set per organization; see Administration.

Credentials: all short-lived, none shared

WhoCredentialObtainedLifetime
A person in the CLIAccess tokenciresk ci auth login, stored in the OS keychain; one sign-in covers ciresk ci and ciresk devRefreshed shortly before it expires
A machine, an agent sandboxcsk_ key, acting for its ownerplatform.ciresk.comUntil revoked
A deviceEd25519 device keyGenerated by the CLI, kept in the OS keychain; granted per sessionUntil the grant is revoked
A tunnel clientdvt_ ticket for one session, epoch, grant and deviceThe dev API, on a request the device signed30 s, renewed every 20 s
A person's browserdvh_ handoff, then the __Host-ciresk_gw cookieThe dev API, for people only60 s and one use; the cookie at most 8 hours
A worker joiningdve_ enrolment secretFor a host you run: an admin of your organization, with ciresk dev admin enrol-worker or the console's Workers page (a person or a key with dev:workers.admin, never an agent), stored on the host in a root-only file (see hybrid.md). For a host Ciresk runs for your organization: Ciresk provisions itUntil rotated or revoked
A workerdvw_ worker tokenExchanged for the enrolment secret at registration; stored only as a hash24 hours; revoked with the worker
A worker on the relaydvl_ lease assertion for one sessionSigned by the dev API with each lease30 s

What Ciresk never holds

Never heldWhere it lives instead
A reusable secret inside a sessionNowhere: the session holds its epoch and, for test logins, a key drawn for that session only.
Your device keyYour OS keychain; Ciresk stores its public half.
Your agents' prompts, transcripts and model keysOn your machine, with your agent.
Worker and handoff tokens in readable formOnly their hashes are stored.
Session content outside your cellThe cell's database, workers and object store.

The installer checks every binary's signature and checksum before it installs it (Quickstart).