Getting started
Licensing
Editions
| Free edition | Licensed | |
|---|---|---|
| Simulators | Slack, GitHub | every simulator your license names |
| Setup | download one binary, run it | the rystic CLI plus a license |
| World | one fixed world per simulator | the full scenario pack, seeds, tapes, --live |
| Control plane | version, routes, reset, network only; the rest is compiled out | seed, scenario, clock, inspect, egress, UI |
| Docker | no | registry.rystic.ai, the license is the credential |
| Reports usage | yes — what it sends | no; the CLI records release downloads only (details) |
Getting a license
Request access. A license reaches you one of two ways:
- An install link —
https://www.rystic.ai/api/install?l=…. The longl=value is the license; one pasted line installs the CLI and activates it. - A license file, named like
rystic-license-acme-2026.json.
Both carry the same signed license: licensee, the products it covers, and an expiry date. Installation has the commands for each.
Activation
rystic login ./rystic-license-acme-2026.json
license: /Users/you/.rystic/rystic-license.json
licensee: Acme
products: [stripe-twin, kalshi-twin]
expires: 2027-08-19 (valid)
loginverifies the license and, if Docker is running, logs it in toregistry.rystic.ai.--no-dockerskips that half.rystic license activate <file>is the same command minus the Docker half. You will meet both spellings.rystic licenseprints the active license and its claims.
Where it lives
The license is verified on your machine against a key built into the CLI and parked in $RYSTIC_HOME (default ~/.rystic). Every later command finds it there without a flag. Nothing phones home to check it.
To point a single command or a container at a different license, set RYSTIC_LICENSE to a file path — or to the license itself, JSON or base64. That is how a container gets it; no image ever carries one. Docker has the mounts.
Expiry and renewal
rystic license and rystic doctor show the expiry date. Past it, commands answer license_expired; ask Rystic for a renewal and rystic login the new file. rystic login --force activates a license even when it is expired.
License errors
| you saw | it means | do this |
|---|---|---|
curl: … error: 403 from the install link | the link’s l= value was cut short | paste the full link again, or use the license file |
no license file found … (exit 3) | no license activated on this machine | rystic license activate ./rystic-license-acme-2026.json |
signature does not verify | the file was altered or re-encoded in transit | re-download it exactly as sent; ask for a fresh one |
license_expired | past its date | ask Rystic for a renewal |
asset_not_found … carries no build for this license | that version was never cut for you | ask Rystic, or rystic pull -version <x.y.z> one you have |
Exit code 3 is always a license problem. rystic doctor names the broken piece without revealing your license.
Free edition
A downloadable twin you can run with nothing else: no license, no signup, no command-line tool, no config. The download is the whole setup.
Get it
One fixed link per simulator. Every release replaces the build behind it; there is no version to pick.
| Simulator | Link | World |
|---|---|---|
| Slack | /dl/free/slack-twin | design_studio |
| GitHub | /dl/free/github-pulls-twin | github-pulls_open_queue |
tar xzf slack-twin-free-darwin-arm64.tar.gz
./slack-twin
# expect: rystic: slack-twin <ver> listening on [::]:8080 (model sha256:…) — free edition, world design_studio
curl -s localhost:8080/_rystic/version
# expect: {..."edition":"free",..."product":"slack-twin",...,"world":"design_studio"}
Point your client’s base URL at http://localhost:8080 — https://slack.com/api/ becomes http://localhost:8080/api/, https://api.github.com becomes http://localhost:8080. Any Authorization header works. Port taken? --addr :9000.
What it is
- The modeled API, reads and writes alike. Post a message, open a pull request, submit a review — the world moves as the licensed twin’s does.
- One canonical world per simulator, loaded at start: the table above names it.
POST /_rystic/resetor a restart puts it back. GET /_rystic/versionandGET /_rystic/routes, so a client can tell what it is talking to./_rystic/networkfault injection.
What it is not
The control plane is compiled out, not switched off. Every one of these answers 404 with a body naming the paid unlock:
| Removed | What the licensed twin does with it |
|---|---|
POST /_rystic/seed, /_rystic/scenario, /_rystic/clock, /_rystic/seed-rng | set the world, pin the clock, choose any of the pack’s scenarios |
GET /_rystic/inspect, /_rystic/egress, /_rystic/events, /_rystic/registers, /_rystic/ui | dump state, read rendered webhooks, watch the twin live |
/_replay/*, --scenario (ignored), --seed, RYSTIC_SCENARIO, RYSTIC_SEED, --live | tapes, boot-time worlds, live feeds |
State is visible only through the modeled API’s own GETs. sweep, docker via the registry, and the rystic CLI’s run/pull are licensed too.
What it reports
The free edition phones home; the licensed twin never does. At start, once an hour, and on a clean stop it POSTs to www.rystic.ai/api/free-metrics:
- product, version, edition, world
- operating system and architecture
- a random install id, written to
.rystic-free-idbeside the binary on first run - the event (
start,heartbeat,stop) and seconds since start - request counts per API route
Nothing from request bodies, no hostnames, and the server stores no IP. There is no opt-out; an unreachable endpoint changes nothing about how the twin runs. The EULA in the archive (LICENSE.txt §13) says the same.
Upgrading
A license is the same binary plus the control plane, the scenario pack, seeds, tapes, --live, sweep, and docker via registry.rystic.ai. Get a license, then rystic pull slack-twin or rystic pull github-pulls-twin.