Skip to Content
CLI flag reference

Reference

CLI flag reference

Every command below is generated from rystic help <command>. Use this reference to choose flags; Docker configuration shows where to put them in Compose.

View command help directly from the Docker image without installing anything locally:

docker run --rm registry.rystic.ai/kalshi-twin:<version> rystic help run

Flags belong to a command; there are no inherited global flags. -h or --help prints help for any command. Long flags accept one or two leading hyphens. Booleans use --flag=false to turn off a default; --flag false does not set a boolean to false. Arguments after -- in run, batch and grid belong to the child process.

Defaults below come from help in a clean environment. Empty strings are shown as unset; numeric zero and boolean false are shown explicitly. run --port uses -1 internally to mean “resolve environment/config, then 8080.” See configuration precedence and live environment variables for resolved defaults.

CommandPurpose
rystic pulldownload and install a twin release
rystic updateinstall a newer twin release, or just ask whether one exists
rystic runrun a twin (pulls it if missing); -d detaches, args after — go to the twin
rystic uistart a twin’s verified UI against a running instance (Pro and Enterprise; fetched on first use)
rystic pslist running twins — status from a live probe, not the record
rystic stopstop instances (SIGTERM, then SIGKILL after -timeout); with no name, stops the only instance
rystic restartstop and start again from the recorded spec
rystic rmremove a stopped instance’s record and log (refuses a live one)
rystic logsprint a detached twin’s log; -f follows
rystic reportthe batch P&L table — per-world declared columns, exact totals, settled vs left open
rystic upstart every service of a stack file — each exactly one run -d
rystic downstop every instance of a stack file’s project
rystic clockset or advance the clock of every running instance of a project, together
rystic exportwrite a docker-compose.yaml (digest-pinned, license from .env) or the Antithesis scaffold from a stack file
rystic statuslicense file, runtime, registry, installed, running, updates — one screen
rystic batchrun a bot against every tape in your library — natively, or one container per world with —runner docker
rystic gridrun the bot × world matrix: bundled example bots (plus yours) against the twin’s scenario worlds
rystic gradegrade finished agent cells from their saved files: PASS, FAIL or ERROR with one reason
rystic checkboot an installed twin and confirm it serves and resets
rystic mcpserve MCP on stdio: an agent starts, seeds, clocks, inspects and diffs twins through it
rystic tailstream a running twin’s state events to this terminal
rystic feedfeed a running twin live exchange frames (the twin’s manifest declares the source; a release build carries the key it needs)
rystic evaluatorrun the command evaluators a twin dispatches, sandboxed, and report their outcomes
rystic execrun a command whose calls to a vendor’s hosts land on the project’s —intercept twins
rystic envprint the exports rystic exec sets, for a shell to eval
rystic gatewayrun the project’s HTTPS gateway in the foreground (exec and env start it detached)
rystic doctorcheck this machine end to end without running anything
rystic trusttrust the gateway CA in the macOS login keychain, for Go tools built before Go 1.27
rystic featureslist what the installed twin can do
rystic describewhat an installed twin serves: scenarios, seedable registers with an example record each, operations
rystic docslist, read and search every Rystic doc — the site’s pages (built in, offline) and each installed twin’s HOWTO and SCENARIOS
rystic listreleased twins your API key or license covers — latest, installed, running (—installed: the old per-version lines)
rystic loginsign in through the browser, or install an API key or a license file, and log docker in to the registry with it
rystic licenseshow the active license, or install one
rystic configshow every setting with its source, or set one
rystic feedbackreport a bug, a point of friction or a feature request about the CLI or a twin; filed under your account
rystic self-updatereplace this CLI with the latest release
rystic versionprint this CLI’s version, then every running twin’s
rystic helpprint this message; help <command> shows a command’s flags

pull

usage: rystic pull [flags] [product]

download and install a twin release

Installs into $RYSTIC_HOME/twins/<product>/<version> and marks it current,
verifying the download against the release checksum before unpacking.

FlagDefaultMeaning
--arch stringunsettarget arch (default: this machine’s): amd64 or arm64
--endpoint string$RYSTIC_RELEASE_ENDPOINT or https://www.rystic.ai/api/twin-release release endpoint
--license stringunsetpath to the license file
--os stringunsettarget OS (default: this machine’s); linux for the docker runner
--version string“latest”release version to install (e.g. x.y.z)

update

usage: rystic update [flags] [product]

install a newer twin release, or just ask whether one exists

With -check, prints what is available and exits 10 when a newer
version exists — nothing is downloaded.

FlagDefaultMeaning
--checkfalseonly report whether newer versions exist (exit 10 if so); download nothing
--cli-endpoint string$RYSTIC_CLI_ENDPOINT or https://www.rystic.ai/api/cli-release CLI release endpoint
--endpoint string$RYSTIC_RELEASE_ENDPOINT or https://www.rystic.ai/api/twin-release release endpoint
--license stringunsetpath to the license file
--no-selffalsedo not update the CLI itself

run

usage: rystic run [flags] [product] [-- args...]

run a twin (pulls it if missing); -d detaches, args after -- go to the twin

Prints one identity line — product, version, model, licensee, URL — once
the twin serves. --scenario/--seed/--tapes/--tape choose the starting state,
--clock the starting instant.
Default port 8080; a busy port is a conflict (exit 9); --port 0 picks a free one.

