# 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).
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 |
|---|---|---|---|
|
dlopen / LoadLibrary of a VFS path |
the interpreter’s dln_load, ffi, fiddle, C extensions self-loading |
the OS loader reads real files only |
|
execvp / posix_spawn / shell strings whose argv0 is VFS-resident |
mn2pdf’s java, inkscape CLI, any payload tool |
the kernel execs real files only |
|
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.
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.
See also: The driver contract · Jails and host proxying · Trace and observability.