Getting started
Plans and API keys
Plans
| Free | Pro | Enterprise | |
|---|---|---|---|
| Simulators | GitHub, Linear and Slack | every simulator | every simulator, plus custom builds |
| Credential | an API key | an API key | a license file |
| Usage | 1 seat, 10 hours a month | per seat, pooled credit, then per simulator-minute | unlimited |
| Sign-up | GitHub or Google, no card | 14 days free, card required | one week, then a contract |
| Scenarios | the full scenario pack, seeds | the full scenario pack, seeds, tapes, --live | the full scenario pack, seeds, tapes, --live |
| Control plane | all of it, network faults and latency included | all of it: seed, scenario, clock, inspect, egress, the state UI, network faults and latency | all of it |
| Verified UIs | no: rystic ui is refused | yes: rystic ui <simulator> | yes: rystic ui <simulator> |
| Docker | registry.rystic.ai, the key is the credential | registry.rystic.ai, the key is the credential | registry.rystic.ai, the license is the credential |
| Network | the simulator checks in with Rystic while it runs — what it sends | the same | none; runs offline |
A new account starts on Free: sign up with GitHub or Google, no card. Free runs GitHub, Linear and Slack for 10 hours a month, with every control route, network faults and latency included; Slack’s verified UI is Pro and Enterprise.
Pro is opt-in: start the trial from pricing or the Account page, through Stripe Checkout. The trial converts to paid Pro on day 14 unless you cancel it first under Manage billing on the Account page; canceling within the trial means no charge. An organization whose trial or subscription ends falls back to Free.
A verified UI is a client app built against the service’s real UI and checked against it. rystic ui <simulator> runs it against a running simulator: as the image registry.rystic.ai/<simulator>-ui:<ver>, pulled with your docker login, or without Docker from the UI’s archive on your local Node.js. The registry serves it only to a plan or license that includes the verified_ui feature, and the UI serves only a simulator granted it. It never ships inside a simulator’s archive or image. Every simulator’s own state UI, /_rystic/ui, is on every plan.
Pricing has the prices. Features by plan lists what each plan includes, and the control plane reference lists every route with the feature it needs.
Get an API key
- Sign up at rystic.ai with GitHub or Google. A new account starts on Free; start the Pro trial from pricing for every simulator.
- Open Account → API keys and create one. Personal keys belong to you; Team (for CI) keys belong to the organization and suit CI and shared machines.
- Copy it when it is shown. It starts
rys_and is shown once; the site keeps only a hash.
Revoke a key on the same page. A simulator running on it stops within about a minute.
Sign the CLI in
Installation signs the CLI in, through the browser or with rystic login rys_…. The CLI reference lists where else the CLI looks for a key, such as RYSTIC_TOKEN.
When the CLI refuses the key
pull, update and run of a simulator not installed yet ask the release endpoint for a build with the key. A refusal exits 11 and names where the key came from. So does run of an installed metered build that finds no key, or a malformed one (the last row):
| you saw | it means | do this |
|---|---|---|
release endpoint refused (…): invalid_token, then the API key from <where> is not one Rystic knows — make a new key at https://www.rystic.ai/account/api-keys, then … | the key is mistyped, or not a Rystic key | create a key and rystic login it, or replace it where the message says ($RYSTIC_TOKEN, config.json) |
… refused (…): revoked, … was revoked — … | the key was revoked | create a new key |
… refused (…): key_expired, This API key has expired. … | a key pulled through Vercel Connect is past its 24 hours | pull another with create_api_key, or create a key |
… refused (…): org_deleted, … belongs to an organization that was deleted — … | its organization is gone | create a key in another organization |
… refused (…): no_plan, then a sentence saying why the organization has no plan and linking to pricing; docker pull returns DENIED with the same sentence | the organization has no plan | start the trial, or subscribe, at pricing |
… refused (…): product_not_in_plan, then This organization's plan does not include … | the organization’s plan leaves that simulator out: Free runs only GitHub, Linear and Slack | upgrade |
… refused (…): feature_not_in_plan, then Verified UIs are Pro and Enterprise; this organization's plan does not include them …; docker pull …-ui returns DENIED with the same sentence | rystic ui or a <simulator>-ui pull on a plan without the verified_ui feature | pricing |
… is the metered edition and runs on an API key, or the token from … is not a Rystic token | run of an installed metered build found no key, or a malformed one | rystic login rys_…, or set RYSTIC_TOKEN |
Asking for a build with no license and no key exits 3 instead: pull, update and run of a simulator not installed yet print no license file found — …, ending with or sign in for the metered edition: run `rystic login` to sign in or sign up in the browser (with a key: `rystic login rys_…` or `--token-stdin`). A key not shaped rys_… exits 11, naming where it came from: … is not a Rystic key (rys_ and 43 more characters): replace it.
What a running simulator sends
This is the section a security review reads.
On Free and Pro, the simulator itself checks in with https://lease.rystic.ai over HTTPS while it runs. Every call carries your API key.
| call | when | what it carries |
|---|---|---|
| register | at start, before it serves | product, version, a random id for this process, the start time |
| heartbeat | every 30 seconds | which 30-second windows since the last heartbeat it was in use |
| close | on exit | the same, plus why it stopped |
“In use” means the simulator served an API request, held a subscribed WebSocket, rendered a webhook or played a tape in that window. Control-plane calls alone do not count. So Rystic learns that a simulator was busy, never what it was doing.
Never sent: API request or response bodies, paths, headers or counts; hostnames or usernames; the simulator’s data or state; scenario, seed, tape or network-profile contents; your OS and architecture; any device identifier. Rystic’s server sees the connection’s source address, as any HTTPS server does, and does not store it.
Kept on your machine: the key, and the last lease Rystic signed, which allows one offline start.
The CLI itself talks to Rystic when it needs a release, an image or your plan: What leaves your machine lists every call it makes and what each sends.
The Kalshi simulator fetches a replayed or live-fed market’s public record from Kalshi, not from us: The one outbound call.
Offline and outages
- At start the simulator must reach Rystic. If it can’t, it keeps retrying for up to 10 minutes before giving up.
RYSTIC_REGISTER_WAIT(a duration like2m, or0for one attempt) changes that. - The simulator a machine last ran on an API key can start once more without reaching us, within 24 hours of its last check-in, and serves for 10 minutes while it keeps retrying. A CI runner that starts fresh each time has no such allowance; the retry window is what carries it through a short outage.
- While running, a lost connection leaves the simulator running for up to 2 hours.
- A fully offline machine needs Enterprise.
What stops a simulator
A simulator on Free or Pro prints why it stopped, then exits non-zero.
| you saw | it means | do this |
|---|---|---|
| a message naming the 10 hours the Free plan includes | Free’s 10 hours are used up this month | wait for the next month, or upgrade |
The … plan does not include dropbox-twin. (or any simulator but GitHub, Linear and Slack) | Free runs only GitHub, Linear and Slack; a Pro organization whose trial or subscription ended runs on Free | upgrade, or pay the open invoice at Account |
Free: 10h a month, Linear, GitHub, Slack. Upgrade at https://www.rystic.ai/pricing. | not a stop: a Free organization sees this at start | nothing, or upgrade |
Billing for this organization has lapsed, so its simulators run on a reduced plan. … | not a stop: a Pro organization whose trial or subscription ended unpaid runs on Free | subscribe at pricing, or pay the open invoice at Account |
| a message naming the spend cap | your organization’s spend cap is reached | raise it at Account → Spend limit |
revoked or an invalid key | the key was revoked or mistyped | create a new key and rystic login it |
This API key has expired. … | a key pulled through Vercel Connect reached its 24 hours, and the simulator stopped at its next check-in | pull another with create_api_key and start the simulator again |
| an error naming the clock | the machine’s clock is more than 2 hours fast | fix the clock |
Enterprise: license files
Enterprise runs on a signed license file instead of an API key, and the simulator sends nothing. Talk to us for one. It 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.
Activation
rystic login ./rystic-license-acme-2026.json
license: /Users/you/.rystic/rystic-license.json
licensee: Acme
products: [stripe-twin, kalshi-twin]
issued: 2026-08-19
expires: 2027-08-19 (valid)
signature: verified
loginverifies the license and, if Docker is running, logs Docker in toregistry.rystic.aiwith it.--no-dockerskips that half.rystic license activate <file>is the same command minus the Docker half.rystic licenseprints the active license and its claims. A license scoped to some features lists them underfeatures:; one without that line grants every feature, verified UIs included.
The CLI parks the license in $RYSTIC_HOME and verifies it on your machine against a public key built into the CLI. Nothing phones home to check it. To point one command or container at a different license, set RYSTIC_LICENSE to a file path, or to the license itself, JSON or base64.
A license wins over an API key. Once a license is parked, pull, update, status, list and doctor use it rather than any key, so the machine installs Enterprise builds; status and doctor still report a stored key’s standing. Starting a metered build with --license or $RYSTIC_LICENSE also set is refused (exit 2, … a license was given too … — drop one); drop the flag or unset the variable.
Rystic’s release endpoint records each download (licensee, product, version, OS/arch, command, time) and nothing else. A license signed with "metrics": "off" records nothing; rystic license prints your claims.
Expiry and renewal
Rystic renews your license before it expires, through the end of your contract. Every command that talks to the release endpoint (rystic pull, rystic update, the rystic status update check, and rystic run on a simulator not yet installed) fetches the renewal and installs it over the active license, printing license renewed through <date>. When Docker is logged in to the registry, the CLI logs it in again with the renewal.
A license you pass with --license or $RYSTIC_LICENSE, as CI usually does, is never rewritten: the CLI saves the renewal in $RYSTIC_HOME and prints its path, and you copy that file into your secret. A simulator you start without the CLI, such as a container given RYSTIC_LICENSE, keeps the license it was given until you update it.
rystic license and rystic doctor show the expiry date. For 7 days past it, simulators still start with a warning and rystic pull still fetches your renewal. After that, commands fail with license_expired: ask Rystic for a new license and rystic login it. 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 | more than 7 days past its date | ask Rystic for a new license |
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 |
rystic doctor names the broken piece without revealing your license; Exit codes lists every code.
Features by plan
A simulator’s control plane is split into features. Each feature unlocks a group of routes and boot inputs. Pro and Enterprise include every one; Free includes every one but verified_ui:
| Feature | Unlocks | Free | Pro | Enterprise |
|---|---|---|---|---|
| none | identity, the route list, reset and the simulator’s docs | yes | yes | yes |
state_view | reading state, the egress log, the call log and the event stream | yes | yes | yes |
state_set | seeding, the clock, the RNG, credential bindings and timelines | yes | yes | yes |
scenarios | listing and loading named scenarios | yes | yes | yes |
faults | network latency and rejections, dropped sockets, revoked credentials, duplicate webhook deliveries and capped page sizes | yes | yes | yes |
ui | the state UI and the API explorers | yes | yes | yes |
replay | Kalshi’s tapes and live feed | yes | yes | yes |
verified_ui | the verified UI image, through rystic ui | no | yes | yes |
Control plane → Features maps each feature to its routes.
A simulator on Free or Pro learns its plan from the lease Rystic signs when it starts. The simulator renews the lease as it checks in every 30 seconds. A renewal that changes the plan applies from the next request, with no restart. GET /_rystic/version shows the grant under lease.entitlements.features: ["*"], every feature, on Pro, and every feature but verified_ui on Free.
An Enterprise build runs on a license file, holds no lease and serves every route.