ARCHITECTURE · 04
Runtime as image.
The prebuilt runtime stops being one fat binary with an embedded filesystem. It splits into an interpreter plus a .tfsimage that is downloaded once, verified, and mounted — never extracted.
Why split the runtime.
A prebuilt runtime has always been a ~24 MB binary with its runtime image embedded (incbin) — already better than a tarball, but with three costs: every fat package duplicates the same image bytes in its payload slot; integrity is one whole-binary checksum with no per-block verification; and the press/deploy flow extracts the runtime's layout into the cache — a mutable directory tree built from an immutable artifact. The split removes all three: the image becomes a first-class, content-verified, immutable object that is mounted like any other TFS image.
Two artifacts, one runtime.
SHIPPED · in tebako-rsThe exact semantics are specified in docs/runtime-as-image.md in tebako-rs. The runtime becomes:
THE INTERPRETER — UNCHANGED
tebako-runtime-<ver>-<ruby>-<platform>[.exe]
The existing runtime executable: same resolution, same launcher-ABI-v1 handoff. Today's v0.15.9 executables keep working untouched — they ignore the image env and use their embedded image (graceful degradation, no republish needed).
THE RUNTIME IMAGE — NEW
tebako-runtime-<ver>-<ruby>-<platform>.tfs
The runtime's files (lib/ruby, gems, /local/stub.rb) as one dwarfs-t-native image. Immutable (installed 0444), sha256-verified at install, mounted by the runtime at boot — the same tebako_fs_* mechanism any packaged app uses. No special case, no embedded copy.
Why .tfs and not .dwarfs.
The image's metadata is serialized with FlatBuffers — the default of the dwarfs-t writer in our builds — which upstream DwarFS cannot read. A format upstream can't open doesn't get upstream's extension: .tfs marks a TFS image (readable via libtfs / dwarfs-t / TFS tooling, content-detected by the reader regardless of extension), while .dwarfs stays reserved for upstream-compatible (thrift-metadata) images. The writer binding lives in dwarfs-t-rs — image creation is in-process, never a shelled mkdwarfs.
The ;image runtime_ref flag.
SHIPPED · launcher ABI stays v1The trailer's runtime_ref learns one bare flag — an additive change, not an ABI bump:
ruby@<ruby-version>;tebako=<tebako-version>[;image][;sha256=<64 hex>]A bare flag, on purpose. The image's expected sha256 comes from the release index (manifest.json's image key, else the SHA256SUMS line) — exactly the trust source the executable's own checksum already uses. Encoding it as ;image=<sha> would blow the 127-byte runtime_ref budget for image-era fat refs; the bare flag fits.
Byte-identical without the flag. A v1 ref resolves and execs exactly as before — no image lookup, no download, no env. tebako-cli's press emits ;image only when the release index carries an image entry, so golden parity with the gem is preserved for v1-era releases.
The cache in the image era.
SHIPPED · tebako-rsThe shared cache stops holding an extracted layout tree. An entry holds immutable artifacts and their trusted markers — nothing else:
runtimes/ruby-<rv>-<ver>-<platform>/
tebako-runtime-<ver>-<rv>-<platform>[.exe] # interpreter (0755)
sha256 / origin # executable metadata
tebako-runtime-<ver>-<rv>-<platform>.tfs # runtime image (0444, immutable)
tebako-runtime-<…>.tfs.sha256 # trusted marker: "<sha> <file>\n"
tebako-runtime-<…>.tfs.origin # the URL it was fetched fromThe .tfs.sha256 marker is the trust anchor: its presence means the image was sha256-verified at install; the image is re-verified only when re-fetched, never per run. When the press side needs the runtime's files (the packaging environment), it extracts the cached .tfs in-process through the tfs C ABI into the build prefix — rebuilt per press, never stored in the cache. A checksum mismatch on download deletes the download and leaves the cache untouched.
The handoff.
SHIPPED · additive env, same optionsThe v1 exec handoff is unchanged — --tebako-image options exactly as before — with one additive environment variable when the ref carried ;image: TEBAKO_RUNTIME_IMAGE=<abs path to the cached .tfs>. The runtime driver change is a few lines — prefer the env image over the incbin image at startup (docs/tebako-main.cpp.30b.patch) — and it is needed only for runtime builds that stop embedding. App images are self-contained and mount as today; the lean handoff is untouched.
Compatibility, stated plainly.
| COMBINATION | BEHAVIOR |
|---|---|
| v1 package (no ;image) | Byte-identical resolve + exec; no image is fetched even when the release publishes one. |
| ;image ref against a v1-era release (no image in the index) | Cannot arise from tebako-cli (no entry → no flag emitted); a hand-stitched ref fails resolution with the named "no checksum" error. |
| Fat payloads | Unchanged — the payload slot carries the interpreter; ;image additionally resolves the image from the mirror at first run. (Carrying the .tfs inside the package is 30c's CAS work — planned.) |
| Today's v0.15.9 executables | Ignore TEBAKO_RUNTIME_IMAGE, use the embedded image — no republish needed. |