CLI reference. Every command and flag.

This is what karts prints when you run it with no arguments:

karts: run this branch in a fresh micro-VM with its own database and a URL.

  karts login                      sign in (device code; works over SSH)
  karts logout                     revoke this device's token
  karts whoami                     who you are signed in as
  karts init                       link this repository to a project (writes project: into karts.yml)
  karts onboard [-o FILE]          the prompt that lets your coding agent write karts.yml
  karts check-config               check karts.yml and the upload locally, as the host would
      --output text|json           json: one object for agents and CI
  karts up                         build this branch and serve it
      --new                        a second environment for this branch
      --commit [ref]               upload a commit's tree instead of the working tree (default HEAD)
      --branch NAME                the branch name (needed with a detached HEAD)
      --workspace ID               the workspace id (default: this clone's)
      --base REF                   the base commit's ref (default: origin/<default branch>)
      --reseed                     run this branch's seed after its migrations
      --from-scratch               empty database, all of this branch's migrations, its seed
      --allow-destructive          apply migrations that drop or rewrite data
      --snapshot FILE              seed the template from a masked snapshot (default: karts.yml seed_snapshot:)
      --no-snapshot                ignore seed_snapshot:
      --timings                    print how long each step took
      --output text|json|url       json: one object for CI (--json is the same); url: only the URL
  karts fork [NAME]                a new environment copied from a running one, memory and all
      --name N                     the fork's name (default: the branch's slug and 4 hex)
      --restart-app                restart the fork's app with its own URLs (Postgres keeps running)
      --output text|json|url       as karts up; --timings as karts up
  karts down [NAME]                tear the environment down (--output json for CI)
  karts status [NAME]              the environment, its revisions and warnings
  karts url [NAME]                 the public URL of a serving environment (--output json for CI)
  karts list [--branch B]          this project's environments (--output json for CI)
  karts logs [NAME] [--follow] [--building|--failed|--revision N] [--tail N]
      --template                   the build log of the revision's database template (its seed's output)
  karts exec [NAME] [-i] [--building|--failed|--revision N] -- CMD...
      -i, --stdin                  send local stdin to CMD (without it, CMD gets an empty stdin)
  karts fakes calls [NAME]         what the app sent to karts.yml fakes: and stubs: (and unmatched hosts)
      --fake F, -f, --output json  one fake's calls; keep following; one JSON call per line
  karts egress log [NAME] [-f]     what karts.yml internet: let the app reach, and what it refused
      --since 24h, --output json   from when (90m, 7d, a date); kept up to 30 days, gone envs too
  karts egress off|on [--team T]   the team's internet kill switch (owners)
  karts egress approvals           production approvals (karts egress help: allow-production)
  karts recordings list|rm|promote the project's recorded calls (karts.yml recordings:; karts recordings help)
  karts claims [release]           migration-number claims in this project
  karts secret set|list|rm|import  values the app needs that stay out of karts.yml (karts secret help)
  karts db snapshot [--check]      a masked copy of real data, made here (karts db snapshot help)
  karts project delete [PROJECT]   delete a project (owners only): its claims, uploads and templates
      --down-all                   take its environments down first (refused otherwise)
      --yes                        do not ask to type the project name
  karts webhook send [ENV] stripe TYPE   a signed Stripe event from the fake, to the app (--local)
  karts webhook send [ENV] --url /PATH   any provider's webhook, --sign github|slack (karts webhook help)
  karts report [FILE] [--yes]      send the Karts team a problem report (shows it, then asks)
  karts mcp                        the same commands as MCP tools on stdio
  karts version [--capabilities]   the version, commit and build date; or the karts.yml keys

