Skip to content

ARCHITECTURE · 02

What is tebako?

Tebako is a packaging and loading ecosystem rather than a Ruby tool. It stitches a payload into a single transparent executable that end users run without thinking about its mechanics, or it ships the payload as a managed, versioned, and signed package with shared runtimes. Ruby is the first runtime instance, and the loader neither knows nor cares which language it starts. Beneath this surface sits TFS, a userland virtual filesystem that allows ordinary binaries to mount disk images in-process.

Note

Standalone press is shipped: the lean and fat executables are released and downloadable today.

Managed mode is shipped: the shims, per-invocation dispatch, and the shared cache are released.

OpenPGP verification in the loader is planned in part and follows the unverified-first rollout; see the chain of trust for details.

One design, generic across four axes

The platform is a single design, and that design is generic across four axes. Nothing in the loader, the container, or the store is shaped around Ruby; Ruby happens to be the first runtime that the factories produce.

Any runtime

A runtime is a payload that provides an interpreter: ruby today, and python, julia, or any other language later. The loader hands off to an entrypoint, and it never learns a language.

Any platform

Artifacts are built per platform triplet; eight triplets are locked, from aarch64-macos to aarch64-windows-ucrt. The eighth triplet joined the axis at tebako v2.8.13 with the windows-arm64 authoring tools and the link unit, and the arm64 runtimes follow as the factories publish their legs; the other seven triplets carry the full tool set and the full runtime catalog. Pure-language payloads may also be universal.

Any payload

Application code, data, fonts, native tools, and runtimes themselves are all .tfs images of the same kind, and they differ only in what their manifests declare.

Any composition

Payloads stitch onto a single binary, and payloads recursively mount other payloads at paths. Composition is a manifest declaration, not a rebuild.

The six capabilities

Everything tebako does decomposes into six verbs.

Capability What it does

Stitch

The stitch operation combines N payload images and the bootstrap into one executable (fat), or it produces a small executable that resolves the parts on demand (lean).

Load

The loader mounts payloads, recursively, into one VFS and then transfers control to the declared entrypoint.

Share

Runtimes and payloads download once into a machine-wide content cache, and every package on the machine reuses them.

Shim

Every executable an installed payload provides becomes a registered command on PATH, dispatched per invocation.

Jail

The jail applies a declarative host-filesystem policy per run, and the VFS layer enforces the policy rather than the payload’s good behavior.

Trust

Signing and encryption are opt-in per package. Unsigned packages remain first-class: a signed package is verified strictly, and nothing is ever silently downgraded.

A map of the platform

Everything in the platform orbits one seam: the tebako_fs_* C ABI, implemented by the Rust tfs crate. The factories feed runtimes to this seam, and feedstock repositories, one per package, build payloads on those runtimes and host them on their own releases; the factories walk that chain. The trust layer sits beneath everything that moves bytes.

tamatebako/ruby the source factory — canonical patches, versions.yml, the release monitor patched-src tarballs · SHIPPED tebako-runtime-ruby the runtime factory — interpreter exe + env .tfs image per ruby × triplet exe + image + index · SHIPPED tebako-bootstrap the loader — reads its trailer, resolves the runtime, verifies, hands off Rust · static · < 3 MB · SHIPPED TFS · the tfs crate tebako_fs_* — the 33-export C ABI seam limnifs · dwarfs-t · squashfs · zip · tar · multi-mount binaries mount disk images in-process — userland, no kernel, no FUSE required THE TRUST LAYER OpenPGP (rnp-rs) · tpkg v2 digests + signature · TOFU key pins dwarfs-t · dwarfs-t-rs the image format — the only C++ in the ecosystem — + its standalone Rust binding SHIPPED the tebako CLI the packager — press lean/fat, install, publish, cache — one static binary tamatebako/tebako · SHIPPED tebako-pkg · tfs trailer surgery · the generic image CLI — parity-pinned against the C++ oracle SHIPPED in tamatebako/tebako

Figure 1 — Platform map: the TFS core, the factories and the product line around it, the trust layer beneath.

The gem-era orchestrator is retired: the C++/gem codebase is archived at tamatebako/tebako-v1 and the product is the Rust workspace at tamatebako/tebako.

The three parts of every package

A tebako package is a composition of up to three independently distributable parts, glued by the tpkg trailer: a 166-byte header at end of file plus 280-byte slot records. The format version is 1 forever: extensions live in flags (LEAN, SIGNED_V2, NO_INSTALL) rather than in version bumps, so v1 readers parse v2 packages untouched. The full byte tables live in concepts.

Anatomy of a tebako package: the bootstrap, an optional runtime image (fat packages), one to eight payload image slots, an optional v2 signature extension, and the tpkg trailer header at end of file

Figure 2 — Anatomy of a tebako package: bootstrap, optional runtime slot (fat packages), one to eight payload slots, optional v2 signature extension, and the tpkg trailer header at end of file.

