mirage CLI is a thin httpx wrapper over the Mirage daemon. It
auto-spawns the daemon on first workspace create, and the daemon
auto-exits 30 seconds after the last workspace is deleted. Most users
never type a daemon command directly.
Output is structured JSON to stdout for every verb — pipe to jq,
save to file, or read it directly.
Install
Define a workspace in YAML
A workspace is a set of prefixed mounts plus some workspace-level settings. Save this asworkspace.yaml:
${VAR} placeholders are interpolated from your shell environment at
mirage workspace create time. Missing vars fail fast with the full
list, not lazily on first use.
Walkthrough
A guided tour from creating a workspace to snapshotting it. Each step builds on the previous one; you can copy them in order. For a runnable end-to-end version against a real multi-mount workspace (/s3, /gdrive, /gmail, /slack, /discord), see
examples/python/cross/README.md.
1. Source env and create a workspace
The YAML’s${...} placeholders resolve from your shell at create
time, so source your env first. The daemon auto-spawns on the first
create.
2. Inspect
list is one line per workspace; get returns the full mount and
session detail.
3. Run commands against your mounts
execute runs a shell command inside the workspace. Paths resolve
through the mount registry (/s3/... hits S3, / hits the RAM
backing, etc.).
4. Pipe stdin
When stdout isn’t a TTY, the CLI forwards stdin to the command automatically.5. Dry-run with provision
provision returns a ProvisionResult (network bytes, cache hits,
estimated cost) without running the command — handy for predicting
spend before kicking off an expensive read.
6. Cache: network → hit after a real read
After a realcat, provision flips that path from a network read
to a cache hit (cache_hits=1).
7. Command history
Every executed command is recorded by a hidden recorder (the Observer). Thehistory builtin shows the calling
session’s commands (GNU bash semantics, history -c clears only that
session’s view), and /.bash_history renders the GNU histfile across all
sessions, readable with the ordinary file commands.
8. Background jobs
Long-running commands take--background and return a job_id
immediately. mirage job wait blocks until it’s done.
9. Snapshot to disk
10. Restore from snapshot
Snapshots redact cloud creds at save time, so loading needs fresh creds via a config file. The same workspace YAML used for create works.A config file is required for any mount whose snapshot config contains
redacted secrets (S3 with inline keys, GDrive, Slack, Discord, Redis,
…). The snapshot stores
"<REDACTED>" in place of those secrets at
save time. Loading without the config 400s with the list of prefixes
that need fresh creds. Local resources (RAM, Disk) restore as-is with
no extra config needed.11. Clean up
The daemon exits ~30s after the last workspace is deleted.Versioning
Every workspace has its own git-backed history kept by the daemon (under~/.mirage/repos/<workspace-id> by default; set MIRAGE_VERSION_ROOT to
relocate). You commit the live state as a version, then log, diff, branch,
and restore in place. The verbs follow git.
Commit and log
Diff
diff reports changed paths (added / modified / deleted), git-style. No refs
compares live state to the branch HEAD; one ref compares live to that ref; two
refs compare two versions.
Branch
branch forks at another branch’s current version; commit onto it with -b.
Checkout
checkout restores the live state to a past version or branch, in place.
It overwrites uncommitted live state.
Clone from a version
clone --at makes a new workspace from one of the source’s past versions
(omit --at to clone the live state).
Verbs at a glance
| Verb | What it does |
|---|---|
mirage workspace create FILE [--id NAME] | Build resources from YAML, register a workspace, return its id. |
mirage workspace list | Brief one-line summary per active workspace. |
mirage workspace get ID [--verbose] | Full detail (mounts, sessions, optionally cache / dirty / history internals). |
mirage workspace delete ID | Stop the workspace; daemon may exit on the idle timer. |
mirage workspace clone SRC_ID [--id NAME] [--at REF] | New workspace from the source; --at REF clones a past version, otherwise the live state. |
mirage workspace commit ID [-m MSG] [-b BRANCH] | Commit the live state as a version; returns the version id. |
mirage workspace log ID [-b BRANCH] | List versions on a branch, newest first. |
mirage workspace diff ID [A] [B] [-b BRANCH] | Changed paths (added/modified/deleted). No refs: live vs HEAD; one ref: live vs A; two: A vs B. |
mirage workspace branch ID NAME [--from BRANCH] | Fork a branch at another branch’s current version. |
mirage workspace checkout ID REF | Restore the live state in place to a version id or branch. |
mirage workspace snapshot ID PATH.tar | Snapshot to a tar file. |
mirage workspace load PATH.tar [CONFIG] [--id NAME] | Restore from tar; optional config re-supplies redacted creds. |
mirage session create WS [--id NAME] | Add a named session (own cwd + env). |
mirage session list WS | List sessions for a workspace. |
mirage session delete WS SESSION | Close a session. |
mirage execute --workspace_id WS [--session_id S] [--background] --command "..." | Run a command. Pipes stdin automatically when stdout is not a TTY. |
mirage provision --workspace_id WS [--session_id S] --command "..." | Dry-run / cost estimate — returns a ProvisionResult shape (network bytes, cache hits, estimated cost) without running the command. |
mirage job list [--workspace_id WS] | List jobs the daemon has run, plus their status. |
mirage job get JOB | Detail for one job. |
mirage job wait JOB [--timeout SECS] | Block until the job is done; returns the result. |
mirage job cancel JOB | Cancel a running job. |
Per-mount safeguards
Per-mount
command_safeguards are Python CLI only today. The TypeScript
CLI config schema does not carry them yet.command_safeguards, so a
runaway cat/grep/rg can’t flood the agent or hang. Each entry sets
max_lines / max_bytes (output cap) and/or timeout_seconds (deadline),
with on_exceed: truncate (stop, exit 0, add a stderr notice) or
on_exceed: error (stop, exit 1):
cat big.txt | head -n 30 still shows 30 lines. Truncation exits 0, error
exits 1, and a timeout exits 124 — each with a stderr notice. Without a
command_safeguards block, cat/grep/rg/head/tail still cap at 2000
lines by default. See Output Safeguards
for the SDK form and the same fields.
Daemon control
Most users never run these directly — the daemon auto-spawns on firstworkspace create and auto-exits after the idle timer fires
(default 30s after the last workspace is deleted). When you need to
intervene — typically during development, when you want code changes
to take effect:
| Verb | What it does |
|---|---|
mirage daemon status | Daemon health, PID, uptime, workspace count. Exit 1 if daemon not reachable. |
mirage daemon stop | POST /v1/shutdown to trip the daemon’s exit event. Daemon closes active workspaces and exits. Falls back to SIGTERM on --timeout. |
mirage daemon restart | Stop, then either wait for next workspace create to auto-spawn (default) or --eager to spawn immediately. Workspaces are LOST on restart — save any you want to keep first with mirage workspace snapshot <id> <path> and reload with mirage workspace load <path>. |
mirage daemon kill | SIGKILL via PID file at ~/.mirage/daemon.pid. Skips graceful shutdown — use only when stop hangs. |
When you change Mirage’s source code (commands, providers, etc.),
the running daemon won’t see your changes — it loaded the old code
at startup. Run
mirage daemon restart to pick up new code.
Same situation as dockerd after recompiling Docker.Where the daemon lives
Most users never need to think about the daemon. If you do:- It listens on
http://127.0.0.1:8765by default. - Override via
MIRAGE_DAEMON_URLenv var or~/.mirage/config.toml: - Logs go to
~/.mirage/daemon.logwhen the CLI auto-spawns it. - It exits 30 seconds after the workspace count hits zero (configurable).