The open source, identity-based, secretless sandbox platform

Run isolated, reproducible sandboxes for developers, AI agents and automated workloads on your own infrastructure, with identity-based, policy-driven, secretless access to databases, SSH servers, APIs and Kubernetes clusters, without credentials ever reaching the sandbox.

  • Free and open source
  • Designed for self-hosting
  • Built on Octelium

Cordium runs every Workspace as an isolated sandbox. Above the sandboxes, the Octelium access layer connects each Workspace to the databases, SSH servers, Kubernetes clusters, HTTP APIs and LLM APIs it is authorized to reach, on a per-request basis and through its own identity, while the upstream credentials stay at the access layer and never reach the sandbox.

Why Cordium

Isolated execution is only half of the problem

Sandboxes for developers, AI agents and automated workloads need isolated, reproducible execution as well as access to real infrastructure. Existing platforms solve one side and leave the other to the user. Cordium unifies both in a single platform.

  1. 01

    Credentials inside the sandbox

    API keys, database passwords, SSH private keys and kubeconfigs end up in environment variables, files and shell history, where a leaked log or a prompt injection can exfiltrate them.

    In Cordium

    Upstream credentials stay at the Octelium identity-aware proxy and are injected at the protocol layer once a request is authorized. The Workspace never holds them.

  2. 02

    Isolation without identity

    Most sandbox platforms isolate processes but leave infrastructure access to whatever credential the sandbox holds, so the credential itself becomes the authorization boundary.

    In Cordium

    Every Workspace run gets a dedicated Octelium Session as its identity, and every request it makes is authorized against that identity with policy-as-code.

  3. 03

    One tool per workload

    Remote development environments, AI agent sandboxes and CI runners are usually separate products, each with its own identities, its own credentials and its own audit trail.

    In Cordium

    The same Workspace serves long-lived coding sessions and short-lived automated runs, through the browser, SSH, the CLI and the SDKs.

  4. 04

    Incomplete visibility

    Network and container logs rarely explain which agent ran which query against which database, or why it was allowed to.

    In Cordium

    OpenTelemetry-native, L7-aware access logs tie every request back to the Workspace Session, its owner and the Policy that decided it.

The same terminal is available in the browser through the Cordium web portal, with nothing to install.

Terminal

The same terminal is available in the browser through the Cordium web portal, with nothing to install.

  • # Create a Workspace and attach a terminal
  • $ cordium run --image node:22 \
  • --repository https://github.com/acme/web
  • ✓ Workspace status: PREPARING (12s)
  • octelium@cordium:~$ cd /workspace/repo && ls
  • README.md package.json src test
  • octelium@cordium:/workspace/repo$ npm test
  • Test Files 14 passed (14)
  • Tests 186 passed (186)

Exec

Output is streamed back and the exit code is propagated, which is what scripts and CI/CD jobs depend on.

  • # Run a command in a running Workspace
  • $ cordium exec abc -w /workspace/repo -- go test ./...
  • ok github.com/acme/api/billing 0.412s
  • ok github.com/acme/api/invoices 0.388s
  • ok github.com/acme/api/payments 0.531s
  • $ echo $?
  • 0
  • # Capture remote output locally
  • $ cordium exec abc -- cat /workspace/repo/report.json > report.json

SSH

SSH is authenticated at the Cluster with your own identity, so Workspaces never need authorized keys or passwords.

  • # Connect once to use native SSH tooling
  • $ octelium connect -d
  • $ cordium ssh abc --print-config >> ~/.ssh/config
  • # VS Code, Zed, JetBrains, rsync and scp work as usual
  • $ code --remote ssh-remote+cordium-abc /workspace/repo
  • $ rsync -avz ./dist/ cordium-abc:/workspace/repo/dist/
  • # Or forward ports with the embedded client
  • $ cordium ssh abc -N -L 5432:localhost:5432

Ephemeral

An ephemeral Workspace starts from fresh storage every time, so nothing carries over from one run to the next.

  • # A disposable sandbox, deleted when the terminal exits
  • $ cordium run --ephemeral --rm \
  • --image python:3.13-slim --cpu 2000 --memory 4096
  • ✓ Workspace status: PREPARING (6s)
  • octelium@cordium:~$ python -c 'print(6 * 7)'
  • 42
  • octelium@cordium:~$ exit

Workspaces