FlagDefaultMeaning
--clock stringunsetstart the twin’s clock at this RFC3339 instant (RYSTIC_CLOCK); it stands in for the boot world’s own clock, and a reset returns to it
--copies int0play —tape as this many markets at once, each under its own ticker (<tape>-1 … <tape>-N) on its own WS stream
-dfalsedetach: return once the twin serves; ‘rystic stop <name>’ ends it
--egress-retain stringunsetRYSTIC_EGRESS_RETAIN for the twin
--env valueunsetextra KEY=VALUE for the twin (repeatable)
--inject stringthen: $RYSTIC_INJECTboot with this inject schedule (RYSTIC_INJECT); a world’s own events are replaced. With —tape it loads alongside the replay and its at values are tape time
--interceptfalseboot behind the project’s gateway: vendor hostnames stay in responses, and ‘rystic exec’ routes the twin’s hosts to it
--jsonfalseprint the JSON envelope instead of the identity line
--license stringunsetpath to the license file
--livefalsefeed the twin live exchange frames from this process (the manifest declares the source; a release build carries the key it needs; default: $RYSTIC_LIVE)
--live-carry int0pre-carry the busiest N before anything is picked; 0 carries nothing until the UI picks; ignored when markets or events are named (default: $RYSTIC_LIVE_CARRY)
--live-env stringunsetexchange environment from the twin’s manifest (default: $RYSTIC_LIVE_ENV, then the manifest’s default_env)
--live-events stringunsetcomma-separated events whose open markets to carry, looked up again on every rescan; adds to —live-markets (default: $RYSTIC_LIVE_EVENTS)
--live-events-file stringunsetstartup event-list file: one event ticker per line or comma-separated; read once at startup (default: $RYSTIC_LIVE_EVENTS_FILE; mutually exclusive with —live-events)
--live-markets stringunsetcomma-separated markets to carry before anything is picked in the UI (default: $RYSTIC_LIVE_MARKETS)
--live-markets-file stringunsetstartup watch-list file: one ticker per line or comma-separated; read once at startup (default: $RYSTIC_LIVE_MARKETS_FILE; mutually exclusive with —live-markets)
--live-max-markets int500how many of the busiest open markets to offer in the twin UI (default: $RYSTIC_LIVE_MAX_MARKETS)
--live-series string”*”comma-separated series to carry; * = every open market on the exchange (default: $RYSTIC_LIVE_SERIES)
--live-shards int3exchange sockets to spread the carried markets over (the exchange caps connections per key; default: $RYSTIC_LIVE_SHARDS)
--name string“default”instance name — what ps/stop/logs call it
--network stringunsetRYSTIC_NETWORK for the twin: off, on, or a profile file
--port int-1listen port; 0 picks a free one (default: $RYSTIC_PORT, config.json, then 8080)
--project string“default”project label — what ‘ps —project’ and ‘down’ group by
--runner stringunsetnative or docker (default: $RYSTIC_RUNNER, config.json, then native)
--scenario stringunsetboot into a named scenario (the twin’s own catalog)
--seed stringunsetboot seed file, applied before the first request
--seed-rng stringunsetpin the twin’s RNG once serving
--speed float1replay speed for —tape; paces a world’s timeline otherwise, never beside —live (0 = manual clock)
--tape stringunsetstart replaying this tape once serving (needs a tape library); a comma-separated list plays them as ONE synchronized group — merged onto a single clock and trimmed to the window they share
--tapes stringunsettape library for replay (validated before start; default: $RYSTIC_TAPES, config.json)
--timeout duration30show long to wait for the twin to serve
-vfalsea metered twin: print one line per lease heartbeat
--version stringunsetinstalled version to run (default: current)

ui

usage: rystic ui [flags] [product]

start a twin's verified UI against a running instance (Pro and Enterprise; fetched on first use)

Installs the UI once under the twin's install dir, with your API key or
license exactly as pull does, then starts its loopback server against the
instance named by --name (default: default) and prints the URL; Ctrl-C stops
it. The download and the start both need the verified_ui entitlement.

FlagDefaultMeaning
--endpoint string$RYSTIC_RELEASE_ENDPOINT or https://www.rystic.ai/api/twin-release release endpoint
--fetch-onlyfalseinstall the UI’s archive and stop without starting it
--license stringunsetpath to the license file
--name string“default”the running instance to point the UI at (what ‘rystic ps’ lists)
--port int0the UI’s loopback port (default: the UI’s own)
--runner stringunsetdocker or native (default: $RYSTIC_RUNNER, config.json, then docker when it answers, else native)
--url stringunsetthe twin’s origin, instead of a running instance’s record
--version stringunsetinstalled version whose UI to run (default: the instance’s)

ps

usage: rystic ps [flags]

list running twins — status from a live probe, not the record

FlagDefaultMeaning
-afalseinclude exited instances (and do not prune their records)
--jsonfalseprint the JSON envelope
--project stringunsetonly instances of this project (‘rystic up’ labels a stack file’s services with its project)

stop

usage: rystic stop [flags] [<name>…] | --all

stop instances (SIGTERM, then SIGKILL after -timeout); with no name, stops the only instance

FlagDefaultMeaning
--allfalsestop every instance
--jsonfalseprint the JSON envelope
--timeout duration8show long to wait after SIGTERM before SIGKILL

restart

usage: rystic restart [flags] <name>… | --all

stop and start again from the recorded spec

FlagDefaultMeaning
--allfalserestart every instance
--jsonfalseprint the JSON envelope
--timeout duration8show long to wait after SIGTERM before SIGKILL

rm

usage: rystic rm <name>…

remove a stopped instance's record and log (refuses a live one)

No flags.

logs

usage: rystic logs [flags] [-f] <name>

print a detached twin's log; -f follows

FlagDefaultMeaning
-ffalsefollow: keep printing new lines
--feedfalseprint the feeder’s log instead of the twin’s (‘run —live’)

report

usage: rystic report [flags] <batch-out-dir> [-sort col] [-csv]