A · the bootstrap — the Rust loader, a static binary under 3 MB. It is the process entry point of every stitched package: it reads its own trailer, resolves the runtime, verifies it, and hands off.

B · the runtime — two artifacts rather than one: the interpreter executable and the env image (a .tfs carrying the runtime’s files). They are downloaded once into the shared cache, sha256-verified, and mounted — never extracted. Fat packages carry the runtime as a slot; lean packages carry a reference and fetch on first run.

C · the payload slices — bare .tfs images, self-describing through an in-image manifest (/tpkg/manifest.yaml). The container’s format_id answers exactly one question, how to read the bytes (limnifs, dwarfs-t, squashfs, zip); whether a slot is a runtime, and which payload carries the entrypoint, are manifest semantics that are never encoded in the format axis. LimniFS is the default image format as of tebako v0.2.0, and dwarfs-t remains a first-class read backend.

The two execution modes

The engine is the same, and the two modes differ in the relationship between the developer and the user: the developer hands over one file and disappears, or the developer publishes versioned payloads that a dispatcher assembles per invocation.

STANDALONE — fully transparent SHIPPED developer: tebako press once per app per platform — e.g. metanorma per-platform executables myapp-macos-arm64 · myapp-linux-x86_64 · myapp.exe bootstrap + runtime slot (fat) + payload image(s) + tpkg trailer lean: the runtime downloads ONCE, ever user downloads one file → runs no ruby · no gems · no tebako to install downloads: once ever (lean) · never (fat) the runtime cache is shared machine-wide users never know tebako exists MANAGED — shims + dispatch SHIPPED developer: press + sign → publish signed .tfs payloads on their OWN GitHub releases + tpkg-registry.yaml — zero central infrastructure install once: download + verify ~/.tebako/shims/metanorma one directory on PATH — one-time setup, no eval-init the dispatcher picks, per invocation payload version (env → project pin → user default) + a compatible CACHED runtime — no download runs — payloads immutable swap runtimes without touching the payload

Figure 3 — Standalone mode versus managed mode, side by side.

1 · The standalone mode

Note

This capability is shipped.

A developer presses the application once per platform with the tebako CLI, a single static binary that requires no host Ruby, and publishes plain executables. The users of those executables download one file and run it: no Ruby, no gem environment, no tebako installation, and nothing else to know. The lean variant keeps the download small: the first run fetches the shared runtime once into the machine-wide cache, where every tebako package on the machine reuses it. The fat variant carries the runtime inside and never touches the network.

Lean versus fat package flows: lean resolves the runtime from the shared machine cache and downloads it once on a cache miss; fat embeds the runtime and never touches the network; both land in the same machine-wide cache

Figure 4 — Lean versus fat package flows: lean resolves the runtime from the shared cache on first run; fat embeds the runtime and never touches the network.

TEBAKO_OFFLINE=1 forces cache-only mode: the result is a cache hit or the named error EX_TEBAKO_UNAVAILABLE (exit 69), never a surprise download.

2 · The managed mode

Note

This capability is shipped.

A developer publishes signed .tfs payloads to the developer’s own GitHub releases; the registry is a tpkg-registry.yaml manifest on infrastructure the developer already has, and there is no central service and no default registry. A user with tebako installed puts ~/.tebako/shims on PATH once and then runs metanorma directly: the dispatcher picks the payload version and a compatible cached runtime, with no download and no per-shell hook. Payloads are immutable, and swapping rubies under a running tool is a dispatch choice rather than a reinstall. Native-extension payloads lock to the ABI line they were built against, and an incompatible line produces a named compatibility error instead of a segfault. mnenv retires in favor of the dispatcher, and metanorma is the first consumer of the mechanism. The dispatch page describes that mechanism.

STEP 1 — WHICH PAYLOAD VERSION (first hit wins) 1 · TEBAKO_<TOOL>_VERSION environment override — highest precedence 2 · .tebako-tools.yaml nearest file walking up from cwd — per-project pin 3 · defaults: in ~/.tebako/config.yaml the user default 4 · registry default the publisher's recommendation constraint STEP 2 — WHICH RUNTIME the payload's declared constraint pure-language: a range — ruby >= 3.3, < 5.0 native-ext: the ABI line — ruby ~> 3.3.0 + platform newest compatible runtime ALREADY CACHED the common case — no download, ever cache miss only download newest compatible → verify → cache from the runtime factory releases — once, then shared exec the payload against the runtime mount + exec — verified at install, not per run

Figure 5 — Version resolution: the payload version chain, then a compatible runtime from the cache.

TFS, the engine underneath

TFS is a product in its own right: a userland virtual filesystem with a mount table, longest-prefix dispatch, pluggable read-only backends, and copy-on-write and encryption transforms above them. The Rust tfs crate is the shipping implementation of the tebako_fs_* C ABI (33 exports), and it drives every mount on this page. The C++ libtfs survives as the legacy parity oracle, and the Rust ABI runs its contract suite byte-for-byte.