Persistent Workspaces keep their filesystem across stops and restarts, and resume exactly where they left off.

  • $ cordium get workspaces
  • ┌──────┬─────────┬──────────┬─────────┬─────────┬──────────────┐
  • │ NAME │ CREATED │ TEMPLATE │ SPACE │ STATE │ LAST STARTED │
  • ├──────┼─────────┼──────────┼─────────┼─────────┼──────────────┤
  • │ abc │ 3d │ default │ default │ RUNNING │ 2h │
  • │ x7k2 │ 12d │ backend │ acme │ RUNNING │ 41m │
  • │ ci4m │ 6m │ ci │ acme │ STOPPED │ 6m │
  • └──────┴─────────┴──────────┴─────────┴─────────┴──────────────┘
  • $ cordium stop x7k2
  • Successfully stopped the Workspace: x7k2
  • $ cordium start x7k2
  • Successfully started Workspace: x7k2
Secretless access

Access infrastructure without credentials inside the sandbox

Every Workspace run gets a dedicated Octelium identity. Processes inside the Workspace reach authorized resources with standard tools, while the upstream credentials stay at the Octelium identity-aware proxy.

Read about accessing Octelium Services

The Workspace connects without a password. The proxy authenticates to the database on its behalf, and MySQL works the same way.

A Workspace reaches authorized Octelium Services with standard tools. The upstream credential is kept in the Cluster as a Secret and injected at the identity-aware proxy once the request is authorized, so it never reaches the Workspace.

  • PostgreSQL: the Workspace runs psql -h reporting.db -c "select count(*) from invoices" and holds no credential. The POSTGRES Service injects the database password from the Cluster Secret reporting-password and authenticates to pg.internal:5432. The Workspace connects without a password. The proxy authenticates to the database on its behalf, and MySQL works the same way.
  • SSH: the Workspace runs ssh bastion.prod uptime and holds no credential. The SSH Service injects the ssh private key from the Cluster Secret bastion-key and authenticates to 10.0.4.21:22. No private keys or passwords inside the Workspace, and the session is recorded at the proxy on the way through.
  • Kubernetes: the Workspace runs octelium cfg k8s-prod then kubectl get pods -n production and holds no credential. The KUBERNETES Service injects the kubeconfig from the Cluster Secret kubeconfig-prod and authenticates to k8s.internal:6443. The kubeconfig inside the Workspace only points at the Service. The cluster credential itself never leaves the Cluster.
  • HTTP: the Workspace runs curl http://payments/v1/charges?limit=1 and holds no credential. The HTTP Service injects the api key from the Cluster Secret stripe-api-key and authenticates to api.stripe.com. A prompt injection cannot exfiltrate an API key the agent never had, and revoking access is a Policy change rather than a rotation.
  • mTLS: the Workspace runs nats -s events-bus:4222 sub "orders.>" and holds no credential. The TCP Service injects the client certificate from the Cluster Secret events-bus-crt and authenticates to nats.internal:4222. Any upstream protected by mutual TLS, including protocols that Octelium does not parse at the application layer.
Database names and database users
An agent's Workspace reaches one database, as one database user

Database names and database users

An agent's Workspace reaches one database, as one database user

  • Policy p-agents-reporting
  • all:
  • of:
  • - match: '"agents" in ctx.user.spec.groups'
  • - match: ctx.request.postgres.connect.database == "reporting"
  • - match: ctx.request.postgres.connect.user == "readonly"
  • ALLOW: agent-07 · Workspace abc, connect database=reporting user=readonly, all three expressions match.
  • DENY: agent-07 · Workspace abc, connect database=payments user=readonly, database is outside the rule.
  • DENY: agent-07 · Workspace abc, connect database=reporting user=admin, database user is not readonly.

HTTP methods, paths and bodies

Agents read from the payments API, but never write to it

  • Policy p-agents-payments
  • - effect: DENY
  • condition:
  • match: ctx.request.http.method != "GET"
  • - effect: ALLOW
  • condition:
  • match: '"agents" in ctx.user.spec.groups'
  • ALLOW: agent-07 · Workspace abc, GET /v1/charges?limit=10, rule 2, User is in the agents Group.
  • DENY: agent-07 · Workspace abc, POST /v1/refunds, rule 1, DENY rules override ALLOW rules.
  • DENY: linus@acme.com · Workspace m3p, GET /v1/charges, no rule matched, nothing is allowed by default.