Local mode: this branch on this machine, its own database and a *.localhost URL; no account,
no upload (macOS; Postgres in Docker).
  karts up --local                 build this branch here and print its URL
      --new, --branch, --base, --snapshot FILE, --no-snapshot, --output text|json|url, --timings as above
      --reinstall                  run install: even when its inputs are unchanged
      --pass-unmatched             outside calls no karts.yml fake or stub answers reach the internet
      --allow-destructive          apply migrations that drop or rewrite data
      --keep-db                    keep this environment's database (the default on a repeat up) or fail
      --fresh-db                   rebuild it from scratch instead
      --reuse-template             with seed: or migrate:, clone the base's template when nothing
                                   Karts can see changed (those steps may read what it cannot see)
  karts exec --local [NAME] [--service S] -- CMD...  run CMD here with the environment's variables
  karts psql --local [NAME] [-- ARGS]  psql into its database
  karts list --local               local environments and their URLs
  karts logs --local [NAME] [-f] [--tail N] [--service S] [--build]
  karts fakes calls --local [NAME] [--fake F] [-f] [--output json]
  karts egress log --local [NAME] [-f] [--since 24h] [--output json]
  karts status --local [NAME]      an environment (or the daemon and every environment)
  karts down --local [NAME|--all [--yes]]  stop it, drop its database and role, free its ports
  karts check-config --local       also report what local mode does not support
  karts local serve|stop           the per-user daemon (started on demand)
  karts local uninstall [--yes]    remove everything this install made, after listing it; the shared
      --all-installs               Postgres container and volume too, when other installs use them

Environment: KARTS_API (control API URL), KARTS_TOKEN (token; overrides the stored one),
KARTS_TOKEN_STORE=file (keep the token in a 0600 file, never the OS keychain).

karts login, logout, whoami

karts login signs in with a device code: the CLI prints a code and a link, and you approve it in a browser. The token is stored in the macOS Keychain or the Linux Secret Service where available. karts logout revokes this device's token on the server. karts whoami shows who you are signed in as, your teams and when the token expires.

karts init

Links the repository to a project in your team and writes project: into karts.yml, creating a starter karts.yml if there is none. Run it once per repository and commit the file.

karts onboard

Prints the onboarding prompt: instructions any coding agent follows to explore the repository, write karts.yml, check it, run karts up, fix failures, and report what Karts runs every time. -o FILE writes it to a file instead (--force overwrites). The same prompt is at /onboard.md; the CLI's copy matches its own version. No sign-in needed.

karts check-config

Checks karts.yml and the files karts up would upload, locally and offline, with the Karts host's own parser and migration rules: indentation, keys, reserved variables, services: references, the upload limits, the migrations directory (including git-ignored migration files), service directories, and heuristics for credentials in karts.yml and credential files that would be uploaded. It warns about downloads the VM cannot make (hosts outside the package registries), about a start bound to 127.0.0.1, and when the default branch has no karts.yml yet. It then lists what this karts.yml runs. --output json prints one object for agents and CI. Exit status 0 means no errors. It covers this tree only: karts up still checks migrations against the default branch, destructive SQL, the runtime image and links, and runs your commands. The secret checks are heuristics, not proof.

karts up

Builds the current branch and serves it. The first run on a branch creates its environment; each later run builds a new revision beside the current one and switches to it when it is ready. If the new revision fails, the old one keeps serving.

FlagUse it when
--newYou want a second environment for the same branch, beside the first.
--commit [ref]You want an exact snapshot: upload a commit's tree instead of the working tree. Use it in CI.
--branch NAMEThe checkout has a detached HEAD, as in CI. The name is also what migration claims use.
--workspace IDYou want repeated CI runs to update one environment.
--base REFThe template should come from another ref than usual: origin/<default branch>, or the base: branch when karts.yml sets one.
--reseedYour branch changes the seed and you want it run on top of the template.
--from-scratchYour branch edits or removes a migration main already ran. Builds an empty database, then all of the branch's migrations, then its seed.
--allow-destructiveYou mean to drop or rewrite data. The statements are printed.
--snapshot FILEThe template should load a masked copy of real data instead of running seed. Without it, seed_snapshot: in karts.yml is used. See karts db snapshot.
--no-snapshotYou want the plain seed, ignoring seed_snapshot:.
--timingsYou want the time each step took.
--output jsonA script or agent is reading the result: one JSON object on stdout (--json is the same). See the fields.
--output urlYou want only the URL, for example in CI. Exits 1, with the reason on stderr, if the environment did not become ready.

karts fork

