Skip to content
All posts
2 min readtebakoarchitecturepackaging

Architecture: The runtime as an image (2 of 5)

How tebako v2 works, concept by concept — this post: the runtime as an image.

The Tebako team

github.com/tamatebako
Note

Post 2 of 5 in the series Tebako v2 architecture.

○ ● ○ ○ ○

The previous post introduced the three parts of a package. This one describes the part that does the most work: the runtime, shipped and consumed as an image.

A tebako runtime is two artifacts:

  • The interpreter executable — a normal binary, patched so that tebako can mount images into the process it starts.

  • The environment image — a single .tfs file holding everything the interpreter’s load paths expect: the standard library, the gems, the configuration. It mounts whole at the interpreter’s root and is never extracted to disk.

The runtime as an image the runtime — two artifacts the interpreter executable — a normal binary the environment image stdlib + gems, one .tfs file downloaded once to ~/.tebako verified against the published SHA-256, mounted — never extracted at run time the driver inside the interpreter executable the environment image mounts whole at the interpreter’s root payload slices mount beside it at their declared paths one runtime serves every tebako package on the machine

Figure 1 — The runtime ships as two artifacts; the environment image mounts whole at the interpreter’s root, and one runtime serves every package on the machine.

Why two artifacts

Splitting the interpreter from its environment is what makes sharing possible. The first package on a machine that needs a runtime downloads both artifacts once, verifies them against the published SHA-256 sums, and stores them under ~/.tebako/runtimes/. Every other tebako package on that machine — present or future — reuses the same two files.

Upgrading the runtime therefore never rebuilds an application: applications declare a version range, and the newest compatible runtime in the store is mounted.

What runs inside the runtime

A component inside the interpreter executable, the driver, does the mounting: the environment image at the interpreter’s root, then each payload slice at the path its manifest declares. When it is done, the interpreter starts on the application’s entrypoint, and the program reads its files from the mounted images as if they were an ordinary filesystem.

A failure at any step unmounts everything and reports a named error — there is no partial mount.

What this buys you

The lean-versus-fat choice you meet as a packager is a direct consequence of this model:

  • A fat executable carries its payload and resolves its runtime on first run — small file, shared runtime.

  • A lean executable references everything and resolves it all at run time — smaller file, same guarantees.

The next post covers the grammar that ties runtimes, toolkits, and slices together: composition.

Note

Continue with part 3 of 5: Composition.