Kubernetes verbs, resources and namespaces

CI Workspaces can read production, and nothing more

  • Policy p-ci-readonly
  • all:
  • of:
  • - match: '"ci" in ctx.user.spec.groups'
  • - match: ctx.request.kubernetes.verb in ["get", "list", "watch"]
  • - match: ctx.request.kubernetes.namespace == "production"
  • ALLOW: ci-runner · Workspace ci4m, list pods in production, Group, verb and namespace all match.
  • DENY: ci-runner · Workspace ci4m, delete deployment in production, verb delete is not in the allowed set.
  • DENY: agent-07 · Workspace abc, list pods in production, User is not in the ci Group.

The same Service, different reach

People and agents use the same Workspaces, under different Policies

  • Policy p-bastion
  • all:
  • of:
  • - match: ctx.user.spec.type == "HUMAN"
  • - match: '"sre" in ctx.user.spec.groups'
  • ALLOW: jane@acme.com · Workspace x7k2, ssh bastion.prod, HUMAN User in the sre Group.
  • DENY: agent-07 · Workspace abc, ssh bastion.prod, User type is WORKLOAD.
  • DENY: linus@acme.com · Workspace m3p, ssh bastion.prod, User is not in the sre Group.
Visibility

Real-time, L7-aware visibility for every Workspace

Every request a Workspace makes to infrastructure is logged in real time with its full identity context and exported to your OpenTelemetry OTLP receivers.

Read about visibility and logs
AccessLogOTLP
ALLOWagent-07reporting.dbPOSTGRESWorkspace abc, query: select id, total from invoices limit 50
ALLOWci-runnerk8s-prodKUBERNETESWorkspace ci4m, verb=list resource=pods namespace=production
DENYagent-07paymentsHTTPWorkspace abc, POST /v1/refunds, policy=p-agents-payments
ALLOWjane@acme.combastion.prodSSHWorkspace x7k2, session recorded, upstream user=deploy
ALLOWagent-07geminiHTTPWorkspace q9w2, GET /v1beta/models 200
Programmable

A programmable sandbox platform for agents and automation

Everything the CLI and the web portal do is available over the gRPC API, with official SDKs for Go, TypeScript, Python and Rust. Create, start, drive and destroy sandboxes from a CI pipeline, a scheduler, an internal developer platform or an AI agent.

Read about the APIs and SDKs
main.gogithub.com/octelium/cordium/cordium-go

Credentials are read from the environment, including OIDC assertion files for workload identity federation, so the same code runs on a laptop, in CI and inside a Workspace.

Go SDK (github.com/octelium/cordium/cordium-go): create an ephemeral Workspace, run a command, print its output and delete the Workspace.
c, err := cordium.New(ctx)
if err != nil {
	return err
}
defer c.Close()

ws, err := c.Workspaces().Run(ctx,
	cordium.WithImage("python:3.13"),
	cordium.Ephemeral(),
)
if err != nil {
	return err
}
defer ws.Delete(context.WithoutCancel(ctx))

res, err := ws.Exec(ctx, "python -c 'print(6 * 7)'")
if err != nil {
	return err
}
fmt.Println(res.Output(), res.ExitCode) // 42 0
TypeScript SDK (@octelium/cordium): create an ephemeral Workspace, run a command, print its output and delete the Workspace.
import { Cordium, argv } from "@octelium/cordium";

const client = new Cordium();

const workspace = await client.workspaces.run({
  image: "node:22",
  ephemeral: true,
});

try {
  const result = await workspace.exec(argv("node", "-p", "6 * 7"));
  console.log(result.stdout); // 42
} finally {
  await workspace.delete();
  client.close();
}
Python SDK (cordium-sdk): create an ephemeral Workspace, run a command, print its output and delete the Workspace.
from cordium import Cordium

with Cordium() as client:
    workspace = client.workspaces.run(
        image="python:3.13",
        ephemeral=True,
    )
    try:
        result = workspace.exec(["python", "-c", "print(6 * 7)"])
        print(result.stdout)  # 42
    finally:
        workspace.stop().wait_until_stopped()
        workspace.delete()
Rust SDK (cordium): create an ephemeral Workspace, run a command, print its output and delete the Workspace.
use cordium::{Client, Command, WorkspaceOptions};

let client = Client::from_env().await?;

