Skip to content

ARCHITECTURE · 23

How tebako compares.

"Package once, run anywhere" is a large promise, and most tools attach conditions to it: a compatible JVM already installed, an engine already running, or a machine that looks like the build machine. This page shows where tebako fits among the tools a developer already knows, and it names the cases where another tool is the better choice: a virtual machine when the strongest isolation is needed, the system package manager when the whole machine is administered, and Docker where an engine is already running.

Note

The position is stated plainly. Tebako is not a replacement for the developer’s toolchain: development still happens against the developer’s own ruby, the developer’s own gems, and the developer’s own OS packages. Tebako enters at the distribution seam, the moment a resolved, working application has to run on a machine that the developer does not control. Everything this page attributes to tebako, the jails, the dispatcher, the shared cache, and 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

Once the packaging formats are stripped away, the difference is one question: when the program calls open(), what answers?

TEBAKO your process the VFS — userland, in-process mounted images host, jail-gated no kernel, no FUSE, no daemon DOCKER / OCI your process kernel namespaces + overlayfs the container image — Linux only needs the engine; a hidden VM off-Linux HOMEBREW / DNF your process the kernel — the plain host FS /opt/homebrew — mutated per install versions float with the repo RBENV / RVM your process a shell shim re-points PATH ~/.rbenv/versions/x.y.z plain host files ruby versions managed; apps not packaged A VIRTUAL MACHINE your process a guest kernel, on virtual hardware a whole guest OS + its disk image the real boundary — at a gigabyte's weight

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

  1. The install footprint is what lands on the machine to make the app run: megabytes, trees, daemons, or hypervisors.

  2. Runtime sharing is the question of whether N apps on one machine carry N copies of their runtime or one copy.

  3. The startup cost is what happens between the user pressing enter and the app running, paid per run.

  4. Isolation is what the app can see and touch beyond itself, and who enforces that boundary.

  5. The integrity model is how the user knows the bytes are the bytes the publisher shipped: checksums, signatures, or chains of trust.

  6. The offline behavior is what works with the network cable pulled, at first run and in steady state.

  7. The cross-platform reach is which operating systems and libcs the same story covers, and at what cost per target.

  8. Transparency is what the end user has to know, install, or run before the app, where the ideal is 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 and not once per project but once. The cache is content-verified, installed read-only, and shared by design.

The comparison matrix

The matrix has eight axes, eleven answers, and one line per tool. No row is all green, including tebako’s own row, so the reader should weigh the columns that matter for the case at hand.

TOOL INSTALL FOOTPRINT RUNTIME SHARING STARTUP COST ISOLATION INTEGRITY MODEL OFFLINE BEHAVIOR CROSS-PLATFORM REACH TRANSPARENCY

tebako

The install is one file, and the runtime lands in the shared cache once.

Yes: one verified runtime serves every tebako application on the machine.

Startup is an exec plus an in-process mount, with no extraction and no per-run re-hashing.

Declarative ro/rw jails apply per run, in userland, with no daemon and no root.

The integrity model is opt-in OpenPGP plus per-slot sha256, and it fails closed with named exits.

A fat package always works offline; a lean package works fully after the first fetch (TEBAKO_OFFLINE=1).

The reach covers linux gnu/musl, macOS, and Windows, and pure-language payloads are universal.

Transparency is total: the user need not know that tebako exists.

rbenv / rvm

The install is a git clone, a build toolchain, and per-machine ruby builds.

Rubies are shared across projects.

Startup is a shim re-exec hop and then native execution.

There is none.

Source tarballs are checksummed at build, and gems travel over TLS.

Yes, once the rubies are built.

It covers POSIX only, with no Windows.

It serves developers only.

homebrew

The install is a host-wide /opt tree plus dependency kegs.

Kegs are shared host-wide.

Startup is native exec with nothing in between.

There is none: install scripts run as the invoking user, and services run higher.

Formulae are sha256-pinned, bottles are signed, and attestations exist.

No: installing or updating needs the network.

It covers macOS and Linux, with no Windows.

The user installs brew first.

rubygems / bundler

Gems install per interpreter, per machine, and per deploy.

Gems are duplicated per ruby version on the box.

Startup pays gem activation at every process start.

There is none.

TLS protects the gem server, and X.509 gem signing is rare.

vendor/cache makes deploys offline-capable.

It runs anywhere ruby runs, which is genuinely universal.

It serves developers and servers, not end users.

.dmg / .msi

The install is a per-app bundle with the runtime embedded.

There is none: every app re-carries its world.

Startup is native exec.

Isolation is only what the OS sandbox gives an opted-in app.

Integrity rests on Gatekeeper notarization or Authenticode, an OS-checked PKI.

Yes: the installer itself is the offline artifact.

Each artifact covers one OS and architecture.

Transparency is total: the user drags, clicks, and runs.

dnf / apt

The install is a root-owned system tree.

System libraries are shared across every app on the box.

Startup is native exec.

There is none: packages own system paths, and scripts run as root.