the batch P&L table — per-world declared columns, exact totals, settled vs left open

FlagDefaultMeaning
--csvfalseCSV to stdout (no totals row — spreadsheets sum themselves)
--jsonfalseprint the JSON envelope
--sort stringunsetsort by a declared column, worst first; prefix - for best first

up

usage: rystic up [flags] [-d] [-f rystic.yaml] [service…]

start every service of a stack file — each exactly one `run -d`

A stack file is a hand-picked set of twins: version: 1, project, clock,
speed, and services: {name: {product, version, runner, port, scenario, seed,
inject, tapes, tape, speed, clock, copies, env, args, seed_rng, egress_retain,
network, live, live_env, live_series, live_max_markets, live_shards,
live_carry, live_markets, live_markets_file, live_events, live_events_file}}
— the same keys as run's flags (live_x_y is --live-x-y); the service name is
the instance name; the project labels every twin for `ps --project`, `clock`
and `down`. A top-level clock (RFC3339) starts every twin's clock at that
instant and a top-level speed paces every twin; a service's own clock or
speed wins. The clock stands in for the clock a boot world (scenario, seed)
names, so its events count from it, and a reset returns to it; `rystic
clock` moves every twin's clock together afterwards. Already-running
services are reported, not restarted. Without -d, up holds the terminal and
Ctrl-C is `down`.

FlagDefaultMeaning
-dfalsedetach: return once every service serves; ‘rystic down’ ends them
-f string./rystic.yamlstack file
--jsonfalseprint the JSON envelope instead of identity lines
--timeout duration30show long to wait for each twin to serve

down

usage: rystic down [flags] [-f rystic.yaml | --project p]

stop every instance of a stack file's project

FlagDefaultMeaning
-f string./rystic.yamlstack file whose project to stop
--jsonfalseprint the JSON envelope
--project stringunsetproject to stop (instead of reading a stack file)
--timeout duration8sgrace before SIGKILL

clock

usage: rystic clock [flags] [-f rystic.yaml | --project p] --set <RFC3339> | --advance <duration>

set or advance the clock of every running instance of a project, together

