Architecture
mvmforge is documentation-first. Every meaningful design decision lives in an Architecture Decision Record (ADR) under specs/adrs/. This page is the high-level tour; the ADRs are the binding source of truth.
The pipeline
Decorated app code (.py or .ts) │ mvmforge emit ▼Canonical Workload IR (RFC 8785 JSON) │ mvmforge validate / compile ▼Artifact directory ├─ flake.nix (calls mvm.lib.<system>.mkGuest) ├─ launch.json (canonical launch plan) └─ src/ (bundled user source) │ mvmforge up (or mvmctl up --flake .) ▼mvm — Firecracker / Apple Virtualization / Docker / Limamvmforge’s scope ends at producing the artifact. Nix locks inputs and builds the rootfs; mvm boots and supervises. Each tool stays in its lane.
Repository layout
crates/├── mvmforge-ir/ canonical IR types, canonicalize, validate, schema emit, ir_hash, ErrorCode└── mvmforge/ host CLI: canonicalize / validate / emit / compile / up; flake + launch templates
sdks/├── python/ datamodel-code-generator → dataclass lower layer + keyword-argument decorator DSL└── typescript/ json-schema-to-typescript → discriminated-union lower layer + higher-order-call DSL
schema/├── workload-ir-v0.json generated canonical JSON Schema└── error-codes.json stable error-code registry
tests/├── corpus/ cross-SDK byte-identity entries (.py + .ts + expected.canonical.json)└── fixtures/ fake-mvm shim, real-mvm-boot-log runbook
specs/├── adrs/ immutable architecture decisions├── plans/ execution plans├── backlog/ completed and deferred work└── project.md / SPRINT.mdLayered architecture
IR (single contract)
The canonical Workload IR is the only contract between SDKs and the host. Anything that wants to participate in the ecosystem produces or consumes IR — never Nix or mvm artifacts directly. See Workload IR for the field set.
SDKs (two layers each)
Per ADR-0003, each SDK has:
- A lower layer (generated): per-language types codegen’d from the JSON Schema at
schema/workload-ir-v0.json. Internal —mvmforge._irin Python,src/irin TypeScript. Drift is caught byjust sdk-{python,ts}-check. - An upper layer (hand-authored): the DSL users import. Stable. Imports only from the lower layer.
Cross-language behavior is enforced by a golden corpus — not by sharing code.
Host CLI
mvmforge (the binary) is the only consumer of IR and the only producer of substrate artifacts. It exposes:
canonicalize— RFC 8785 normalizationvalidate— schema + host-authoritative rules with stable error codesemit— subprocess-invokes an SDK to produce IRcompile— rendersflake.nix+launch.json+src/deterministicallyup— composes everything plusmvmctl up
Substrate boundary
mvm is the v1 execution substrate. The host invokes mvmctl only as a subprocess; no Rust library coupling. mvmctl’s stdout/stderr/exit pass through unchanged. The handoff is a single filesystem path (the artifact directory).
Per ADR-0005, the substrate boundary is sharp: anything past artifact production belongs to mvm.
Golden corpus
tests/corpus/<entry>/ holds:
app.pyandapp.ts— same workload in each SDKexpected.canonical.json— the canonical IR both must produce
just corpus-check runs every active SDK against every entry and asserts byte-identity. Adding a new pattern means adding a corpus entry first; the SDKs are not “done” with that pattern until the entry passes.
This is the cross-language conformance contract from ADR-0003 §7.
ADRs
ADRs are the binding source of truth. Read in this order:
CI surface
just ci runs:
| Check | Purpose |
|---|---|
docs-check | Required project docs are present; banned terms aren’t used. |
adr-check | ADRs conform to the template. |
schema-check | schema/workload-ir-v0.json matches what mvmforge-ir would generate. |
sdk-python-check | Python lower layer matches fresh codegen. |
sdk-ts-check | TypeScript lower layer matches fresh codegen. |
cargo test --workspace | Rust unit + integration tests. |
sdk-python-test | Python SDK tests. |
sdk-ts-test | TypeScript SDK tests. |
corpus-check | Cross-SDK byte-identity per corpus entry. |
Total: 105 tests + 4 corpus checks at v0.1.
Working with the project
- ADRs are immutable once accepted. Changing an accepted decision means a superseding ADR.
specs/SPRINT.mdis the live source of truth for in-progress work.specs/backlog/0001-foundation-backlog.mdtracks deferred and completed items.- New IR fields are
MINORbumps if additive; breaking changes areMAJORbumps requiring an ADR. - Generated files (
schema/, SDK lower layers) are committed; CI verifies they match fresh regeneration.
For governance details, read AGENTS.md at the repo root.