Skip to content

CLI reference: opendsecctl ​

opendsecctl is OpenDsec's command-line tool, for administrators and callers alike: create and operate sandboxes, manage images, projects, principals and tokens, inspect the cluster, issue certificates, and manage cluster releases (fleet).

opendsecctl <command> [arguments] [options]
opendsecctl help                 # every command
opendsecctl fleet                # help for the fleet subcommands
  • It is installed as /usr/local/bin/opendsecctl (single-node install) or /opt/opendsec/current/bin/opendsecctl (cluster nodes); on the ops machine it is $OPENDSEC_OPS/current/bin/opendsecctl.
  • Options may come before or after positional arguments: opendsecctl sandbox stop <id> -addr x and opendsecctl sandbox stop -addr x <id> are the same.
  • Unless noted, output is JSON (protobuf's JSON form; fields at their default value are left out).
  • Exit code 0 on success; on failure it prints error: <reason>: <message> and exits non-zero. exec exits with the command's own exit code.

Connection and environment ​

OptionEnvironment variableDefaultMeaning
-addrOPENDSEC_ADDR—Apiserver address; several, comma separated
-tokenOPENDSEC_TOKEN—Token. Prefer the variable, so it stays out of shell history and process lists
-projectOPENDSEC_PROJECTrootDefault project
-caOPENDSEC_CA—Cluster CA certificate; set, TLS is used
-storeOPENDSEC_IMAGE_STORE—Mount point of the image store (image import, image gc, image store-check)
-deadline—5mTime limit for the whole command; none for image import

After a single-node install, the administrator's and the caller's environments are:

sh
. /root/opendsec-ops/env         # administrator: admin token, project root
. /root/opendsec-ops/demo.env    # caller: a token for project root/demo, plus the E2B SDK's variables

Sandboxes ​

sandbox create ​

opendsecctl sandbox create -backend <backend> [-image I] [-cpu N] [-mem MB] [-disk MB] [-gpus N]
                           [-task T] [-ttl-idle S] [-ttl-max S] [-net rule,...] [-count N]
OptionDefaultMeaning
-backendsimmicrovm, container, fncall, fullvm, sim. The default, sim, only simulates: always set it
-image—Image, e.g. root/py:3.12
-cpu1CPU cores
-mem1024Memory, MB
-disk0Writable disk, MB; 0 for the backend's default
-gpus0GPUs
-task—Task name
-ttl-idle0Idle timeout (seconds); 0 for the cluster default
-ttl-max0Lifetime (seconds); 0 for the cluster default
-net—Network rules, comma separated; !rule takes back one of the cluster defaults, e.g. -net 'pypi,github.com:443,!npm'
-count1Number of sandboxes

With -count 1 the output is the sandbox's JSON. With a larger -count, each result is a line (index<TAB>id, or index<TAB>error<TAB>reason: message for a failure), followed by a summary:

$ opendsecctl sandbox create -backend microvm -image root/py:3.12 -count 3 -task job-1
0	node-a1.4k2m…
1	node-b1.9xq1…
2	node-a1.p0zz…
created 3/3 in 412ms; failures: map[]

The id in a script:

sh
ID=$(opendsecctl sandbox create -backend microvm -image root/py:3.12 | grep -m1 '"id"' | cut -d'"' -f4)

sandbox get | stop | pause | resume | touch <id> ​

View, stop, pause, resume, or reset the idle timer. Prints the updated sandbox.

sandbox list [-task T] [-all] ​

Sandboxes of -project and its subprojects; -task narrows to a task, -all includes recently ended ones.

sandbox fork <id> [-count N] [-task T] [-ttl-idle S] [-ttl-max S] ​

Copies a running sandbox into N (1 to 256); output as for batch creation.

sandbox network <id> <rule>... [-replace] ​

Changes network rules: pypi allows, !pypi takes back. Merged into the current rules by default; -replace replaces them.

sh
opendsecctl sandbox network $ID pypi github.com:443
opendsecctl sandbox network $ID '!github.com:443'
opendsecctl sandbox network $ID '*' -replace          # only the whole public internet

exec <id> [-session S] [-user U] [-timeout S] [--] <command...> ​

Runs a command in a sandbox, streaming its stdout and stderr, and exits with its exit code.

  • One argument is a whole shell command, interpreted by the sandbox's shell: opendsecctl exec $ID 'cd /srv && make test | tail'.
  • Several arguments are each quoted and joined, as ssh and docker exec do: opendsecctl exec $ID python3 -c 'print(6*7)' runs exactly python3 -c 'print(6*7)' in the sandbox.
  • If an argument starts with - (as in ls -la), put -- before the command so opendsecctl does not take it as its own option: opendsecctl exec $ID -- ls -la /.
  • -session: session name, default default; -user: the session's user when this command starts it; -timeout: seconds; on expiry it prints opendsecctl: command timed out.

Before 0.11, several arguments were joined with spaces and no quoting. Scripts that added an extra layer of quotes to work around that (e.g. "'print(6*7)'") must drop it.

cp <id>:<path> <local file> / cp <local file> <id>:<path> ​

Copies one file between this machine and a sandbox, up to 64 MB; use the SDK's chunked reads and writes for larger files.

Tasks ​

opendsecctl task pause|resume|stop <task>

Acts on every sandbox of the task in -project and prints how many succeeded and failed.

Images ​

image import <source> -as <project>/<name>:<tag> -store <store> ​

Imports an image into the shared store and registers it. Run it as root on a machine that mounts the image store; it needs layer.publish on the target project.

The source can be:

SourceExample
Public registrydocker.io/library/python:3.12, python:3.12, ghcr.io/org/image:tag
Private registryharbor.example.com/team/app:1.0
Registry without TLSadd -plain-http
OCI layout, directory or tar./python-3.12.oci.tar
docker save tar./app.tar; with several images in the file, file#name picks one

Local paths must start with / or .; a missing file is an error (it is never looked up in a registry).

Private registry credentials are taken from, in order:

  1. -username U -password-stdin: the password or access token is read from stdin, e.g. echo "$TOKEN" | opendsecctl image import ... -username 'robot$ci' -password-stdin;
  2. -registry-auth FILE: a docker-format credentials file;
  3. $REGISTRY_AUTH_FILE, $DOCKER_CONFIG/config.json, ~/.docker/config.json (what docker login saved).

An import has no overall time limit (a large image can take long); progress goes to stderr.

image save <source> -out <file> ​

Saves a registry image as an OCI tar, for machines without internet access to image import ./file. Needs no cluster. Takes -plain-http, -username, -password-stdin.

image compose <new image> <part>... [-overwrite] ​

Stacks images bottom first into one, without copying data:

sh
opendsecctl image compose root/rl/swe-4821:v1 root/rl/python:3.12 root/rl/django-ws:a1b2 root/tools/agent-kit:3

image list [-project P], image get <name>, image delete <name> ​

List the images of a project and its subprojects; show one image; delete an image's registration (image gc reclaims the layers).

image pack <sandbox-id> <new image> [-scrub a,b] [-overwrite] ​

pack_diff: saves a container sandbox's changes against the image it started from as a new image. /tmp, caches and shell history are removed by default; -scrub adds paths (globs relative to /).

image snapshot <sandbox-id> <new image> [-memory] [-overwrite] ​

Saves a sandbox as an image as it is. microVMs need -memory (memory included).

image gc -store <store> [-grace 24h] [-dry-run] ​

Removes layers and snapshots no image refers to. Anything younger than -grace (default 24 hours) stays, so imports in progress and fresh snapshots are safe. Try -dry-run first to see what would go. It needs the connection settings but works on the store only.

image store-check -store <dir> ​

Checks whether a file system (a newly mounted NAS, say) can hold the image store: permissions, hard links, renames, xattrs, sparse files and more. Every required item must be ok. Needs no cluster.

$ opendsecctl image store-check -store /mnt/opendsec-images
ok    write, sync and read back a file
ok    files a root process creates are root's (no root squash)
ok    chown to another user (image files keep their owners)
ok    exclusive create (one edge builds a VM disk at a time)
ok    rename a file over another (manifests, records)
ok    rename a directory (layers are published so)
ok    hard links (snapshots link their disks)
ok    symbolic links
ok    finds the holes of sparse files (snapshots move only their data; NFS needs vers=4.2)
ok    0:0 character devices (layer whiteouts)
ok    overlay over layers kept here, whiteouts honored
ok    trusted.* xattrs
ok    user.* xattrs
/mnt/opendsec-images can hold the image store

Items that are not required (sparse-file holes, trusted.* xattrs) only warn when they fail, and the store still works; a failed required item rules the file system out.

Projects, principals, permissions and tokens ​

CommandMeaning
project create <parent> <name> [-quota k=v,...]Create a subproject, e.g. project create root team-a -quota sandboxes=1000,cpu_cores=4000,memory_mb=4096000
project get <path>Quota, current usage, each principal's permissions, subprojects
project delete <path>Delete an empty project
principal create <project> <id> [-kind agent|user|service] [-ttl S]Create a principal; the token in the output is shown only this once
principal delete <id>Delete a principal and all its tokens
grant <project> <principal> <perm>[,<perm>...]Grant permissions, e.g. grant root/team-a trainer sandbox.create,sandbox.operate
revoke <project> <principal> [<perm>,...]Revoke permissions; all of them when none are named
token create [<principal>] [-ttl S] [-desc D]Issue a token, by default for yourself
token list [<principal>]List tokens (no secrets)
token revoke <principal> <token-id>Revoke; takes effect cluster-wide within about 1 s. primary is the legacy primary token
whoamiThe principal of the current token
usage [<project>] [-from YYYY-MM-DD] [-to YYYY-MM-DD] [-daily] [-csv]Usage: by default each project's total over the period; -daily per day and project; -csv CSV output

Quota keys: sandboxes, cpu_cores, memory_mb, gpus. What the permissions mean: Projects, principals and permissions.

Cluster ​

CommandMeaningNeeds
statusCluster snapshot: each edge's health, draining state, capacity, allocations, sandbox counts, free memory and disk, pressure—
edge drain <edge-id> [-undo]Stop (-undo: resume) placing new sandboxes on an edge; its sandboxes keep runningproject.admin on root
iam backup -out FILEBack up IAM (gzipped JSON, mode 0600): principals, projects, grants, quotas, token hashesas above
iam restore FILEReplace IAM's contents with a backup (quota leases of running sandboxes are kept)as above
iam membersList the IAM Raft replicasas above
iam member add <id> -raft-addr H:P -iam-addr H:PAdd a replica started with opendsec-iam -joinas above
iam member remove <id>Remove a replica (its machine is gone, say)as above
versionThis build's version—

Certificates ​

These need no cluster.

CommandMeaning
pki init -config opendsec.json -out DIR [-days 365] [-ca-days 3650] [-hosts h1,h2]Create a cluster CA and a certificate for every component in the config. -hosts adds host names or IPs to every certificate
pki ca -out DIR [-days N]A new CA only (CA rotation)
pki issue -ca DIR -role <role> -id <id> [-hosts ip1,ip2] -out DIR [-days 365]A certificate for one component. Roles: edge, apiserver, watcher, iam, fleet, node, ops

Production clusters usually use opendsecops certs, which calls these; see Cluster deployment.

Local clusters and benchmarks ​

CommandMeaning
cluster upRun a whole cluster in this process, for development and tests
benchScheduling benchmark on an in-process simulated cluster; -compare compares random placement with power-of-2

cluster up options:

OptionDefaultMeaning
-configa generated simulated clusterCluster config file
-sim-edges4Simulated edges without -config
-apiservers1Apiservers without -config
-runtimesimRuntime of the container backend in generated clusters: sim or linux (real containers, needs root)
-microvm-kernel—Also offer the microvm backend, with this guest kernel
-image-store—Shared image store directory (enables the store and pack_diff)
-state-dir/var/lib/opendsecSandbox state directory (linux runtime)
-host-imagetrueOffer the development image host (the host's / as a read-only lower layer: it exposes host files, development only)
-disk-reserve-mb512Reject creations while the state directory has less free space; -1 disables
-tlsfalseCreate a CA and certificates and run with mTLS
-audit-dir—Apiserver audit log directory
-metrics—Prometheus metrics address, e.g. :9464
-write-config—Write the effective config (with the bound addresses) to this file
-loginfoLog level
sh
./bin/opendsecctl cluster up -sim-edges 1 -runtime linux -image-store /mnt/opendsec-images \
    -microvm-kernel /opt/opendsec/vm/vmlinux-6.1.167-opendsec
# then set OPENDSEC_ADDR, OPENDSEC_TOKEN and OPENDSEC_PROJECT as it prints

fleet: cluster releases and configuration ​

These go through an apiserver and need fleet.view (read) or fleet.operate (change) on root; project.admin includes both. Every action is audited.

CommandMeaning
fleet nodes [-state S] [-role R] [-domain D]Machines: state, failure domain, roles, desired and running release, units ready, last heartbeat
fleet node get <name>One machine
fleet node accept <name> -roles edge=<id>[,...] [-domain D] [-addr-ip IP] [-labels k=v,...]Accept a newly reporting machine: deliver the current release and config and start it
fleet node set <name> [-roles ...] [-domain D] [-addr-ip IP] [-labels k=v,k=]Change roles, failure domain, labels (k= removes a label). Changes the record only; deploys nothing
fleet node sync <name>Bring a machine to its desired release, config and roles
fleet node cordon | uncordon <name>Stop or resume placing sandboxes on it
fleet node evacuate <name> [-deadline 1h]Cordon, then wait for its sandboxes to end
fleet node retire <name>Take a machine out: stop and disable its components
fleet node pin <name> [-reason R], unpin <name>Pin or unpin: pinned machines are left out of rollouts
fleet import -inventory FILE -release V [-config-gen G] [-domain D]Register machines already running release V (when moving from opendsecops)
fleet releasesUploaded releases
fleet release pack -bin DIR -units DIR [-deps DIR] -out DIRPackage a release directory (usually done by opendsecops release)
fleet release push <release dir> [-notes N]Upload a release to every fleet replica
fleet config put <cluster.json> [-note N]Upload a new cluster config as a new config generation
fleet config get [-gen N] [-out FILE]Fetch a config generation
fleet rollout create [-release V] [-config-gen G] [...]Start an upgrade or config change; options below
fleet rollout status [<id>] [-steps]Progress, of the newest rollout by default; -steps lists every machine
fleet rollout pause | resume | abort | rollback <id>Pause, continue, abort, roll back
fleet rolloutsPast rollouts

fleet rollout create options:

OptionMeaning
-release V, -config-gen GTarget release, target config generation (at least one)
-roles, -domains, -nodes, -labelsOnly machines with these roles, domains, names, labels
-canary NMachines upgraded alone first; default 1, -1 for none
-bake SObservation time (seconds) after the canary and after each batch; default 300, -1 for none
-batch NMachines per batch; default 5% of the non-control-plane machines (at least 1, at most 100)
-max-per-domain NAt most this many machines of one failure domain per batch; default half the batch (at least 1)
-pause SSeconds between batches; default 60, -1 for none
-max-failures NFailed machines tolerated before pausing; default 0, i.e. pause at the first failure
-step-timeout STimeout per machine (seconds), default 600
-note NA note

With no apiserver available, connect to fleet directly from the ops machine with the ops certificate:

sh
opendsecctl fleet nodes -direct -fleet 10.0.0.1:7300,10.0.0.2:7300 -pki $OPENDSEC_OPS/pki -ops-id ops

The full release procedure is in the operations manual §6.

Implements the paper DeepSeek Elastic Compute (DSec) (arXiv:2609.22978). An independent project, not affiliated with DeepSeek.