Posts one /_rystic/clock to every running instance of the project (the stack
file's, or --project) at once, then prints each one's clock as its
/_rystic/version reads it. --set pins every clock to one RFC3339 instant;
--advance moves each by a duration of whole seconds (90s, 15m, 48h), so
clocks in step stay in step. Events fire as the clock passes them. Under a
manual clock (speed: 0) the instances then read the same instant, apart from
the millisecond each request they serve adds; a paced clock keeps moving,
and a playing tape or live feed re-pins its own. An instance that refuses
(a plan without state_set) or does not answer is named, and the command
exits non-zero.

FlagDefaultMeaning
--advance stringunsetmove every clock by this many whole seconds (90s, 15m, 48h; negative moves back)
-f string./rystic.yamlstack file whose project to move
--jsonfalseprint the JSON envelope
--project stringunsetproject to move (instead of reading a stack file)
--set stringunsetpin every clock to this RFC3339 instant
--timeout duration5show long each instance has to answer

export

usage: rystic export [flags] compose|antithesis [-f rystic.yaml] [-o dir]

write a docker-compose.yaml (digest-pinned, license from .env) or the Antithesis scaffold from a stack file

compose: each service becomes a compose service on registry.rystic.ai/<product>@<digest>
(resolved through the front door with your license file — the run is reproducible),
RYSTIC_LICENSE from .env (.env.example is written beside it, never a value),
init: true, the image's HEALTHCHECK for depends_on. antithesis: the same plus
platform linux/amd64, Dockerfile.config (FROM scratch, the compose file + .env)
and a README with the tenant push commands (--tenant, --gcp-project). A tape:
key cannot be expressed in compose and is reported.

FlagDefaultMeaning
-f string./rystic.yamlstack file
--gcp-project stringunsetantithesis: the GCP project of your tenant repository
--jsonfalseprint the JSON envelope; with no -o it carries the compose file
--no-digestfalsepin images by tag, no registry call (a digest pin is what makes a run reproducible)
-o stringunsetwrite the files here (compose: default stdout; antithesis: required)
--tenant stringunsetantithesis: your tenant name (the README’s push commands)

status

usage: rystic status [flags]

license file, runtime, registry, installed, running, updates — one screen

FlagDefaultMeaning
--jsonfalseprint the JSON envelope
--license stringunsetpath to the license file
--offlinefalsedo not ask the release endpoint about updates
--pub stringunsetpath to a license public key (overrides the built-in one)

batch

usage: rystic batch [flags] [product] [flags] -- <bot command>

run a bot against every tape in your library — natively, or one container per world with --runner docker

One twin+bot pair per tape, -j at a time, longest tape first, at speed 1 —
real time, so the code under test keeps a coherent clock. One JSONL ledger
row per tape in -out. -dry-run prints the plan without running anything.
The bot contract is five environment variables — RYSTIC_ADDR, RYSTIC_WORLD,
RYSTIC_WORLD_KIND, RYSTIC_RESULT, PORT (a loopback port the cell's bot alone
holds), plus RYSTIC_MARKET on a tape cell — and the same values are
substituted into the bot's own argv as {{addr}} {{world}} {{world_kind}}
{{result}} {{port}} {{market}}, for a bot that takes them as
flags rather than from the environment: `-- python3 bot.py --event
{{market}}` gives each cell its own market instead of one market N times.
{{market}} is the MARKET ticker the tape directory is named for; a bot
wanting a coarser id derives it itself, and on -scenarios it is refused
rather than substituted empty. With --runner docker the substitution happens
inside your image, and an image whose rystic predates placeholders is
refused before it runs rather than passing the braces through verbatim.
--runner docker --image <ref> runs each world in its own container from YOUR
image (FROM registry.rystic.ai/<product>:<version> + your bot's runtime): the
same ledger, state and logs, kernel-isolated, no shared ports; the license
reaches each container only through its environment.
-json emits completed slots (not bot passes), failed slot names, skipped
world count and an interrupted flag; interruptions include a warning.
Bot outcomes are recorded in the ledger. Exit 7 means a slot failed.

FlagDefaultMeaning
--dry-runfalseprint the plan and exit without running anything
--egress-retain stringunsetvalue for RYSTIC_EGRESS_RETAIN on each twin (” = the twin’s default)
--image stringunsetwith —runner docker: the image each world runs in — yours, built FROM registry.rystic.ai/<product>:<version> plus your bot’s runtime
-j int2concurrent slots (twin+bot pairs); -j auto is not built yet
--jsonfalseprint one summary as the JSON envelope when the run ends; progress goes to stderr
--license stringunsetpath to the license file
--limit int0run at most N tapes (0 = all)
--market stringunsetonly tapes whose market matches this pattern (e.g. ‘KXBTC15M*’)
--out stringbatch-<timestamp>output directory
--runner stringunsetnative (one twin process per world here) or docker (one container per world from —image; default: $RYSTIC_RUNNER, config.json, then native)
--scenarios stringunsetrun named scenarios instead of tapes: ‘all’, or a comma-separated list; the catalog is the twin’s own, so planning boots one twin briefly — even with -dry-run
--settle-route stringunsetsettlement endpoint to POST to (default: whatever the twin archive’s manifest declares)
--settlements stringunsetJSON file of market -> outcome, applied when each tape ends so held positions realize (a capture never records how a market resolved)
--tape stringunsetexactly one tape, by market id — the unit a container runs per world; not with -market
--tapes string$RYSTIC_TAPES, then $RYSTIC_HOME/tapes, then where the twin manifest says the twin lookstape library
--timeout duration30m0sper-tape budget: twin boot, playback, teardown
--version stringunsetinstalled version to run (default: current)

grid

usage: rystic grid [flags] [product] [flags] [-- <bot command>] | <agent folder> -evals <dir|list> [flags] [-- <agent command>]

run the bot × world matrix: bundled example bots (plus yours) against the twin's scenario worlds

One twin per cell. Rows are the archive's declared grid bots, plus your own
command after `--` (same contract as batch: read RYSTIC_ADDR, exit 0 for
pass, and the same argv placeholders — except {{market}}, which a scenario
world lacks and which grid therefore refuses). Columns default to the
archive's declared worlds; -worlds overrides them ('all' runs the twin's
whole catalog). Each cell's bot gets a loopback port
of its own as $PORT and {{port}}, so parallel webhook agents never share a
listener, and each cell's twin delivers webhooks to the endpoints its bot
registers (-webhooks=false keeps cells hermetic). Prints the grid and exits
non-zero if any cell failed.
With -evals, grid runs the agent (a folder carrying agent.yaml, or a command
after `--`) once per eval instead: each cell boots the eval's twins, applies
its world, seeds them with -seed and loads its injected events, then starts
the agent behind a strict gateway of its own, so the agent's unchanged calls
to the vendor's hosts land on the cell's twins and any host outside the world
and agent.yaml's allow list is refused. The cell ends on the manifest's done
rule or -timeout, and <out>/<eval>/ keeps each twin's state before and after,
its calls and egress and the agent's log.
Each cell is then graded as `rystic grade` grades it:
its verdict.json and its <out>/ledger.jsonl row carry PASS, FAIL or ERROR,
the one reason and the checks digest. A cell passes on its verdict, never on
the agent's exit code; exit 7 when a cell is not PASS.
-ui serves the grid page on a local port over the live run: "Run selected"
and "Run failing" run those cells with the folder as it is now, "Run all"
reruns every cell, and grid serves until interrupted, then exits on each
cell's latest verdict.

FlagDefaultMeaning
--clock-step duration0with -evals and -pace 0, how far grid moves every twin’s clock each wall second
--dry-runfalseprint the plan and exit (‘-worlds all’ still boots one twin briefly to read the catalog)
--evals stringunsetrun the agent named in place of the product (a folder carrying agent.yaml), or the command after ’—’, on these evals: a directory of eval files, or a comma-separated list
-j int2concurrent cells (twin+bot pairs)
--jsonfalseprint one summary as the JSON envelope when the grid ends; progress and the grid go to stderr
--license stringunsetpath to the license file
--out stringgrid-<timestamp>; must not already existoutput directory
--pace float1with -evals, simulated seconds each twin’s clock moves per wall second (0 keeps it manual)
--seed int1with -evals, the RNG seed every cell’s twins are pinned to
--ticks int15tick budget handed to each cell’s bot via RYSTIC_TICKS (0 = no budget)
--timeout duration5m0sper-cell budget: twin boot, bot run, teardown; with -evals, the agent’s wall-clock cap
--uifalsewith -evals, serve the grid page on a local port: rows go green or red on each cell’s verdict, Run selected and Run failing run those cells, Run all reruns the grid; runs until interrupted
--version stringunsetinstalled version to run (default: current)
--webhookstrueeach cell’s twin delivers webhooks to the endpoints its bot registers (a bot listens on its own $PORT); false keeps every cell hermetic
--worlds stringunsetcomma-separated scenario names, or ‘all’ (default: the archive’s declared grid worlds)

grade

usage: rystic grade [flags] <cell-dir | grid-out-dir>…

grade finished agent cells from their saved files: PASS, FAIL or ERROR with one reason

Runs each cell's checks over the files its run saved (each twin's inspect
before and after, calls, egress and world.json), never a live twin, so a
cell regrades without rerunning its agent. Checks run in order: "nothing
destructive" (a row deleted, or a change the twin lists such as a channel
archived, unless the eval's allow_delete permits it), the eval's checks,
then the agent folder's checks/ (YAML files, and executable scripts printing
`PASS <name>` or `FAIL <name> (<reason>)` lines). FAIL carries the first
failing check's desc, or "never finished" when the time cap was hit; ERROR is
infrastructure only. Writes verdict.json into each cell. Exit 7 when a
cell is not PASS, 1 when one could not be graded.
--draft <cell-dir> grades nothing: it prints an eval drawn from one finished
cell, so a good run becomes a regression case. Its world, prompt and inject
are the cell's; its checks expect each row the run created or changed (the
fields it changed), forbid any other new row, changed field, call actor or
call method, and its allow_delete names each row the run deleted or
destructively changed. Values the run generated, its own ids and clock
times, stay out. The cell passes its draft; trim the draft to what the
task requires before keeping it.

