Skip to content

ARCHITECTURE · 00

What is Tebako?

Tebako is a portable-application platform. Package any Ruby application once and run it anywhere — as a single transparent executable your users never think about, or as a managed, versioned, signed payload with shared runtimes. Underneath sits TFS, a userland virtual filesystem that lets ordinary binaries mount embedded disk images.

SHIPPEDstandalone + library modes — released and downloadable todaySHIPPEDmanaged mode (the version manager) — shipped in v2.0.0, incl. zero-runtime native payloads

The platform, on one map.

Everything orbits one seam: the tebako_fs_* C ABI. The product line mounts images through it; the patch home and the runtime factory feed it rubies; the trust layer sits beneath everything that moves bytes.

tamatebako/rubythe patch home — canonical patches,release monitor, scenario srcv0.2.1 · SHIPPEDtebako-runtime-rubythe runtime factory — interpreter+ .tfs image per ruby × platformv0.15.9 · SHIPPEDtebako-bootstrapthe launcher — resolves the runtime,verifies, hands offC v0.2.0 + Rust · SHIPPEDTFS · libtfstebako_fs_* — the C ABI seamzip · dwarfs · squashfs backends · multi-mountbinaries mount embedded disk images — userland,no kernel, no FUSE requiredTHE TRUST LAYEROpenPGP (rnp-rs) · tpkg v2 digests + signature · TOFU keyringdwarfs-t · dwarfs-t-rsthe compression foundation + itsstandalone Rust bindingSHIPPEDtebako-cli · the gemthe packagers — press lean/fat/classic,golden byte-parity between themgem 0.15.9 + Rust CLI · SHIPPEDtebako-pkg · tfstrailer surgery · the generic VFS CLI —oracle-verified against the C++ toolsSHIPPED in tebako-rs

Three ways to use it.

Same engine, three relationships between the developer and the user. The first two are the spine; the third is the engine itself, on loan to everyone.

STANDALONE — fully transparentSHIPPEDdeveloper: tebako pressonce per app — e.g. metanormaper-platform executablesmyapp-macos-arm64 · myapp-linux-x86_64 · myapp.exebootstrap + runtime payload + app image(s) + tpkg trailerlean: runtime downloads ONCE, everuser downloads one file → runsno ruby · no gems · no tebako to installdownloads: once ever (lean) · never (fat)the runtime cache is shared machine-wideusers never know tebako existsMANAGED — the version managerSHIPPED in v2.0.0developer: press + sign → publishsigned .tfs payloads on their OWN GitHub releases+ tpkg-registry.json — zero central infrastructureinstall once: download + verify~/.tebako/shims/metanormaone directory on PATH — one-time setup, no eval-initdispatcher picks, per invocationpayload version (env → project pin → user default)+ compatible CACHED runtime — no downloadruns — payloads immutableswap runtimes without touching the payload

1 · Standalone mode — ship the app, hide the machinery

SHIPPED

A developer presses their app once per platform and publishes plain executables. Their users download one file and run it — no Ruby, no gem environment, no tebako to install, nothing to know. The lean variant keeps the download small: the first run fetches the shared runtime once into the machine-wide cache (image-era: an immutable .tfs, mounted, never extracted), where every tebako package on the machine reuses it. The fat variant carries the runtime inside and never touches the network. This is how the gem ships today — and how the Rust tebako-cli presses with golden byte-parity.

2 · Managed mode — the version manager

SHIPPED · v2.0.0

A developer publishes signed .tfs payloads to their own GitHub releases — the registry is a manifest on infrastructure they already have. A user with tebako installed puts ~/.tebako/shims on PATH once and just runs metanorma: the dispatcher picks the payload version and a compatible cached runtime — no download, no per-shell hook. Payloads are immutable; swapping rubies under a running tool is a dispatch choice, not a reinstall. Native-extension payloads are ABI-locked to their ruby line, honestly, with named errors instead of segfaults. mnenv retires to the dispatcher — metanorma is the first dogfood. The mechanism: the version manager.