Repo metadata (InRelease / repomd) is signed and always on.

Local mirrors and offline media are first-class.

Coverage is per distro and per release, and linux only.

It is invisible after install, and it serves admins.

snap

The install is the snapd daemon plus per-app squashfs packages.

Base and content snaps are shared between apps.

A cold start pays a squashfs mount plus confinement setup.

Isolation is AppArmor/seccomp, at full strength on Ubuntu and varying by host kernel.

Assertions are store-signed and verified by snapd at install.

Installs need the store, and installed apps run offline.

It covers linux only and works best on Ubuntu.

The user needs snapd and the store.

flatpak

The install is the flatpak daemon, an ostree repository, and portals.

Runtimes are shared, and ostree dedups content between apps.

Startup is near-native once installed.

Isolation is bubblewrap plus portals, a real sandbox that depends on the host.

Ostree carries GPG signatures, and Flathub builds from manifests.

It needs a remote to install, and it runs offline afterwards.

It covers desktop linux only.

The user installs flatpak plus a remote.

AppImage

The install is one file per app, with the whole world bundled inside.

There is none: every app re-carries its runtime.

Startup is exec plus a FUSE mount of the embedded image.

The format has none, and firejail is a separate add-on.

There are no standard signatures and no chain of trust.

Yes: the file is the whole app.

It covers linux only.

On linux it is total: the user marks the file executable and runs it.

docker / OCI

The install is an engine plus a GB-scale content store.

Layer dedup works across images sharing a base.

Startup is container create plus start through the daemon.

Isolation is namespaces plus cgroups, real and shipped boundaries.

Integrity rests on content digests, with cosign / notary signing as opt-in.

Yes, after the first pull, but the engine must run.

The reach is the linux ABI, with a hidden VM on macOS/Windows.

The user installs docker.

VMs

The install is gigabytes per guest plus a hypervisor.

Nothing is shared between guests.

Startup is a guest OS boot, from seconds to minutes.

Isolation is a hardware boundary, the strongest on this page.

Integrity is whatever the guest’s own stack does.

Yes, once provisioned.

Any guest OS runs on any host.

It serves 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 application into a file. Python has PyInstaller, PyOxidizer, and PEX; ruby has ocra and traveling-ruby. These are the tools tebako is most often confused with, and the difference is the approach rather than the feature list.

Tool Language What it builds The trade-off

PyInstaller

python

It builds a one-file or one-dir bundle with CPython embedded, per target OS.

It asks for nothing extra, but one-file mode unpacks to a temp dir at every start, and each OS needs its own build.

PyOxidizer

python

It builds a single binary with an embedded CPython, with imports served from memory.

It asks for nothing extra, but upstream development stalled in 2024 and its future is uncertain.

PEX

python

It builds a .pex zipapp of the code and its dependencies.

It needs a compatible python interpreter on the machine, or one fetched at first run via scies.

ocra

ruby

It builds a Windows self-extracting exe from the ruby installed on the build machine.

It asks for nothing extra, but it is Windows only, and it unpacks to a temp dir at every start.

traveling-ruby

ruby

It builds a tar.gz/zip bundling a prebuilt portable ruby plus the gems, one per platform.

It asks for nothing extra, and 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, and 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 applications share one digest-pinned runtime from the machine-wide cache, downloaded once and verified once, and 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. The ecosystem around the artifact is per-invocation version dispatch, per-run declarative jails, opt-in signatures, and recursive payload composition, and that ecosystem is the product rather than an afterthought.

Note

Non-ruby runtimes are planned.

The honest limit is the following: today, tebako’s published runtimes are ruby-only. When a python application ships this quarter, PyInstaller or PEX is the working answer, mature tooling that knows the language’s quirks and asks for no ecosystem buy-in. The tebako claim here is architectural: the per-language packagers solve packaging once per language, while tebako solves packaging and loading once for any runtime, and ruby is the proof of existence rather than the boundary.

Gatekeeper and Authenticode: the OS trust gates

Gatekeeper (macOS) and Authenticode (Windows) are the two OS-level trust systems that users actually rely on. Gatekeeper and Authenticode 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 verified object is the app bundle or binary.

The verified object is the binary or driver.

The verified objects are every slice, every manifest, every runtime, registry items, and the sums manifest, and partial re-stacking stays verifiable through per-slot digests.

when verified

Verification happens at every execution, by the OS.

Verification happens at install/UAC and through SmartScreen heuristics.

Verification happens at fetch, at install, at mount (the per-slot digest before attach), and at publish on the author side.

anchor of trust

The anchor is an Apple-issued Developer ID certificate from a vendor CA.

The anchor is a CA-issued certificate, EV for reputation.

The anchor is the embedded tamatebako root key (self-sovereign) plus the user trust store plus successor-chain rotation.

unsigned path

The OS blocks or warns, by policy.

The OS warns through SmartScreen reputation.

The path is unverified-first with a loud warning and an audit journal, and TEBAKO_REQUIRE_SIGNED=1 fails closed.