FlagDefaultMeaning
--draftfalseprint a draft eval drawn from one cell’s run, its checks for a person to trim; grade nothing
--jsonfalseprint the JSON envelope

check

usage: rystic check [flags] [product]

boot an installed twin and confirm it serves and resets

FlagDefaultMeaning
--license stringunsetpath to the license file
--timeout duration15show long to wait for the twin to serve
--version stringunsetinstalled version to check (default: current)

mcp

usage: rystic mcp [flags]

serve MCP on stdio: an agent starts, seeds, clocks, inspects and diffs twins through it

A client config runs `rystic mcp` as its command (command: rystic, args:
["mcp"]). Tools: list_twins, start_twin, stop_twin, list_scenarios,
load_scenario, seed, set_clock, advance_clock, reset, inspect_state,
list_calls, list_sent, diff_state, save_test_case, check_test_case,
snapshot_twin and restore_twin. start_twin starts a twin as `run -d` does,
on a free port unless told one, pulling it first, with progress on stderr,
when it is not installed. snapshot_twin saves an instance's state, clock,
pending events and network shaping under a name in
$RYSTIC_HOME/run/snapshots/; restore_twin resets the instance to it.
inspect_state, list_calls, list_sent and diff_state answer a page of at
most about 20 KB: next_cursor reads the next page, filters and limit narrow
it, format detailed carries whole entries, and truncated with a hint says
what was cut and which call gets the rest. inspect_state links a larger
state whole as the resource rystic://instances/<twin>/<name>/state, and
diff_state's baseline is the resource .../baseline.
diff_state compares an instance's state with the baseline taken when the
server started it, loaded a scenario or reset it, kept beside the instance
record in $RYSTIC_HOME/run/ so it outlives the server; an instance with
none gets one on its first diff. save_test_case writes the run since that
baseline as a grid cell dir and returns the eval `grade --draft` drafts
from it, which the cell passes; check_test_case grades a later run, saved
as a new cell, against that eval's checks as `rystic grade` does. Each tool declares MCP annotations. read-only: diff_state, inspect_state, list_calls, list_scenarios, list_sent, list_twins; destructive: advance_clock, load_scenario, reset, restore_twin, seed, set_clock, stop_twin; open-world: start_twin. The prompts reproduce_webhook_bug and
write_test_for_flow walk an agent through those tools in order, and
completions offer their twin and scenario arguments from the running and
installed twins. Each tool also declares an outputSchema: its result
shapes or the error object, which every result's structuredContent
matches. Every tool error, a malformed argument included, is an error
result, never a protocol error, carrying a stable code, its cause, its
fix and retryable (with retry_after_ms when a wait helps); -errors prints
the table. A stdin line that is not one JSON-RPC message (invalid JSON,
nested over 10000 levels, over 32 MiB, or a batch) gets a JSON-RPC error,
-32700 or -32600, and the session goes on. Stdout carries only the
protocol, everything else goes to stderr; twins it started keep running
after it exits. `rystic mcp install --client <client>` writes the config
that runs it for Claude Code, Cursor, Codex, VS Code or Windsurf.
The server's instructions give the call order; the rystic://guide resource
carries each step, the instance forms, every error code and an example.
--twin <twin> serves one twin, named as `run` takes it: its tools take no
instance argument and act on its one running instance, or on --instance
<name>, and a call naming another twin is refused as out_of_scope;
start_twin starts that instance, and refuses while an instance it serves runs.
--read-only serves only the read-only tools; --tools a,b,c only those named
(with --read-only, read-only ones only). The instructions and the guide
then say what the server serves. A bad scope flag exits 2.

FlagDefaultMeaning
--errorsfalseprint every tool error code with its cause and fix, as a markdown table, and exit
--instance valueunsetwith —twin, act on this instance of it (default: its one running instance)
--read-onlyfalseserve only the tools that change no twin state
--tools valueunsetserve only these tools, comma-separated
--twin valueunsetserve only this twin, as ‘rystic list’ names it; its tools then take no instance argument

tail

usage: rystic tail [flags]

stream a running twin's state events to this terminal

Connects to the twin's /_rystic/events stream and prints one line per
event. -events scopes the feed (private, public, writes, reads, control;
or all) — the default shows fills, order updates and other state changes
and skips public market-data floods.

FlagDefaultMeaning
--addr string“http://localhost:8080 ”twin base URL
--events stringprivate,writes,control; or off/all/comma listevent scope

feed

usage: rystic feed [flags] [product] --instance <name> | --url <base>

