Skip to content

Declaration cookbook

Use declarations when you want code-first workload authoring without executing the user module during compile. The static compiler reads the supported literal shape and emits Workload IR for the same build and launch path used by the CLI.

This page uses the current Python and TypeScript declaration helpers. It avoids runtime-only Sandbox.create(...) calls; use Operations cookbook for imperative sandbox lifecycle scripts.

import mvm
mvm.workload(id="hello-python")
@mvm.app(
name="hello",
source=mvm.local_path(".", include=["src/**", "pyproject.toml", "uv.lock"]),
image=mvm.python_image(python="3.12", packages=["uv"]),
resources=mvm.resources(cpu_cores=1, memory_mb=512, rootfs_size_mb=1024),
dependencies=mvm.python_deps(lockfile="uv.lock", tool="uv"),
network=mvm.network(mode="none"),
entrypoint=mvm.entrypoint_function(
module="src.tool",
function="run",
primary=True,
),
)
def run(prompt: str) -> str:
return prompt.upper()

Compile and build:

Terminal window
mvmctl compile ./tool.py --out /tmp/hello-python
mvmctl build /tmp/hello-python

Security posture:

  • mode="none" is the default closed network posture;
  • the lockfile is declared so dependency integrity can be checked;
  • source includes are narrow rather than copying the entire home directory;
  • the function body is guest workload code and is not executed during static declaration extraction.

Use a command entrypoint when the workload should boot into a process instead of exposing a function-call entrypoint.

@mvm.app(
name="worker",
source=mvm.local_path(".", include=["worker.py", "uv.lock"]),
image=mvm.python_image(packages=["uv"]),
resources=mvm.resources(cpu_cores=1, memory_mb=512, rootfs_size_mb=1024),
dependencies=mvm.python_deps(lockfile="uv.lock"),
network=mvm.network(mode="none"),
entrypoint=mvm.entrypoint(command=["python", "/app/worker.py"]),
)
def worker() -> None:
pass

Keep startup commands deterministic. Fetching unpinned code in an entrypoint or hook weakens the audit story because the build artifact no longer captures the full workload input.

Use closed networking by default. When a workload needs outbound access, make the destination concrete.

network=mvm.network(
mode="bridge",
egress=mvm.egress([
mvm.host_port("api.openai.com", 443),
]),
dns=mvm.dns_system(),
)

Security posture:

  • allow specific hosts and ports, not broad wildcard listeners;
  • keep DNS behavior explicit for workloads that depend on names;
  • review generated IR before moving a workload from mode="none" to mode="bridge".

Declarations should refer to secrets without embedding raw values in source.

env={
"MODEL": mvm.literal("gpt-4.1-mini"),
"OPENAI_API_KEY": mvm.secret("openai-api-key", var="OPENAI_API_KEY"),
}

The SDK declaration records the reference. Secret resolution belongs to the runtime admission path, and logs/errors should not print resolved values.

Hooks are useful for deterministic setup that belongs in the workload contract.

@mvm.app(
name="service",
source=mvm.local_path("."),
image=mvm.python_image(packages=["uv"]),
resources=mvm.resources(cpu_cores=1, memory_mb=512, rootfs_size_mb=1024),
network=mvm.network(mode="none"),
entrypoint=mvm.entrypoint(command=["python", "/app/server.py"]),
before_build=mvm.hook(["uv", "sync", "--frozen"]),
before_start=mvm.hook("python /app/migrate.py"),
after_start=mvm.hook(["python", "/app/healthcheck.py"]),
)
def service() -> None:
pass

Security posture:

  • prefer argv hooks for commands with user-controlled values;
  • keep hooks deterministic and tied to pinned inputs;
  • avoid network-fetching hooks unless policy and provenance explicitly allow them.

One declaration can expose multiple function entrypoints. Mark the default with primary=True.

@mvm.app(
name="math",
source=mvm.local_path(".", include=["mathsvc.py"]),
image=mvm.python_image(),
resources=mvm.resources(cpu_cores=1, memory_mb=256, rootfs_size_mb=512),
network=mvm.network(mode="none"),
dependencies=mvm.no_deps(),
entrypoints=[
mvm.entrypoint_function(module="mathsvc", function="add", primary=True),
mvm.entrypoint_function(module="mathsvc", function="mul"),
],
)
def math() -> None:
pass

Use separate apps when the entrypoints need different resource, network, or secret policy.

Before a declaration becomes a shared artifact:

  • compile to a temporary output path;
  • inspect the generated Workload IR and source bundle;
  • confirm source include/exclude rules are narrow;
  • confirm network starts closed and grants are concrete;
  • confirm dependency lockfiles are present and pinned;
  • confirm secrets are references;
  • confirm hooks do not fetch unpinned code;
  • build through the normal secure build boundary.