Skip to content

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 / Lima

mvmforge’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.md

Layered 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._ir in Python, src/ir in TypeScript. Drift is caught by just 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 normalization
  • validate — schema + host-authoritative rules with stable error codes
  • emit — subprocess-invokes an SDK to produce IR
  • compile — renders flake.nix + launch.json + src/ deterministically
  • up — composes everything plus mvmctl 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.py and app.ts — same workload in each SDK
  • expected.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:

ADRTitle
0001Record Architecture Decisions
0002Separate declaration from execution via canonical Workload IR
0003IR schema source-of-truth + two-layer SDK + golden corpus
0004Schema canonicalization + pre-flight validation contract
0005mvm as v1 execution substrate
0006Nix flakes as v1 generated artifact
0007mvm guest-lib flake contract
0008Source-tree bundling

CI surface

just ci runs:

CheckPurpose
docs-checkRequired project docs are present; banned terms aren’t used.
adr-checkADRs conform to the template.
schema-checkschema/workload-ir-v0.json matches what mvmforge-ir would generate.
sdk-python-checkPython lower layer matches fresh codegen.
sdk-ts-checkTypeScript lower layer matches fresh codegen.
cargo test --workspaceRust unit + integration tests.
sdk-python-testPython SDK tests.
sdk-ts-testTypeScript SDK tests.
corpus-checkCross-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.md is the live source of truth for in-progress work.
  • specs/backlog/0001-foundation-backlog.md tracks deferred and completed items.
  • New IR fields are MINOR bumps if additive; breaking changes are MAJOR bumps 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.