feed a running twin live exchange frames (the twin's manifest declares the source; a release build carries the key it needs)

Runs in the foreground until Ctrl-C. `rystic run --live` starts a twin and
this feeder together (either runner: the feeder is a host process posting
to the twin's URL); use `feed` alone for a twin already running — any
runner, any machine. --live-series * carries whatever the twin's Markets
card picks from the exchange's busiest open markets; nothing is carried
until something is picked (--live-carry N pre-carries the busiest N,
--live-markets A,B carries exactly those). Every --live-x-y flag defaults
from RYSTIC_LIVE_X_Y. The twin's manifest names its exchange key vars: an
id, then either <X>_PRIVATE_KEY_FILE (a path) or <X>_PRIVATE_KEY_B64 (the PEM).

FlagDefaultMeaning
--instance stringunsetfeed the named running instance (‘rystic ps’)
--live-carry int0pre-carry the busiest N before anything is picked; 0 carries nothing until the UI picks; ignored when markets or events are named (default: $RYSTIC_LIVE_CARRY)
--live-env stringunsetexchange environment from the twin’s manifest (default: $RYSTIC_LIVE_ENV, then the manifest’s default_env)
--live-events stringunsetcomma-separated events whose open markets to carry, looked up again on every rescan; adds to —live-markets (default: $RYSTIC_LIVE_EVENTS)
--live-events-file stringunsetstartup event-list file: one event ticker per line or comma-separated; read once at startup (default: $RYSTIC_LIVE_EVENTS_FILE; mutually exclusive with —live-events)
--live-markets stringunsetcomma-separated markets to carry before anything is picked in the UI (default: $RYSTIC_LIVE_MARKETS)
--live-markets-file stringunsetstartup watch-list file: one ticker per line or comma-separated; read once at startup (default: $RYSTIC_LIVE_MARKETS_FILE; mutually exclusive with —live-markets)
--live-max-markets int500how many of the busiest open markets to offer in the twin UI (default: $RYSTIC_LIVE_MAX_MARKETS)
--live-series string”*”comma-separated series to carry; * = every open market on the exchange (default: $RYSTIC_LIVE_SERIES)
--live-shards int3exchange sockets to spread the carried markets over (the exchange caps connections per key; default: $RYSTIC_LIVE_SHARDS)
--url stringunsetfeed the twin at this base URL, e.g. http://localhost:8080 
--version stringunsetinstalled version whose manifest to read (default: current)

evaluator

usage: rystic evaluator [flags] --instance <name> | --url <base>

run the command evaluators a twin dispatches, sandboxed, and report their outcomes

Runs in the foreground until Ctrl-C (--once: what is waiting, then exit).
Claims work from the twin's evaluation engine, unpacks the snapshot it
streams, and runs each command in a digest-pinned image (default --runner
docker) with no network and capped resources; --runner podman is also
supported.
Local commands require --runner local --trusted-local and a trusted_local
manifest. They have host access and produce unpinned feedback only. A
command exits 0 to pass, 1 to fail, 78 to decline the input, or writes an
outcome to $EVAL_RESULT; anything else is an infrastructure error.

FlagDefaultMeaning
--concurrency int2dispatches run at once
--instance stringunsetserve the named running instance (‘rystic ps’)
--oncefalserun what is waiting, then exit
--poll duration500mswait between empty claims
--runner string“docker”how a command evaluator runs: local, docker or podman; an evaluator that names an image needs a container runner
--trusted-localfalseallow local commands with host filesystem and network access; trusted code only
--url stringunsetserve the twin at this base URL, e.g. http://localhost:8080 

exec

usage: rystic exec [flags] -- <command> [args…]

run a command whose calls to a vendor's hosts land on the project's --intercept twins

Starts the project's gateway when none runs (detached, loopback only, recorded
in $RYSTIC_HOME/run/gateway/), then becomes the command, so its exit code and
signals are the command's own. The command's HTTPS_PROXY and HTTP_PROXY name the
gateway; SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE, GIT_SSL_CAINFO
and AWS_CA_BUNDLE name a bundle of the system roots plus the gateway CA
($RYSTIC_HOME/ca/); NODE_EXTRA_CA_CERTS names the CA; GODEBUG lets a Go
1.27+ tool read SSL_CERT_FILE on macOS; NO_PROXY keeps localhost direct.
A host declared by a twin started with `rystic run --intercept` is decrypted and
served by that twin; a host Rystic simulates with no such twin running is
refused (403, naming the twin to start), apart from a model provider's API,
which reaches the real service; any other host is tunneled unread, unless
--strict, which refuses every host --allow does not name. On a Mac, Go tools
built before Go 1.27 read only the keychain; run `rystic trust` once.
Requires a client that honors these proxy and CA settings. Custom transports
or certificate pinning may need configuration. --strict controls requests
reaching the gateway; it does not prevent direct connections.

FlagDefaultMeaning
--allow valueunsetwith —strict, a host still tunneled (repeatable)
--project string“default”the project whose —intercept twins the gateway routes to
--strictfalserefuse every host no twin claims, except —allow ones

env

usage: rystic env [flags]

print the exports rystic exec sets, for a shell to eval

eval "$(rystic env)" routes the rest of the shell's session the way `rystic exec`
routes one command, starting the project's gateway when none runs.

FlagDefaultMeaning
--allow valueunsetwith —strict, a host still tunneled (repeatable)
--jsonfalseprint the JSON envelope
--project string“default”the project whose —intercept twins the gateway routes to
--strictfalserefuse every host no twin claims, except —allow ones

gateway

usage: rystic gateway [flags]

run the project's HTTPS gateway in the foreground (exec and env start it detached)

One gateway per project routes the hosts its --intercept twins declare on
/_rystic/version, re-read as requests arrive, so a twin started later is
picked up. Two twins claiming one host is a conflict (exit 9) naming both.
-stop ends the project's detached gateway; its log is
$RYSTIC_HOME/run/gateway/<project>.log.

FlagDefaultMeaning
--allow valueunsetwith —strict, a host still tunneled (repeatable)
--port int0loopback port; 0 picks a free one
--project string“default”the project whose —intercept twins it routes to
--stopfalsestop the project’s detached gateway
--strictfalserefuse every host no twin claims, except —allow ones

doctor

usage: rystic doctor [flags] [product]

check this machine end to end without running anything

Verifies the license file, the install, that the twin and the license agree
on a signing key, and that the data the twin needs is where it expects it.
Exits 0 when everything passes; warnings (!) are features you do not have
yet, not failures. Run it before a demo, not during one.

FlagDefaultMeaning
--license stringunsetpath to the license file
--pub stringunsetpath to a license public key (overrides the built-in one)
--version stringunsetinstalled version to inspect (default: current)

trust

usage: rystic trust [flags]

trust the gateway CA in the macOS login keychain, for Go tools built before Go 1.27

Adds $RYSTIC_HOME/ca/ca.pem to the login keychain as a trusted root (one
password prompt); --remove revokes current and superseded Rystic gateway CAs
belonging to this RYSTIC_HOME from the login keychain. The CA is constrained to
hosts Rystic simulates, so trusting it vouches for no other site. When a newer
rystic simulates more hosts it replaces the CA and says to run this again.
SSL_CERT_FILE carries the CA to bundle-aware clients on other platforms.
On Windows, Go tools built before Go 1.27 require native-store trust.

FlagDefaultMeaning
--removefalseremove the CA’s trust and the certificate from the login keychain

features

usage: rystic features [flags] [product]

list what the installed twin can do

Read from the twin's own manifest, so it describes the build you have rather
than what this CLI was written against. -v adds detail and control-plane URLs.

FlagDefaultMeaning
--jsonfalseprint the JSON envelope: every feature in full, and whether its data is here
-vfalseinclude the long description and control-plane endpoints for each feature
--version stringunsetinstalled version to describe (default: current)

describe

usage: rystic describe [flags] [product…]

what an installed twin serves: scenarios, seedable registers with an example record each, operations

Asks each installed twin's own binary (`<twin> --describe`; for a linux-only
install, the image built from it) and adds what the CLI knows: the manifest's
summary and features, the hosts `rystic exec` routes to the twin, and the
installed version. With no product named and $RYSTIC_PRODUCT unset, it
describes every installed twin. Text shows the scenarios, each register's
kind, key and field count, and the operation count per method; -v lists every
operation. -json (data.twins[]) carries everything: each register's typed
fields, the endpoint that seeds it and an example record — POST
{<register>: <example>} to /_rystic/seed. For owner-scoped registers, set
the field named by owner_field to your caller identity (see example_note).
Otherwise the record can be accepted but invisible to your API requests.
[start] (start_scenario) is the scenario the quickstart offers.
Without --scenario every build starts empty.

