@shardflux/cli: shard
New accounts can register and create credentials automatically, but must complete the Stripe $0 card check before starting compute. Run shard billing trial --wait or use Usage & billing. One card unlocks one signup trial across all accounts, ever. Google, GitHub and Codex sign-in do not bypass this check. The trial and subsequent Free compute belong to the organization that received the trial; additional organizations need a paid plan. Existing accounts keep their access.
Serverless VMs without lifecycle code.
Give each customer of your product a persistent computer for their agent: open a workspace by a key you choose
(customer-42/main) and the same key returns the same Linux microVM, with its files, tools and running processes. It
parks seconds after its last call, wakes on the next call or HTTPS request, and you pay for the compute it uses while
Shardflux manages the infrastructure.
shard is the command line for Shardflux. Open a workspace by key, run commands in it, move
files in and out, and suspend, resume, fork or delete it, from a terminal, a script or CI. It also does everything the
web app does: create an account, sign in (with two-factor authentication), set up an organization, project and API
key, manage members, invitations and keys, buy or change a plan, export data and delete an account. It is built for
the teams that run an agent loop inside their app: your app keeps its own identity, model calls and agent loop;
Shardflux is the computer underneath. Built on @shardflux/sdk, so it
gets the SDK's retries, idempotency keys, operation polling and tool-token handling.
- Workspaces by key.
shard ws open customer-42/main creates the workspace on first use and returns the same one
every time after.
- Park and wake on use. A workspace parks seconds after its last call; the next command, file call or HTTPS
request wakes it.
- Suspend, resume and fork. Suspend and resume keep memory and running processes; a fork copies files, memory and
processes to a new key.
- Elastic memory and live resize. A workspace holds the memory its commands use and grows when they need more.
- Templates. Build your customers' starting environment from a
template.yaml.
- Ports and previews. Serve a workspace port at its own private HTTPS URL, with tokens, browser links and callback
URLs.
- Secrets, shared volumes and egress policies for every workspace.
- Computer use. A desktop in the workspace that your agent drives with mouse and keyboard actions.
- File-first workspaces for agents that keep only files between commands.
- GPUs on the Startup and Scale plans.
- The same workspaces from every client:
@shardflux/sdk, the
Python SDK shardflux, the MCP server @shardflux/mcp and the
HTTP API.
Plans, sizes and regions: docs.shardflux.dev/limits. Full CLI reference:
docs.shardflux.dev/reference/cli.
Runtime security
Runs on Node.js 24.22.1 or 26.12.1 and later releases in each line. Updating the npm package does not replace Node.js.
Install
npm i -g shardflux
shard --help
Requires Node.js 24 or later. The shardflux package installs the shard
command. Use shard for your workspaces, templates and account. shard on its own (0.32.0+) or shard --help
lists the commands, and shard <command> --help shows one; both work before you sign in.
If npm reports EACCES or EPERM, install only Shardflux in your user directory: npm install --global --prefix "$HOME/.local" shardflux@latest. Run "$HOME/.local/bin/shard" (including "$HOME/.local/bin/shard" update for updates), or use shard if command -v shard points there. Keep npm config, PATH and other global packages unchanged; do not use sudo or chmod. Installation help.
Quick start
Create an account. shard signup also runs shard setup: an organization, a project and an API key, saved for every
workspace command.
shard signup --email you@example.com --accept-terms
shard verify
For an existing account, approve this computer on the sign-in page, then save a key:
shard auth login --browser
shard setup
Open a workspace by a key you choose, run a command in it, and write and read a file:
shard ws open customer-42/main --template python-node-browser
shard ws exec customer-42/main -- python3 -c 'print(40 + 2)'
echo 'hello from customer 42' | shard files write customer-42/main /home/user/notes.txt
shard files read customer-42/main /home/user/notes.txt
The same key returns the same workspace: ws open creates it on first use and reconnects or resumes it afterwards,
never resetting it. shard exec <key> --template <slug> -- <cmd> (0.10.0+) opens and runs in one step.
Suspend it, wake it with the next call, and fork it:
shard ws suspend customer-42/main --wait
shard ws exec customer-42/main -- cat /home/user/notes.txt
shard ws fork customer-42/main customer-42/experiment --wait
shard ws suspend customer-42/main --when-idle 0
shard files read of a suspended workspace reads its saved disk without waking it. Your app does the same through
the SDKs, the MCP server or the HTTP API: eval "$(shard env)" exports the saved key as SHARDFLUX_API_KEY. The
quickstart walks through it in each client.
For AI agents
shard is built to be driven by an agent: no prompts and no full-screen views when standard input is not a terminal
or the shell belongs to an agent (0.17.1+), --json on every command, secrets only on standard input, and documented
exit codes.
You run permitted steps; your user decides (0.22.0+): ask one multiple-choice question at a time with the
recommended answer first, keep each message to a sentence or two, and never ask for a secret or sign-in code in
the chat. Your user answers your questions, agrees to the Terms, approves sign-ins in the browser tabs shard opens
and types secrets into the page shard opens. shard setup --guide prints the ordered steps an agent follows to set
Shardflux up for its user's product (the account, the API key, the integration), --json as a list with each step's
choices (0.17.1+), from the sign-in and API-key state saved on this computer.
If a permission system or auto-mode classifier refuses an action, stop that action. Explain the reason and hand
your user the exact refused command to run in their own terminal; ask only for its non-secret result, then resume
permitted steps. Do not change permission settings, wrap the action in a script or use another tool to bypass the
refusal. If setup output or a status check is refused, have your user run shard setup --guide --json and confirm
guide.signed_in.email and guide.api_key_saved. This local check never prints the credentials and does not prove
that the key works. Never read redirected logs or credential files instead. An exit code alone does not prove
setup; keep blocked or unobserved behavior unverified.
- Account. Ask whether they agree to the Terms of Service and the
Privacy Policy (Yes / No); then
shard signup --email <their email> --accept-terms --json creates the account and runs shard setup: an organization, a project and an API key, saved. For an
existing account, shard auth login --browser --json opens the sign-in page and waits until they approve it (run it
again if your tool's time limit cuts it off: it waits for the same request), then shard setup --json.
eval "$(shard env)" exports SHARDFLUX_API_KEY for the SDKs, the MCP server and your product's server. Keep it in
server-side secrets.
- Ask how the product calls Shardflux (TypeScript SDK / Python SDK / HTTP API / MCP server) and integrate with
@shardflux/sdk, the Python SDK shardflux, the HTTP API or the MCP server. Your customers' workspaces start from
a template: shard templates build template.yaml. shard exec <key> --template default -- <cmd> tries a workspace
from here (0.10.0+).
- A paid plan:
shard billing upgrade <plan> --json prints the Stripe Checkout URL; give it to your user, then
shard billing upgrade <plan> --wait (or shard billing status) until it is active.
Commands that wait for your user in the browser (sign-ins, --ask) take up to 10 minutes: run them with a long timeout
or in the background.
Any time: shard setup --json is idempotent, and sensitive actions (exports, deletions, two-factor and email changes)
need a recent step-up: printf '%s\n' "$PASSWORD" | shard auth step-up (without a terminal the command fails with
step_up_required, exit 4, and the hint names this command). When something fails, confuses you or is missing, or
your user asks for an improvement, offer a shard feedback preview and send it only once your user approves it (see
Feedback).
What a person does: agreeing to the terms, approving the browser sign-in, entering a card in Stripe Checkout and the
authenticator app's codes.
Decide between known options (0.32.4+)
When your agent has to understand what a person or another agent meant and the options are known (which project,
which file, which kind of request), shard decide asks the decision model instead of matching words. Each answer is
one of your choices with its confidence; a low confidence means ask the person:
shard decide "The person wrote: check on our model training. Their projects: status-board, site." \
--name where --instructions "Where does what the person means most likely live?" \
--choice "status-board=The status-board project" --choice "site=The site project" \
--choice "their_computer=A folder or job on their own computer"
--questions <file|-> takes several questions at once as JSON ([{"name", "instructions", "choices": [{"value", "description"}]}], up to 12 with 2 to 60 choices each), --input-file <file|-> reads a long input, --cache <seconds> reuses the answer for the same input and questions, and --json prints every choice's probability. A
dependency_unavailable (model_unavailable) failure is retryable: run the same command again after the time the hint
names.
Feedback: preview and user approval
(0.22.0+) shard feedback previews the exact message, category and context without sending. Add --yes only after reviewing that preview and obtaining explicit user authorization. Earlier CLI versions send immediately.
Agents: report only when your user asks to send feedback. Show the exact message and context first, then obtain explicit authorization. A failure or request for a feature is not consent. Leave out private data, secrets and code the user did not mean to share; paraphrase, never paste chat logs. Tell the user what was sent.
Keep the request id and error code from the failure. The suggested command previews a report; it never includes --yes:
error: Operation not found. [code=not_found status=404 request_id=req-2b9d4f6a8c0e4a1b3d5f7e90]
feedback: shard feedback --category bug --request-id req-2b9d4f6a8c0e4a1b3d5f7e90 --error-code not_found "<what you expected>" # preview only; show the message and context, then use --yes only after explicit user authorization
shard feedback --category bug --request-id req-5c7744b2bb5c457b86b76101 --error-code template_not_found \
"ws open --template python-node failed; expected a suggestion of the closest slug"
shard feedback --category idea \
"user would like shard ws open to remember the last template they used"
shard feedback --category praise forking a workspace with its dev server still running is exactly what we needed
npm test 2>&1 | tail -n 60 | shard feedback --category bug --workspace acme/demo --from-file -
The commands above only preview. After the user approves the exact message and context, repeat the chosen command with --yes to submit. Re-preview file or piped input if its contents changed.
- The message is the remaining arguments joined with spaces. Quote it, or put it after
--, when it contains shell
characters or words starting with -. --from-file <file|-> reads it from a file or standard input instead. Any
length (0.32.4+): a whole log goes as it is.
- Options:
--category bug|confusing|missing|idea|praise|other (default other), --workspace <id|key>,
--request-id <id>, --error-code <code>, --command <text> (what you ran). Name the reporting agent with
--agent-label or SHARDFLUX_AGENT_LABEL (e.g. support-agent); shard adds its own version as the client.
- Preview output includes
sent: false, authorization_required: true, preview and note with --json.
- Approved submission output: one line with the feedback id.
Already received means the same message was sent from this key in the last
24 hours; it is not emailed again. With --json: {"id", "received_at", "duplicate"}.
- It sends with the project API key (any key; no tool permission), or, when no key is configured yet, with your
signed-in session (
shard auth login), so it works while you are still setting up. It is rate limited per key or
user (rate_limited, exit 1, with the wait in the hint). If it cannot be sent, email shardflux@heliosone.fi.
- Anything shaped like an API key is redacted before the message is stored or emailed; leave other secrets out.
Accounts and sign-in (0.5.0+)
shard signup [--email you@example.com] [--name "Ada Lovelace"] [--no-setup] [--accept-terms]
shard verify [<code>]
shard auth login --browser [--no-open]
shard auth login --browser --no-wait
printf '%s\n' "$PASSWORD" | shard auth register --email you@example.com [--name "Ada Lovelace"] [--accept-terms]
shard auth verify-email '<link from the email>'
shard auth resend-verification
printf '%s\n' "$PASSWORD" | shard auth login --email you@example.com [--code 123456 | --recovery-code X]
shard auth mfa --code 123456
shard auth status
shard auth sessions [--revoke <session-id>]
shard auth logout [--all]
printf '%s\n' "$PASSWORD" | shard auth step-up [--code 123456]
printf '%s\n%s\n' "$CURRENT" "$NEW" | shard auth password change
shard auth password reset --email you@example.com
printf '%s\n' "$NEW" | shard auth password reset-confirm '<link from the email>'
shard auth email change --email new@example.com
shard auth totp enroll
shard auth totp confirm --code 123456
shard auth totp disable --code 123456
shard auth totp recovery-codes
shard signup (0.9.0+) creates your account and runs shard setup (--no-setup skips it). With --email the
account works at once on the Free plan; shard verify verifies the address, and shard billing trial (0.32.4+)
starts the 30-day trial with a card check (nothing is charged).
shard auth login --browser (0.15.0+) opens the sign-in page (https://app.shardflux.dev/cli?code=XXXX-XXXX)
in the default browser and prints the link and the code. Sign in or create your account on the page, check that it
shows the same code, and approve: shard saves the session like a password sign-in, and two-factor accounts use
their code on the page. --no-open only prints the link; --client-name sets the name the page shows (default
shard CLI on <hostname>). The code expires after 10 minutes. On a terminal, shard auth login without --email
signs in this way. With --json
it prints one JSON object per line: {"event":"open","verification_uri_complete":"…","user_code":"ABCD-EFGH","expires_in":600, "terms_url":"https://shardflux.dev/terms","privacy_url":"https://shardflux.dev/privacy"}, then
{"event":"signed_in","email":"…"}, or {"event":"error","code":"access_denied","message":"…"} (also
expired_token, exit 4). Before the browser opens it prints the terms notice: by continuing you agree to the
Terms of Service and acknowledge the Privacy Policy.
- The terms (0.15.0+): creating an account (
shard signup, shard auth register) needs your agreement to the
Terms of Service and the Privacy Policy:
--accept-terms (or SHARDFLUX_ACCEPT_TERMS=1), or "Continue? [Y/n]" on a terminal. Without either they stop
before sending anything: terms_not_accepted, exit 2, with terms_url, privacy_url and flag in details. The
account records the agreement. Signing in only shows the line (terms_url and privacy_url in --json); so does
auth login --browser, which needs no flag: an account created on the sign-in page agrees there.
- Passwords never come from arguments (
--password is refused). On a terminal shard asks without
echo (a new password twice); otherwise it reads standard input, one line per secret: auth password change reads the current password, then the new one. Emailed links and tokens are single-use
arguments; TOTP codes and recovery codes may be arguments.
- Sessions:
auth login saves the session (sfu_...) in the credentials file for this API URL. It
lasts 30 days of use and at most 90. The API renews it on step-up, password change and two-factor
changes, and shard saves the new one at once. A saved session the API does not accept is removed,
and the error says to sign in again. SHARDFLUX_SESSION_TOKEN is used instead of the saved session
when it is set.
- Two-factor sign-in:
--code/--recovery-code, or the code typed at the prompt. Without either
(and without a terminal) the pending sign-in is saved and auth login exits 4 (mfa_required);
auth mfa --code completes it.
- Step-up: exports, deletions, email changes and two-factor changes need the password again within
the last 10 minutes. On a terminal
shard asks (and a code with two-factor authentication) and
retries once; otherwise run printf '%s\n' "$PASSWORD" | shard auth step-up first (with two-factor
authentication add --code, or pipe the code as the second line).
- A verification link from
resend-verification also needs a new password: auth verify-email reads
it from standard input (or asks), sets it, and otherwise prints the auth password reset-confirm
command to run.
- After
auth login, shard prints who you are, whether the email is verified, your organizations
and the next step (shard setup, or shard verify).
Agent accounts (0.18.0+)
The organization's Claude, ChatGPT/Codex and Google (Antigravity, 0.33.0+) sign-ins. Each provider has one active
account: new conversations and every running conversation VM of that provider use its login.
shard agents accounts
shard agents accounts add claude
shard agents accounts add codex
shard agents accounts add antigravity
shard agents accounts use antigravity:1
add antigravity prints the Google link, opens it, and takes the code the page shows after you sign in:
Sign in with Google: https://accounts.google.com/o/oauth2/auth?...
Paste the code from the page here:
Antigravity runs on Gemini 4 Argon, for creative work: 3D modeling, video and audio. Sign in with a Google account
whose plan includes Gemini 4 Argon. Then shard agents new --app antigravity (or shard ./ antigravity) starts a
conversation on it in its own cloud computer (--model argon, its default), and shard models shows it as what
creative work starts on. Every role of your tasks runs on Claude or Codex.
Setup (0.5.0+)
shard setup makes a signed-in account ready for the workspace commands:
- the organization:
--org, the saved default, or your only one; with none it creates
"<your name>'s organization" (--org-name to choose); with several it lists them;
- the project:
--project, the saved default, or the organization's only one; with none it creates
"Default" (--project-name);
- an API key with every workspace tool,
computer included (0.15.1+; --tool repeatable to narrow it), named
shard-cli <hostname> (--key-name), saved in the credentials file and checked against the API.
shard setup --guide (0.17.1+) prints the ordered steps an agent follows to set Shardflux up for its user's product:
the account, the API key and the integration. The agent does the permitted steps and hands off actual policy
refusals, and the user answers multiple-choice questions (0.22.0+); --json gives the steps as a list with each
step's choices. See For AI agents.
It is idempotent: a saved key that still works is kept (--new-key makes a new one). A saved key from an earlier
shard setup (every tool but computer) is replaced with one that has every tool (0.15.1+); the old key keeps working
until you revoke it. It prints what
it found or created and the next command. The key is never printed; shard env prints it for tools
that read SHARDFLUX_API_KEY:
eval "$(shard env)"
shard env --shell fish | source
shard env --shell powershell | Invoke-Expression
Organizations, projects, keys, members (0.5.0+)
Account commands work on the default organization and project: --org/--project (an id or a
slug), else the one saved by orgs use, projects use or setup, else the only one you have.
With several and no default, the error lists them.
shard orgs list | create <name> [--slug] [--use] | get [org] | use <org>
shard orgs export [org] [--out file]
shard orgs workspaces [org] [--project P] [--state S] [--all]
shard orgs deletion [org]
shard orgs delete <org> --confirm <slug> --yes
shard projects list | create <name> [--slug] [--use] | get <project> | use <project>
shard api-keys list | create <name> [--tool T]... [--read-only] [--expires 30d|90d|365d|never] [--save] | revoke <id|key_id> --yes
shard members list | set-role <user|email> <owner|admin|member|billing> | remove <user|email> --yes
shard invitations list | create <email> --role <role> | revoke <id|email> | accept '<link>' [--use]
shard templates publish <slug>@<version> | archive <slug>@<version>
api-keys create prints the new key once, alone on stdout (KEY=$(shard api-keys create ci)), with
what it is on stderr; --json includes it as secret; --save stores it in the credentials file for
the workspace commands instead of printing it. Revoking the saved key also removes it from the file.
In more than one organization (0.22.0+): workspace and usage commands act where the API key belongs, account
commands where the default is. orgs use and projects use say when the key belongs to another organization and
name the command that makes one for the new default (shard setup --org <org> --new-key; in a terminal they offer to
run it). With a signed-in session, whoami distinguishes a saved default or key from an organization you left from
a key in another organization you still belong to. Warnings name shard orgs use <org> for a stale default and
shard setup --org <org> --new-key for the key. The switches preserve the key unless you accept the replacement
prompt. SHARDFLUX_API_KEY wins over the saved key: recovery saves the replacement, then you must unset the
environment variable to use it. --json carries the same warnings (code, message, fix); setup --json
also warns when the environment key still overrides its saved key. shard usage --org <org> reads any of your
organizations with your signed-in session.
For organization usage (0.22.0+), the summary, estimate and series keep using your project key in its own
organization. An explicit --org <id|slug> for another organization uses your session only when you are a member.
Without a session, sign in with shard auth login; to save a project key for that organization, then run
shard setup --org <org> --new-key. If SHARDFLUX_API_KEY is set, unset it after saving the new key so the saved
key takes effect. Workspace usage keeps the project key's authorization, and API errors stay errors.
Billing (0.5.0+)
shard billing plans
shard billing status
shard billing upgrade <plan> [--open] [--wait] [--timeout 15m]
shard billing trial [--open] [--wait] [--timeout 15m]
shard billing portal [--open]
shard billing invoices [--all]
shard billing alerts [--set 50,80,100 | --clear]
shard billing overage [status]
shard billing overage on [--cap <dollars>]
shard billing overage cap <dollars>
shard billing overage off
billing upgrade checks the plan against the public catalog (an unknown one lists the purchasable
plans), starts Stripe Checkout and prints its URL: a person pays there. --open also opens it in the
default browser ($BROWSER, else xdg-open, open or start). The API activates the
plan only when Stripe confirms the payment, so --wait polls until the subscription is active (exit
0), the checkout expires or is canceled (exit 6), or --timeout runs out (exit 5; the checkout stays
open). An organization that already has a subscription changes plans in the billing portal: the
command prints the portal URL instead (exit 0).
For new accounts, the signup trial (30 days of Pro) requires the Stripe card check, regardless of sign-in method.
Run billing trial (0.32.4+): it prints a Stripe page where a person enters a card number directly
(nothing is charged; one trial per person and per card across all accounts, ever). --wait polls until
the trial has started (exit 0), the page expires (exit 6) or --timeout runs out (exit 5).
billing status says when the trial is available. The trial and subsequent Free compute belong to the
organization that received the trial; additional organizations need a paid plan. Existing accounts keep their access.
On the signup trial, billing status shows when it ends and what follows (0.6.2+):
plan trial, ends 2026-11-29 14:03:00Z, then free (trial, catalog ...), then
keep these limits after the trial: shard billing upgrade pro (0.13.4+). A subscription outranks the
trial and ends it. The trial is granted, never bought, so billing plans leaves it out. The trial, the plans and
their allowances are in /limits.
billing overage shows and changes opt-in overage (owners and billing members). With it on,
workspaces keep opening and running past the CPU-hours and RAM GiB-hours allowances, and the usage
past them is charged on the next invoice until the charges reach the spend cap. The cap is per
billing period, from $1 up to the plan price; shard usage shows this period's charges. Every owner
and billing member gets an email when overage is turned on or off or the cap changes. --json
prints the API's spend policy; --if-version <n> (its version) refuses the change if someone
changed the policy since (exit 1, version_mismatch). A refused cap exits 2 and the hint names the
bound: the plan price (spend_cap_above_plan_price), $1 (spend_cap_below_minimum) or what overage
already charged this period (spend_cap_below_charges); a plan without overage answers
overage_unavailable, and turning it on without a cap spend_cap_required.
$ shard billing overage on --cap 9
Acme: overage is on, spend cap $9.00 per billing period ($1.00 to $9.00, the plan price).
Rates past the allowances: $0.015 per RAM GiB-hour, $0.04 per CPU-hour, charged on the next invoice (this period: shard usage).
Saved (policy version 1).
Usage and overage
shard usage prints the current period's plan, meters and allowances. With opt-in overage (0.5.1+), an allowance past
included while overage is on shows cap state overage, and a block follows with the overage state, the spend cap,
the charges of the cap, the date the cap is projected to be reached, and per allowance the units past it and their
amount:
overage accruing (usage past the allowances is charged on the next invoice)
spend cap $9.00 (at most $9.00, the plan price)
overage charges $1.10 of $9.00 (12.22%), $7.90 left; billed on the next invoice
cap reached 2026-09-29 15:02:46Z (projected at this period's average use)
OVERAGE UNIT PAST ALLOWANCE BILLED RATE AMOUNT
ram_gib_hours gib_hours 70 60 $0.015 $0.90
cpu_hours hours 5 5 $0.04 $0.20
--json prints the API's summary, with spend_cap and exhausted_reason. Owners and billing members turn overage on
and set the cap with shard billing overage or under Usage & billing in the console.
While an allowance is used up, opens, resumes and forks are refused with 402 allowance_exhausted (exit 1). The error
names details.reason, and a hint says what to do (0.5.0+):
allowance_used | Upgrade (shard billing plans), or turn on overage: shard billing overage on --cap <dollars>. |
spend_cap_reached | The spend cap is reached: raise it (shard billing overage cap <dollars>, at most the plan price) or upgrade. |
overage_paused | A payment is past due: update the payment method (shard billing portal --open). |
GPU-hours (0.19.0+): on plans with GPU-hours, shard usage adds a GPU-hours line with the hours used of those
included (and, with overage on, the hours past them and the part billed), and a GPU line with the next step whenever
GPU rendering needs one:
GPU-hours 3.4 of 25 used
shard billing overage lists the GPU-hour rate with the others, and a 402 entitlement_required refusal with reason
not_included gets a hint naming the plans that include the feature. /limits
has the allowances and the rate.
Audit, account export and deletion (0.5.0+)
shard audit list [--action api_key.] [--from 7d] [--to <time>] [--source app|cell_tool] [--outcome denied] [--all]
shard audit export [--format ndjson|csv] [--out file] [filters]
shard account status
shard account export [--out file]
shard account delete --confirm you@example.com --yes
shard account cancel-deletion
--from/--to take an RFC 3339 time or a duration ago (90m, 24h, 7d, 2w). Files shard
writes with exports are created with mode 0600 and never overwritten without --force.
Commands
shard --help lists the commands and shard <command> --help explains one, before you sign in.
signup, verify, auth register|verify-email|resend-verification|login|mfa|status|logout|sessions|step-up, auth password change|reset|reset-confirm, auth email change|confirm, auth totp enroll|confirm|disable|recovery-codes | The account and its sign-in (0.5.0+; signup and verify 0.9.0+): see Accounts and sign-in. |
setup [--org O] [--project P] [--org-name N] [--project-name N] [--key-name N] [--tool T]... [--new-key] [--guide], env [--shell sh|fish|powershell] | Organization, project and a saved API key in one step; the saved key as shell exports (0.5.0+). See Setup. |
orgs, projects list|create|get|use|retention, api-keys, members, invitations, templates publish|archive | Organizations, projects, API keys, members and invitations with the session (0.5.0+). See Organizations, projects, keys, members. |
billing plans|status|upgrade|portal|invoices|alerts|overage | Plans, Stripe Checkout, the billing portal, invoices, usage alerts (0.5.0+) and opt-in overage (overage, 0.5.1+). See Billing. |
audit list|export, account status|export|delete|cancel-deletion | The audit log, data exports and account deletion (0.5.0+). |
egress get|set|history|clear, volumes list|create|open|get|delete|attachments|attach|detach | Outbound network policies and shared volumes with the project key (0.5.0+). |
login, whoami | Show the principal behind the project key (SHARDFLUX_API_KEY, else the saved one). |
status | The project's workspaces and shared volumes at a glance. |
version [--check] | Print the CLI and SDK versions and the update status (--check asks the API now). |
update [--check] | Update shard, its SDK and a globally installed @shardflux/mcp (0.13.5+). See Update. |
usage [--org <id>] [--estimate | --series | --workspace <id|key>] [--from T] [--to T] [--granularity hour|day] [--meter M] | Usage summary for the current period: plan, meters and allowances. --estimate: the bill so far and projected allowance use; --series: usage over time; --workspace: one workspace's usage over time (0.5.0+). The summary includes the overage block (0.5.1+). |
feedback [--yes] [--category C] [--workspace W] [--request-id ID] [--error-code CODE] [--command TEXT] [--from-file F|-] <message...> | Preview feedback; --yes sends after explicit user authorization (0.22.0+). See Feedback. |
workspaces open <key> --template <slug> [--secret NAME]... [--input NAME=value]... [--input-file F] [--lifetime persistent|session] [--mode processful|file-first] [--cpu-millis N --memory-mib N --disk-gib N] [--allocation-mode fixed|elastic] [--memory-held N] [--no-wait] [--timeout 5m] [--timing] | Open by key. Creates the workspace on first use and reconnects or resumes it afterwards, never resetting it. Waits until it is ready (the template's start commands and services included) unless you pass --no-wait. --secret binds secret names (replacing the binding of an existing workspace). --input (0.4.0+) passes the template's text inputs (replacing those of an existing workspace); --input-file reads NAME=value lines or a JSON object. --lifetime session: the workspace is discarded when the session ends (workspaces close or the idle timeout), and the key then opens a new workspace. --mode file-first (0.5.0+) opens a file-first workspace; the default is processful for a new key and the stored mode for an existing one. --allocation-mode elastic (0.8.0+): elastic memory. --labels, --idle-policy and --delete-after-idle: integration controls and idle deletion retention. |
workspaces list [--state S] [--prefix P] [--labels JSON] [--lifetime persistent|session|any] [--purpose standard|template_draft|template_test|any] [--include-deleted] [--all] [--limit N] [--cursor C] | One page, or every page with --all. The next cursor is printed on stderr. By default only persistent standard workspaces are listed. A file-first workspace's state reads running (file-first) (0.5.0+). |
workspaces get <id|key> | One workspace (deleted ones included), with the startup of its template's start commands and services (0.4.0+): a failed step shows its exit code, reason and output tail. Its mode, and a file-first workspace's tree revision (0.5.0+). Its memory (0.8.0+): fixed 4096 MiB, or elastic: promised …, held …, plugged +… MiB. The template version whose immutable paths it has mounted (0.8.0+): immutable paths v3 (mounted read-only). |
workspaces inputs <id|key> | The workspace's text inputs (0.4.0+). |
workspaces exec <id|key> [--template S] [--cwd D] [--env K=V]... [--timeout T] [--stdin F|-] [-v] [--execution-id ID] [--output-limit N] [--resource-hint auto|light|heavy] [--burst [--burst-vcpus N] [--burst-memory MIB]] -- <cmd> [args...] | Runs argv with no shell. shard exec is the same command (0.10.0+). --template <slug> (0.10.0+) opens the key first: the workspace is created on first use (stderr says so) and resumed if suspended; without it the key must have a workspace. Output is streamed (in the start request itself, 0.8.0+) and resumes from byte offsets after dropped connections. shard exits with the command's exit code. Ctrl-C cancels the command. -v (0.5.0+) then prints the session id and exit status on stderr, and the memory grow the command waited for on an elastic workspace (0.8.0+; --json: memory_grow). --cwd is an absolute path (default /home/user); a relative one is refused (invalid_cwd, exit 2). A command that could not start (a --cwd that is not a directory, a program not on PATH) exits 6 with the workspace's reason and a hint (0.5.1+). On a file-first workspace (0.5.0+) the command runs as an execution instead: see File-first workspaces. --burst (0.8.0+): burst execution. --resource-hint (0.9.0+): elastic memory. |
workspaces exec-start <id|key> [--template S] [--session-id ID] [--cwd D] [--timeout T] [--stdin-open] [--resource-hint auto|light|heavy] -- <cmd> [args...], workspaces exec-read <id|key> <session-id> [--follow], workspaces exec-cancel <id|key> <session-id> [--grace D], workspaces exec-input <id|key> <session-id> <data> --offset N [--close] | Background commands (0.8.0+): start a command and return at once, read its output and state (--follow streams it to its exit and exits with its code; while it runs exec-read exits 5), stop it, write to its open standard input (0.7.0+). See Background commands. |
executions get <id|key> <execution-id> [--wait D] | The result of a file-first workspace's execution (0.5.0+): its output, then what ran. Exits with the command's code, 6 if the execution failed or was lost, 5 while it still runs. |
files read <id|key> <path> [--out F] | Raw bytes to stdout, or to a local file. |
files read-many <id|key> --path P... --generation G | Complete files from one sealed suspended generation, without waking the workspace (0.12.0+). See Sealed-file reads. |
files write <id|key> <path> [--from F|-] [--append] [--no-parents] [--if-revision N] | Atomic write. The default source is stdin. --if-revision (0.5.0+): file-first workspaces only, see Tree revisions. |
files ls <id|key> <path> [--limit N] | Directory listing. |
files stat <id|key> <path> [--revision] | One path's metadata; --revision adds the content's SHA-256 (0.5.0+). |
files search <id|key> <path> <pattern> [--regex] [-i|--ignore-case] [--include G]... [--exclude G]... [--max N] | Search file contents under a directory (or in one file; gitignore-style globs): path:line:column: text per match (0.5.0+). |
files patch <id|key> <path> (--old T --new T)... [--replace-all] | --content-file F|- [--expected-revision R] [--if-revision N] | Replace exact text (each --old must occur once unless --replace-all), or the whole content, atomically; R is a revision from files stat --revision or absent (0.5.0+). --if-revision (0.5.0+): file-first workspaces only. |
workspaces suspend|resume <id|key> [--wait] [--timeout T] [--timing] | Lifecycle operation. Returns immediately unless you pass --wait. |
workspaces suspend <id|key> --durable [--timeout T] | (0.7.0+) Suspend and wait until the copy is in durable storage. See Durable storage. |
workspaces suspend <id|key> --when-idle D / --cancel-when-idle | (0.5.1+) Suspend once the workspace has been idle for D (0 to 1h; 0 = as soon as it is idle, 0.6.1+), or cancel that. See Suspend when idle. |
workspaces fork <id|key> <new-key> [caps] [--allocation-mode fixed|elastic] [--memory-held N] [--wait] | Fork into a new key. Without caps the fork keeps the source's caps and memory allocation (0.8.0+). With --wait the API holds the fork until the copy runs and returns it ready in the same request (0.8.0+; --timing shows one request phase marked (held)). |
workspaces resize <id|key> [--memory MIB] [--held MIB] [--fixed|--elastic] [--cpu MILLIS] [--disk GIB] [--at-least] [--timeout 5m] [--timing] | Change the memory, CPU and disk of a running or suspended workspace without a restart (0.9.0+). Prints per resource the change and when it applies (now, on resume, next start). See Resize a workspace. |
workspaces upgrade <id|key> [--at now|next-resume] [--wait] | Upgrade in place, keeping files and installed packages (0.11.0+). See Workspace upgrade. |
workspaces delete <id|key> --yes [--wait] | Delete the workspace. Tool access ends at once. After deletion finishes, the key can open a new workspace ID. |
workspaces close <id|key> [--wait] | End a session workspace now: it is deleted, and the key then opens a new workspace. A persistent workspace is refused (not_session). |
workspaces reset <id|key> --yes [--wait] | Layered workspaces: wipe every change and restart on the template. The previous state stays restorable for 7 days. |
workspaces save-as-template <id|key> --template <slug> [--display-name N] [--description T] [--checkpoint ID] [--default-lifetime L] [--default-idle-timeout D] [--acknowledge PATH]... [--no-auto-publish] [--wait] | Save a layered workspace as the next version of an organization template. --wait follows the build. |
workspaces changes <id|key> [--path P] [--hash] [--summary] [--all] [--limit N] [--cursor C] | What a layered, running workspace changed against its template. |
workspaces labels <id|key> --set JSON, workspaces idle <id|key> [--policy P] [--keepalive S], workspaces hint <id|key>, workspaces retention <key> <policy> | Labels, the idle policy and keepalives, a hint ahead of an imminent call, and idle deletion (0.7.0+; hint 0.9.0+, retention 0.11.0+). See Integration controls. |
workspaces sessions <id|key> | Attributed agent sessions: label, principal and tokens issued. |
workspaces secrets <id|key> [--set NAME]... [--clear] | Show the secret names bound to the workspace and their status, or replace them. |
workspaces port expose <id|key> <port>, workspaces port ls <id|key>, workspaces port close <id|key> <port> | Serve a port of the workspace at its own private HTTPS URL, list the exposed ports, close one (0.9.0+; see Inbound ports). |
workspaces port token <id|key> <port> [--ttl D], workspaces port link <id|key> <port> [--ttl D] [--path P] [--embed-origin O]..., workspaces port callback <id|key> <port> [--revoke] | A port token (printed alone), a link that opens the port in a browser, the port's callback URL for webhooks and OAuth redirects (0.9.0+). |
workspaces tunnel <id|key> --remote PORT --to HOST:PORT [--bind ADDR] [--transport auto|pty|exec] | A foreground TCP tunnel from a workspace port to a service on this machine (0.11.0+; see Reverse tunnels). |
workspaces computer status|screenshot|act|start|stop|stream <id|key>, workspaces computer click|type|paste-text|key|scroll|move|drag|mouse-down|mouse-up|hold-key|zoom|cursor|wait <id|key> ..., workspaces computer enable|disable|inherit <id|key>, templates computer-use <slug> on|off | The workspace desktop for computer use: its state, a screenshot (--out F), one command per mouse and keyboard action, a batch of actions (a JSON array), a private link to watch or use it (stream [--interactive] [--ttl D], --stop), and the switch for a workspace or every workspace of a template (0.10.0+; see Computer use). |
secrets create <NAME> [--from-file F] [--ask] [--description T] [--tool T]... [--allow-workspace ID]... [--org ID [--allow-project ID]... [--all-projects]] | Create a secret. The value comes from standard input, --from-file or the --ask page, never from an argument. |
secrets list [--org ID] [--include-deleted] [--all], secrets get <id|NAME> | Secret metadata. Values are never shown. |
secrets update <id|NAME> [--description T] [--tool T]... [--allow-workspace ID]... [--any-workspace] [--allow-project ID]... [--all-projects] [--clear-projects] | Change the description or usage permissions. |
secrets rotate <id|NAME> [--from-file F] | Store a new value (standard input or file); older values are erased. |
secrets versions <id|NAME>, secrets delete <id|NAME> --yes, secrets access-log <id|NAME> | Versions, deletion (also removes the name from every workspace binding), and the access log. |
operations list <id|key> [--state S] [--kind K], operations get <op>, operations wait <op> [--timeout T] [--durable] [--timing] | Operations. |
operations progress <op>, operations cancel <op> [--wait] [--timeout T] | A suspend's phase, deadline and cancellation window, and cancellation before capture (0.22.0+). See Suspension controls. |
templates list [--owner platform|organization] [--include-archived] [--all] | Templates the project can open, with their open version, layouts, file list state and draft. |
templates get <slug> [--owner O] [--include-archived] | One template (0.4.0+): its versions, what open resolves to, and the open version's settings (env, inputs, start commands, services, defaults). Each version's immutable paths and their image size (0.8.0+). |
templates init --base <slug[@version]> [--out F] [--force] | Write a starter template.yaml on a base (0.4.0+). |
templates build <file> [--slug S] [--display-name N] [--description T] [--publish] [--acknowledge PATH]... [--wait] [--timeout 30m] [--timing] | Build a template from template.yaml (0.4.0+): local files and folders are uploaded, then the build is created. See Build a template. |
templates export <slug>@<version> [--out F] [--force] | The template.yaml a version was built from, ready to build again (0.4.0+). |
templates test <slug>@<version> [--input NAME=value]... [--instance-key K] [caps] [--no-wait] [--timing] | A test instance of a version, published or not: a session workspace that runs its start commands and services (0.4.0+). |
templates builds list [--template S] [--state S] [--all], templates builds get|log|cancel <build-id> | The organization's template builds, one build, its full log (--out F), or cancel it (0.4.0+). |
templates languages --base <slug>@<version> | The languages and versions a base offers build.languages (0.4.0+). |
templates packages search <apt|pip|npm> <query> [--base slug@version] [--limit N] | Package names for build.packages (0.4.0+). apt searches the base's index and needs --base. |
templates files <slug> <version> [path] [--stat] [--owner O] [--all] [--limit N] | One directory of a version's file tree, or one entry with --stat. |
templates diff <slug> --from <version|base> --to <version> [--prefix P] [--change K] [--all] | Diff two versions: added, removed, changed, type_changed, metadata. The totals come first. |
templates draft open <slug> [--base slug@version] [caps] [--no-wait] | Open the template's draft, a layered workspace you edit with workspaces exec/files. |
templates draft status <slug> | The draft's workspace, base version, states and live test instances. |
templates draft state <slug> [--label T] [--wait], ... state <slug> --list | Capture a state of the running draft, or list its states. |
templates draft test <slug> [--state ID] [--instance-key K] [--no-wait], ... test <slug> --list [--include-ended] | Open a test instance (a session on a copy of a state), or list them. |
templates draft publish <slug> [--state ID] [--description T] [--default-lifetime L] [--acknowledge PATH]... [--no-auto-publish] [--wait] | Publish the draft as the template's next version. |
templates draft discard <slug> --yes [--wait] | Delete the draft and end its live test instances. |
Aliases: ws/workspace for workspaces, ops/operation for operations, execution for executions, file
for files, secret for secrets, org/organization/organizations for orgs, project for projects,
keys/api-key for api-keys, member for members, invite/invites/invitation for invitations, volume
for volumes. shard workspaces files ..., shard workspaces operations ... and shard workspaces executions ...
(0.5.0+) also work. <id|key> is tried as an id when it
is a UUID. Otherwise, or if no workspace has that id, it is matched as an exact workspace key across every lifetime
and purpose, so sessions, drafts and test instances resolve too. The live workspace wins over tombstones that ended
sessions left with the same key.
Build a template from template.yaml
(0.4.0+) A template is a recipe v2: a base, what the build adds (languages, packages, files, build steps) and the
settings a workspace gets when it opens (environment, inputs, start commands, services). template.yaml is that
document; a file entry may name a local from path (relative to the file), which shard uploads.
shard templates init --base ubuntu-24.04
$EDITOR template.yaml
shard templates languages --base ubuntu-24.04@1
shard templates packages search apt ffmpeg --base ubuntu-24.04@1
shard templates build template.yaml --slug acme-dev --wait --timing
shard templates test acme-dev@1 --input PROJECT_NAME=demo
shard templates build template.yaml --slug acme-dev --publish --wait
shard ws open acme/main --template acme-dev --input PROJECT_NAME=acme
base: ubuntu-24.04@1
build:
languages: [{ id: python }]
packages: { apt: [jq], pip: { packages: [pandas==2.3.2] } }
files:
- { from: ./app, to: /home/user/app, owner: user }
- { from: ./settings.toml, to: /home/user/.config/acme/settings.toml, owner: user, mode: "0600" }
steps:
- { name: install, run: pip install -e /home/user/app, user: user }
settings:
env: { APP_ENV: development }
inputs: { PROJECT_NAME: { kind: text, required: true } }
start: [{ name: seed, when: create, run: python -m app.seed, user: user, cwd: /home/user/app }]
- Folders are packed as a reproducible tar (the same bytes as the SDKs pack), so the same folder gives the same
upload and the same
recipe_sha256. Bytes the organization already has are not sent again. Progress lines go to
stderr (packed, uploaded or already uploaded, then the build's states with --wait).
- The version stays unpublished unless you pass
--publish: try it with shard templates test <slug>@<version>.
Without --slug the template slug is the name of the directory holding the file.
--wait follows the build until it is registered or fails (exit 6 when it failed or was canceled, 5 when
--timeout ran out; the build continues). A build whose network refused a host lists it under denied hosts: add
it to build.network.extra_hosts and build again.
--timing prints where the time went: packing, uploads, the request, the wait, and the build's queued and run time.
- A file
shard cannot read or pack (a symlink that leaves the folder, a device) is refused before any request (exit
2). The API's refusals (upload_missing, language_unavailable, invalid_settings, ...) exit 2 with the reason.
- Template objects, file lists (
shard templates files) and version history stay available for every version. A
file list that is still being prepared answers a retryable 409 file_list_indexing; TypeScript SDK 0.27.0+ and
Python SDK 0.23.0+ wait the full Retry-After on GET/HEAD within the admission timeout.
Immutable paths (0.8.0+)
Directories listed under immutable follow the template: every workspace of it mounts them read-only from the
template's newest published version, picked up at its next cold boot or resume. Ship tools, models or data there and
existing workspaces get each new version; everything else in a workspace stays its own.
immutable: [/opt/acme]
shard templates get acme-dev
shard ws get acme/main
A template.yaml without immutable keeps the list of the template's open version, and a new version keeps every path
of it (immutable_path_removed names the ones to add back). shard templates builds get lists a build's immutable
paths; shard templates export writes them back into template.yaml. A write under an immutable path
(shard files write) exits 1 with read_only_path and a hint.
Initialized CPU services (0.12.0+)
Template services accept initialized_cpu with compatibility, reset, and timeout_seconds. Use cpu-service-v1 for immutable initialization, or cpu-service-fresh-inputs-v1 with fresh_env_names for values supplied at each open. Declare an unprivileged user and command readiness. Each open runs the reset script and its owned startup hooks before returning. Initialization-dependent and secret inputs use ordinary startup.
File-first workspaces
(0.5.0+) A file-first workspace has no VM between commands. Its state is a versioned file tree under /home/user.
Each shard ws exec runs the command as an execution: a fresh VM on the latest tree revision, and the files the
command changed under /home/user become the next revision. Processes, memory and files elsewhere do not survive
an execution. A file-first workspace is ready at once, never suspended and always persistent, and it needs a layered
template version. A workspace's mode never changes.
shard ws open acme/agent-7 --template python-node-browser --mode file-first
shard files write acme/agent-7 /home/user/app/main.py --from main.py
shard ws exec acme/agent-7 -v -- python3 /home/user/app/main.py
shard ws exec acme/agent-7 --execution-id build-42 --timeout 10m -- bash -lc "make -C /home/user/app"
shard executions get acme/agent-7 build-42
shard files patch acme/agent-7 /home/user/app/main.py --old "DEBUG = True" --new "DEBUG = False" \
--if-revision "$(shard ws get acme/agent-7 --json | jq .tree_revision)"
-v prints what ran on stderr, after the command's own output:
$ shard ws exec acme/agent-7 -v -- bash -lc "python3 app/main.py > app/out.txt"
execution ex-6f1c2a9e-0d7b-4c1e-9a53-2b8e1f0c4d7a
state succeeded (exit code 0)
revision 1 -> 2
timings blobs_fetched=… exec_ms=… publish_ms=… queue_ms=… run_ms=… total_ms=… vm_seconds=…
changed 1 path
A /home/user/app/out.txt
-
Exit code: the command's own once it ran to its end: 124 if it timed out (--timeout), 128+N if a signal N
killed it. An execution that failed (it could not run the command, e.g. exec_failed_to_start) or was lost
(see details.reason) published nothing and exits 6. The error names the reason and says retryable=true|false;
running it again needs a new --execution-id.
-
Output is printed when the command ended, not streamed: stdout and stderr byte for byte, also when they are not
UTF-8. --output-limit <bytes> keeps at most that much of each (default 1 MiB, at most 16 MiB). A cut stream is
noted on stderr.
-
-v / --verbose: the execution id, its state and exit status, base -> new tree revision (unchanged when
the command changed nothing), replayed when this was not the call that ran it, the timings, and the changed paths
(A added, M modified, D deleted; a directory ends in /; the first 100, --json lists all). Without -v,
--timing prints the timings line alone.
-
--json: one object, {execution_id, state, exit_code, term_signal, timed_out, base_revision, tree_revision, changed, changed_truncated, stdout, stdout_truncated, stderr, stderr_truncated, replayed, timings, error, created_at, finished_at}. stdout and stderr are UTF-8 text; a stream that is not valid UTF-8 also has
stdout_base64 / stderr_base64 with the exact bytes.
-
--execution-id <id>: the execution's idempotency key (8-128 characters of A-Z a-z 0-9 . _ : -, starting
with a letter or digit; default a fresh ex-<uuid>). The same id with the same command returns the recorded result
(replayed) instead of running it twice, so a script can repeat the call safely; the same id with another command
is refused (execution_id_reused). shard re-sends the request with the same id after a dropped connection and
after no_execution_host (the execution cannot be placed right now; Retry-After, at most 5 times), and waits
within --wake-timeout while another execution holds the workspace (execution_in_progress).
-
Ctrl-C: an execution runs to completion. Ctrl-C stops waiting (exit 130) and prints the command that fetches
the result later:
error: Interrupted; execution build-42 keeps running server side if it started (an execution runs to completion). [code=interrupted execution_id=build-42]
hint: fetch its result later with: shard executions get acme/agent-7 build-42
-
shard executions get <id|key> <execution-id> [--wait D] (also shard ws executions get): prints the output and
the -v summary (--json: the object above) and exits like exec. An execution still queued or running exits 5;
--wait 10m waits up to that long for it to end. Results are kept 7 days.
-
Processful-only commands: shard refuses suspend, resume, fork, reset, save-as-template and workspaces changes
on a file-first workspace without sending them (not_supported_for_mode, exit 1, with a hint). Only paths under
/home/user exist (outside_tree_root). --mode file-first with --lifetime session is a usage error (exit 2),
and so are --execution-id, --output-limit and --if-revision on a processful workspace.
-
Opening: reopening a key with the other mode is mode_mismatch (exit 1); an account without file-first
workspaces gets mode_not_available (exit 2); a template version without layered disks layout_unsupported
(exit 1). Each comes with a hint.
Tree revisions
A file-first workspace starts at tree revision 0. Every write, patch or execution that changes files publishes the
next revision. shard ws get shows the current one (tree_revision with --json), and files write and
files patch print the revision after their change (tree_revision with --json).
--if-revision <n> on files write and files patch applies the change only while the tree is at revision n.
Otherwise nothing changes, shard exits 1 and names the revision the tree is at:
error: The workspace's tree is not at the If-Match revision. (tree_revision_mismatch) [code=conflict status=409 request_id=... source=cell]
hint: the tree is at revision 6 now (another write or execution changed it); nothing changed. Read what you need again, then retry with --if-revision 6
Suspended workspaces
workspaces exec, files read|write|ls|stat|search|patch and workspaces changes wake a suspended workspace:
they resume it (or join the resume or open already running), wait until it runs, then run the request.
A request made during a suspend or resume waits for the transition to finish. The request itself
never runs twice, because the workspace refused it before doing anything. Since 0.5.0 the wake is one
request: the API holds the resume until the workspace runs and answers with its tool token.
A file-first workspace (0.5.0+) is never suspended, so none of this applies to it.
files read|ls|stat|search of a suspended workspace are answered from its saved disk
without waking it (the state at suspension); files search then notes it on stderr (served_from: "disk" with
--json).
--wake-timeout <ms> (or SHARDFLUX_WAKE_TIMEOUT_MS, default 120000) bounds the wait. Past
it, shard exits 5 and names the operation and its state (for example Operation <id> (open) is still queued after 120000 ms). The operation continues server side:
shard operations wait <id> keeps waiting. Without either (0.16.0+), a resume queued for capacity is waited for
until its deadline, its place shown on stderr (see Start deadlines).
- Admission queues (0.28.0+, SDK 0.29.0): an open keeps its body and Idempotency-Key while waiting, and stderr says
queued for admission; retrying in 2 s. Each wait honors Retry-After within the SDK admission timeout (default 2 minutes).
- A call answered 503
host_capacity or 429 quota_exceeded (every workspace the plan lets work at once is busy) did
not run: shard sends it again after each Retry-After, for up to 2 minutes (0.16.0+), and says once on stderr
that it is waiting, with its position.
- A resume or open that fails exits 6.
--no-wake (or SHARDFLUX_NO_WAKE=1) turns this off. A workspace that is not running is then
refused at once (exit 1, workspace_not_running).
Detecting a cold resume (0.6.0+). A resume restores memory and running processes. After a platform runtime
update, or after the machine the workspace ran on failed (0.8.1+), a resume can boot from the saved disk instead of
restoring memory (a cold boot), also when a command wakes the workspace; check memory_restored. The command still runs
with the files kept, and processes start fresh, as after a reboot: start dev servers and background jobs again. shard
then prints one line on stderr after the output (also with --json, except on a failure, whose stderr is only the
error document), whether or not you pass --timing:
note: workspace 01a0e5a8-3edd-74ba-b489-d62b8925e342 resumed from its saved disk; running processes were restarted (runtime_changed)
shard ws resume --wait --json prints the operation with its result as the API sent it (resume_path
cold_boot, memory_restored false, cold_boot_reason), and --timing shows resume from cold_boot: processes restarted (runtime_changed). The same applies to operations wait of such a resume. From 0.28.0, cold resume output
identifies guest_filesystem in JSON and in the restart notice; the
workspace lifecycle covers retained files and process restarts.
Workspaces recover by themselves (0.8.1+). When the machine a workspace runs on fails, the workspace is suspended,
and the next command on it resumes it; there is nothing extra to run. From its own disk, the note above ends in
(the machine it ran on failed); from its newest checkpoint, the note names the checkpoint and the time its state is
from (state as of <time>). --json carries result.host_lost (detected_at, restored_from disk or
checkpoint, restored_checkpoint_id, state_as_of), and --timing adds host_lost to server.
Suspension controls (0.22.0+)
shard operations progress <operation-id> reads a suspend's actual phase, deadline, cancellation window and safe next
action without waking the workspace. shard operations cancel <operation-id> requests cancellation while the suspend
is still preparing; --wait --timeout 5m waits for confirmed suspend_canceled recovery of the original workspace,
and asynchronous acceptance is labeled separately. Once capture has started, wait for the outcome and resume if it
succeeded. --json preserves the reported values; unavailable progress is explicit.
Sealed-file reads (0.12.0+)
Additive: shard files read-many <workspace> --path /file --path /other --generation <generation> prints a sealed-generation JSON envelope with exact base64 bytes. Exit 1 means explicit missing/refused paths; exit 0 means every requested path succeeded. It never wakes or resumes the workspace.
Use sealed-file reads for complete files from one suspended generation. If the generation changes, request a fresh read. See lifecycle for the read contract.
Suspend when idle (0.5.1+)
A running workspace is billed while it is awake. When an agent's turn ends, ask for a suspend once the workspace has
been idle for a short time, instead of waiting for its idle policy:
shard ws suspend acme/demo --when-idle 2m
shard ws get acme/demo
shard ws suspend acme/demo --cancel-when-idle
--when-idle 0 (0.6.1+) suspends the workspace as soon as it is idle (a few seconds after its last work).
- The idle time counts from the later of the workspace's last work and the request.
- A command still running, an attached stream or a keepalive postpones the suspend until the duration after it ends.
- The next tool call on the workspace (the next turn) or a resume cancels the request. Repeating replaces it.
- If a suspend is already in progress,
shard names its operation and schedules nothing.
--json prints {workspace, operation, suspend_request}; with --cancel-when-idle, the workspace.
--when-idle and --cancel-when-idle cannot be combined with each other, --wait or --timeout (exit 2). A
workspace that is not running is refused with conflict (not_running, exit 1), and a file-first workspace (never
suspended) with not_supported_for_mode (exit 1).
Durable storage (0.7.0+)
shard ws suspend --wait returns as soon as the workspace is sealed, typically in a few hundred ms, and its RAM and
CPU are released then. The copy lands in durable storage right after, typically within a second:
shard ws suspend acme/demo --wait
shard ws suspend acme/demo --durable
shard operations wait <op> --durable
--durable implies --wait and shares its --timeout.
--json prints the operation as the API returns it: result.durable and result.durability (state
pending or durable, local_commit_at, durable_at, ...). --timing adds durable, suspend_path and
durability to server.
operations get|wait show a durable line for suspends and forks. A fork of a running workspace reports its copy the
same way.
- The exit codes and errors of
--durable are in the CLI reference.
Idle deletion retention (0.11.0+)
shard ws open chat/42 --template python-node-browser --delete-after-idle 14d
shard ws retention chat/42 30d
shard projects retention PROJECT_ID 14d --labels '{"app":"chat"}'
shard projects retention PROJECT_ID
shard ws retention chat/42 off
Use 1d through 3650d with persistent workspaces. off removes the workspace's own policy and follows its
project default; shard projects retention PROJECT_ID off clears that default. All selector labels match
exactly, and a workspace's own setting wins. With no policy, workspaces stay until you delete them.
Workspace upgrade (0.11.0+)
shard ws upgrade <key> keeps files and installed package files through one cold start. Memory and processes end. Use --at next-resume to schedule it without stopping the VM; --json returns the operation and its applied/scheduled result.
Elastic memory (0.8.0+)
A workspace can hold only the memory its commands use and grow up to --memory-mib when they need more; billing counts
the memory it holds. It idles at --memory-held (default 1024 MiB). Package installs, test runners, compilers and
bundlers get their memory before they start, so they see what they need from their first instruction; everything else
starts at once and the workspace grows while it runs.
shard ws open acme/demo --template python-node-browser --memory-mib 8192 --allocation-mode elastic
shard ws get acme/demo
shard ws exec acme/demo -v -- npm test
shard ws exec acme/demo --resource-hint heavy -- python3 train.py
--allocation-mode fixed gives a static allocation of --memory-mib. --resource-hint (0.9.0+) is auto (the
default: decided from the command), heavy or light. --json output carries memory on the workspace and
memory_grow on a command that grew it.
Resize a workspace (0.9.0+)
Change the memory, CPU and disk of any workspace, running or suspended, fixed or elastic, without a restart:
$ shard ws resize acme/demo --memory 6144 --disk 20
Resized acme/demo (0f6c…), running:
memory 2048 → 6144 MiB now
disk 10 → 20 GiB now
$ shard ws resize acme/demo --elastic --held 1024
Memory changes live, and disks grow online. A suspended workspace gets its new size when it resumes, before its first
call. CPU changes live within the vCPUs the workspace booted with; a larger CPU size applies at the next start. Each
resource says when it applies (now, on resume or next start), and the new caps are stored, so every later start
uses them. --json prints the result with applies_at per resource and caps. A non-null caps.disk_gib is bounded
by the admitted plan and template ceiling and the acknowledged disk size. disk.requested_gib retains the resize request.
Resize controls (0.11.0+): shard ws resize <workspace> --memory 6144 --disk 2 --at-least --no-wait grows only
resources that need more capacity and returns the held server answer with the operation. The normal command waits
for completion. Fixed memory without --memory-mib uses the template default.
At its maximum (0.32.4+): a workspace already uses its full CPU and memory from boot and its disk grows on its own as it
fills, up to the plan's and the template's maximum, so resize to lift caps you set lower, not to speed up a build. A
resource already at its maximum prints already at its maximum (plan limit): nothing changed and what it already does
by itself; --json carries at_maximum per resource.
Burst execution (0.8.0+)
Run one heavy command, such as a cold build or a full test suite, on a larger VM without resizing the workspace:
--burst runs it on a short-lived burst VM over a copy of the workspace and applies its file changes back when it
exits. Output streams as usual and shard exits with the command's exit code.
shard ws exec acme/demo --burst --burst-vcpus 16 -v -- bash -lc "cd app && go build ./..."
--burst-vcpus and --burst-memory size the burst VM (default 8 vCPU and 8192 MiB within the plan's
per-workspace limits); --json adds burst to the result.
Background commands (0.8.0+)
A long command (a build, a test suite, a training run, a server) can run in the background of a processful workspace
while you or your agent do other work:
shard ws exec-start acme/demo --session-id build-1 --timeout 2h -- bash -lc "make -C /home/user/app"
shard ws exec-read acme/demo build-1
shard ws exec-read acme/demo build-1 --follow
shard ws exec-cancel acme/demo build-1 --grace 10s
--timeout is the longest the command may run; the workspace stays awake until it ends or that time passes.
exec-read prints stdout and stderr from the start. --follow resumes from byte offsets after a dropped
connection; Ctrl-C stops following and the command keeps running. --json prints one object with the state, exit
code and output.
exec-cancel sends SIGTERM, then SIGKILL after --grace (default 5s, at most 60s).
Exec survives a dropped connection (0.23.0+): a lost start response is retried with the same session ID and reattaches to the command that already started, and a cut-off JSON answer is retried within the same safe retry boundary.
Pending execution cancellation (0.23.0+)
A successful cancellation can return the actual running state with canceled: true while the kernel still holds
a killed process. The execution's error explains the pending drain, and becomes unverified after 30 seconds
without claiming an exit or freeing ownership. Read or follow that session until its real final status; repeating
the cancellation is idempotent. An already exited foreground keeps its result while descendant drain can remain
pending. The TypeScript and Python agent tools and the MCP server expose the reason as status_reason and keep the
exit code null while active; shard prints the reason and the follow command.
Integration controls (0.7.0+)
shard ws open acme/project-42 --template python-node-browser --labels '{"scope":"project-42"}' --idle-policy never
shard ws list --labels '{"scope":"project-42"}' --json
shard ws labels acme/project-42 --set '{"scope":"project-42","owner":"acme"}'
shard ws idle acme/project-42 --json
shard ws idle acme/project-42 --keepalive 60
shard ws idle acme/project-42 --policy adaptive
shard ws suspend acme/project-42 --when-idle 2s
shard ws exec-start acme/project-42 --stdin-open --session-id worker-1 -- cat
shard ws exec-input acme/project-42 worker-1 hello --offset 0
shard ws exec-input acme/project-42 worker-1 --offset 5 --close
Use the byte offset in each acknowledgement, including partial writes. ws resume can recover a failed workspace with its existing ID and disk. A completed delete frees the key; old IDs remain deleted. Protocol-error JSON includes source; HTTP 404 alone does not mean a workspace was deleted.
Prepare an imminent command (0.9.0+)
Call shard ws hint <workspace> ahead of a tool call to stage a parked workspace while the input is prepared. It returns without waiting for restore. Resume a suspended workspace first. The hint is explicit; ordinary commands add no early hint.
Inbound ports (0.9.0+)
Serve a port of a workspace at its own HTTPS URL: a preview of the app your agent is building, a webhook receiver, an
OAuth redirect, a live view. A request wakes a parked or suspended workspace and is answered once it runs. Every port
is private: a request carries a port token, a browser link's session, or arrives on the port's callback URL.
shard ws exec-start acme/demo -- python3 -m http.server 3000 --bind 0.0.0.0
shard ws port expose acme/demo 3000
curl -H "Authorization: Bearer $(shard ws port token acme/demo 3000)" https://3000-<handle>.shardflux.app/
shard ws port link acme/demo 3000 --path /dashboard
shard ws port callback acme/demo 3000
shard ws port close acme/demo 3000
- The server inside the workspace listens on
0.0.0.0, not only 127.0.0.1.
expose is idempotent: an exposed port keeps its URL, tokens, links and callback URL. port ls lists them.
token prints the token alone on stdout (--ttl 1m to 24h, default 1h). Send it as Authorization: Bearer <token>, or as X-Shardflux-Token when the app reads Authorization itself.
link prints a URL that starts a browser session for the port (--ttl 1m to 7d, default 24h) and lands on
--path. Anyone with the link can open the port until it expires.
callback prints the callback URL once; a request to <url><rest> reaches /<rest> without a token. Running it
again replaces the URL; --revoke removes it.
close stops the port's tokens, links and callback URL at once.
Embed a live viewer (0.11.0+)
Watch the desktop or a port preview inside your web app. Choose 1–10 distinct HTTPS origins, then use the returned URL as the iframe source.
shard ws computer stream acme/demo --embed-origin https://app.example.com
shard ws port link acme/demo 3000 --embed-origin https://app.example.com --embed-origin https://admin.example.com
<iframe src="RETURNED_LINK_URL" title="Watch the agent" style="width:100%;height:600px;border:0"></iframe>
The browser session is partitioned by the top-level site. Each origin is exactly a scheme and host with an optional port, such as https://app.example.com:8443. The viewer permits framing by those origins; interactive viewing uses the same allowlist.
Reverse tunnels (0.11.0+)
Run a foreground TCP tunnel from a workspace to a service on this machine:
shard ws tunnel customer-42/main --remote 8081 --to localhost:8081
Commands in the workspace can then call http://127.0.0.1:8081. The target is dialed on the machine running
shard. Half-close and resets pass through: a reset on either side reaches the other as ECONNRESET (0.14.5+). Ctrl-C closes the sockets and guest process. --bind 0.0.0.0 selects all guest interfaces; loopback (127.0.0.1
and ::1, 0.22.0+) is the default. An open tunnel keeps the workspace awake, and persistent workspaces preserve it across explicit suspend/resume.
The tunnel selects a streaming transport automatically when pty permission is granted. Choose transport: 'pty' or transport: 'exec' in the SDK, or --transport pty|exec in the CLI, to select it explicitly. The handle reports transport.
Bulk transfers batch writes automatically. Active transfers retain their byte position through network reconnection.
Reverse listener recovery (0.24.1+): the foreground tunnel replaces its helper automatically after replay is lost and accepts fresh connections. Keep the command running for callbacks; Ctrl-C closes it.
Computer use (0.10.0+)
A desktop in the workspace for agents that operate GUI software: a screen, a mouse and a keyboard. Switch it on for a
workspace (or every workspace of your template); the platform starts the desktop on the first computer command.
shard ws open acme/demo --template python-node-browser --computer-use on
shard ws exec-start acme/demo -- chromium https://example.com
shard ws computer screenshot acme/demo --out desktop.png
shard ws computer click acme/demo 640 400
shard ws computer type acme/demo "hello" --screenshot after.png
shard ws computer key acme/demo ctrl+s
open "$(shard ws computer stream acme/demo)"
click <ws> [x y] [--button left|right|middle] [--count 2|3] [--mods keys] | A click, double or triple click at a point or at the pointer |
type <ws> <text|-> | Types text (Unicode; - reads standard input) |
paste-text <ws> <text|-> [--paste-chord keys] | Pastes exact text through the clipboard (0.12.0+; see Exact text entry) |
key <ws> <keys> [--repeat n] | Keys and combinations: Return, ctrl+s, alt+Tab, several separated by spaces |
scroll <ws> up|down|left|right [x y] [--amount n] [--mods keys] | Wheel clicks (default 3) |
move <ws> <x> <y>, drag <ws> <x1> <y1> <x2> <y2> | Moves the pointer; drags with the left button |
mouse-down <ws>, mouse-up <ws>, hold-key <ws> <keys> <seconds> | Holds the left button or keys |
zoom <ws> <x0> <y0> <x1> <y1> [--out F] | Saves an enlarged view of a region |
cursor <ws>, wait <ws> <seconds> | Prints the pointer position; waits |
act <ws> <json|-> | Any of these in one batch: a JSON array of actions |
Every input command takes --screenshot F (a screenshot after it, written to F), --settle MS (the wait before
that screenshot, default 250) and --format jpeg --quality N for smaller images; screenshot and zoom take
--format and --quality too.
- The actions are the Anthropic computer-use tool's actions with their parameters (
screenshot, zoom,
left_click, right_click, middle_click, double_click, triple_click, left_click_drag, mouse_move,
left_mouse_down, left_mouse_up, cursor_position, scroll, type, key, hold_key, wait). Each command
above and act print one line per action and exit 1 when one failed (in a batch the rest are skipped).
stream prints a link to the viewer (view only unless --interactive; --ttl 1m to 7d, default 24h); anyone with
it can watch until it expires. stream --stop ends every view. From 0.11.0, stream reuses a private viewer link
with more than 60 seconds left across CLI invocations; --fresh mints a new link, and --stop clears it.
ws computer enable|disable|inherit sets the workspace's own switch (inherit follows the template);
templates computer-use <slug> on|off sets it for every workspace of one of your templates.
The computer use guide covers action results and driving the
desktop from your agent's tool calls.
Explicit pixel completion (0.12.0+)
Pass the caller's predicate JSON in PREDICATE_JSON:
shard ws computer key acme/demo Return --screenshot result.png --pixel-predicate "$PREDICATE_JSON"
An unmatched condition exits 1 and writes no successful screenshot.
Explicit predicates are enabled when supplied; no environment switch is required. Omitted predicates retain ordinary 250 ms settling.
Require an appended lossless PNG and omit settle_ms. Digests cover row-major RGBA8 bytes including cursor pixels. The exact matched full frame is encoded and returned. A condition already satisfied before input refuses the batch unless allow_pre_satisfied explicitly allows that semantics. Timeout returns no success image; input may already have run, so inspect input results before retrying. Use a known, semantically unambiguous result. A pixel condition cannot certify unpainted backend state or infer completion from focus or quiet.
Exact text entry (0.12.0+)
pasteText replaces the clipboard with UTF-8 text and sends the chosen paste chord. Use shift+Insert for xterm. Ordinary type sends line breaks as Return. Start with all keys released. Clipboard replacement remains in effect after cancellation; see computer use for action results.
Use shard ws computer paste-text <key> - --paste-chord shift+Insert --screenshot after.png with text on standard input. Leading-dash positional text goes after --.
Git in workspaces (0.12.0+)
shard ws exec runs Git like any other command; from 0.12.0 this package follows SDK 0.17.0, whose git_clone tool
takes the options below.
- Blobless clones.
shard ws exec <key> -- git clone --filter=blob:none -- https://github.com/python/cpython.git /home/user/cpython
fetches the current checkout files now and historical blobs from origin later. Plain and shallow clone defaults
remain unchanged.
- Treeless clones. Set
filter: "tree:0" (Python: filter="tree:0") to fetch the requested checkout and commit
graph now, with historical trees and blobs fetched from origin as needed. CLI callers use
git clone --filter=tree:0 through shard ws exec.
- Parallel checkout. Set
parallelism (1–16) to choose Git indexing threads and checkout workers for a clone. The
git_clone tool forwards it; CLI callers use git -c pack.threads=4 -c checkout.workers=4 clone ... through
shard ws exec.
See agent tools for clone options.
Secrets
printf %s "$OPENAI_API_KEY" | shard secrets create OPENAI_API_KEY
shard secrets create DATABASE_URL --from-file ./database-url.txt
shard secrets create GITHUB_TOKEN --ask
shard ws open acme/demo --template python-node-browser --secret OPENAI_API_KEY --secret DATABASE_URL
shard ws exec acme/demo -- python3 agent.py
shard ws secrets acme/demo
printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
- Secret values are read only from standard input,
--from-file (one trailing newline is
removed) or the --ask page. shard refuses --value, --password and similar flags, and never prints a value.
- Bound secrets reach every
exec and terminal of the workspace. A name that is unknown, or a
secret the workspace may not use, is refused (exit 2, secret_not_available with the names).
- Secrets are project secrets of the API key's project by default.
--org <id> addresses
organization-wide secrets; those, and secrets access-log, belong to organization owners and
admins in the console, so a project API key gets 403 (exit 4).
<id|NAME> is tried as an id when it is a UUID; otherwise it is matched by name in the project
(or in the organization with --org).
Egress and volumes (0.5.0+)
These use the project API key, like the other workspace commands.
shard egress get [--project <id> | --workspace <ws>]
shard egress set --file policy.json [--project <id> | --workspace <ws>] [--if-version N]
shard egress history [--project <id> | --workspace <ws>]
shard egress clear --workspace <ws>
shard volumes list [--include-org-shared] | create <name> --quota-gib N [--org-shared] | get <id|name>
shard volumes attach <ws> <volume> --mount /mnt/data [--read-only] | detach <ws> <volume>
shard volumes attachments <id|name> | delete <id|name> [--force] --yes
A policy file is {"mode": "allow_all" | "allowlist" | "deny_all", "rules": [{"host": "api.github.com", "ports": [443]}], "cidrs": []}. Volume commands wait until the volume change is applied unless --no-wait. Volume
lists and details (0.14.0+) show kind.
Start deadlines
A queued open, resume or fork (capacity_pending) has a deadline 15 minutes after it was created.
While shard waits for it, stderr says where it stands (0.16.0+), again whenever that changes (dim on a terminal,
never with --json):
queued for capacity (position 3); it holds its place until 21:51Z
Without --timeout, a queued start is waited for until its deadline (0.16.0+). An explicit --timeout is kept: if it
ends first, shard exits 5 and the timeout error names the deadline and the queue position (--json:
deadline_at, queue_position, last_reason). A start still pending at the deadline fails with
capacity_unavailable: nothing was started, and a suspended workspace stays suspended. shard exits 6 as for any
failed operation, the error says retryable=true (with --json, "retryable": true), and the hint says to run the
same command again. A definitive failure has retryable=false.
Timing
Host metric units (0.30.1+): host metric text honors explicit microsecond/millisecond units and historical millisecond phases. Counters, flags, and unknown metrics print as numbers. --json retains the raw numeric host metrics.
--timing (0.3.0+) says where the time of an open or a wait went. After the call, shard prints the lifecycle
timing that @shardflux/sdk measured (its formatTiming()) on
stderr. It is printed when the call fails, times out or is interrupted too, before the error, and the exit code does
not change. A resume reads, for example:
$ shard ws resume acme/demo --wait --timing
Resuming acme/demo (01a0ead6-92e5-7d19-ab7a-f565bf4676cb): operation 01a0ead6-bd61-737a-ae88-66e2d01a6e25 resume succeeded
resume 413 ms, succeeded (workspace 01a0ead6-92e5-7d19-ab7a-f565bf4676cb, operation 01a0ead6-bd61-737a-ae88-66e2d01a6e25)
client: request 218 ms → queued 195 ms
server: queued 51 ms, ran 290 ms, total 341 ms; resume from local_cache, boot to ready 211 ms, host disk 0 ms, load 6 ms, ready 62 ms, total 211 ms, after_restore 36 ms
outside the server: 72 ms
This resume brought the workspace back with its memory and running processes in 341 ms on the server and 413 ms end
to end.
- client: phases on your clock.
request is the API call; (held) means the API held it until the workspace
was ready, up to 20 s at a time. Then come the operation states seen while waiting, with the server's reason, and
reading the workspace.
- server: the operation's own timing.
queued runs until the operation began running, including any wait for
capacity; ran is the platform's work. Then the start or resume path the platform reported.
- outside the server: your total minus the operation's, so network, polling and the final reads. A large value
with a small server total points at the connection, not at the workspace.
- retries: transient failures that were retried.
Which call is timed:
workspaces open, templates test and templates draft open|test: the open, including the wait unless --no-wait.
templates build (0.4.0+): packing, uploads, the request and the wait, then the build's own queued and run time.
workspaces suspend|resume|fork|delete|close|reset and templates draft state|discard: with --wait, the request
and the whole wait; without it, the request.
operations wait: the wait.
workspaces exec, files read|write|ls|stat|search|patch and workspaces changes: the wake, when the command had
to wake a suspended workspace. Without a wake nothing is printed.
workspaces exec on a file-first workspace (0.5.0+): the execution's own timings as the platform reports them
(execution <id> timings: blobs_fetched=... exec_ms=... publish_ms=... queue_ms=... run_ms=... total_ms=...);
nothing is woken.
With --json, stdout carries the timing as a timing field of the command's JSON instead: action, outcome,
workspace_id, operation_id, total_ms, phases (phase, reason, operation_id, start_ms, duration_ms),
retries, server (queued_ms, run_ms, total_ms, start_path, resume_path, memory_restored and
cold_boot_reason (0.6.0+; memory_restored is false when a resume booted the saved disk, null when the API did
not say), host_lost (0.8.1+), boot_to_ready_ms, ...) and outside_server_ms. It is null when the command made no lifecycle call. On a failure the error document on stderr
carries it: {"error": {...}, "timing": {...}}.
For live progress events, use the SDK's onProgress.
Output
-
Human (default): aligned tables and key/value blocks on stdout. Errors go to stderr as
error: <message> (<reason>) [code=... status=... request_id=... operation_id=... execution_id=...], with a
hint when there is one. A failed operation adds retryable=true|false (0.3.1+), and so does a failed or lost
execution (0.5.0+). A failure ends with a feedback: shard feedback ... line carrying the request id and error
code (0.5.0+), except the expected flow: usage and validation errors, Ctrl-C, a missing or refused credential, a
sign-in or step-up to finish, a wait that gave up, and refusals whose hint names the next step.
-
--json: stdout carries the API's own objects:
workspaces open/get print the workspace view; list prints {data, next_cursor}.
- Lifecycle commands print the operation;
fork prints {operation, workspace}.
login/whoami print {api_url, organization_id, project_id, api_key:{id,key_id}, tool_permissions}; whoami
adds warnings (code, message, fix) for a departed organization or a key in another accessible default
organization (0.22.0+). Without a signed-in session, membership and default warnings are unavailable.
exec prints {session_id, exit_code, term_signal, timed_out, canceled, stdout, stderr, truncated, reconnects}.
On a file-first workspace (0.5.0+), exec and executions get print the execution (see
File-first workspaces).
files read prints {path, bytes, content_base64}; files write prints the write result. On a file-first
workspace files write and files patch add tree_revision (0.5.0+).
files stat prints the file info (with revision when asked); files search prints
{matches, truncated, stop_reason?, files_scanned, served_from}; files patch prints
{path, revision, previous_revision, bytes_written, durable, replacements?, file?}.
secrets create/get/update print the secret's metadata; list, versions and access-log
print {data, next_cursor}; rotate prints {secret, version}; delete prints
{deleted, id, name, scope}; workspaces secrets prints {workspace_id, names, secrets}.
workspaces port expose prints the port {port, url, created_at, callback}; port ls prints {ports};
port token prints {token, expires_at, url} (jq -r .token); port link prints {url, expires_at};
port callback prints {url, created_at}; port close prints {workspace_id, port, closed} and
port callback --revoke prints {workspace_id, port, revoked} (0.9.0+).
- Account commands (0.5.0+) print the API's objects too: lists are
{data, next_cursor} (with
default_organization_id, default_project_id or saved_api_key_id where a default applies);
auth login/auth mfa print {status, user, session: {expires_at, credentials_file}, organizations, next} (never the token; auth login --browser prints NDJSON events instead, see above); auth status prints {api_url, credentials_file, session, api_key, defaults};
setup prints {organization, project, api_key: {id, key_id, name, project_id, tool_permissions}, created: {organization, project, api_key}, credentials_file, next}; api-keys create prints
{api_key, secret, saved: false} (or {api_key, saved: true, credentials_file} with --save);
billing upgrade prints {subscription_exists: false, organization_id, plan_key, checkout, url, opened, subscription_active} or {subscription_exists: true, organization_id, plan_key, portal_url, opened};
exports print {export, out, bytes}; env prints {shell, variables}; version prints
{cli, sdk, update}.
Errors go to stderr as {"error": {code, message, status, request_id, ...}}. A failed operation is
{"error": {"code": "operation_failed", "message", "operation_id", "retryable", "details": {"error_code", "reason"}, "operation"}}; retryable (0.3.1+) is the operation error's own flag. A failed or lost execution
(0.5.0+) is {"error": {"code": "execution_failed", "message", "execution_id", "retryable", "details": {"reason", "error_code", "state"}}} after the execution on stdout; one still running (executions get) is
{"error": {"code": "timeout", "message", "execution_id", "last_state"}}.
The same failures that print a feedback: line add a "feedback" field next to "error" with that command
(0.5.0+).
feedback prints {id, received_at, duplicate}.
-
--timing: the timing goes to stderr after the output. With --json it is a timing field instead
(see "Timing").
API budgets
Plan-derived API budgets (0.23.0+, SDK 0.27.0): --json error output includes rate_limit and
retry_after_seconds; the text hint names the shared scope and delay. API request pacing follows the SDK admission
timeout and preserves the request body. Reuse workspace credentials and reduce status polling when the returned
budget asks for a delay.
Credential key pacing (0.23.2+): when the plan has API budgets, workspace credentials share organization and
workspace hourly allowances, plus a separate API-key burst. A tool_token_budget refusal can name api_key; honor
Retry-After and reuse valid credentials. A refusal does not promise that lifecycle work has not started, so it never
authorizes replay of an arbitrary POST.
Exit codes
| 0 | success |
| 1 | error: an API refusal such as conflict, quota_exceeded or workspace_not_running, or a network or protocol failure |
| 2 | usage error: bad arguments, or input the API rejected as validation_failed |
| 3 | not found: workspace, operation or route |
| 4 | authentication or authorization: missing, malformed, revoked or insufficient key or session; step_up_required (run shard auth step-up), mfa_required (a sign-in waiting for its code), email_unverified, invalid_credentials; access_denied or expired_token from auth login --browser (0.15.0+) |
| 5 | timeout: waiting gave up (--timeout, or --wake-timeout while waking a workspace); the operation continues server side (shard operations wait <id>), up to a queued start's deadline (deadline_at). Without --timeout, a wait on a start queued for capacity lasts until that deadline (0.16.0+). executions get (0.5.0+): the execution is still queued or running. ws exec-read (0.8.0+): the background command still runs. billing upgrade --wait (0.5.0+): nobody paid in time (the checkout stays open) |
| 6 | the awaited operation ended failed or canceled (also a resume or open that failed while waking a workspace). retryable=true in the error means a retry can succeed (capacity_unavailable). Also a file-first execution that ended failed or lost (0.5.0+): nothing was published. exec (0.5.1+): the command could not start (exec_failed_to_start). billing upgrade --wait (0.5.0+): the checkout expired or was canceled (checkout_ended) |
| 130 | interrupted (Ctrl-C). Waits stop at once, and exec sends a cancel to the session. A file-first execution runs to completion, and the error names the shard executions get command for its result. |
| exec, executions get, ws exec-read | the command's own code: 124 if it timed out, 128+N if killed by signal N. Failures before the command ran use the codes above; use --json to tell them apart. |
Canceled exec commands exit 130, timeouts exit 124, and signal exits use 128 + signal (0.11.0+). For error codes,
retry timing, plan recovery, and disk growth actions, see the
error reference.
HTTP connections
shard 0.7.0 uses the SDK’s pooled HTTP/1.1 transport on Node 26. Custom fetch is preserved. SHARDFLUX_HTTP_KEEPALIVE=0 forces connection closure for diagnosis; =1 opts into native pooling. From 0.11.0 the SDK transport honors the environment's HTTP_PROXY, HTTPS_PROXY and NO_PROXY.
Credentials file
(0.5.0+) shard saves your session and API key in a credentials file. SHARDFLUX_API_KEY is read on every run and
wins over the file; the file is only written by the commands that sign in or save a key (auth login, auth mfa,
setup, api-keys create --save, and the session renewals of step-up and password changes).
-
Where: $SHARDFLUX_CONFIG_DIR/credentials.json, else $XDG_CONFIG_HOME/shardflux/, else
~/.config/shardflux/ (Windows: %APPDATA%\shardflux\).
-
Protection: the directory is created with mode 0700 and the file 0600, written atomically (a
temporary file, then a rename). shard warns when the file is readable by others. A file that is not
valid JSON (or not this format) is never overwritten silently: commands that read it stop with exit 2
naming it, and a command that writes credentials first moves it to credentials.json.corrupt-<time>.
-
Profiles: one per API URL, so production, staging and a local API never mix:
{"version": 1, "profiles": {"https://api.shardflux.dev": {
"session": {"token": "sfu_...", "expires_at": "...", "user": {"id": "...", "email": "..."}, "state": "active"},
"organization_id": "...", "project_id": "...",
"api_key": {"token": "sfk_...", "id": "...", "key_id": "...", "project_id": "...", "name": "shard-cli laptop"}}}}
-
Precedence: the project API key is SHARDFLUX_API_KEY, else the saved api_key. The session is
SHARDFLUX_SESSION_TOKEN, else the saved session. shard auth logout removes the session (the key
stays); delete the file to forget everything.
-
Never printed: the session and the saved key appear only in the file. Output shows key ids; error
output redacts anything shaped like a key or session token. shard env prints the key on purpose.
Environment
SHARDFLUX_API_KEY | Project API key (sfk_<key id>_<secret>) for the workspace commands. Default: the key shard setup saved. shard never accepts a key as an argument (--api-key, --token and similar flags are refused) and never prints it (except shard env); anything shaped like a key is redacted from error output. |
SHARDFLUX_SESSION_TOKEN | User session (sfu_...) for the account commands (0.5.0+). Default: the session shard auth login saved. |
SHARDFLUX_API_URL | API base URL. Default https://api.shardflux.dev; --api-url overrides it. Plain http:// is allowed only for loopback hosts. Credentials are saved per API URL. |
SHARDFLUX_BASE_URL | Alias of SHARDFLUX_API_URL (0.11.0+); SHARDFLUX_API_URL and --api-url take precedence. |
SHARDFLUX_CONFIG_DIR | Directory of credentials.json and update-check.json (0.5.0+). Default $XDG_CONFIG_HOME/shardflux, else ~/.config/shardflux (%APPDATA%\shardflux on Windows). |
SHARDFLUX_NO_UPDATE_CHECK=1 | Never ask whether a newer shard exists (0.5.0+; also true, yes, on). NO_UPDATE_NOTIFIER=1 does the same. |
SHARDFLUX_AGENT_LABEL | Attribution label for the tool tokens shard obtains for exec and files, and the reporting agent of shard feedback (0.5.0+). Default cli; --agent-label overrides it. Each label is one agent session, listed by shard ws sessions. |
SHARDFLUX_NO_WAKE=1 | Do not resume a suspended workspace on use; same as --no-wake (see "Suspended workspaces" above). --wake overrides it. |
SHARDFLUX_WAKE_TIMEOUT_MS | Longest wait, in milliseconds, for a workspace to wake or finish a transition. Default 120000; --wake-timeout <ms> overrides it. |
SHARDFLUX_HTTP_KEEPALIVE=1 | Reuse HTTP connections (see "HTTP connections" above). |
BROWSER | Program --open runs with the URL (0.5.0+). Default xdg-open, open (macOS) or rundll32 (Windows). |
Update
shard update (0.13.5+) brings everything Shardflux installed on this computer up to date: shard itself (the
shardflux package or @shardflux/cli), its SDK and a globally installed @shardflux/mcp. It runs one install of
the latest versions with the package manager that installed shard (npm, pnpm, bun or Volta). Right after a release,
while npm is still rolling it out, it waits and tries again until the install goes through (0.32.5+):
npm is still rolling out shardflux 0.34.4 (published 2 min ago). Trying again in 15 s.
Updated shardflux 0.34.3 → 0.34.4 · shard 0.32.4 → 0.32.5.
shard update --check
shard update
Projects that depend on @shardflux/sdk or the Python shardflux package keep the versions their lockfiles pin.
Version check
shard tells you when a newer version exists, on stderr after the command's output:
@shardflux/cli 0.23.0 is outdated: 0.23.1 is available. Update: shard update
or when the API needs a newer one (...: the API needs 0.6.0 or later (latest 0.23.1). Update: shard update). It asks
GET /v1/client-versions at most once a day per API URL (cached in
<config dir>/update-check.json), in parallel with the command: a slow answer is skipped (the next run
uses it), the exit code never changes, and nothing is printed after a command that failed with
--json. Installed through the shardflux npm package it checks shardflux instead. shard version
shows the status; shard version --check asks now (--json: {"cli", "sdk", "update": {"package", "current", "status", "latest", "minimum_supported", "upgrade_command", "checked_at"}}). Turn it off with
SHARDFLUX_NO_UPDATE_CHECK=1 (or NO_UPDATE_NOTIFIER=1). The SDK's own check is off inside shard.
Support and bug reports
Report a bug or
request a feature.
Include the package and runtime versions, a minimal reproduction, expected and actual behavior, and the request ID
or error code when available. Reports are public: leave out secrets and confidential data.
For usage, account, billing, or private support, email shardflux@heliosone.fi.
Report vulnerabilities through private security reporting.
See the support guide for all reporting options.
Compatibility
The API is versioned (/v1). Breaking changes ship only in minor releases and are marked Breaking in the
changelog.
License
Apache-2.0
Saved site logins (0.32.4+)
Sign in with shard auth login to manage the same encrypted login store your agents use.
shard credentials logins
shard credentials add https://example.com --username me@example.com --ask
shard credentials import --from 1password --item example-login --site https://example.com
shard credentials export <login-id> --to hsec
shard credentials show <login-id>
shard credentials rm <login-id>
Add opens a secure page or reads a password from piped stdin. Scope defaults to all your agents; choose
--scope project|conversation --scope-id <id> to narrow it. Use --org <id> when you belong to several
organizations. logins prints metadata. show reveals on a terminal; an explicit redirected export uses
--json --reveal. Import and export support hsec, 1Password, Bitwarden and macOS Keychain; values travel
through stdin and the store records the local vault item. Keychain carries the account and password, with a
TOTP seed in an encrypted companion item. credentials list lists pending requests across your projects.
Your device helper copies agent-created logins into your chosen local vault every three minutes.
shard credentials vault shows the vault and any wait; shard credentials vault --set auto|hsec|1password|bitwarden|keychain|none
chooses it. Automatic uses hsec when it is installed and unlocked. A locked vault waits visibly and resumes
after unlock. hsec gets the password under <HOST>_<USERNAME> and the authenticator seed under <ITEM>_TOTP;
Keychain gets the account and password. Existing items are preserved with a distinct name for the new copy.