Copies a running environment into a new one, with its memory and its data: what the app holds in memory and every row written so far. The source pauses for about 0.1 s while it is copied (66 to 134 ms in our runs, median 82 ms). The first fork of a new revision pauses it longer. After that, each side's changes stay its own.

With no name it forks this branch's environment. --name N names the fork. --restart-app restarts the fork's app so it uses its own URLs; the app's in-memory state is lost, and the database keeps running. --output and --timings work as in karts up. Forking is hosted only.

karts down

Tears the environment down: its VM and database are destroyed. The branch's migration-number claims are kept. Environments it linked to keep running, and the CLI names them. --output json prints {schema_version, env, name, state: "gone"}.

karts status, url, list

With no name, status, url, logs, exec and down act on this branch's environment in this clone; --branch and --workspace pick another, as in CI.

  • karts status prints the environment's state, URL, revisions, warnings and links.
  • karts url prints the public URL of a serving environment and exits 0, or exits 1 when none serves. --output json adds its name, state and key.
  • karts list lists every environment of this project, with its branch, state and URL. --branch B narrows it to one branch.

karts logs, exec

logs and exec use the serving revision; --building, --failed or --revision N pick another. logs --follow streams; --tail N shows the last lines.

exec runs a command inside the VM with DATABASE_URL set. Its stdin is empty unless you pass -i (--stdin), which sends your local stdin, up to 1 MiB: karts exec -i -- psql < dump.sql. Without -i, karts exec never waits for your stdin, so it does not hang in an agent's shell. It has no terminal: interactive prompts do not work.

karts fakes calls

karts fakes calls [NAME] [--fake F] [-f] [--output json] [--local]

Lists what your app sent to its fakes and stubs, oldest first: the time, the fake, the request and the answer. Calls to hosts nothing answers are listed as unmatched. Keys, passwords, cookies and card numbers are removed before a call is stored.

  • --fake F shows one fake's calls, for example stripe, stub or unmatched.
  • -f (--follow) keeps printing new calls. --output json prints one call per line.
  • It reads the serving revision, like karts logs; --building, --failed and --revision N pick another. --local reads a local environment.

karts egress

karts egress log [NAME] [-f] [--since 24h] [--branch B] [--output json] [--local]
karts egress off|on [--team T]
karts egress approvals
karts egress allow-production PROVIDER [--for 7d] [--remove]

Shows and controls what internet: lets your app reach.

  • log lists each outside connection and refusal: the time, host, port, result and bytes each way, never what was sent. Records are kept for up to 30 days, also for environments that are gone: name one by its old name. --since takes 90m, 24h (the default), 7d or a date. -f keeps printing. --output json prints every record. --local reads this machine's records.
  • off turns the team's internet off on every Karts host: new connections are refused and open ones close. on turns it back on. Team owners only.
  • allow-production lets branches whose karts.yml has allow_production: [PROVIDER] reach that provider's production hosts, or, for stripe, use live keys. Team owners only. It lasts 7 days, or what --for says, from 1h to 30d. --remove revokes it at once. approvals lists the project's approvals and the team's switch.

karts recordings

karts recordings list [--branch B] [--host H] [--output text|json]
karts recordings rm [--branch B] [--host H] [--key K]
karts recordings promote --branch B

Manages the project's recorded calls on its Karts host. list shows them without their bodies. rm deletes the ones that match its flags, and needs at least one. promote makes a branch's recordings the default branch's, so every branch can replay them.

karts webhook send

karts webhook send [ENV] stripe EVENT-TYPE [--id OBJ] [--data FILE.json] --local
karts webhook send [ENV] --url /PATH [--body FILE|-] [--header 'K: V']... [--method POST]
                   [--sign github|slack --secret-env VAR] [--local]

Sends your app a fake webhook, signed the way the real service signs it, so you can test the code that receives it.

  • Stripe: the environment's fake Stripe sends an event of that type, such as checkout.session.completed, about its latest object of that kind (or --id), to your webhook_path. --data changes fields of the object. This works with --local only for now; in the cloud the fake Stripe still sends its own webhooks as payments happen.
  • Any other service: --url posts the body to that path on the environment's own URL, in the cloud or locally. --sign github or --sign slack signs it as GitHub or Slack does, with the secret in the variable --secret-env names. The secret is never printed.
  • It exits 0 only when your app answered with a 2xx.

