Skip to content

ARCHITECTURE · 18

Runtime-native interposition.

A mounted image is not real disk, so three interpreter-level operations physically cannot work without help. The law is that interception is runtime-internal and generic. No per-payload and no per-gem code exists anywhere in the stack, and a payload that exercises these paths needs zero adapter work.

Note

Class L (POSIX plus the windows phase-W), class E (ELF plus the macOS launcher tier), and class R declarative materialization are shipped, and the runtime gem’s per-gem require maps ship empty.

Every need reduces to three classes

CLASS TRIGGER CONSUMERS FAILS BECAUSE

L — native-lib load

dlopen / LoadLibrary of a VFS path

the interpreter’s dln_load, ffi, fiddle, C extensions self-loading

the OS loader reads real files only

E — exec of a VFS binary

execvp / posix_spawn / shell strings whose argv0 is VFS-resident

mn2pdf’s java, inkscape CLI, any payload tool

the kernel execs real files only

R — C-library resource paths

a C lib reads a path through its own IO, bypassing the interpreter’s patched IO

OpenSSL reading the CA cert, ICU data files

the path does not exist on the host

Anything that does not fit L/E/R is a design smell, and the spec gets extended rather than gaining a bolted-on one-off. The v1-era per-gem require adapters were the legacy form; this mechanism retires them, and an adapter’s deletion merges only with a green dogfood run without it (metanorma and fontist are the oracle pair, with ffi/fiddle/excavate-load, java-exec, and cert-resource all exercised).

Class L: native-library loads

A load whose path resolves inside a mounted image materializes the library, and its dependency closure, to the runtime’s exec cache, and it loads the real file. A host path passes through untouched. The decision is a path-prefix check against the mount table, nothing more. The interposed symbols are dlopen and dlerror; dlsym is deliberately not interposed, because the wrapper returns real loader handles and symbol resolution needs no routing. A failed VFS materialization surfaces the tebako context line (the library, the mount, the verdict) through the standard dlerror channel, never a stale loader message and never a silent fallback.

PLATFORM CLASS L DELIVERY EXEC, ARRAY FORM EXEC, SHELL STRING

ELF (gnu/musl)

exe-defined dlopen/dlerror wrappers preempt for the whole process

spawn hook materializes + injects the child (LD_PRELOAD + TEBAKO_TFS_MOUNTS)

works unmodified — inherited LD_PRELOAD injects every child at exec, /bin/sh included

macOS

driver self-inserts an interpose dylib (DYLD_INSERT_LIBRARIES + sentinel, one re-exec)

the same hook; the materialized child is a non-Apple binary, insertion honored

A named boundary: SIP strips or fatals an inherited insertion at Apple-binary exec, and the launcher tier (wrap-bin) re-arms per spawn.

windows (msys)

the patched dln.c dlmap route + the phase-W IAT rebind for path-carrying raw calls

boot-materialized alias dirs on PATH carry declared bare names interception-free

deferred (phases after class L)

The macOS mechanics bear stating: dyld honors interpose tuples only from a DYLIB image, so the driver self-inserts at boot. It writes an embedded micro interpose-dylib to a content-keyed cache path, sets DYLD_INSERT_LIBRARIES plus a sentinel, and re-executes itself exactly once, before any mount. On windows there is no process-wide preemption surface: coverage routes through the interpreter’s own patched dln.c, extended by the phase-W IAT rebind for path-carrying raw LoadLibrary calls and by declared aliases whose boot-materialized copies bind through the OS’s own search for bare-name callers, interception-free.

Class E: exec of a VFS binary

Exec of a VFS-resident binary materializes it plus its loader closure and execs the real path with the original argv/env; a covered path the mounts do not hold answers ENOENT, so a libc PATH loop simply moves to its next candidate. A VFS binary that itself spawns children re-enters the same interposition, and children of materialized binaries keep the VFS view, never a silent host fallback. A bare command name also resolves with no payload code learning tebako: the driver prepends every co-mounted dependency image’s declared bin dirs to PATH in the handoff env; the image declares, and the driver flows. The explicit, windows-safe, shell-free reference surface is TEBAKO_MOUNT_<SLUG> per dependency mount, where the slug derives from the mount path by dropping the drive qualifier, uppercasing the alphanumerics, mapping the rest to _, and trimming; the root mount is reserved and exports nothing.

# metanorma → openjdk: mn2pdf runs `java -jar …` by shell string.
# The openjdk payload declares its bin dir; the driver's handoff env
# leads PATH with the launcher dir, then dependency bin dirs:
#   <exec-cache>/wrap-bin  (self-injecting launchers — re-arm the
#   platform's injection var past the SIP strip)
#   then every co-mounted image's declared bin dirs
# The JVM boots under the jail: platform floor (automatic) + the JRE
# tree (an authored ro grant) + the user domain (an authored home read).

Class R: declarative boot materialization

An image manifest may declare materialize: [paths], absolute in-image regular files that the driver extracts after the mounts and the jail, before the interpreter handoff, landing at <TEBAKO_EXEC_CACHE>/resources/<image-key>/<P>. The canonical consumer pattern is an image-owned default: the image that ships a resource also ships the configuration pointing at its materialized location (the runtime env image’s own OpenSSL cert default is the first entry, and the driver exports SSL_CERT_FILE at boot as the one env-surface exception). The mechanics are locked: write-once with the tfs-merkle-1 file construction; re-hash before install; the digest record renamed into place before the content file, so a crash never leaves content without its record; per-boot re-verification, where a mismatch is a named sha256-mismatch (exit 70) and never a silently served corruption; the installed copy read-only; and a listed path absent from the image is a named 65, because the manifest lied and no skipped entry is tolerated.

The stable surface, forever

Payload authors and runtime factories may rely on the following: the path rule (a native library or executable inside a mounted image loads/execs exactly as if on disk, from any caller, with no gem- or payload-specific action ever); TEBAKO_EXEC_CACHE (read-only to payloads, with per-image-sha segregation contractual and its contents an implementation detail; POSIX materializations are per-process and reaped at exit, while windows DLLs are OS-locked, so they are leave-in-place, content-keyed, and reaped by the store’s cache maintenance); the materialized-resource convention; the TEBAKO_MOUNT_<SLUG> discovery surface; and the dependency PATH wiring. Everything else, the mount table layout, the closure-walk order, and the cache’s on-disk naming, is implementation detail and may change between runtime releases. Loader-side failures keep the loader’s named codes (65–78), and runtime-side failures surface as the interpreter’s own error with the tebako context line. There are no silent fallbacks, ever.