It never pulls; a linux-only install's image is built and loaded into the
local image store the way `run --runner docker` does. A named twin that is not
installed exits 5 before anything runs. A build cut before --describe gets
the CLI's half and a warning, exit 0. A twin that crashes or does not answer
is reported with its error and exits 7; a container runtime that does
not answer, 8. Every other twin is still described.

FlagDefaultMeaning
--jsonfalseprint the JSON envelope: every twin in full, with an example record per register
-vfalselist every operation, not only the count per method

docs

usage: rystic docs [flags] [<topic> [scenarios]] | search <terms…>

list, read and search every Rystic doc — the site's pages (built in, offline) and each installed twin's HOWTO and SCENARIOS

With no topic, a numbered index: the site's pages, from a snapshot built
into this CLI (-online reads the live site instead), then each installed
twin's HOWTO.md and SCENARIOS.md; the last line names the snapshot, and
-json carries its content_sha256. A topic prints raw markdown: a page slug
(agent-quick-start, simulators/<name>, or the page's /docs/ path or site
URL), or a product or its short name, which prints the installed twin's
HOWTO — plus the live routes of each running instance — and falls back to
the site's page for it. `<product> scenarios` prints SCENARIOS.md;
`<topic>#<anchor>` prints one section. search lists every section holding
all the terms as <topic>#<anchor>, release notes last.

FlagDefaultMeaning
--endpoint string$RYSTIC_DOCS_ENDPOINT, config.json, then https://www.rystic.ai site -online reads
--jsonfalseprint the JSON envelope
-n int20search: how many sections to print
--onlinefalseread the live site instead of the snapshot built into this CLI

rystic docs search is covered by the docs help above.

list

usage: rystic list [flags]

released twins your API key or license covers — latest, installed, running (--installed: the old per-version lines)