STEP 1 — WHICH PAYLOAD VERSION (first hit wins)1 · TEBAKO_<TOOL>_VERSIONenvironment override — highest precedence2 · .tebako-tools.yamlnearest file walking up from cwd — per-project pin3 · tebako use <tool>@<version>the user default4 · registry default / stablethe publisher's recommendationconstraintSTEP 2 — WHICH RUNTIMEthe payload's declared constraintpure-ruby: a range — ruby >= 3.3, < 5.0native-ext: ABI line lock — ruby ~> 3.3.0newest compatible runtime ALREADY CACHEDthe common case — no download, evercache miss onlydownload newest compatible → verify → cachefrom the runtime releases — once, then sharedexec payload against runtimemount + exec — verified at install, not per run

3 · Library mode — libtfs for everyone

SHIPPED · C ABI · C++ today, Rust interior shipping

The engine is a product in its own right. Any project, in any language, can attach dwarfs/zip/squashfs images to its binaries through the tebako_fs_* C ABI — 24 exports, multi-mount, read-only semantics — or drive images from the shell with the tfs CLI. dwarfs-t, dwarfs-t-rs and libtfs are standalone open-source components: consumable without knowing tebako exists, licensed for it (BSD-2-Clause; note the dwarfs backend links GPL-3.0 DwarFS). The Rust tfs crate is a drop-in libtfs.so/.a — same ABI, second implementation.

Anatomy of a package.

Every package is the same byte-strip: three parts plus a trailer that says what's inside and — when signed — proves it. The full field tables live in concepts and the chain of trust.

byte 0 →→ EOFA · Bootstrapresolves + verifies theruntime, hands offthe executable partB · Runtime payloadfat packages only — theinterpreter, installed to cachelean: a runtime_ref insteadC · Image slot 0.tfs — the applicationdwarfs / squashfs / zipmounted at run timeC · slot 1…7extra images —data, assetsmulti-mountv2 extension8 × 32 B slot sha256 digestssigner keyid (8 B)OpenPGP detached signatureonly with --signtpkg header166 B at EOFmagic · flags · slotsruntime_ref · abicrc32 ≠ authenticitylean = A + C + trailer · fat adds B · signing adds the v2 extension — flagged, the version field stays 1, v1 readers parse on untouched

The trust chain.

One signature mechanism — OpenPGP via rnp-rs — from the publisher's key to the user's run. Verification happens at install, never per run; failures are named exits, never crashes. Details: the chain of trust.

signed payload.tfs + OpenPGP signaturefrom the publisher's registryor a --sign'ed packageTOFU keyring$TEBAKO_HOME/keyring/trusted.pgpnew keys: named promptverify at installtrailer signature, theneach slot's sha256one streaming passimmutable cacheimage 0444 + .sha256 /.origin trusted markersre-verify only on re-fetchdispatch → runno per-run hashing,no per-run ceremonyfail closed — named exits, never a crash71 bad signature · 72 untrusted signer key · 70 sha256 mismatchunsigned v1 → legacy warning + journal · TEBAKO_REQUIRE_SIGNED=1 rejects it

Who it's for.

Developers publishing apps

Press once per platform in a matrix CI job — no heroics per target. Native-extension apps build against the runtime's SDK instead of a prayer. The packager needs no host Ruby: tebako-cli is a single static binary.

Developers distributing versions

Your own GitHub releases are the registry: signed .tfs payloads, a manifest, per-version runtime constraints. No central store, no gatekeeper — and your users get version pinning for free.

End users

Run a binary and never think about Ruby — or, with tebako installed, manage every tool's version with zero reinstalls: one shims directory on PATH, project pins that just work, runtimes shared and downloaded once.

The ecosystem

TFS is a general userland VFS: libtfs for in-process mounting, the tfs CLI for humans, dwarfs-t underneath. Take the parts; leave the story.

Keep reading.

The rest of this section is the same story told precisely: concepts · the release pipeline · the VFS model · runtime as image · the chain of trust · the feature surface · the Rust track · the repo map · the version manager · for contributors.