ARCHITECTURE · 09
Running a binary that lives inside an image.
An interpreted application never execs anything — the interpreter runs inside the mounts. But a toolkit payload ships real executables, and an exec target held by a virtual filesystem needs a path to the CPU. Tebako has four, and the payload's manifest declares which one it takes — consumers never care which path a tool took.
|
Note
|
Status: shipped — dynamic, tfs-native, and static are shipping. Status: planned — wrapped is named, not yet specified. |
Choosing a tier, as a flow.
The payload publisher makes this choice once; the dispatcher honors it silently. The questions, in order:
Figure 1 — Exec tier decision flow: interpreted, source-available dynamic, static, wrapped.
dynamic — the preload shim.
|
Note
|
Status: shipped. |
A small interposition library is injected into the process at launch and 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; jails apply, because the IO flows through the same policy choke point.
Choose it when: the mainline. Any dynamically linked executable: Inkscape, a JVM, ffmpeg, git.
Honest limits: dynamic binaries only. A raw-syscall caller bypasses interposition; macOS system binaries strip the injection variables.
tfs-native — built against the VFS.
|
Note
|
Status: 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 stdlib from the env image, no shim involved.
Choose it when: you build the tool yourself and can link the interface. A build-time property — it cannot be imposed on an arbitrary binary.
Honest limits: requires source access and the tebako SDK.
static — extract the closure.
|
Note
|
Status: 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.
Choose it when: statically linked binaries; platforms without a syscall-mediation surface.
Honest limits: such processes see only the host filesystem — no VFS, and jails do not reach them. That is a platform-API statement, not a trust statement.
wrapped — link-time interposition.
|
Note
|
Status: planned. |
The same interposition as the dynamic tier, bound at link time instead of launch time — for binaries you build but cannot make fully TFS-native.
Choose it when: named in the manifest vocabulary today; the mechanics ship in a later milestone.
Honest limits: not yet specified.
The hardest case: an image execs an image.
The metanorma proof runs exactly this: 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 no userland hook can serve. And 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.