Skip to Content
The rystic CLI

Getting started

The rystic CLI

Two commands: rystic login, then rystic run. Installation is the walkthrough; this is the reference.

Install

curl -fsSL "https://www.rystic.ai/api/install?l=<from-your-install-link>" | sh   # link pasted whole
curl -fsSL https://www.rystic.ai/install.sh | sh                                # no license, no link
rystic login ./rystic-license.json                                              # activate a file (+ docker login)
  1. The CLI is the downloader, not the product — it isn’t license-gated. Simulator binaries and images stay behind the license.
  2. macOS and Linux; Windows zip on request. RYSTIC_BIN overrides where it lands.
  3. login verifies offline, parks the license in $RYSTIC_HOME, and runs docker login registry.rystic.ai when a runtime answers. --no-docker skips that half; rystic license activate <file> is the same command without it.

Commands

CommandWhat it does
login <file>install a license file, and log docker in to the registry with it
run [product]run a simulator (pulls it if missing); -d detaches, --live feeds it the exchange as it moves, args after -- go to the simulator
feed <product> --instance <name> | --url <base>feed a simulator already running the exchange as it moves — any runner, any machine
pslist running simulators — status from a live probe, not the record
stop <name>… | --allstop instances (SIGTERM, then SIGKILL after -timeout)
restart <name>… | --allstop and start again from the recorded spec
rm <name>…remove a stopped instance’s record and log
logs [-f] [--feed] <name>print a detached simulator’s log; -f follows, --feed prints its feeder’s
tail [-events …]stream a running simulator’s state events to this terminal
report <batch-out-dir> [-sort col] [-csv]the batch P&L table — exact totals, settled vs left open
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
statuslicense, runtime, registry, installed, running, updates — one screen
listreleased sims your license covers — latest, installed, running
pull [product]download and install a simulator release without running it
updateinstall a newer simulator release, or just ask whether one exists
checkboot an installed simulator and confirm it serves and resets
doctor [product]check this machine end to end without running anything
featureslist what the installed simulator can do
batch [--runner docker --image <ref>]run a bot against every tape in your library. Your bot’s own arguments take {{addr}} {{world}} {{world_kind}} {{result}} {{market}}, substituted per run
grid [product] [-- <bot command>]the bot × world matrix — bundled example bots plus yours
config [set k v]show every setting with its source, or set one
license [activate <file>]show the active license, or install one
self-updatereplace this CLI with the latest release
versionthis CLI’s version, then every running simulator’s — warning on any drift

rystic help <command> prints a command’s flags.

--json prints {schema_version, command, status, data, warnings, error}. Only run, ps, restart, up, status, list, version, config and report take it; the rest refuse it (exit 2).

run

rystic run kalshi-twin                                # serves :8080, streams events
rystic run kalshi-twin -d --name a --port 0           # detach on a free port
rystic run kalshi-twin --scenario thin_book           # boot into a named scenario
rystic run kalshi-twin --seed ./scenario.json         # seed file, applied before the first request
rystic run kalshi-twin --tapes ~/tapes --tape <id> --speed 5   # replay a captured day
rystic run kalshi-twin --live                         # a book tracking the exchange right now
rystic run kalshi-twin --runner docker -d             # as a container, from registry.rystic.ai
rystic run kalshi-twin -- --list-scenarios            # args after -- go to the simulator
  1. Flags before the product name are the CLI’s; anything after -- goes to the simulator.
  2. Default port 8080; a busy port is exit 9; --port 0 picks a free one.
  3. --runner docker needs a container runtime. The image comes from the registry, or is built locally from a pulled linux archive when image_source=archive or the registry is unreachable. See Docker.
  4. --tapes mounts the library read-only at /tapes and is validated against the simulator’s manifest before start; --tape starts replaying that market once the simulator is serving.
  5. --live opens a live session once the simulator is serving and feeds it from this process — see Live feed below.

Live feed