let workspace = client
    .workspaces()
    .run(WorkspaceOptions::new().image("python:3.13").ephemeral(true))
    .await?;

let result = workspace
    .exec(Command::argv(["python", "-c", "print(6 * 7)"]))
    .await?;
println!("{}", result.stdout_text()); // 42

workspace.delete().await?;
workspace.yaml
A WorkspaceAn image, a repository and a lifecycle task, applied with cordium run --file.
A Workspace. An image, a repository and a lifecycle task, applied with cordium run --file.
spec:
  image:
    registry:
      url: python:3.12-bookworm
  repository:
    url: https://github.com/acme/api
  runtime:
    tasks:
      - name: install
        type: ON_CREATE
        workingDir: /workspace/repo
        run: pip install -r requirements.txt
        onFailure: ON_FAILURE_ABORT
An inline Dockerfile. The image is built by the Cluster, and the limits are capped by the Space and the Cluster.
spec:
  image:
    dockerfile:
      inline: |
        FROM node:22-bookworm
        RUN apt-get update && apt-get install -y \
          podman postgresql-client
        RUN npm install -g @anthropic-ai/claude-code
  limit:
    cpu:
      millicores: 4000
    memory:
      megabytes: 8192
    storage:
      megabytes: 20480
A devcontainer. The image, features and hooks come from the repository's devcontainer spec, and port 3000 is served by the portal.
spec:
  repository:
    url: https://github.com/acme/web
  image:
    repository:
      devcontainer:
        dirPath: .devcontainer
  applications:
    - name: web
      port: 3000
      isDefault: true
A parameterized Template. One Template serves many runs. Variables are overridden per run, and the Workspace stops once its tasks complete.
spec:
  vars:
    - name: BRANCH
      value: main
    - name: TASK
      value: Fix the failing tests.
  repository:
    url: https://github.com/acme/api
    cloneOptions:
      branch: ${{ vars.BRANCH }}
  runtime:
    autoStop: true
    tasks:
      - name: run-agent
        type: POST_START
        workingDir: /workspace/repo
        run: claude "${{ vars.TASK }}"

A running Workspace yields a crash-consistent snapshot and a stopped one a clean snapshot. Both outlive their source.

Snapshots

A running Workspace yields a crash-consistent snapshot and a stopped one a clean snapshot. Both outlive their source.

  • # Snapshot a Workspace while it keeps running
  • $ cordium create snapshot before-upgrade --workspace abc
  • Started taking the WorkspaceSnapshot: before-upgrade of the Workspace: abc
  • # Restore it into a brand new Workspace
  • $ cordium run --snapshot before-upgrade
  • ✓ Workspace status: PREPARING (6s)
  • octelium@cordium:~$ git -C /workspace/repo status --short
  • M src/billing/invoice.go
  • ?? migrations/0042_add_tax_rates.sql

Volumes

Exclusive Volumes are mounted by one running Workspace at a time. Shared Volumes need a shared filesystem backend such as NFS, CephFS or EFS.

  • # Volumes have a lifecycle of their own
  • $ cordium create volume datasets --size 50000
  • Successfully created the Volume: datasets of 50000 MB
  • $ cordium create volume build-cache --size 20000 --shared
  • Successfully created the Volume: build-cache of 20000 MB
  • # Mount read-only or read-write, per Workspace
  • $ cordium run --volume datasets:/data:ro --volume build-cache:/cache
  • ✓ Workspace status: PREPARING (9s)
  • octelium@cordium:~$ ls /data
  • eval images labels train

Pre-builds

A pre-build runs a Template's initialization once and snapshots the result, so dependency-heavy Templates start in seconds.

  • # Pre-build a Template into a VolumeSnapshot
  • $ cordium build ml-env.research
  • Started a build for Template: ml-env
  • # New Workspaces restore from it instead of starting cold
  • $ cordium run --template ml-env.research
  • ✓ Workspace status: PREPARING (5s)
  • octelium@cordium:~$ python -c 'import torch; print(torch.__version__)'
  • 2.8.0
Isolation

Rootless sandboxes with egress network policies

Each Workspace runs as a rootless container inside a restricted outer container, with dropped capabilities, a custom seccomp profile and enforced resource limits. Egress network policies decide where it can connect.

Read about Workspace configuration
Public networks only, by default
With no rules, a Workspace reaches the internet but not private networks