FlagDefaultMeaning
--endpoint stringunsetcatalog endpoint (default $RYSTIC_CATALOG_ENDPOINT, config.json, then https://www.rystic.ai/api/catalog ); file:// reads a JSON file
--installedfalseonly what is installed here, one line per version (the old output)
--jsonfalseprint the JSON envelope
--license stringunsetpath to the license file

login

usage: rystic login [flags] [rys_key | file]

sign in through the browser, or install an API key or a license file, and log docker in to the registry with it

With no argument, prints a sign-in link and the code its page shows, and
waits up to 15 minutes: a human opens the link, signs in on www.rystic.ai
and clicks Approve, and the API key this CLI made is stored as below. An
agent relays the link to its human. On a terminal with $CI unset it also
opens the browser; --no-browser prints the link only.

Given an API key (rys_…, made at https://www.rystic.ai/account/api-keys;
or --token-stdin, which keeps it out of shell history), stores it in
$RYSTIC_HOME/token, where pull, run, doctor, status and list read it, and
logs docker in as `token`: `rystic run <product>` then pulls the metered
edition. Given an Enterprise license file, verifies it and parks it in
$RYSTIC_HOME (`rystic license` shows it afterwards), and logs docker in as
`license`. Either way, `docker pull <registry>/<product>:<version>` and
`rystic run --runner docker` then work with nothing else; --no-docker
skips that half, and without a container runtime it is skipped with the
manual command printed.

Last, it offers to start one of the simulators the credential covers,
exactly as `rystic run <product> -d --scenario <its quickstart>` — only
when stdin and stdout are both terminals and $CI is unset. Piped input,
an agent, a script or CI never sees the offer; --no-start skips it.

FlagDefaultMeaning
--forcefalseactivate a license file even if it is expired
--no-browserfalsewith no key: print the sign-in link only, do not try to open a browser
--no-dockerfalsestore the API key or license only; do not ‘docker login’ to the registry
--no-startfalsedo not offer to start a simulator afterwards (the offer needs a terminal and no $CI)
--pub stringunsetpath to a license public key (overrides the built-in one)
--token-stdinfalseread the API key (rys_…) from stdin instead of the command line, so it stays out of shell history

license

usage: rystic license [flags] [activate <file>]

show the active license, or install one

`rystic license activate <file>` verifies a license and parks it in
$RYSTIC_HOME, where every later command finds it without a flag.

FlagDefaultMeaning
--license stringunsetpath to the license file
--pub stringunsetpath to a license public key (overrides the built-in one)

license activate

usage: rystic license activate [flags] <file>

verify a license file and park it in $RYSTIC_HOME

Verifies the license file's signature and expiry, then installs it as the
active license, so every later command finds it with no flag and no env
var.

FlagDefaultMeaning
--forcefalseactivate even if the license is expired
--pub stringunsetpath to a license public key (overrides the built-in one)

config

usage: rystic config [flags] [set <key> <value>]

show every setting with its source, or set one

Settings resolve flag > environment > $RYSTIC_HOME/config.json > built-in,
and this prints which. Keys: runner, port, tapes, release_endpoint,
catalog_endpoint, docs_endpoint, image_source (registry|archive), registry,
docker.binary, docker.host.

FlagDefaultMeaning
--jsonfalseemit the JSON envelope instead of a table

config set

usage: rystic config set <key> <value>

No flags.

feedback

usage: rystic feedback [flags] [message | -]

report a bug, a point of friction or a feature request about the CLI or a twin; filed under your account

Sends one report with the API key `rystic login` stored, filed under your
account, and prints its ID. --type is bug,
feature_request or friction; --command names the rystic command or twin
endpoint ("stripe POST /v1/charges"); --task, --expected and --actual say
what you tried, expected and got. The message is the argument, or stdin;
on a terminal with neither, it asks for each field. The CLI adds its
version, OS/arch, runner (native or a container engine) and the running
twins, never an env file, a request body or a key. An agent sends one only
when its user asks.

FlagDefaultMeaning
--actual stringunsetwhat happened
--command stringunsetthe rystic command or twin endpoint it is about, e.g. “stripe POST /v1/charges”
--expected stringunsetwhat you expected
--jsonfalseprint the JSON envelope: the filed issue’s ID and URL
--task stringunsetwhat you were trying to do
--type stringunsetwhat this is: bug | feature_request | friction

self-update

usage: rystic self-update [flags]

replace this CLI with the latest release

The CLI itself is not license-gated: it downloads from the public release
endpoint. With -check, reports and exits 10 without replacing anything.

FlagDefaultMeaning
--checkfalseonly report whether a newer CLI exists (exit 10 if so); download nothing
--endpoint string$RYSTIC_CLI_ENDPOINT or https://www.rystic.ai/api/cli-release CLI release endpoint
--forcefalsereplace even a dev build, or re-install the version already running
--version string“latest”CLI version to install (e.g. x.y.z)

version

usage: rystic version [flags]

print this CLI's version, then every running twin's

Prints this CLI's version, commit, build date and platform, then every
running twin's product, version and model, and warns when a running twin
is behind the installed one.

FlagDefaultMeaning
--jsonfalseprint the JSON envelope

help

rystic help lists commands; rystic help <command> lists that command’s flags.

Environment and exit codes

From the end of rystic help.

Files

$RYSTIC_HOME (default ~/.rystic) holds twins/ (installs), token (the API key
login stored), rystic-license.json (the active license file), config.json
(rystic config) and run/ (each instance's record and log).
$RYSTIC_PRODUCT names the twin used by any command run without a product
argument.

Twins on an API key

rystic finds the API key in $RYSTIC_TOKEN or the file $RYSTIC_TOKEN_FILE
names, else /run/secrets/rystic_token (a Compose secret), else config.json's
token, else $RYSTIC_HOME/token. A token file named or mounted but unreadable
or empty is an error, never a reason to try the next place.
The twin keeps its own lease; $RYSTIC_LEASE_URL, $RYSTIC_REGISTER_WAIT and
$RYSTIC_LEASE_CACHE reach it, and a container, when set; run -v prints its
heartbeats. A twin whose lease is stopped exits 1 with the reason on stderr.

Exit codes

CodeMeaning
0ok
1error
2usage
3license
4network
5not found (not installed, or no such doc)
6integrity
7twin unhealthy
8runtime unavailable
9conflict
10update available (-check)
11API key missing, or refused by the release endpoint
Last updated on