rystic run kalshi-twin --live                         # boot + feed from this process; Ctrl-C stops both
rystic run kalshi-twin --live -d --name live          # detached: `ps` shows "live · 1 carried · lag 120 ms"
rystic feed kalshi-twin --url http://localhost:8080   # a simulator already running — any runner, any machine
rystic feed kalshi-twin --instance live               # …or a detached instance by name
rystic logs --feed live                               # the feeder's log
FlagDefaultMeaning
--live-carry N0pre-carry the busiest N markets; 0 carries nothing until the state UI picks
--live-series a,b*series to carry; * offers every open market on the exchange
--live-max-markets N500how many of the busiest open markets the state UI offers
--live-shards N3exchange sockets the carried markets spread over, max 5
--live-envthe simulator’s default (prod)exchange environment; demo needs your own demo key
  1. The exchange socket lives in the CLI process. The simulator itself still dials nothing, and nothing your bot sends it reaches the exchange.
  2. No key setup. A release build carries a read-only exchange key; doctor reports it as built into this release. KALSHI_KEY_ID + KALSHI_PRIVATE_KEY_FILE use your own key instead — set both or neither.
  3. Only a simulator whose manifest declares a feed takes --live — today kalshi-twin. rystic features lists it as live feed.
  4. stop ends the feeder first, then the simulator; restart re-runs the recorded spec, feed included.

What the feed carries and what changes in live mode: Kalshi simulator → Live mode.

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:
  a: {product: kalshi-twin, port: 0, scenario: thin_book}
  b: {product: kalshi-twin, runner: docker, port: 0, tapes: ~/data/kalshi-orderbook, tape: <market>}
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 license. A malformed one is refused naming the file, service and key. export warns when a tape: cannot be expressed in compose.

batch —runner docker

rystic batch kalshi-twin --runner docker --image kalshi-fleet:local -tapes ~/tapes -j 4 -out ./fleet -- python3 my-bot.py

One container per world from your image (FROM registry.rystic.ai/<product>:<version> plus your bot’s runtime). Same ledger, state and logs under -out. The license only in each container’s environment; workers never in rystic ps, removed when the run ends. See Docker.

ps, stop, logs, status

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.

doctor

rystic doctor                # the product your license names
rystic doctor kalshi-twin    # name it if the license covers more than one

The pre-demo check, no boot: license, install, signing-key agreement, simulator data, container runtime, registry login, and — for a simulator that takes --live — the exchange key and where it came from. Exits 0 on pass.

list, pull, update

rystic list                                # PRODUCT LATEST INSTALLED RUNNING SUMMARY
rystic pull -version <x.y.z> kalshi-twin   # omit for latest
rystic update                              # the CLI and every installed simulator
rystic update -check                       # report only; exit 10 if newer 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
KeyEnvBuilt-in
runnerRYSTIC_RUNNERnative
portRYSTIC_PORT8080
tapesRYSTIC_TAPES
image_sourceRYSTIC_IMAGE_SOURCEregistry (archive builds locally)
registryRYSTIC_REGISTRYregistry.rystic.ai
docker.binaryRYSTIC_DOCKERdocker, else podman
docker.hostDOCKER_HOST
release_endpointRYSTIC_RELEASE_ENDPOINThttps://www.rystic.ai/api/twin-release
catalog_endpointRYSTIC_CATALOG_ENDPOINThttps://www.rystic.ai/api/catalog

Files and environment

Everything lives under $RYSTIC_HOME (default ~/.rystic): installed simulators, the current marker per product, the active license, config.json (once you config set something), and run/ — one record per instance.

VariableMeaning
RYSTIC_HOMEinstall root
RYSTIC_LICENSEa license file path — or the license itself (JSON or base64), which is how containers get it
RYSTIC_BINwhere the installer puts rystic

Exit codes

Stable — script against them.

CodeMeaning
0ok
1error
2usage
3license
4network
5not installed
6integrity
7simulator unhealthy
8runtime unavailable
9conflict (port or name in use)
10update available (-check)

Next

  1. Docker — pull, compose, CI.
  2. Kalshi simulator — a real matching engine for trading bots.

What leaves your machine

The simulator itself makes no outbound calls (the Kalshi simulator’s replayed-market metadata fetch, documented on its page, is the one exception and goes to Kalshi). The CLI talks to Rystic in one exchange: when it needs a release it sends your license file plus the product, version, OS, architecture and which command asked. That happens on pull, update, status (its update check — status --offline skips it) and run when the simulator is not installed yet; a simulator you have already pulled runs with no call. --runner docker also pulls the image from registry.rystic.ai with the license as the credential.

--live and feed add the exchange itself: the CLI dials it from your machine — a scan of open markets over REST, then WebSocket subscriptions for the markets you carry — with the read-only key built into the release, or yours. The simulator still dials nothing.

Rystic records that exchange — licensee, product, version, OS/arch, command, time — and nothing else; no IP or hostname is kept. If your agreement excludes you from usage metrics, your license carries "metrics": "off" and nothing is recorded for it; rystic license prints your claims.

Last updated on