Skip to content

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:

does the payload exec a binary at all? ruby apps just run — the interpreter is already inside no interpreted no exec tier yes do you build the tool yourself? source access + the tebako SDK yes, fully tfs-native carries the VFS no — it's a third-party binary is it dynamically linked? almost everything is: inkscape, java, ffmpeg yes dynamic the preload shim no — static, or unservable by the shim static — extract the closure materialized to the exec cache, verified at install sees the host FS only — no VFS, no jails wrapped — PLANNED link-time interposition, when you build but can't go fully native One rule holds for every tier: tebako never uses FUSE and never needs a kernel module, a daemon, or root. Interposition stays in userland — no kernel component. try the dynamic tier by hand: tfs exec app.tfs:/mnt --jail deny -- /mnt/bin/tool --help

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.

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.