Overview
A runtime for isolated sandboxes on your own infrastructure. (pre-alpha)
shard [OPTIONS] COMMAND [ARGS...]Set SHARD_REMOTE and SHARD_API_KEY for remote access. Without a remote URL or a saved connection, shard connects to the local daemon.
Get started
shard setupFor automated setup options
shard setup --helpRun shard COMMAND --help for options and examples.
Commands
Section titled “Commands”Sandboxes
| Command | Description |
|---|---|
shard create |
create a sandbox and print its ID |
shard run |
start a supervised process in a running sandbox |
shard exec |
execute a command in a running sandbox |
shard shell |
open an interactive shell in a running sandbox |
shard list |
list active sandboxes |
shard ps |
list the processes of a sandbox |
shard logs |
show output from a process |
shard kill |
stop a process and cancel its restarts |
shard inspect |
show detailed information about a sandbox |
shard stop |
stop sandboxes and preserve their files |
shard start |
start a stopped sandbox with its saved files |
shard remove |
remove sandboxes and their files |
shard prune |
remove all stopped sandboxes |
shard pause |
suspend sandboxes and save their state |
shard resume |
resume paused sandboxes from their saved state |
shard fork |
create a sandbox from a running sandbox’s memory and files |
shard cp |
copy files or directories between your machine and a running sandbox |
shard port |
forward host ports to ports inside sandboxes |
Images, snapshots, secrets and network policies
| Command | Description |
|---|---|
shard pull |
download an image |
shard image |
manage downloaded images |
shard snapshot |
save and manage filesystem snapshots |
shard secret |
manage secrets and sandbox access to them |
shard policy |
manage outbound network rules and view network logs |
Host and access
| Command | Description |
|---|---|
shard capabilities |
show the lifecycle verbs and swap the server supports |
shard daemon |
start the daemon or show its status |
shard info |
show available providers and the default for this host |
shard serve |
start an HTTP API server with token authentication |
shard setup |
configure a local sandbox host or a remote connection |
shard tokens |
create, list and revoke API keys |
shard version |
show the client and daemon versions |
Global options
Section titled “Global options”| Option | Description | Default |
|---|---|---|
--root <dir> |
directory for local shard data | /var/lib/shard |
--remote <url> |
URL of the shard API server; HTTPS recommended | |
--version |
show the client version |
Environment variables
Section titled “Environment variables”| Variable | Description |
|---|---|
SHARD_REMOTE |
API server URL; --remote overrides it |
SHARD_API_KEY |
API key from shard tokens mint |
SHARD_CA_FILE |
custom CA certificate file; HTTPS only |
Option order
Section titled “Option order”Global options go before the command. If you type one after it, the command fails, and the error shows where the option goes:
shard: --remote goes before the verb: shard --remote <url> listA command’s own options go before its arguments, as in shard exec -it web sh. shard reads an option after an argument as an argument, and the error says to put the flags before the arguments.
run, logs and kill also take their options after the sandbox and the process name, as in shard logs web api -f. In run, the command starts at the first word that is not an option. Put -- before a command that starts with a dash.
Remote and local commands
Section titled “Remote and local commands”With --remote, SHARD_REMOTE or a connection that shard setup saved, a command goes to that server. --remote overrides SHARD_REMOTE, and SHARD_REMOTE overrides the saved connection. SHARD_API_KEY overrides the saved key.
An http remote encrypts nothing. Every command that uses one prints this line once, to stderr and never to stdout:
Warning: HTTP does not encrypt this connection. Use it only on localhost or through a trusted encrypted network.The client refuses SHARD_CA_FILE with an http remote before it connects.
Some commands act on this host’s files and processes, so they run only on the daemon host:
pull,image list,image remove,image prune,daemon status,info,tokens mint,tokens listandtokens revokerefuse--remote,SHARD_REMOTEand a saved connection. Their error names the command.daemonandserverefuse--remoteandSHARD_REMOTE, and ignore a saved connection.--remote ""runs a command on this host even when a connection is saved.tokens scopesasks the server, so it follows the remote.
Names and aliases
Section titled “Names and aliases”A command has one name. list and remove also take ls and rm, at the top level and under image, secret, policy and snapshot. tokens list takes ls. An alias runs the same code and prints the same help, and the help never lists it.
Short options:
| Command | Short | Long |
|---|---|---|
exec |
-i, -t, -it for both |
--interactive, --tty |
list |
-q |
--quiet |
logs, policy logs |
-f |
--follow |
run |
-e, -w, -u |
--env, --workdir, --user |
setup |
-y |
--yes |
secret set |
--dest and --destination are the same option |
What each command prints
Section titled “What each command prints”The Format column shows the default --format. A dash means the command has no --format. A command that prints the id prints it even when you pass the name.
Sandboxes
| Command | Format | Prints |
|---|---|---|
create |
- | the id |
run |
- | the process name, or the process’s output with --attach |
exec |
- | the command’s output |
list |
table | the sandboxes, or one id per line with -q |
ps |
table | the processes of the sandbox |
logs |
- | the output of one process |
kill |
- | the process name, once the process has ended |
inspect |
json | the record |
stop, start, pause, resume |
- | the id of each sandbox |
remove |
- | the id of each sandbox, or nothing for a missing one with --force |
prune |
- | the id of each stopped sandbox it removed |
fork |
- | the new id |
cp |
- | nothing |
Without --all, list leaves out stopped sandboxes. The table then ends with a count on stderr, such as 2 stopped sandboxes; shard list --all. JSON and -q output have no such line.
stop, remove, pause and resume take more than one sandbox, as in shard stop $(shard list -q). Each id prints as soon as its sandbox succeeds. If a sandbox fails, the command goes on to the next one. It prints the failures on stderr at the end and exits with code 1.
Images, snapshots, secrets and network policies
| Command | Format | Prints |
|---|---|---|
pull |
- | the reference and the digest |
image list |
table | the images |
image remove |
- | the reference |
image prune |
- | each reference it removed |
snapshot create |
- | the snapshot id |
snapshot list |
table | the snapshots |
snapshot inspect |
json | the record |
snapshot remove |
- | the id or name it was given |
secret set, secret remove |
- | the name |
secret list |
table | the secrets, never a value |
secret grant, secret ungrant |
- | the sandbox id |
policy create, policy remove |
- | the name |
policy show |
json | the policy and the sandboxes that hold it |
policy list |
table | the policies |
policy attach, policy detach |
- | the sandbox id |
policy logs |
- | the egress decisions, one JSON record per line |
Host and access
| Command | Format | Prints |
|---|---|---|
capabilities |
table | the nine lifecycle verbs and swap, and whether the server supports each |
daemon |
- | its log |
daemon status |
table | the daemon’s state and its tasks |
info |
table | the provider a daemon would pick, and why |
serve |
- | its log |
setup |
- | the wizard, or a checklist when you give every answer as an option |
tokens mint |
json | the token record |
tokens list |
table | the token ledger |
tokens revoke |
- | revoked token <id>, or revoked <n> tokens of <name> with --name |
tokens scopes |
table | every scope a token can carry on the server it asks |
version |
table | the client and daemon versions |
JSON output
Section titled “JSON output”--format takes json or table. Any other word is a usage error. If you name the default, nothing changes: list --format table prints the same as list.
A --format json call writes one value or nothing:
- The value is indented by two spaces, and a list command prints an array.
tokens mintwrites its value on one line.- A failure to parse the command, to reach the daemon or to encode the value writes nothing to stdout.
- A list with warnings, or a
daemon statuswith a task in backoff, still writes the whole value. Then it writes the warnings or the error to stderr and exits 1. version --format jsonwith no daemon writes nothing and fails.
inspect, snapshot inspect, policy show and tokens mint print JSON by default. The values in the examples below are synthetic.
Sandbox
Section titled “Sandbox”list prints an array of sandbox records, the same on the daemon host and with --remote:
[ { "id": "misty-otter-81c0", "name": "web", "image": "python:3.12", "provider": "gvisor", "state": "running", "resources": {"memory_mib": 512, "vcpus": 1, "disk_mib": 2048}, "processes": [ { "name": "api", "command": ["python", "-m", "http.server"], "restart": {"policy": "on-failure", "retries": 3, "backoff": 1}, "status": {"state": "running", "restarts": 0, "started_at": "2026-10-01T09:30:06Z"} } ], "secrets": ["API_TOKEN"], "policy": "api-only", "started_at": "2026-10-01T09:30:05Z", "created_at": "2026-10-01T09:30:00Z" }]id, image, provider, state, resources and created_at are always present. state is pending, created, running, paused, unresponsive, stopped or failed. resources holds memory_mib, vcpus and disk_mib. Every other field is absent when empty:
| Field | Present when |
|---|---|
name |
the sandbox has a --name |
digest |
the provider created it; the image digest |
snapshot |
create --snapshot made it; the snapshot id |
forked_from |
fork made it; the id of the source sandbox |
stopped_reason |
shard stopped it with no operator, or shard-init died on a stop |
oom |
the host ended it for its memory: {"kills": 1, "killed_at": "2026-10-01T09:40:00Z", "restart_at": "2026-10-01T09:40:05Z"}, where restart_at is a start the daemon still owes |
failed_reason |
state is failed |
processes |
run started a process in it |
secrets |
it holds a placeholder; the secret names |
policy |
it holds a network policy |
started_at |
the daemon started it at least once; a resume keeps the first start |
A process has name, command, restart and status, and env, workdir and user when the run set them. killed is true once shard kill ended it, until a run of the same name. ps --format json prints the same records as an array.
restart.policyisno,on-failure,alwaysorunless-stopped.retriesis absent when there is no cap.backoffis the first wait in seconds.status.stateisrunning,restarting,exited,killed,gave-uporstopped.status.restartscounts the restarts since the run or the sandbox’s last start.status.exitis the last exit, such as{"code": 3, "signal": 0}, and is absent before the first one.status.started_atis the last time shard-init started the process.
inspect prints one sandbox record. It adds egress when the record names a network policy:
{ "id": "misty-otter-81c0", "image": "python:3.12", "provider": "gvisor", "state": "stopped", "resources": {"memory_mib": 512, "vcpus": 1, "disk_mib": 2048}, "processes": [ { "name": "api", "command": ["python", "-m", "http.server"], "restart": {"policy": "unless-stopped", "backoff": 1}, "status": {"state": "stopped", "restarts": 0, "exit": {"code": 0, "signal": 15}} } ], "policy": "api-only", "created_at": "2026-10-01T09:30:00Z", "egress": { "rules": [ {"action": "allow", "destination": {"kind": "group", "value": "dns"}, "protocol": "udp", "ports": [53], "id": "1", "implied": "dns"}, {"action": "allow", "destination": {"kind": "group", "value": "dns"}, "protocol": "tcp", "ports": [53], "id": "2", "implied": "dns"}, {"action": "allow", "destination": {"kind": "domain", "value": "api.example.com"}, "protocol": "tcp", "ports": [80, 443], "id": "3"} ] }}egress.rules lists the rules in the order the host and the proxy enforce them. id is the rule’s position in that order, starting at "1". action is allow or deny. destination.kind is cidr, domain, domain-suffix or group. protocol and ports are absent for a rule that covers every protocol. implied marks a rule that the policy did not write: dns when a name rule opened DNS, and dns-rule when a dns rule did. When the store no longer holds the policy, egress is {"missing": true, "rules": []} and the sandbox reaches nothing.
Snapshot
Section titled “Snapshot”snapshot inspect prints one snapshot record, and snapshot list prints an array of them:
{ "id": "quiet-heron-4f2a", "name": "web-base", "source": "misty-otter-81c0", "source_name": "web", "image": "python:3.12", "digest": "sha256:0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0", "provider": "gvisor", "disk_mib": 1024, "memory_mib": 512, "size": 7340032, "created_at": "2026-10-01T09:30:00Z"}name and source_name are absent when empty. disk_mib and memory_mib are the bounds the source ran under. size is the space the copy takes on the host, in bytes.
Images, secrets, policies and tokens
Section titled “Images, secrets, policies and tokens”image list prints an array of images. digest is the full digest, size is in bytes, and broken is absent when empty:
[ { "reference": "python:3.12", "digest": "sha256:0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0", "size": 52428800, "created": "2026-10-01T09:30:00Z" }]secret list prints the secrets, never a value:
[ { "name": "API_TOKEN", "destinations": ["api.example.com"], "placeholder": "mock-API_TOKEN", "updated_at": "2026-10-01T09:30:00Z" }]policy list prints each policy with its number of rules:
[{"name": "api-only", "rule_count": 2}]policy show prints {name, rules, dns, holders}. dns is open or closed. holders names the sandboxes whose record names the policy, and is absent when there are none.
tokens list prints the token ledger. expires_at is null for a token that never expires, and status is active, revoked or expired:
[ { "id": "0123456789abcdef0123456789abcdef", "name": "build-agent", "issued_at": "2026-10-01T09:30:00Z", "expires_at": null, "scopes": ["sandbox:read", "exec"], "status": "active" }]tokens mint prints {token, expires_at, scopes} on one line.
info prints the provider a daemon would pick, and why. unreadable is absent unless the root holds a record that shard cannot read:
{"provider": "gvisor", "reason": "no /dev/kvm"}daemon status is the body of GET /v0/daemon. A task has last_error when it failed:
{ "version": "v0.1.0", "pid": 4242, "started_at": "2026-10-01T09:30:00Z", "socket": "/var/lib/shard/shard.sock", "provider": "gvisor", "capabilities": {"pause": true, "resume": true, "fork": true, "port": true, "swap": false}, "proxy": {"plain_port": 30080, "tls_port": 30443}, "tasks": [{"name": "proxy", "state": "running", "restarts": 0}]}capabilities is the body of GET /v0/capabilities, with the same ten keys for every provider:
{"create": true, "start": true, "stop": true, "remove": true, "pause": true, "resume": true, "fork": false, "snapshot": true, "port": true, "swap": false}version prints the client and daemon versions. On a Mac, it adds shim, which is embedded or absent:
{"client": "v0.1.0", "daemon": "v0.1.0"}Tables
Section titled “Tables”| Command | Columns |
|---|---|
list |
ID NAME IMAGE STATE UPTIME PROCESSES POLICY |
ps |
NAME STATE RESTARTS EXIT POLICY COMMAND |
image list |
REFERENCE DIGEST SIZE CREATED |
snapshot list |
ID NAME SOURCE IMAGE SIZE CREATED |
secret list |
NAME DESTINATIONS PLACEHOLDER UPDATED |
policy list |
NAME RULES |
tokens list |
ID NAME ISSUED EXPIRES SCOPES STATUS |
tokens scopes |
SCOPE DESCRIPTION |
capabilities |
CAPABILITY SUPPORTED |
In list, PROCESSES is the processes that have not ended, of all the sandbox has, such as 2/3, or - for none. In ps, RESTARTS is 2/5 for an on-failure policy with 5 retries, and EXIT is - before the first exit, the exit code, or signal 15.
The commands that print JSON by default have these tables:
inspectprintsFIELD VALUErows, with every time in RFC 3339 to the second, in UTC. A sandbox with a network policy then gets anID RULE IMPLIEDsection, with each rule aspolicy createtakes it, for exampleallow suffix:example.com tcp:443.snapshot inspectprintsFIELD VALUErows.policy showprintsname,dnsandholders, then aRULEsection.tokens mintprintsTOKEN EXPIRES SCOPES.
Exit codes
Section titled “Exit codes”| Code | When |
|---|---|
| 0 | the verb did what it says |
| 1 | any failure of shard, or of the request, with shard: <reason> on stderr |
| the command’s code | exec, and run --attach with the process’s last code |
| 126, 127 | exec of a command that is not executable, or not found |
| 1 or more | exec whose output was lost, so a lost stream never reads as a pass |
| 128 + n | run --attach of a process that signal n ended |
| 125 | run when shard itself fails, so a script tells it from a process that exits 1 |
| 130 | run that Ctrl+C left before the daemon answered |
| 130 | setup that Ctrl+C left; the steps done so far stay in place |
Ctrl+C on run --attach detaches: the process runs on, and run exits 0.
daemon status exits 1 when a background task is in backoff, after it prints the whole status.