ARCHITECTURE · 23
How tebako compares.
"Package once, run anywhere" is a big promise, and most tools attach conditions to it: a compatible JVM already installed, an engine already running, a machine that looks like the build machine. This page shows where tebako fits among the tools you already know — and names the cases where another tool is the better choice: a virtual machine when you need the strongest isolation, your system package manager when you administer the whole machine, and Docker where an engine is already running.
|
Note
|
Stated plainly. Tebako is not a replacement for your toolchain. You still develop against your own ruby, your own gems, your own OS packages. Tebako enters at the distribution seam: the moment a resolved, working app has to run on a machine you don’t control. Everything this page attributes to tebako — the jails, the dispatcher, the shared cache, the signing — is shipped behavior; the one forward-looking claim (non-ruby runtimes) is badged where it appears. |
What a process sees, in each world.
Strip away the packaging formats and the difference is one question: when
your program calls open(), what answers?
Figure 1 — What a process sees in each world: the tebako VFS, Docker namespaces, Homebrew system paths, rbenv shims, and a virtual machine.
The eight axes.
-
Install footprint — what lands on the machine to make the app run — megabytes, trees, daemons, hypervisors.
-
Runtime sharing — whether N apps on one machine carry N copies of their runtime, or one.
-
Startup cost — what happens between the user pressing enter and the app running — per run.
-
Isolation — what the app can see and touch beyond itself, and who enforces that.
-
Integrity model — how you know the bytes are the bytes the publisher shipped — checksums, signatures, chains of trust.
-
Offline behavior — what works with the network cable pulled — first run and steady state.
-
Cross-platform reach — which operating systems and libcs the same story covers, and at what cost per target.
-
Transparency — what the end user has to know, install, or run before the app — ideally: nothing.
"Runtime sharing" deserves its own sentence, because it is tebako’s structural difference: a tebako runtime — the interpreter executable plus its environment image — is downloaded once per machine and reused by every tebako application on it. Not once per app, not once per project: once. The cache is content-verified, installed read-only, and shared by design.
The matrix.
Eight axes, eleven answers, one line each. No row is all green, including ours — read the columns you care about.
| TOOL | INSTALL FOOTPRINT | RUNTIME SHARING | STARTUP COST | ISOLATION | INTEGRITY MODEL | OFFLINE BEHAVIOR | CROSS-PLATFORM REACH | TRANSPARENCY |
|---|---|---|---|---|---|---|---|---|
tebako |
one file; the runtime lands in the shared cache once |
yes — one verified runtime for every tebako app on the machine |
exec + in-process mount; no extraction, no per-run re-hashing |
declarative ro/rw jails per run — userland, no daemon, no root |
opt-in OpenPGP + per-slot sha256, fail closed with named exits |
fat: always; lean: full after the first fetch (TEBAKO_OFFLINE=1) |
linux gnu/musl · macOS · Windows; pure-language payloads are universal |
total — the user need not know tebako exists |
rbenv / rvm |
git clone + a build toolchain + per-machine ruby builds |
rubies shared across projects |
a shim re-exec hop, then native |
none |
source tarball checksums at build; gems over TLS |
yes, once rubies are built |
POSIX only — no Windows |
developers only |
homebrew |
a host-wide /opt tree plus dependency kegs |
kegs shared host-wide |
native exec — nothing between |
none — install scripts run as you, services higher |
sha256-pinned formulae, signed bottles, attestations |
no — network to install or update |
macOS + Linux; no Windows |
the user installs brew first |
rubygems / bundler |
gems per interpreter, per machine, per deploy |
duplicated per ruby version on the box |
gem activation at every process start |
none |
TLS to the gem server; X.509 gem signing is rare |
vendor/cache makes deploys offline-capable |
anywhere ruby runs — genuinely universal |
developers and servers, not end users |
.dmg / .msi |
a per-app bundle with the runtime embedded |
none — every app re-carries its world |
native exec |
only what the OS sandbox gives an opted-in app |
Gatekeeper notarization / Authenticode — OS-checked PKI |
yes — the installer itself is the offline artifact |
one OS (and arch) per artifact |
total — drag, click, run |
dnf / apt |
a root-owned system tree |
system libraries shared across every app on the box |
native exec |
none — packages own system paths, scripts run as root |
signed repo metadata (InRelease / repomd), always on |
local mirrors and offline media are first-class |
per-distro, per-release; linux only |
invisible after install — for admins |
snap |
the snapd daemon plus per-app squashfs packages |
base and content snaps shared between apps |
cold start: squashfs mount + confinement setup |
AppArmor/seccomp — full strength on Ubuntu, varies by host kernel |
store-signed assertions, verified by snapd at install |
installs need the store; installed apps run offline |
linux only — best on Ubuntu |
the user needs snapd and the store |
flatpak |
the flatpak daemon, an ostree repo, and portals |
runtimes shared; ostree dedups content between apps |
near-native once installed |
bubblewrap + portals — a real sandbox, host-dependent |
ostree GPG signatures; Flathub builds from manifests |
needs a remote to install; runs offline after |
desktop linux only |
the user installs flatpak plus a remote |
AppImage |
one file per app — the whole world bundled inside |
none — every app re-carries its runtime |
exec plus a FUSE mount of the embedded image |
none in the format (firejail is a separate add-on) |
no standard signatures, no chain of trust |
yes — the file is the whole app |
linux only |
total on linux — chmod +x and run |
docker / OCI |
an engine plus a GB-scale content store |
layer dedup across images sharing a base |
container create + start through the daemon |
namespaces + cgroups — real, shipped boundaries |
content digests; cosign / notary signing opt-in |
yes, after the first pull — but the engine must run |
linux ABI; a hidden VM on macOS/Windows |
the user installs docker |
VMs |
gigabytes per guest, plus a hypervisor |
nothing shared between guests |
a guest OS boot — seconds to minutes |
a hardware boundary — the strongest on this page |
whatever the guest’s own stack does |
yes, once provisioned |
any guest OS on any host |
ops teams, never end users |
The language packagers — a different category.
Everything above distributes software to machines generically. A second family of tools answers a narrower question: freeze this language’s app into a file. Python has PyInstaller, PyOxidizer, and PEX; ruby has ocra and traveling-ruby. They are the tools tebako is most often confused with — and the difference is the approach, not the feature list.
| Tool | Language | What it builds | The trade-off |
|---|---|---|---|
PyInstaller |
python |
a one-file or one-dir bundle with CPython embedded, per target OS |
nothing extra — but one-file mode unpacks to a temp dir at every start, and each OS needs its own build |
PyOxidizer |
python |
a single binary with an embedded CPython, imports served from memory |
nothing extra — but upstream development stalled in 2024; its future is uncertain |
PEX |
python |
a .pex zipapp of your code and dependencies |
a compatible python interpreter on the machine (or one fetched at first run via scies) |
ocra |
ruby |
a Windows self-extracting exe from the ruby installed on the build machine |
nothing extra — but Windows only, and it unpacks to a temp dir at every start |
traveling-ruby |
ruby |
a tar.gz/zip bundling a prebuilt portable ruby plus your gems, one per platform |
nothing extra — the app carries its own ruby copy for each platform |
The difference in approach.
-
They bind a language; tebako binds none. A language packager is a per-language answer — PyInstaller will never package a ruby app. Tebako’s loader neither knows nor cares what language it starts: a runtime is just a payload that provides an interpreter, plugged into the same provides/requires graph as every other payload. Ruby is the first instance; python and julia factories follow the same shape.
-
They embed the interpreter per app; tebako shares it per machine. Ten PyInstaller apps carry ten CPython copies. Ten tebako apps share one digest-pinned runtime from the machine-wide cache — downloaded once, verified once, mounted — never extracted — by all of them.
-
They produce per-OS artifacts; tebako’s pure-language payload is universal. PyInstaller must build on each target OS; traveling-ruby ships one tarball per platform. A pure-language tebako payload is one image that runs identically on every platform triplet, over per-triplet static loaders.
-
They end at the file; tebako starts there. Per-invocation version dispatch, per-run declarative jails, opt-in signatures, recursive payload composition — the ecosystem around the artifact is the product, not an afterthought.
|
Note
|
Status: planned — non-ruby runtimes. Honest limit. Today, tebako’s published runtimes are ruby-only. If you ship a python app this quarter, PyInstaller or PEX is the working answer — mature tooling that knows the language’s quirks, with no ecosystem to buy into. Tebako’s claim here is architectural: the per-language packagers solve packaging once per language; tebako solves packaging and loading once, for any runtime — and ruby is the proof of existence, not the boundary. |
Gatekeeper and Authenticode: the OS trust gates.
Gatekeeper (macOS) and Authenticode (Windows) are the two OS-level trust systems users actually rely on. They answer one question — "may this binary run?" — and tebako answers a different one — "is every part of what runs what it claims to be?" The systems are complementary layers, not competitors.
| Gatekeeper (macOS) | Authenticode (Windows) | Tebako | |
|---|---|---|---|
object verified |
the app bundle / binary |
the binary / driver |
every slice + every manifest + every runtime + registry items + the sums manifest — and partial re-stacking stays verifiable (per-slot digests) |
when verified |
at every execution, by the OS |
at install/UAC + SmartScreen heuristics |
at fetch, at install, at mount (per-slot digest before attach), at publish (author side) |
anchor of trust |
Apple-issued Developer ID cert (vendor CA) |
CA-issued cert (EV for reputation) |
embedded tamatebako root key (self-sovereign) + user trust store + successor-chain rotation |
unsigned path |
block or warn (policy) |
warn via SmartScreen reputation |
unverified-first with a loud warning + audit journal; TEBAKO_REQUIRE_SIGNED=1 fails closed |
revocation |
CRL/OCSP (online, coarse) |
CRL/OCSP (online, coarse) |
signed successor statements — offline, forward-verifiable — plus per-key registration/removal |
offline / air-gap |
verifies offline; notarizing needs online at build time |
mostly offline; timestamping needs online at sign time |
full offline first-class: embedded root + pinned keys + signed sums + air-gap bundles |
scope beyond executables |
none (binaries only) |
none (binaries only) |
runtimes, data slices, toolkits, suites, registries, manifests, indexes — the whole object graph |
cross-platform |
macOS only |
Windows only |
one model across linux gnu/musl, macOS, and Windows |
key rotation |
cert renewal via the CA |
cert renewal via the CA |
self-describing: the old root signs its successor; clients forward-verify offline |
enforcement level |
OS syscall gate — the app cannot bypass it |
OS gate + kernel driver signing |
loader-level — honestly: a patched loader is beyond any loader, which is exactly where the OS gates protect us |
ubiquity |
every Mac, nothing to install |
every Windows box, nothing to install |
anywhere a single binary lands — zero host prerequisites |
provenance |
developer identity only |
publisher identity only |
identity + builder + source ref + payload tree hash + contract_version |
Where tebako defers — and integrates. Continuous execution-time enforcement. Gatekeeper’s syscall-level gate and SmartScreen’s reputation network cannot be replicated by an application-level loader, and a patched loader is outside any loader’s reach. Tebako’s answer is to sign tebako-produced binaries for both gates, so the OS protects the root anchor continuously: their enforcement guards our bootstrap, our verification guards everything below it. Apple and Microsoft are also already on the machine, and notarization includes actual scanning — tebako does not imitate this; it slots into it.
Where tebako goes further. Granularity: they see a binary; tebako sees every slice inside it — swap one slice and only its slot needs re-verifying. Scope: runtimes, data slices, toolkits, suites, registries, and indexes all use the same signed-image machinery. Sovereignty: no corporate CA in the loop — root rotation is a self-describing signed chain, verifiable offline. Provenance: builder, source ref, payload tree hash, and contract version — compared to "Developer ID: Company X".
Where each alternative genuinely wins.
Tebako is a distribution format for applications, not a bid to replace your package manager, your container engine, your language tooling, or your hypervisor.
| Tool | Where it genuinely wins |
|---|---|
RubyGems |
sharing code between developers. Tebako packages applications; gems remain how developers share libraries. |
rbenv / rvm |
hacking on ruby itself. If your job is building interpreters, you want a version manager, not a package. |
Homebrew / apt / dnf |
OS-integrated components with a security-update channel. Tebako deliberately never manages the OS. |
Installers (.dmg/.msi) |
deep OS integration: drivers, system extensions, things that must be on the host — plus enterprise deployment and notarization review. |
snap / flatpak |
store discoverability and daemon-managed automatic updates — a real distribution channel, paid for with the daemon and platform coverage. |
AppImage |
zero-machinery single-file desktop linux, with mature tooling and a large catalog. The closest neighbor of a fat tebako package. |
Language packagers |
a single-language app, today, in that language: PyInstaller/PyOxidizer/PEX know python’s quirks, ocra/traveling-ruby know ruby’s, and none of them ask you to buy into an ecosystem. |
Docker / OCI |
server-side multi-process orchestration and its ecosystem. Tebako distributes tools to people; Docker runs services on fleets. |
Virtual machines |
hard boundaries for hostile native code. Jails are filesystem policy, not a hardware security boundary. |
What exists nowhere else.
-
A single file that is simultaneously the app, its verifier, and its runtime resolver — self-authenticating, offline, over any transport including a USB stick.
-
Universal payloads over digest-pinned shared runtimes — a pure-language app ships one image for every platform, and the runtime underneath it downloads once per machine. Electron’s fidelity with a shared runtime’s footprint, and neither’s lock-in.
-
Per-entrypoint runtime pins inside one package — suites: multiple commands in one artifact, each with its own runtime requirement.
-
Recursive payload composition — an app declares toolkits and data at mount points, each independently published and versioned, resolved from a provides/requires graph.
-
Per-run declarative jails for the end user — no daemon, no root, no kernel module; tighten any command’s filesystem view at invocation time.
-
Object-level encryption with per-subtree audiences — hierarchical keys, crypto-shredding revocation, in-memory-only plaintext — in an application distribution format.
-
One signed-image algebra at every level — sources, runtimes, payloads, packages, registries — verified the same way, with verification that survives redistribution through untrusted mirrors.
Where tebako loses, stated plainly.
-
It is ruby-shaped today. The contracts are language-neutral; the published artifacts are not — yet. Until the python and julia factories follow the ruby one, the language packagers keep their home field.
-
No orchestration, no dev loop, no store. There is no compose, no kubernetes, no browsable catalog. Registries are plain YAML files on the developer’s own git host — the reference syntax (
tfs:github:/tfs:gitlab:/tfs+https:///file://,?sha256=pins) deliberately has no default service. And tebako doesn’t develop with you: it presses a finished, resolved app; it is not where you hack. -
Per-platform artifacts for native code. A standalone package is native — one artifact per platform triplet, built in a matrix. Only pure-language payloads collapse to one universal image; native extensions stay per-triplet, and a wrong ABI line is a named error, never a segfault.
-
Jails are VFS policy, not a security boundary. The declarative ro/rw jail controls what the process can see and touch through the filesystem — it does not contain hostile native code. That is what VMs are for, and this page won’t pretend otherwise.
-
The integrity story is opt-in. Signing and encryption are the publisher’s choice; an unsigned package runs with a loud warning, recorded in the audit journal. The distro channel’s always-on signed metadata is the stronger default — tebako’s answer is that verification, when a signature exists, is strict and fails closed with named exit codes, and
TEBAKO_REQUIRE_SIGNED=1makes it mandatory where the environment demands it.
Keep reading.
Then the platform story: the overview for what tebako is · runtime as image for the shared cache · dispatch for per-invocation version management · the chain of trust for the integrity machinery · the store for where everything lives.