karts claims

Lists the migration numbers main has, the numbers other branches have claimed, and the next free number. karts claims release gives up your branch's claims, for example when you abandon it.

karts secret

karts secret set NAME [--project | --branch B | --env E] [--local]
karts secret list [--local] [--output json]
karts secret rm NAME [--project | --branch B | --env E] [--local]
karts secret import FILE [--project | --branch B | --env E] [--yes] [--local]

Stores values your app needs that must not be in karts.yml or the repository. set reads the value from stdin, or asks for it without showing it. list shows names, scopes and times, never values. import reads a .env file, lists the names and asks first.

A value is for the whole project (the default), one branch or one environment; the most specific wins. --local keeps it on this machine for karts up --local. The app gets only the names listed in secrets:, and a change reaches an environment at its next karts up. Logs show ‹secret› in place of a value.

karts db snapshot

karts db snapshot [--from URL] [--out FILE] [--check] [--salt-file FILE] [--allow-kept-pii]
karts db snapshot ls
karts db snapshot push FILE
karts db snapshot rm FILE|SHA

Makes a masked copy of real data, on this machine. It reads the Postgres database at --from (or KARTS_SNAPSHOT_FROM; a replica is best) in one read-only transaction, replaces personal data with fakes, keeps the links between tables, and writes a file. The raw data never leaves your machine.

  • --check prints the plan and the decision for every column, and writes nothing.
  • --salt-file FILE keeps masked values the same across snapshots. The file must be outside the repository.
  • --allow-kept-pii allows columns you marked keep_pii.
  • ls lists the snapshots here and on the project's host. push uploads one; karts up does that for you when it needs one. Only the masked file is uploaded, and it is stored encrypted. rm deletes one here and on the host, with the templates built from it.

The rules live under masking: in karts.yml. Branches use the file through seed_snapshot: or karts up --snapshot FILE.

karts project delete

Deletes a project: its migration-number claims, uploads and template databases, and frees one of your team's project places. Only a team owner can delete a project. It prints the project's name, team and environments, and asks you to type the name; --yes skips that. A project that still has environments is refused unless you pass --down-all, which takes them down first. With no argument it deletes the project named in karts.yml; the file itself is not changed.

karts report

Sends the Karts team a plain-text report when Karts itself fails. It reads FILE, or the report on stdin, adds the CLI's version and OS, and shows you exactly what it will send before it asks. Keys and passwords it can spot are removed, on your machine and again before the report is stored. --dry-run shows it and sends nothing. --yes sends without asking, once you have said yes. An agent without the CLI can send the same plain text to https://api.karts.kartikey.fyi/report; opening that address says what to include.

karts mcp

Runs an MCP server on stdio with the tools up, down, status, logs, exec and check_config. See MCP setup.

karts version

Prints the CLI's version, the commit it was built from and the build date. karts version --capabilities prints instead the karts.yml keys, service keys and features this CLI accepts, one line each.

Local mode

karts up --local runs this branch on your own Mac with the same karts.yml. No account, and nothing is uploaded. Each branch gets its own database and its own *.localhost URL. You need Docker Desktop running: Karts keeps Postgres (and MySQL) in its own containers, named karts-local-…, and never touches yours. SQLite needs no container. Apple Silicon Macs are tested.