Public networks only, by default

With no rules, a Workspace reaches the internet but not private networks

  • egress:
  • # no rules, so every destination falls
  • # back to the default action
  • defaultAction: ALLOW_PUBLIC
  • ALLOW: pip install requests to 151.101.0.223:443, public destination, default action.
  • DENY: psql -h 10.0.4.40 to 10.0.4.40:5432, private network, default action.
  • DENY: curl http://169.254.169.254 to 169.254.169.254:80, protected network, denied whatever the rules say.

Deny everything except what a job needs

A DENY default with explicit ALLOW rules by CIDR and port

  • egress:
  • defaultAction: DENY
  • rules:
  • - action: ALLOW
  • cidrs: ["140.82.112.0/20"]
  • ports: [443]
  • ALLOW: git clone https://github.com/acme/api to 140.82.112.4:443, matches the ALLOW rule.
  • DENY: pip install requests to 151.101.0.223:443, no rule matched, default action.
  • DENY: ssh -T git@github.com to 140.82.112.4:22, port 22 is outside the rule.

DENY rules always take precedence

A destination matched by a DENY rule is denied even if an ALLOW rule matches it

  • egress:
  • rules:
  • - action: ALLOW
  • cidrs: ["10.0.0.0/8"]
  • ports: [5432]
  • - action: DENY
  • cidrs: ["10.0.9.0/24"]
  • ALLOW: psql -h 10.0.4.40 to 10.0.4.40:5432, private, but allowed by a rule.
  • DENY: psql -h 10.0.9.12 to 10.0.9.12:5432, the DENY rule overrides the ALLOW rule.
  • DENY: redis-cli -h 10.0.4.7 to 10.0.4.7:6379, outside the rules, private by default.
Use cases

Use Cordium across development and automation

Start with remote development environments or AI agent sandboxes, then apply the same Workspaces, identities, Policies and audit trail to CI/CD, automation and secretless access to infrastructure.

Questions

Frequently asked

Is Cordium free and open source?
Yes. Cordium is free and open source under the Apache 2.0 license and is designed for self-hosting. There is no proprietary cloud-based control plane, no tiered feature set, and it is not a limited version of a separate paid SaaS product. Cordium is built on Octelium, whose Cluster components are licensed under AGPLv3.
How is Cordium different from other sandbox platforms?
Remote development environments such as GitHub Codespaces and Coder, and sandbox platforms such as E2B and Daytona, provide the execution environment and leave infrastructure access to whatever credentials are placed inside it. Cordium additionally gives every Workspace its own identity and provides secretless, policy-driven access to SSH servers, databases, HTTP APIs, Kubernetes clusters and mTLS services, so no upstream credential is ever exposed, distributed or managed inside the sandbox.
How are Workspaces isolated?
Each Workspace runs as a rootless container inside a restricted outer container, with all Linux capabilities dropped except a small explicit set, a custom seccomp profile, a restricted /proc and enforced CPU, memory and storage limits. It runs on standard Kubernetes with no VMs, bare-metal nodes or specialized hardware, and egress network policies decide which destinations it can reach.
Can AI agents use Cordium programmatically?
Yes. Everything the CLI and the web portal do is available over the gRPC API, with official SDKs for Go, TypeScript, Python and Rust. Agents and orchestrators authenticate as workload Users, including secretlessly through OIDC assertions issued by platforms such as GitHub Actions and Kubernetes, and every Workspace they run gets its own Session identity.
Do I need an existing Octelium Cluster?
Cordium runs on top of Octelium. The quick installer can automatically set up a complete Octelium Cluster and installs Cordium on top of it. If you already run an Octelium Cluster, Cordium can be installed on it as a package.
Do I need Kubernetes experience to run Cordium?
No. Cordium runs on Kubernetes, but the quick installer provisions a complete single-node Cluster, Kubernetes included, on any Linux machine or VM with at least 4GB of RAM and 20GB of disk storage. Production installations can run on managed or on-premises multi-node Kubernetes.
Get started

Install Cordium on your own infrastructure in minutes

One fresh Linux VM/server and a domain you own. The installer takes care of Octelium, Cordium and all of their dependencies, Kubernetes included.

$ curl -o install-cluster.sh https://octelium.com/install-cluster.sh$ chmod +x install-cluster.sh$ ./install-cluster.sh --domain <DOMAIN> --cordium