Skip to content

You’ll mint a token on a shard server, connect to it from another machine, and run a sandbox there as if it ran locally.

You need shell access to a server you set up with shard setup: see Quickstart: server. Steps 2 and 3 below add the HTTP API and HTTPS, if the server has neither yet.

Terminal window
# On the server: mint a token for this client.
# Prints JSON, and its token field is the client's API key.
shard tokens mint --name laptop --duration 720h \
--scopes sandbox:read,sandbox:write,sandbox:delete,exec \
--signing-key-file /etc/shard/serve.secret
# On the client: set the key, then check and save the connection.
export SHARD_API_KEY=<the token field>
shard setup --remote https://shard.example.com --save -y
# Every command now goes to the server. The exec prints hello.
shard create --name demo --memory 512MiB alpine:3.20
shard exec demo echo hello
shard remove --force demo
# On the server: revoke the token. Prints revoked 1 token(s) named laptop.
shard tokens revoke --name laptop --signing-key-file /etc/shard/serve.secret

On a Mac server, leave out --signing-key-file: the token commands and shard serve both use the default key.

shard setup --remote checks that it can reach the server, that the server accepts the key, and which commands the server supports. With --save, it saves the connection, so later commands need no variables.

The server needs three things before a client can connect: a daemon, shard serve, and an HTTPS proxy or a tunnel in front of it. The steps below set them up on a Linux host with systemd.

Set up the host to run sandboxes, as in Quickstart: server:

Terminal window
shard setup --local --provider gvisor --start-at-boot=true -y

The daemon serves its API on a unix socket on the host, never on TCP. This step does not expose it to the network.

shard serve is the HTTP API, and the only way in from the network. It is a separate process that does not run as root. It checks the key of each request, then passes the request to the daemon’s socket. Setup runs it as a background service:

Terminal window
shard setup --local --provider gvisor --http-api=true -y

On a host where setup already ran, this changes only the HTTP API. On a new host, it sets up the daemon too, so step 1 is optional. Without -y, setup asks Set up the HTTP API? after the daemon startup question.

Setup then:

  1. Checks the address, the port, the permissions and the service manager, and shows the configuration. Nothing changes before this.
  2. Creates the shard account, the token signing key in /etc/shard/serve.secret, and an API key.
  3. Starts the service. It starts at boot after the daemon, stops with it, and starts again with it. API requests fail while the daemon is stopped.
  4. Sends an authenticated request to prove the API works.
  • The address: 127.0.0.1:7850 by default, which accepts connections only from the host. Pass --listen with a VPN address, such as --listen 100.64.0.5:7850, to permit direct access through that private network.
  • The API key: in /etc/shard/api-key, which only root can read. Setup shows the file, never the key. A repeated setup keeps the key, and --replace-api-key replaces it, which cuts off every client that uses the old key.
  • On a Mac: the key is /var/lib/shard/auth/api-key, and the service is the LaunchDaemon /Library/LaunchDaemons/shard.serve.plist.

Read the key on the server:

Terminal window
cat /etc/shard/api-key

shard serve has no TLS, and setup does not configure HTTPS. For one client, an SSH tunnel is enough. Run this on the client, then use http://127.0.0.1:7850 as the server URL:

Terminal window
ssh -N -L 7850:127.0.0.1:7850 <user>@<server>

For more clients, put a proxy or a tunnel in front of it that gives clients an https URL on port 443. The proxy must:

  • Pass WebSocket upgrades through, and not buffer responses. Otherwise shard exec, shard shell and shard logs -f stall.
  • Not log the Authorization header. That header carries the token.
shard.example.com {
reverse_proxy 127.0.0.1:7850 {
flush_interval -1
}
}

Caddy gets a certificate from a public CA for a public name. On a host with no public name, add tls internal inside the site block, and give clients the root certificate of Caddy’s own CA in SHARD_CA_FILE.

shard serve sees every client as one source: the proxy. It holds at most 32 connections from one source that have not yet shown a valid token, so all clients share this limit. Rate-limit floods at the proxy.

Mint one token for each client, on the server:

Terminal window
shard tokens mint --name laptop --duration 720h \
--scopes sandbox:read,sandbox:write,sandbox:delete,exec \
--signing-key-file /etc/shard/serve.secret
{"token":"<jwt>","expires_at":"2026-11-04T18:00:00Z","scopes":["sandbox:read","sandbox:write","sandbox:delete","exec"]}

Setup’s own key, shard-setup, can do everything and never expires. Mint a narrower token for each client instead.

The token field is the client’s API key. Give shard tokens mint, list and revoke the same --signing-key-file, so the token lands in the ledger that shard serve reads.

Without --scopes, a token can do everything, secrets and policies included. Without --duration, it never expires. Authentication lists the scopes and what each one allows.

Set the key in SHARD_API_KEY, never as a command argument. Then save the connection:

Terminal window
shard setup --remote https://shard.example.com --save -y

Setup writes the server URL and the key to ~/.config/shard/config.json by default, and only your user can read the file.

  • To connect without saving anything, set SHARD_REMOTE and SHARD_API_KEY instead. The SDKs read the same two variables, or take remote and apiKey (remote= and api_key= in Python). They never read the saved connection.
  • If a private CA signed the server’s certificate, set SHARD_CA_FILE to that CA’s certificate. shard always verifies the certificate, and has no option to skip it.
  • An http URL works too, but nothing encrypts the key or the requests. Use it only on localhost or through an encrypted VPN. Each command then prints a warning to stderr.

Environment and config describes SHARD_REMOTE, SHARD_API_KEY and SHARD_CA_FILE.

List the tokens, then revoke one by its ID, or every token of a name:

Terminal window
# Prints each token's ID, name, issue time, expiry, scopes and status.
shard tokens list --signing-key-file /etc/shard/serve.secret
# Revoke every token named laptop.
shard tokens revoke --name laptop --signing-key-file /etc/shard/serve.secret

shard serve refuses a revoked token on the next request, and needs no restart. It also closes any open stream that uses that token, such as shard exec, shard shell or shard logs -f, within a second. The command and its sandbox keep running.

These commands refuse a remote server: shard pull, shard image list, shard image remove, shard image prune, shard daemon, shard daemon status, shard info, shard serve, and shard tokens mint, list and revoke. Run them on the server.

On a machine with a saved connection, add --remote "" to send one command to this machine’s daemon:

Terminal window
shard --remote "" list

For how a request travels from a client to the daemon, see Architecture. For the socket’s permissions, see Security model.