Skip to content

ARCHITECTURE · EXEC TIERS

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.

SHIPPEDdynamic · tfs-native · static — shippingPLANNEDwrapped — 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:

does the payload exec a binary at all?ruby apps just run — the interpreter is already insidenointerpretedno exec tieryesdo you build the tool yourself?source access + the tebako SDKyes, fullytfs-nativecarries the VFSno — it's a third-party binaryis it dynamically linked?almost everything is: inkscape, java, ffmpegyesdynamicthe preload shimno — static, or unservable by the shimstatic — extract the closurematerialized to the exec cache, verified at installsees the host FS only —no VFS, no jailswrapped — PLANNEDlink-time interposition, when you buildbut can't go fully nativeOne rule holds for every tier: tebako never uses FUSE and never needs a kernel module, a daemon, or root.Interposition is the tebako way — userland all the way down.try the dynamic tier by hand: tfs exec app.tfs:/mnt --jail deny -- /mnt/bin/tool --help

dynamic — the preload shim

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

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: 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

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

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.