Files
TheRock/rocm-systems/emulation/mirage/docs/architecture.md
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

126 lines
7.4 KiB
Markdown

# Architecture
mirage is a single Cargo workspace that builds one unified `mirage`
executable. The binary bundles three roles — the control plane (`ctl`), the
per-session host (`host`), and the optional web dashboard (`daemon`) — plus a
set of pluggable emulator backends. Which role runs is decided by the
subcommand:
* `mirage <ctl-command>` — every user-facing verb (`profile`, `topology`,
`agent`, `emulators`, `session`, `exec`, `run`, `attach`, `logs`, `state`,
`paths`, `about`).
* `mirage host --session <id>` — the per-session host (spawned internally).
* `mirage webui` — the cross-session HTTP/WebSocket dashboard.
## Crates
| Crate | Role |
| ------------------ | ------------------------------------------------------------------- |
| `mirage` (root) | The unified binary: parses argv, owns the Tokio runtime, dispatches to a role. |
| `mirage_ctl` | The control plane: the `CtlCmd` subcommand tree and `dispatch`. |
| `mirage_core` | Shared types (`ProfileDef`, `SessionDef`, `ExecDef`, …), XDG path resolution, atomic state I/O, `FileCtl`, the attach stream, and the emulator/registry traits. |
| `mirage_host` | The per-session host: watches `exec/`, runs commands, publishes health. |
| `mirage_container` | Container provider abstraction (podman/docker) for containerised sessions. |
| `mirage_builtin` | Embedded builtin agents, topologies, and profiles, plus their unpackers. |
| `mirage_daemon` | The web UI server (HTTP API + WebSocket), built only with the `daemon`/`webui` features. |
| `mirage_dashboard` | The embedded React/Vite single-page app served by `mirage_daemon`. |
| `mirage_noop` | The `noop` backend: runs commands directly with no emulation. |
| `mirage_rocjitsu` | The `rocjitsu` (and `rocjitsu-dbt`) backend and its asset embedding. |
| `mirage_hotswap` | The `hotswap` load-time ISA-rewriting backend. |
| `rocjitsu_sys` | Low-level FFI bindings used by the rocjitsu backend. |
Emulator backends are **link-only** dependencies: each registers itself into
the emulator registry via [`inventory`] at link time. The binary never names a
backend directly, so enabling or disabling a backend's Cargo feature simply
adds or removes it from `mirage emulators`.
[`inventory`]: https://docs.rs/inventory
## Data flow
```text
┌──────────────────────────────────────────────────────────────────┐
│ mirage <ctl-command> (mirage_ctl::dispatch) │
│ • parses argv (clap) │
│ • owns the only Tokio runtime in the CLI process │
│ • renders text / JSON │
│ • drives a MirageCtl implementation (FileCtl by default) │
└───────────────┬──────────────────────────────────────────────────┘
│ trait MirageCtl (read/write on-disk state)
▼
┌──────────────────────────────────────────────────────────────────┐
│ Filesystem under the mirage config / runtime / state directories │
│ profiles, agents, topologies (config) │
│ session/<id>/... (runtime) │
└───────────────▲──────────────────────────────────────────────────┘
│ writes def.json + exec defs; reads health/status/stdout
│
┌───────────────┴──────────────────────────────────────────────────┐
│ mirage host --session <id> (mirage_host) │
│ • one detached process per session │
│ • spawned by `session start` / `run` │
│ • watches exec/ for new defs, runs them through the emulator │
│ • writes health.json continuously │
└──────────────────────────────────────────────────────────────────┘
```
The CLI and the host never talk over a socket or network — only through the
session directory. See [`state-layout.md`](state-layout.md) for the exact
files and formats.
## Why file-backed?
* **Inspectability.** Every interesting state can be read with `ls`, `cat`,
and `jq`. Debugging is far easier than tracing through an IPC daemon.
* **Recoverability.** If any process crashes, the others keep going.
Restarting the CLI is free; restarting a host is safe.
* **No daemon required.** Read-only commands (list, show, paths) work with no
host running at all.
* **Simple lifetime.** `$XDG_RUNTIME_DIR` is cleared on logout, so sessions do
not leak across reboots.
## Why a per-session host?
* **Isolation.** A buggy emulator in one session cannot affect another.
* **Containerisation.** A host can be the entry point of a container while the
CLI on the outside still drives it through the bind-mounted session
directory.
* **Clean shutdown.** `SIGTERM` to a host means exactly "stop this session";
nothing else needs to know.
## Concurrency model
The CLI process runs a single Tokio runtime created in `main`. Each host
process runs its own runtime.
* **Attach** streams output via `tokio_stream`'s `ReceiverStream` over a tail
task that polls each node's `stdout` and `exit_code`. Interactive attaches
put the terminal into raw mode and forward stdin to the exec's node-0 FIFO.
* **The host** scans `exec/` on a fixed tick, runs each discovered exec in its
own task, and shares a `tokio::sync::Notify` between its signal handler and
main loop for clean shutdown.
## Adding an emulator backend
1. Create a crate that implements the `mirage_core::emulator::Emulator` trait.
2. Register it into the registry with an `inventory::submit!` in its `lib.rs`.
3. Add it as an optional, feature-gated dependency of the root `mirage` crate
and reference it with `extern crate … as _` so the linker keeps the
registration.
No code in the binary needs to name the backend; it appears automatically in
`mirage emulators` and becomes selectable via `--emulator`.
## Testing strategy
* **Unit tests** live in each crate's `*::tests` modules and cover id
validation, path resolution, atomic state I/O, option parsing, profile
overrides, and the `FileCtl` surface. Tests that mutate process-wide env
vars serialize through a shared lock.
* **End-to-end tests** under `tests/` exercise the full stack through the
public CLI (`assert_cmd`) and the HTTP/WebSocket surface. Each runs in a
fresh tempdir with overridden mirage directories, so they are independent
and parallel-safe even though they spawn real host processes. The
rocjitsu-backed suites require a working rocjitsu runtime; the `noop`-backed
ones do not.