Templates hold the weight. ROMs carry the change. Together they turn environment setup from minutes into milliseconds.
A template is a full snapshot of a prepared machine. Filesystem, and on the production plane, memory and running processes too. 30 GiB of toolchain boots as fast as an empty VM because nothing initializes. It already initialized when you packed it. The Gradle daemon is warm, the database is seeded, the browsers are installed.
# from an image, warmed before snapshotting
numinous template pack --name jvm-gradle \
--image eclipse-temurin:21 \
--warm "gradle --version && gradle build --dry-run" # warms the daemon# from the SDK, with a Dockerfile
tpl = nc.templates.pack(
"node-monorepo",
dockerfile=open("Dockerfile").read(),
warm_cmd="pnpm install --frozen-lockfile",
)
tpl["build_log"] # every pack returns its full build log, durableuser_image_build_failed with the full build log. You will never stare at an opaque build failure.A ROM is a read-only, content-addressed bundle mounted into a booted sandbox at /rom. Your checkout, a dependency delta, a test shard, a config file. Identical content never uploads twice.
rom = nc.roms.create("checkout-abc123", files={
"src/main.py": open("src/main.py").read(),
"run.sh": "python3 /rom/src/main.py",
})
rom["sha256"] # content address
rom["deduplicated"] # True if this exact content already existed
sb = nc.sandboxes.create(template_id=tpl["id"], rom_id=rom["id"], ...)
nc.sandboxes.exec(sb["id"], "sh /rom/run.sh")The template stays generic; the ROM makes it yours. One warm base, thousands of variants.