ARCHITECTURE · 09
Running a binary that lives inside an image.
An interpreted application never execs anything, because the interpreter runs inside the mounts. A toolkit payload, however, ships real executables, and an exec target held by a virtual filesystem needs a path to the CPU. Tebako has four such paths, and the payload's manifest declares which one it takes; consumers never care which path a tool took.
|
Note
|
The dynamic, tfs-native, and static tiers are shipping. The wrapped tier is named but not yet specified, and it is planned. |
Choosing a tier, as a flow
The payload publisher makes this choice once, and the dispatcher honors it silently. The questions follow, in order:
Figure 1 — Exec tier decision flow: interpreted, source-available dynamic, static, wrapped.
The dynamic tier: the preload shim
|
Note
|
This tier is shipped. |
A small interposition library is injected into the process at launch, and it maps the standard file-IO calls onto the mounted images. The binary, and every library it dynamically loads, sees the images as ordinary directories. Nothing is extracted, and jails apply, because the IO flows through the same policy choke point.
The choice fits the mainline: any dynamically linked executable, for example Inkscape, a JVM, ffmpeg, or git.
The honest limits are dynamic binaries only: a raw-syscall caller bypasses interposition, and macOS system binaries strip the injection variables.
The tfs-native tier: built against the VFS
|
Note
|
This tier is shipped. |
The binary is compiled against the tebako filesystem interface and carries the VFS inside itself. The patched ruby interpreter is the canonical example: it mounts and serves its own standard library from the env image, with no shim involved.
The choice fits a tool built in-house, where linking the interface is possible. The property is a build-time property, and it cannot be imposed on an arbitrary binary.
The honest limit is that the tier requires source access and the tebako SDK.
The static tier: closure extraction
|
Note
|
This tier is shipped. |
For statically linked tools, interposition has nothing to attach to. The executable and its declared closure are materialized once into a content-addressed exec cache in the store, verified at install, and run from there. On macOS and Windows this is also the fallback for anything the shim cannot mediate.
The choice fits statically linked binaries and platforms without a syscall-mediation surface.
The honest limits are that such processes see only the host filesystem, with no VFS, and jails do not reach them; that is a platform-API statement, not a trust statement.
The wrapped tier: link-time interposition
|
Note
|
This tier is planned. |
The mechanism is the same interposition as the dynamic tier, bound at link time instead of launch time, for binaries built in-house that cannot be made fully TFS-native.
The tier is named in the manifest vocabulary today, and the mechanics ship in a later milestone.
The honest limit is that the mechanics are not yet specified.
The hardest case: an image execs an image
The metanorma proof runs exactly this case: a ruby process, inside its
mounts, shells out to a JVM and to Inkscape, both of which live inside disk
images. Three mechanisms make it work. The runtime mediates process
spawning: an exec target held by the mounts is materialized to a host cache
and executed there. Before exec, the loader walks the binary’s dependency
closure (rpaths, @executable_path, $ORIGIN) and materializes every
in-image library it needs, because the platform loader probes for shared
libraries with raw syscalls that no userland hook can serve. The spawned
child re-enters the namespace through the preload shim with the mount
table injected into its environment, so the grandchild of a packaged app
sees the same virtual filesystem as its parent.
The result is unglamorous and important: PATH= metanorma compile site.adoc
produces a PDF. Nothing on the host participated, so nothing on the host can
break it.