# Architecture Source: https://docs.mirage.strukto.ai/home/architecture Four layers, one filesystem. How Mirage turns mounted services into one bash-driven environment.
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`.
```bash theme={null}
set -a && source .env.development && set +a
mirage workspace create workspace.yaml --id demo
```
### 2. Inspect
`list` is one line per workspace; `get` returns the full mount and
session detail.
```bash theme={null}
mirage workspace list
mirage workspace get demo
```
### 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.).
```bash theme={null}
mirage execute --workspace_id demo --command "ls /s3/"
mirage execute --workspace_id demo --command "head -n 1 /s3/data/example.jsonl"
```
### 4. Pipe stdin
When stdout isn't a TTY, the CLI forwards stdin to the command
automatically.
```bash theme={null}
echo -e "a\nb\nc" | mirage execute --workspace_id demo --command "wc -l"
```
### 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.
```bash theme={null}
mirage provision --workspace_id demo \
--command "cat /s3/data/example.jsonl | wc -l"
```
Every factory-built backend estimates commands out of the box, by
family: whole-file readers (`cat`, `sort`, `md5`, ...) charge the byte
total from `stat`, `head`/`tail`/`file` charge a bounded range,
`grep`/`rg` charge a worst-case full read, and metadata commands (`ls`,
`find`, `stat`, `du`, ...) charge op counts only. Transforms (`gzip`,
`tar`, `split`, ...) keep the read total as a floor with
`precision=unknown` output; `cp` brackets both read and write between 0
(server-side copy) and the source total; metadata writes (`rm`,
`mkdir`, `touch`, ...) are zero-byte op counts, with recursive `rm`
degrading to a floor; pure commands (`seq`, `date`, `bc`, `expr`) and
shell builtins (`echo`, `cd`, ...) are zero-cost. Anything the planner
cannot estimate honestly -- `mv` (free rename or full cross-mount
copy), `tee` (stdin size), arbitrary programs -- reports
`precision=unknown` with all totals as floors, never an error. Virtual
files whose size cannot be resolved (for example a rendered
`chat.jsonl`) degrade the estimate to `precision=unknown` while keeping
the known byte total as a floor.
Provision is optional when you register your own commands: leave it out
and the planner reports `precision=unknown`. To opt in with one line,
reuse the estimator helpers (`make_file_read_provision`,
`make_search_provision`, `metadata_provision`, ... in Python;
`makeFileReadProvision` and friends in TypeScript), or pass
`provision_overrides={"grep": my_estimator}` to the command factory. An
explicit `None`/`null` override disables a default.
Pipelines combine field-wise: `|`, `;` and `&&` sum the estimates, `||`
brackets the branches (cheapest low, priciest high), and `for` loops
multiply by the iteration count. A stage downstream of an unknown stage
is also unknown, and totals under `precision=unknown` are floors.
### 6. Cache: network → hit after a real read
After a real `cat`, `provision` flips that path from a network read
to a cache hit (`cache_hits=1`).
```bash theme={null}
mirage execute --workspace_id demo --command "cat /s3/data/example.jsonl > /dev/null"
mirage provision --workspace_id demo --command "cat /s3/data/example.jsonl"
```
### 7. Command history
Every executed command is recorded by a hidden recorder (the
[Observer](/home/observer)). The `history` 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.
```bash theme={null}
mirage execute --workspace_id demo --command "history 5"
mirage execute --workspace_id demo --command "tail -n 6 /.bash_history"
mirage execute --workspace_id demo --command "grep cat /.bash_history"
```
### 8. Background jobs
Long-running commands take `--background` and return a `job_id`
immediately. `mirage job wait` blocks until it's done.
```bash theme={null}
JOB=$(mirage execute --workspace_id demo --background \
--command "wc -l /s3/data/example.jsonl" \
| jq -r .job_id)
mirage job wait $JOB
```
### 9. Snapshot to disk
```bash theme={null}
mirage workspace snapshot demo /tmp/demo.tar
```
### 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.
```bash theme={null}
mirage workspace load /tmp/demo.tar workspace.yaml \
--id demo_loaded
mirage workspace get demo_loaded --verbose
mirage execute --workspace_id demo_loaded \
--command "head -n 1 /s3/data/example.jsonl"
```
Mount your resources, then run shell commands across them. The same echo, ls, grep work against an in-memory mount,
Add GitHub, Postgres, SSH, Notion, Google Drive, ... and the same shell vocabulary keeps working. That's the whole pitch.
Mirage is a Unified Virtual Filesystem for AI agents. It mounts your apps, services, and systems behind one filesystem interface, so an agent reaches every backend with the same handful of Unix-like tools instead of a new SDK per service.
Every service speaks the same filesystem semantics, so agents reason about one abstraction instead of N SDKs and M MCPs. S3, R2, Google Drive, GitHub, Linear, Notion, Slack, Discord, MongoDB, Redis, SSH, and more mount side-by-side under a single root.
Agents reuse the same handful of Unix-like tools (ls, find, grep, cat, ...) instead of learning a new API per service. Pipelines compose across services as naturally as on a local disk, the exact corpus modern LLMs are most heavily trained on.
Snapshot, clone, and version a workspace the way git treats source. Move agent runs between machines without restarting, fork from any past state, and replay a run on demand.
Python and TypeScript SDKs give your AI agents a virtual filesystem directly inside FastAPI, Express, browser apps, or any async runtime, no separate process required. Works with the major agent frameworks (OpenAI Agents SDK, Vercel AI SDK, LangChain, Pydantic AI, CAMEL, OpenHands) and a lightweight CLI plugs into coding agents like Claude Code and
Codex.
An agent watches your team's #incident channel. A user posts a screenshot of mirage --help with the message "the CLI design is confusing and hard to follow".
Built with the OpenAI Agents SDK, the agent walks
Full runnable source: examples/python/demo/design\_feedback.py.
Runnable source: examples/typescript/agents/openai/multi\_resource\_agent.ts, the same shell-tool pattern over Slack and S3.
Inside the workspace shell, the agent runs three steps:
The agent files a new issue in
Mirage shows up wherever an agent needs to read, write, or stitch together data that doesn't already live on a local disk.
tail, grep, jq over remote logs, metrics, and config without per-source plugins.
ws.execute(...) parses and dispatches commands without mounting anything on the host. FUSE is an optional surface if you also want host tools (editors, language servers, rg) to see the workspace.
/bin/bash and no os.system. Most common Unix verbs work (ls, cat, grep, find, head, wc, jq, ...) plus pipes, redirects, globs, and &&/||.
cat /s3/... is one GetObject; find /postgres/... is a SQL query. Reads cache per session, and mirage provision returns a dry-run estimate (network bytes, cache hits, projected cost) before you commit to an expensive operation.
@struktoai/mirage-node) ships the same Workspace/execute surface and most resources. Some agent integrations land on Python first.