The TFS router: calls enter through the tebako_fs_* C ABI; the Rust router dispatches by longest mount-point prefix onto read-only backends — limnifs, dwarfs-t, squashfs, zip, tar — with a copy-on-write overlay above; paths outside every mount pass through to the host filesystem

Figure 6 — The TFS router: longest-prefix dispatch onto read-only backends, with a copy-on-write overlay above and pass-through to the host outside every mount.

Any project, in any language, can attach images to its binaries through the same ABI, or it can drive images from the shell with the tfs CLI. dwarfs-t, the only C++ in the ecosystem, and dwarfs-t-rs, its standalone Rust binding, are consumable without any knowledge of tebako. Every backend is read-only at runtime forever; writing exists only as the copy-on-write overlay plus a creation-time writer. The the VFS model page presents the full model.

The trust chain

One signature mechanism, OpenPGP via rnp-rs, runs from the publisher’s key to the user’s run. Signing is opt-in per package, and verification of anything signed is always strict. Verification happens at install and never per run; failures are named exits, never crashes. Details appear on the the chain of trust page.

The tebako chain of trust: a root key signs release keys, release keys sign the release manifest, the manifest carries per-artifact SHA256 digests; on the package side the tpkg v2 trailer carries per-slot SHA256 and an OpenPGP signature, verified at install with named failure exits

Figure 7 — The tebako chain of trust: root key, release keys, per-artifact SHA256 digests, and the tpkg v2 signature verified at install.

Note

The rollout phase is stated plainly. The loader that ships today is built without OpenPGP verification, which is the unverified-first rollout. Signed packages run with a loud UNVERIFIED warning plus an audit-journal entry, while their per-slot sha256 is still enforced as an integrity check; unsigned packages run with the legacy warning. TEBAKO_REQUIRE_SIGNED=1 fails closed with exit 71, because a strict-mode request is never silently downgraded. Full signature verification in the loader returns with the crypto-toolkit payload. The target model, which combines an embedded root fingerprint, TOFU key pins under $TEBAKO_HOME/trust/, and exits 70 (sha256 mismatch), 71 (bad signature), and 72 (untrusted or changed signer key), is described on the the chain-of-trust page.

The four audiences

The audience rule is stated once: prebuilt artifacts flow downward, and compilation never flows outward. Only the people building tebako itself ever compile tebako.

People running packages

People running packages need no compilation machinery, no libraries, and nothing to install: they download the package and run it. With a fat package, the run does not even need a network. With tebako installed, one shims directory on PATH places every managed tool a command away, with version pinning that requires no attention.

Developers shipping data

Developers shipping data need no compilation machinery: tfs mkimage, a manifest, and a release suffice. Fonts, datasets, and tool assets become content-addressed images, signed when the publisher wants signing, and mounted whole.

Developers shipping applications

Developers shipping applications compile only their own code. Tebako itself arrives as binaries, and the runtimes are prebuilt downloads. A developer presses once per platform in a matrix CI job, without special handling per target, and publishes to releases the developer already owns.

People hacking on tebako

People hacking on tebako are the only audience that needs the tebako toolchain: the factories, the product workspace, and the contract suites. The entry points are the repo map and the contributor page.

The four audiences meet the platform matrix at different depths, and the windows-arm64 triplet is the one place where that depth differs per audience.

Audience On macOS and Linux On Windows

People running packages

Stitched packages and the managed shim install run on every macOS and Linux triplet, x64 and arm64 alike.

Every published package runs on windows x64 today. A windows-arm64 machine runs those same x64 artifacts under the emulation layer of Windows 11 today; native arm64 packages arrive with the arm64 bootstrap, shim, and factory runtimes.

Developers shipping data

tfs builds and inspects images natively on all four triplets.

tfs ships natively on windows x64 and, from tebako v2.8.13, on windows-arm64. A universal image needs no triplet at all.

Developers shipping applications

tebako press presses once per target triplet in a matrix job, and the prebuilt runtimes resolve for every macOS and Linux target.

The full authoring flow runs on windows x64 today. On windows-arm64, tebako v2.8.13 ships tebako-pkg natively — trailer surgery, signing, and release-index work — while tebako press itself arrives on arm64 with the remaining tools in the phase that ships the bootstrap and the shim; pressing a package whose target is windows-arm64 additionally awaits the arm64 runtimes from the factories.

People hacking on tebako

The product workspace and the contract suites build on every triplet that the continuous integration matrix covers.

The product builds on windows x64 with the MSYS2 ucrt64 toolchain and on windows-arm64 with the MSYS2 clangarm64 toolchain, and the aarch64 link unit ships from tebako v2.8.13 as the input that the runtime factories consume.

One toolchain note applies to Windows as a whole. The shipped binaries are compiled with GCC under MSYS2 ucrt64 on x64 and with clang and libc++ under MSYS2 clangarm64 on arm64, because MSYS2 publishes no GCC for aarch64 windows. An MSVC build of tebako is not shipped on either architecture; MSVC appears in the pipeline only as the host compiler for build-time helpers.