karts check-config --local   # also says what local mode does not support
karts up --local             # build this branch here and print its URL
karts list --local           # local environments and their URLs
karts status --local         # one environment, or the daemon and all of them
karts logs --local -f        # the app's output
karts exec --local -- CMD    # run CMD here with the environment's variables
karts psql --local           # psql into its database
karts down --local           # stop it, drop its database, free its ports
  • Each branch's URL stays the same: http://<branch>.<project>.localhost:7400. KARTS_URL, ${services.<name>.url}, links, the mail inbox and the fakes' sign-in pages all use that one host, so cookies and sign-in callbacks meet. The longer hashed name an environment also has still answers; a browser that opens it is sent to the short one.
  • If port 7400 is taken the first time local mode starts, and KARTS_LOCAL_PORT is not set, Karts takes the next free port, keeps it, and every karts up --local says so. Later starts use the kept port; if something else holds it then, the daemon does not start and names what holds it. With KARTS_LOCAL_PORT=N, Karts uses exactly port N and refuses to start if N is taken. To choose the port: karts local stop --apps, then KARTS_LOCAL_PORT=7400 karts up --local (and karts up --local again in each other environment). Karts moves the port only while no environment runs.
  • karts up --local checks free disk space first, on the disks that hold the worktree and Karts' local files. Below 2 GB it refuses before it stops or builds anything. Below 8 GB it warns. An install and a build can take several GB, and when a Mac's disk fills, Docker Desktop stops its VM and every container on the machine stops with it, Karts' Postgres included. A failure caused by a full disk says the disk is full first. The variables below change both limits.
  • karts up --local takes --new, --branch, --base, --snapshot FILE, --no-snapshot, --output, --timings and --allow-destructive, as hosted.
  • A repeat karts up --local keeps the database. Rows you made by hand stay, only new migrations run, and the seed does not run again. If the database can't be kept (an applied migration was edited, or the database settings changed), it is rebuilt and up says why. --keep-db fails instead of rebuilding. --fresh-db always rebuilds. Postgres only: MySQL and SQLite are rebuilt each time.
  • --link NAME=BRANCH points a links: entry at another branch, or environment, of the linked project. Repeat it for more links.
  • --pass-unmatched lets outside calls that no fake or stub answers through to the real internet. Without it they are caught. Production hosts, ports 25 and 53, IP addresses and live keys stay refused, as with internet:.
  • install runs again only when what it reads changes: the lockfiles, any package.json, patches, Prisma schemas, .npmrc, .env, or the Node and package manager on your PATH. Editing your code or adding a migration does not re-run it, even with a prepare or postinstall script: husky only installs git hooks. A script that generates code from your source files belongs in a service build:, which runs on every up. An install that does more than the package manager's install (npm ci && npm run build) runs every time. --reinstall runs it anyway.
  • With seed: or migrate:, an up that has no database to keep builds it from scratch: those steps may read files Karts cannot see. --reuse-template clones the base's template instead when nothing Karts can see changed. It is faster, and you take on what Karts cannot see.
  • Your machine's own Node and package managers run the app. Use the versions the repository pins.
  • Not in local mode: redis:, storage:, karts fork, --reseed and --from-scratch.
  • karts exec --local -- CMD runs a command on your machine, in the environment's folder, with the variables its start gets: DATABASE_URL, PORT, the fakes' keys and proxy, and the rest. Your shell's own variables are kept, but every name Karts sets replaces the shell's value. --service S picks a service. It can read your terminal, so interactive tools work.
  • karts psql --local opens psql on the environment's Postgres database; arguments after -- go to psql. Without psql on your machine, it uses the one in Karts' Postgres container. In the cloud, use karts exec -i -- psql.
  • Two branches at once: one git worktree per branch, and karts up --local in each.
  • karts down --local --all stops every local environment. karts local serve and karts local stop start and stop the background daemon, which karts up --local starts when it needs it.

karts local uninstall

Removes everything this install of local mode made: every local environment with its database, the background daemon, and Karts' local files. It lists them first and asks; --yes skips the question. Karts' Postgres container and its volume go too, unless another install of Karts on this machine still keeps databases in them; --all-installs removes them anyway.

Environment variables

VariableEffect
KARTS_TOKENThe token to use. Overrides the stored one. For CI and agents.
KARTS_APIThe control API URL. You don't need to set it.
KARTS_LOCAL_PORTLocal mode's port (default 7400). Read when the background daemon starts, and kept for later starts. Karts uses exactly this port: if it is taken, the daemon does not start. Without it, a first start takes the next free port after 7400 and says so.
KARTS_LOCAL_MIN_FREE_GBThe free disk space, in GB, below which karts up --local refuses to start (default 2). 0 turns the check off. Read from the shell at each up.
KARTS_LOCAL_WARN_FREE_GBThe free disk space, in GB, below which karts up --local warns (default 8). 0 turns the warning off. Read from the shell at each up.