Skip to content

SDK — Python and TypeScript

The mvm SDK lets you declare a microVM workload from a single decorated function in Python or TypeScript. Two routes from the same source file produce the same Workload IR:

  • In-process: python app.py or node app.ts imports mvm, the decorator records the declaration, and mvm.emit_json() / mvm.emitJson() writes the canonical IR to stdout.
  • Static compile: mvmctl compile app.py walks the AST without importing the script. Same IR; the host never runs user code.
import mvm
@mvm.app(
name="hello-app",
source=mvm.local_path("."),
image=mvm.python_image(python="3.12"),
resources=mvm.resources(cpu_cores=1, memory_mb=256, rootfs_size_mb=512),
entrypoint=mvm.entrypoint_function(
module="app",
function="greet",
primary=True,
),
env={
"MODEL_PATH": mvm.literal("/data/model.pt"),
"API_KEY": mvm.secret("api-key"),
},
before_start="export HELLO_BANNER='hi'",
after_start=mvm.hook(["curl", "-fsS", "http://127.0.0.1:8080/health"]),
)
def greet(name: str) -> str:
return f"hello {name}"

Build, run, invoke:

Terminal window
mvmctl compile examples/python/hello-app/app.py --out /tmp/hello-app
mvmctl build /tmp/hello-app
mvmctl up hello-app
mvmctl invoke hello-app --input name='ari'

Both SDKs ship a closed set of helpers the static parser also recognizes. Anything else in decorator kwarg position is rejected:

HelperReturnsNotes
python_image(python=)Image::NixPackagespython="3.12"python312 nix attribute
node_image(node=)Image::NixPackagesnode="22"nodejs_22 nix attribute
nix_packages([...])Image::NixPackagesdirect passthrough
resources(...)Resourcescpu/memory_mb/rootfs_size_mb
network(mode=, ports=)Networknone | bridge | host
secret(name, var=)EnvValue::SecretRefhost-mediated secret ref; guest sees opaque token only
literal(value)EnvValue::Literalparity with secret(...)
hook(cmd)HookCmdstr → Shell; list → Argv

Four phases. Each accepts a string (shell line), a list of strings (argv), a single mvm.hook(...), or a list of any of those.

PhaseRuns
before_buildInside the builder VM, after deps install and before the rootfs snapshot.
before_startAt every microVM boot, before the entrypoint dispatch.
after_startReadiness probe — polled to exit-0 before the agent accepts mvmctl invoke.
before_stopAt shutdown, best-effort.

Addons (when attached via addons=[...]) contribute their own hooks; the compiler merges them into the rootfs before the consuming app’s hooks, in attachment order. The result lands in launch.json for the Nix factory to bake into /etc/mvm/hooks/<phase>.sh.

Use mvmctl compile to produce local build artifacts that can be reviewed, signed, built, and launched through the normal mvm workflow. Treat packaging and rollout as a separate admission step, not as part of declaration parsing.

mvm.secret(...) names a managed secret reference, not a raw secret value.

  • The guest sees a normal env var name and an opaque token value.
  • The raw secret stays on the host side.
  • Request-time release is supported only on host-mediated outbound surfaces such as mvm.web_fetch and mvm.web_search.
  • Guest HTTPS CONNECT egress is not a managed-secret substitution path.

mvmctl compile app.ts walks the AST statically for mvm.app({...}) calls and does not run user code. When a .ts script uses the record-mode Sandbox API instead (no mvm.app decorator), mvmctl compile falls back to auto-run: it spawns the script on the host with MVM_SDK_MODE=record set, and the SDK’s atexit hook writes a recording JSON that the CLI then lowers into a Workload.

That auto-run path needs a TypeScript-aware runner — plain node cannot execute .ts in mvm’s supported Node range. Three runners are supported, in priority order: tsx, bun, deno.

OSRecipe
macOS (Homebrew)brew install tsx or brew install oven-sh/bun/bun or brew install deno
Any (npm)npm install -g tsx
Any (pnpm)pnpm add -g tsx
Any (yarn)yarn global add tsx
Any (bun)curl -fsSL https://bun.sh/install | bash
Any (deno)curl -fsSL https://deno.land/install.sh | sh

Prefer the project-local install if you want a reproducible lockfile- pinned runner:

Terminal window
npm install --save-dev tsx

That puts the binary at ./node_modules/.bin/tsx, and mvmctl compile picks it up automatically — see the resolution order below.

mvmctl compile resolves the runner in this order, picking the first hit:

  1. MVM_TSX=<path> env override — explicit pin (any binary).
  2. ./node_modules/.bin/tsx — cwd-relative, lockfile-pinned.
  3. ./node_modules/.bin/bun.
  4. ./node_modules/.bin/deno.
  5. tsx on PATH.
  6. bun on PATH.
  7. deno on PATH.

If nothing resolves, the command fails with the install hint shown above. mvmctl doctor surfaces the same hint in its Tools section — run it after a fresh checkout to confirm your host is wired up.