revocation

Revocation is CRL/OCSP, online and coarse.

Revocation is CRL/OCSP, online and coarse.

Revocation uses signed successor statements, offline and forward-verifiable, plus per-key registration and removal.

offline / air-gap

It verifies offline; notarizing needs the network at build time.

It is mostly offline; timestamping needs the network at sign time.

Full offline operation is first-class: embedded root, pinned keys, signed sums, and air-gap bundles.

scope beyond executables

There is none; binaries only.

There is none; binaries only.

The scope covers runtimes, data slices, toolkits, suites, registries, manifests, and indexes, the whole object graph.

cross-platform

It covers macOS only.

It covers Windows only.

One model covers linux gnu/musl, macOS, and Windows.

key rotation

Rotation is certificate renewal via the CA.

Rotation is certificate renewal via the CA.

Rotation is self-describing: the old root signs its successor, and clients forward-verify offline.

enforcement level

Enforcement is an OS syscall gate that the app cannot bypass.

Enforcement is an OS gate plus kernel driver signing.

Enforcement is loader-level, and the honest statement is that a patched loader is beyond any loader, which is exactly where the OS gates provide protection.

ubiquity

It is on every Mac, with nothing to install.

It is on every Windows box, with nothing to install.

It applies anywhere a single binary lands, with zero host prerequisites.

provenance

Provenance is developer identity only.

Provenance is publisher identity only.

Provenance covers identity, builder, source ref, payload tree hash, and contract_version.

Tebako defers, and integrates, in one area: 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 the tebako bootstrap, and tebako’s verification guards everything below it. Apple and Microsoft are also already on the machine, and notarization includes actual scanning; tebako does not imitate this but slots into it.

Tebako goes further in four respects. Granularity: the OS sees a binary, while tebako sees every slice inside it, so swapping one slice means only that slice’s slot needs re-verifying. Scope: runtimes, data slices, toolkits, suites, registries, and indexes all use the same signed-image machinery. Sovereignty: no corporate CA sits in the loop, and root rotation is a self-describing signed chain that is verifiable offline. Provenance: builder, source ref, payload tree hash, and contract version, compared with "Developer ID: Company X".

Where each alternative genuinely wins

Tebako is a distribution format for applications, not a bid to replace the reader’s package manager, container engine, language tooling, or hypervisor.

Tool Where it genuinely wins

RubyGems

It wins for sharing code between developers. Tebako packages applications; gems remain how developers share libraries.

rbenv / rvm

It wins for hacking on ruby itself. When the job is building interpreters, a version manager is the right tool, not a package.

Homebrew / apt / dnf

These win for OS-integrated components with a security-update channel. Tebako deliberately never manages the OS.

Installers (.dmg/.msi)

They win for deep OS integration: drivers, system extensions, and things that must be on the host, plus enterprise deployment and notarization review.

snap / flatpak

They win for store discoverability and daemon-managed automatic updates, a real distribution channel paid for with the daemon and platform coverage.

AppImage

It wins for zero-machinery single-file desktop linux, with mature tooling and a large catalog, and it is the closest neighbor of a fat tebako package.

Language packagers

They win for 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 the developer to buy into an ecosystem.

Docker / OCI

It wins for server-side multi-process orchestration and its ecosystem. Tebako distributes tools to people; Docker runs services on fleets.

Virtual machines

They win for hard boundaries around 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. It is self-authenticating, works offline, and travels 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; the result is Electron’s fidelity with a shared runtime’s footprint, and the lock-in of neither.

  • Per-entrypoint runtime pins inside one package. Suites place 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. There is no daemon, no root, and no kernel module, and any command’s filesystem view can be tightened at invocation time.

  • Object-level encryption with per-subtree audiences in an application distribution format. It offers hierarchical keys, crypto-shredding revocation, and in-memory-only plaintext.

  • One signed-image algebra at every level, covering sources, runtimes, payloads, packages, and registries. All of them are verified the same way, with verification that survives redistribution through untrusted mirrors.

Where tebako loses

  • 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, and no browsable catalog. Registries are plain YAML files on the developer’s own git host, and the reference syntax (tfs:github: / tfs:gitlab: / tfs+https:// / file://, with ?sha256= pins) deliberately has no default service. Tebako also does not develop alongside the programmer: it presses a finished, resolved app, and it is not where hacking happens.

  • Per-platform artifacts for native code. A standalone package is native, with 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, and it does not contain hostile native code. That is what VMs are for, and this page does not pretend otherwise.

  • The integrity story is opt-in. Signing and encryption are the publisher’s choice, and 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; the tebako answer is that verification, when a signature exists, is strict and fails closed with named exit codes, and TEBAKO_REQUIRE_SIGNED=1 makes it mandatory where the environment demands it.

Further reading

The platform story continues on the overview for what tebako is, on runtime as image for the shared cache, on dispatch for per-invocation version management, on the chain of trust for the integrity machinery, and on the store for where everything lives.