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)
- The CLI is the downloader, not the product — it isn’t license-gated. Simulator binaries and images stay behind the license.
- macOS and Linux; Windows zip on request.
RYSTIC_BINoverrides where it lands. loginverifies offline, parks the license in$RYSTIC_HOME, and runsdocker login registry.rystic.aiwhen a runtime answers.--no-dockerskips that half;rystic license activate <file>is the same command without it.
Commands
| Command | What 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 |
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] [--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 |
status | license, runtime, registry, installed, running, updates — one screen |
list | released sims your license covers — latest, installed, running |
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 |
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-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.
--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
- Flags before the product name are the CLI’s; 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.--tapesmounts the library read-only at/tapesand is validated against the simulator’s manifest before start;--tapestarts replaying that market once the simulator is serving.--liveopens 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
| Flag | Default | Meaning |
|---|---|---|
--live-carry N | 0 | pre-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 N | 500 | how many of the busiest open markets the state UI offers |
--live-shards N | 3 | exchange sockets the carried markets spread over, max 5 |
--live-env | the simulator’s default (prod) | exchange environment; demo needs your own demo key |
- The exchange socket lives in the CLI process. The simulator itself still dials nothing, and nothing your bot sends it reaches the exchange.
- No key setup. A release build carries a read-only exchange key;
doctorreports it asbuilt into this release.KALSHI_KEY_ID+KALSHI_PRIVATE_KEY_FILEuse your own key instead — set both or neither. - Only a simulator whose manifest declares a feed takes
--live— todaykalshi-twin.rystic featureslists it as live feed. stopends the feeder first, then the simulator;restartre-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
| Key | Env | Built-in |
|---|---|---|
runner | RYSTIC_RUNNER | native |
port | RYSTIC_PORT | 8080 |
tapes | RYSTIC_TAPES | — |
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 |
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.
| Variable | Meaning |
|---|---|
RYSTIC_HOME | install root |
RYSTIC_LICENSE | a license file path — or the license itself (JSON or base64), which is how containers get it |
RYSTIC_BIN | where the installer puts rystic |
Exit codes
Stable — script against them.
| Code | Meaning |
|---|---|
| 0 | ok |
| 1 | error |
| 2 | usage |
| 3 | license |
| 4 | network |
| 5 | not installed |
| 6 | integrity |
| 7 | simulator unhealthy |
| 8 | runtime unavailable |
| 9 | conflict (port or name in use) |
| 10 | update available (-check) |
Next
- Docker — pull, compose, CI.
- 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.