Reference
The rystic CLI
Installation is the walkthrough. CLI flag reference lists every command and flag, generated from command help. Docker covers running without a local CLI: image defaults, Compose overrides and environment variables.
Install
curl -fsSL https://www.rystic.ai/install.sh | sh
rystic login rys_… # an API key from /account/api-keys (+ docker login)
rystic login --token-stdin < key.txt # the same, kept out of shell history
rystic login ./rystic-license.json # Enterprise: activate a license file (+ docker login)
- The CLI is the downloader, not the product — anyone can install it. Simulator binaries and images need an API key or, on Enterprise, a license.
- macOS and Linux; Windows zip on request.
RYSTIC_BINoverrides where it lands. login rys_…stores the key at$RYSTIC_HOME/token, readable by you only, and never prints it.login <file>verifies a license offline and parks it in$RYSTIC_HOME. Either runsdocker login registry.rystic.aiwhen a runtime answers;--no-dockerskips that half. Plans and API keys covers both.- After a license login, on a terminal and with
$CIunset,loginoffers a numbered menu of simulators to start, each exactly asrystic run <product> -d --scenario <its quickstart scenario>; a busy port retries once on a free one.--no-startskips the offer, and so does any non-terminal or CI run.
Commands
| Command | What it does |
|---|---|
login <rys_key | file> | store an API key, or install an Enterprise license file, and log Docker in to the registry with it |
docs [topic] | search <terms> | Rystic’s docs, offline: the site’s pages as of this CLI’s release, plus each installed simulator’s HOWTO.md and SCENARIOS.md |
run [product] | run a simulator (pulls it if missing); -d detaches, args after -- go to the simulator |
ui [product] | run a simulator’s verified UI against a running instance: the <product>-ui image through the registry when Docker answers, else its archive on your Node; Pro and Enterprise |
ps | list running simulators — status from a live probe, not the record |
stop <name>… | --all | stop instances (SIGTERM, then SIGKILL after -timeout) |
restart <name>… | --all | stop and start again from the recorded spec |
rm <name>… | remove a stopped instance’s record and log |
logs [-f] <name> | print a detached simulator’s log; -f follows |
tail [-addr url] [-events …] | stream a running simulator’s state events to this terminal |
up [-d] [-f rystic.yaml] [svc…] | start every service of a stack file — each exactly one run -d |
down [-f rystic.yaml | --project p] | stop every instance of a stack file’s project |
export compose|antithesis [-f] [-o dir] | a digest-pinned docker-compose.yaml (+ .env.example), or the Antithesis scaffold |
scenario save <instance> <file> | a running instance’s state as a scenario file that run --seed and POST /_rystic/seed take back; --force replaces an existing file |
status | API key, license, runtime, registry, installed, running, updates — one screen |
list | released simulators — latest, installed, running (reads a license file) |
pull [product] | download and install a simulator release without running it |
update | install a newer simulator release, or just ask whether one exists |
check | boot an installed simulator and confirm it serves and resets |
doctor [product] | check this machine end to end without running anything |
features | list what the installed simulator can do |
config [set k v] | show every setting with its source, or set one |
license [activate <file>] | Enterprise: show the active license, or install one |
mcp [install --client c] | serve MCP tools on stdio so a coding agent drives simulators, or write the server into a client’s config (guide) |
feedback [message] | send Rystic a bug, friction or feature request, filed under your account |
self-update | replace this CLI with the latest release |
version | this CLI’s version, then every running simulator’s — warning on any drift |
rystic help <command> prints a command’s flags on stdout. An older CLI prints them on stderr; rystic help <command> 2>&1 works on either.
--json prints {schema_version, command, status, data, warnings, error}. Only run, ps, restart, up, status, list, version, config, report, docs and scenario save take it; the rest refuse it (exit 2).
Simulator-specific commands
These only apply to a simulator whose manifest declares the feature — today the Kalshi simulator. rystic features lists what the installed build supports.
| Command | What it does |
|---|---|
run --live / feed <product> | feed a simulator from the exchange as it moves — Live mode |
logs --feed <name> | a detached instance’s feeder log |
batch [--runner docker --image <ref>] | run a bot against every tape in a library — Backtesting |
report <batch-out-dir> [-sort col] [-csv] | the batch P&L table — exact totals, settled vs left open |
grid [product] [-- <bot command>] | the bot × scenario matrix — bundled example bots plus yours |
run
rystic run stripe-twin # serves :8080, streams events
rystic run stripe-twin -d --name a --port 0 # detach on a free port
rystic run stripe-twin --scenario design_studio # boot into a named scenario
rystic run stripe-twin --seed ./scenario.json # seed file, applied before the first request
rystic run stripe-twin --runner docker -d # as a container, from registry.rystic.ai
rystic run stripe-twin -- --events=all # args after -- go to the simulator
- CLI flags can appear before or after the product name; anything after
--goes to the simulator. - Default port 8080; a busy port is exit 9;
--port 0picks a free one. --runner dockerneeds a container runtime. The image comes from the registry, or is built locally from a pulled Linux archive whenimage_source=archiveor the registry is unreachable. See Docker.- Replay (
--tapes,--tape,--copies,--speed) and--liveare Kalshi features: Replay, Live mode. - On Free and Pro,
runhands the simulator your API key and the simulator checks in with Rystic while it serves.-vprints one line per check-in. On Free,--networkexits naming thefaultsfeature the plan lacks: Boot inputs.
up, down, export — the stack file
# rystic.yaml — every key is a run flag; the service name is the instance name
version: 1
project: demo
services:
payments: {product: stripe-twin, port: 0, scenario: design_studio}
reviews: {product: github-pulls-twin, port: 0, seed: ./seeds/repo.json}
exchange: {product: kalshi-twin, runner: docker, port: 0, scenario: thin_book}
File paths are relative to the stack file. On Free, a service may set no network. Kalshi-only keys (tapes, tape, copies, live and the live_* family) are listed on the Kalshi page. The Docker builder writes this file for you.
rystic up -d # one identity line per service
rystic ps --project demo
rystic down
rystic export compose -o ./deploy # digest-pinned compose + .env.example
rystic export antithesis -o ./anti --tenant <t> --gcp-project <p>
A stack file never carries a key or license. A malformed one is refused naming the file, service and key. export warns when a tape: cannot be expressed in Compose.
ps, stop, logs, status, tail
rystic ps # NAME PRODUCT VERSION RUNNER URL MODEL LICENSEE UPTIME STATUS
rystic stop a b # or --all
rystic logs -f a
rystic status # --offline skips the network
status prints the registry line — registry.rystic.ai · logged in · reachable. A token: line shows the API key masked (rys_AAAA…), where it came from, and the organization and plan the site reports; --offline prints not checked instead. A license file adds a license: line. With neither, it prints credential: none and the rystic login line to run. --json carries both as token and license, never the key itself.
rystic tail -addr http://localhost:53124 # a detached instance on another port
rystic tail -events all # public market data too
tail reads the simulator’s GET /_rystic/events stream. -addr defaults to http://localhost:8080.
doctor
rystic doctor stripe-twin
The pre-demo check, no boot: the CLI and its home, configuration, the install, signing-key agreement, simulator data, container runtime, registry login — and, for a simulator with a live feed, the exchange key and where it came from. Exits 0 when no line shows ✗.
doctor checks the credential the installed build runs on. On an API key:
✓ api key rys_AAAA… · from /Users/you/.rystic/token · org <org> · <plan> plan · www.rystic.ai answered
✗ api key plan means the plan does not include that product; a refused key is ✗ api key with the exit-11 sentence, and doctor exits 11. If the site does not answer, the line is a ! warning and the simulator checks the key when it starts. With a license file, doctor checks the license instead and reports any stored key as api key (not in use), a warning at most. rystic check <product> boots the installed build once to prove it.
docs
rystic docs # numbered index: the site's pages, then each installed simulator's HOWTO and SCENARIOS
rystic docs agent-quick-start # one page as Markdown
rystic docs cli#exit-codes # one section
rystic docs stripe-twin # the installed HOWTO.md, plus the live routes of each running instance
rystic docs kalshi-twin scenarios # the installed SCENARIOS.md
rystic docs search rate limit # every section holding all the terms, as <topic>#<anchor>
rystic docs -online cli # read the live site instead of the built-in snapshot
The site’s pages are a snapshot built into the CLI when it was released, so rystic docs works offline and needs no key; a page added since needs -online. A product’s short name works too (rystic docs stripe); with nothing installed it falls back to the site’s page for that simulator. -json prints the envelope, -n caps search results. An unknown topic, section or SCENARIOS.md exits 5 and says what to try instead. Stripe ships no SCENARIOS.md.
list, pull, update
rystic list # PRODUCT LATEST INSTALLED RUNNING SUMMARY (on an API key or a license file)
rystic pull -version <x.y.z> stripe-twin # omit for latest
rystic update # the CLI and every installed simulator
rystic update -check # report only; exit 10 if newer ones exist
pull installs into $RYSTIC_HOME/twins/<product>/<version>, checksum-verified, marked current. -os linux -arch amd64 fetches another platform’s build (the air-gap Docker path).
config
rystic config # every setting, its value and where it came from
rystic config set runner docker # flag > env > config.json > built-in
| Key | Env | Built-in |
|---|---|---|
runner | RYSTIC_RUNNER | native |
port | RYSTIC_PORT | 8080 |
tapes | RYSTIC_TAPES | — (Kalshi replay library) |
image_source | RYSTIC_IMAGE_SOURCE | registry (archive builds locally) |
registry | RYSTIC_REGISTRY | registry.rystic.ai |
docker.binary | RYSTIC_DOCKER | docker, else podman |
docker.host | DOCKER_HOST | — |
release_endpoint | RYSTIC_RELEASE_ENDPOINT | https://www.rystic.ai/api/twin-release |
catalog_endpoint | RYSTIC_CATALOG_ENDPOINT | https://www.rystic.ai/api/catalog |
docs_endpoint | RYSTIC_DOCS_ENDPOINT | https://www.rystic.ai (what docs -online reads) |
token | RYSTIC_TOKEN | — (an API key; rystic config shows only (set) and where it came from) |
config set token is refused: edit config.json or use rystic login.
Files and environment
Everything lives under $RYSTIC_HOME (default ~/.rystic): installed simulators, the current marker per product, token (the API key login stored) or the active license, config.json (once you config set something), and run/ — one record per instance.
| Variable | Meaning |
|---|---|
RYSTIC_HOME | install root |
RYSTIC_PRODUCT | product to use when no product argument is given; set by the image |
RYSTIC_TOKEN | an API key; checked before config.json and the stored token, and how containers get it |
RYSTIC_LICENSE | Enterprise: a license file path — or the license itself (JSON or base64), which is how containers get it |
RYSTIC_REGISTER_WAIT | how long a simulator retries reaching Rystic at start, default 10m; 0 tries once |
RYSTIC_LEASE_URL | the lease server a Free or Pro simulator checks in with, default https://lease.rystic.ai |
RYSTIC_LEASE_CACHE | where a simulator keeps its last lease for an offline start, default rystic/leases under your config directory |
RYSTIC_LEASE_VERBOSE | true prints one line per check-in; run -v sets it |
RYSTIC_DOCS_ENDPOINT | the site docs -online reads |
RYSTIC_CLI_ENDPOINT | where update and self-update look for a newer CLI, default https://www.rystic.ai/api/cli-release |
RYSTIC_LICENSE_PUBKEY | a license public key to verify against instead of the built-in one (-pub on the command line) |
DOCKER_CONFIG | where status and doctor look for your Docker login, as Docker does; only the entry names are read |
CI | any value suppresses login’s offer to start a simulator |
RYSTIC_BIN | installer: where it puts rystic |
RYSTIC_VERSION | installer: which CLI version to install, default latest |
RYSTIC_TOKEN, RYSTIC_LEASE_URL, RYSTIC_REGISTER_WAIT, RYSTIC_LEASE_CACHE and RYSTIC_LEASE_VERBOSE reach the simulator, including a --runner docker container, when they are set.
The simulator binary also reads these variables. With the default image command, rystic run inherits them or sets them from the corresponding CLI flags:
| Variable | Meaning |
|---|---|
RYSTIC_ADDR | direct simulator listen address (--addr); rystic run sets it from --port / RYSTIC_PORT |
RYSTIC_SCENARIO | boot into a named scenario (--scenario) |
RYSTIC_SEED | boot seed file (--seed) |
RYSTIC_NETWORK | off, on, or a profile file (run --network); Pro and Enterprise |
RYSTIC_EGRESS_RETAIN | egress frames kept, default 100000 (run --egress-retain) |
RYSTIC_EVENTS | event feed scope (--events) |
RYSTIC_SEED_GATE | reject refuses an incomplete seed instead of serving it with a warning |
RYSTIC_GATEWAY_TOKEN | for a simulator behind your own proxy: the bare binary, or a container with a health check of its own. Every request, /_rystic/version included, must carry X-Rystic-Gateway-Token with this value or gets 403. rystic run’s readiness probe and the image’s HEALTHCHECK send no header, so run -d times out (exit 7) and the stock image never reports healthy |
Control plane and plans lists every simulator variable, its default and which ones a plan gates.
Kalshi-only variables — RYSTIC_LIVE*, KALSHI_KEY_ID, KALSHI_PRIVATE_KEY_*, KALSHI_CAPTURE_DIR — are on the Kalshi page.
Exit codes
Stable — script against them.
| Code | Meaning |
|---|---|
| 0 | ok |
| 1 | error |
| 2 | usage |
| 3 | license (Enterprise): missing, unreadable, unsigned, expired or for another product. Also no license and no usable API key (none, or not shaped rys_…) when pull or update asks for a build, or run does because the simulator is not installed yet |
| 4 | network |
| 5 | not found: not installed, no matching release asset, or no such doc, section or SCENARIOS.md (docs) |
| 6 | integrity |
| 7 | simulator unhealthy |
| 8 | runtime unavailable |
| 9 | conflict (port or name in use, or the file scenario save would write exists) |
| 10 | update available (-check) |
| 11 | API key: missing or malformed when run starts an installed Free or Pro build, or refused by the release endpoint: invalid_token, revoked, org_deleted, key_expired, product_not_in_plan. A new key or a bigger plan fixes it; a retry does not |
A simulator stopped by its lease (monthly limit, spend cap, revoked key) exits 1 with the reason on stderr, and a foreground run hands that back.
Next
- Docker — pull, compose, CI.
- Simulators — one page per API: Anthropic, Clerk, Dropbox, GitHub, Gmail, Google Drive, Kalshi, Linear, Lob, Notion, Okta, PayPal, Resend, S3, Shopify Storefront, Slack, Stripe and Word.
- Control plane and plans — every
/_rysticroute and the plan that serves it.
What leaves your machine
On Free and Pro, the simulator checks in with https://lease.rystic.ai while it runs: at start, every 30 seconds, and on exit, carrying your API key and the 30-second windows in which it was in use. Never your requests, their contents, your data or your hostname. Plans and API keys lists every field. An Enterprise simulator makes no outbound calls. (One exception, Enterprise included: the Kalshi simulator’s replayed-market metadata fetch, documented on its page.)
The CLI itself calls Rystic only in these cases:
| When | Where | What it sends |
|---|---|---|
pull, update, status (its update check; --offline skips it), run or up when a simulator is not installed yet (for a Docker service, only to learn the latest version), and ui when its verified UI is not installed yet | https://www.rystic.ai/api/twin-release | your API key or license, the product, version, OS, architecture, which command asked and, for ui, artifact: "ui" |
list, and the start offer after a license login | https://www.rystic.ai/api/catalog | the license |
update and self-update | https://www.rystic.ai/api/cli-release | OS, architecture and the version asked for; no credential |
update, self-update and install.sh, after cli-release answers | https://release-assets.githubusercontent.com | nothing: a download of the short-lived URL cli-release returned |
login with no key (which install.sh runs) | https://www.rystic.ai/api/device/status | the API key the CLI generated, polled until someone approves its /device link; the link you open carries only the key’s hash, its prefix and a name for the machine |
doctor and status (not with --offline) | https://registry.rystic.ai/v2/ | nothing: an anonymous request to see whether the registry answers |
login | registry.rystic.ai, through your local docker login | the key or license as the registry password |
run --runner docker, up with Docker services, and ui when Docker answers | registry.rystic.ai | an image pull, with the key or license as the credential |
batch --runner docker, when its --image is not on the machine yet | the registry that image names | an image pull, with whatever your docker login holds for that registry |
export compose | registry.rystic.ai | the license, to look up each image’s digest (--no-digest skips it) |
feedback | https://www.rystic.ai/api/feedback | your API key, the report as typed, the CLI’s version, OS, architecture and container engine, and each running simulator’s product, version, name and status; never an env file, a request body or a key’s value |
docs -online | https://www.rystic.ai (RYSTIC_DOCS_ENDPOINT) | nothing: it reads llms.txt and the pages’ .md |
rystic docs without -online, ps, logs, stop, features, config and help stay on the machine.
A live feed adds the source API itself: the CLI dials it from your machine — see Kalshi → Live mode.