gem install tebako
tebako setup # first time, up to 1 h
tebako press -r ./myapp -e start.rb -o myapp.tebako
./myapp.tebako # monolith: runtime embedded, runs anywhere
ARCHITECTURE · 26
Migrate from v1 to v2.
The v1 tebako was the Ruby gem: gem install tebako, a tebako setup that built Ruby from source, and a tebako press that produced one monolithic binary with the runtime embedded. v2 is the same press, rebuilt in Rust: one static binary, no gem, no CMake, and no setup step, plus a package format that can share one runtime between every tebako app on a machine. This page maps a v1 workflow onto v2: flags, outputs, CI, and the behavior differences that matter.
Every v2 command and workflow on this page was executed end-to-end, a CLI
build, a press, and a run, in a clean ubuntu:24.04 container against
tamatebako/tebako (ec12734, the
tip of main at writing time). Where v2 does not yet cover a v1 behavior,
this page says so plainly instead of papering over it.
The short version
V1 — the gem (≤ 0.13.x):
V2 — the Rust CLI:
# install the tebako binary (next section)
tebako press -r ./myapp -e start.rb -o myapp.tebako
./myapp.tebako # lean: runtime fetched once, then cached
tebako press -m fat -r ./myapp -e start.rb -o myapp-fat # ≈ the v1 monolith
The press flags in daily use, -r, -e, -o, -p, -c, -R, and -l,
are spelled exactly the same. What changed is what they produce and what no
longer exists.
Installing v2
v2 is a single static binary, plus its tebako-bootstrap companion.
Prebuilt binaries ship with every tagged release: the
releases page carries
tebako-<version>-<platform> assets for macos-arm64, macos-x86_64, the
linux-gnu-* / linux-musl-* families, and windows ucrt64 (v2.8.12 is
live). Building from source remains available — once, taking 20–40 minutes,
with no Ruby required:
# Debian/Ubuntu (macOS: brew the same list, minus build-essential)
sudo apt-get install -y build-essential cmake ninja-build pkg-config git \
autoconf automake autoconf-archive libtool curl zip unzip tar ca-certificates \
python3 clang libclang-dev zlib1g-dev libbz2-dev
curl -fsSL https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.94.1
. "$HOME/.cargo/env"
git clone https://github.com/tamatebako/tebako tebako
git clone --recurse-submodules https://github.com/tamatebako/dwarfs-rs # sibling path dep
git clone https://github.com/microsoft/vcpkg
git -C vcpkg checkout f14401ca0f2754347c3864da7488a9b955b4e47a # the CI pin
vcpkg/bootstrap-vcpkg.sh -disableMetrics
cd tebako
# Cargo.lock is not tracked upstream; a fresh resolve today pulls a broken
# rnp-rs 0.1.9 (its vendored Botan build fails) — pin the version the
# workspace's own builds use:
cargo update -p rnp-rs --precise 0.1.7
TRIPLET=x64-linux-static; [ "$(uname -m)" = "aarch64" ] && TRIPLET=arm64-linux-static
../vcpkg/vcpkg install --vcpkg-root "$PWD/../vcpkg" --x-wait-for-lock \
--x-manifest-root crates/sqfs-sys --x-install-root "$PWD/../.sqfs-installed" \
--triplet "$TRIPLET" \
--overlay-triplets crates/sqfs-sys/vcpkg_triplets \
--overlay-ports crates/sqfs-sys/vcpkg_ports
# static Botan for the OpenPGP signer (rnp-rs links -lbotan-3 and no
# prebuilt ships the archive — the release legs provide it the same way):
../vcpkg/vcpkg install botan --vcpkg-root "$PWD/../vcpkg" --x-wait-for-lock \
--x-install-root "$PWD/../.crypto-vcpkg" --triplet "$TRIPLET" \
--overlay-triplets ../dwarfs-rs/dwarfs-t/vcpkg_triplets
mkdir -p .crypto-static && cp "../.crypto-vcpkg/$TRIPLET/lib/libbotan-3.a" .crypto-static/
SQFS_SYS_VCPKG_INSTALLED_DIR="$PWD/../.sqfs-installed/$TRIPLET" \
DWARFS_RS_VCPKG_ROOT="$PWD/../vcpkg" \
RUSTFLAGS="-L $PWD/.crypto-static" \
cargo build --release -p tebako-cli -p tebako-bootstrap
sudo install -m755 target/release/tebako target/release/tebako-bootstrap /usr/local/bin/
tebako --version # Tebako executable packager version 0.2.5
# On Linux, also make cc clang if you will press apps whose gems build
# from source (json/bigdecimal/brotli & co. — "behavior differences" 07):
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 50
Keep tebako and tebako-bootstrap side by side, because the press picks
the bootstrap up from next to the CLI (override with --bootstrap or
$TEBAKO_BOOTSTRAP). The v1 gem can stay installed; the two do not
interfere, and they share the same ~/.tebako runtime cache.
The concept map: tebako press, flag by flag
The v1 column is the long-lived gem surface (≤ 0.13.x, the releases most
production CI encodes). On v1 0.14–0.15, the short prebuilt-runtime
transition, the flags already look like v2’s, and the only differences are
classic mode, setup/clean/hash, and the tebafile, all covered below.
| V1 (GEM ≤ 0.13) | V2 (RUST) | WHAT CHANGED |
|---|---|---|
|
|
unchanged |
|
|
unchanged |
|
|
unchanged (entry-point base name in the current folder by default) |
|
|
unchanged (packaging work area, ~/.tebako by default) |
|
|
unchanged (packaged app’s working directory, relative to root) |
|
|
The role is the same, but the choice is limited to the rubies the prebuilt runtime release carries (3.1.6–4.0.6 at v0.16.9, with no 2.7/3.0). |
|
|
unchanged (error |
warn |
debug |
trace) |
`-m, --mode bundle |
both |
runtime |
application` |
`-m, --mode lean |
fat` |
see the mode map below |
|
|
removed (v1 dropped it in 0.14 too): the GLIBC_PRIVATE/patchelf era ended with prebuilt runtimes |
|
|
removed: named an external runtime package; a v2 lean package carries its runtime reference automatically |
|
|
removed: v2 exits with ".tebako.yml is not supported by the tebako-rs CLI (pass the options directly)" |
|
|
unchanged (skips the cache version guard) |
|
|
The mode map
| V1 MODE | V2 EQUIVALENT | WHY |
|---|---|---|
|
|
Fat carries the runtime as a payload slot, and the first run installs it into the shared cache with no network; this is the monolith’s offline guarantee. |
|
|
Lean is the split form, productized: the runtime is named in the package trailer and resolved into the machine cache on first run, with no --ref and no separate file to ship. |
|
|
The two modes are cheap enough to run as two presses. |
|
|
The mode is named but rejected: "the 'classic' press mode is a later tebako-rs milestone (use --mode=lean or --mode=fat)". Use fat for the one-file shape. |
|
|
The answer is the same as late v1: runtime packages come from the tebako-runtime-ruby pipeline, never from a press. |
Removed commands.
-
tebako setup→nothing: there is no environment to build, because runtimes are prebuilt downloads. v2 answers "'tebako setup' is a later tebako-rs milestone". -
tebako clean→tebako cache prune [--all]: prune covers the runtime cache, and the packaging prefix is disposable, so deleting the directory suffices. -
tebako clean_ruby→—: the command is gone with the in-gem Ruby source build. -
tebako hash→—: there is no build script to cache-key, so CI caches key on the Ruby version instead. -
tebako cache list / prune→tebako cache list [--json] / prune: the commands are the same, and --json adds a machine-readable contract (the banner moves to stderr).
New in v2.
-
--suite <suite.yaml>: one package with N commands, per-entry images and slots, and one runtime per entry. Lean only; -r/-e are not accepted with it. -
--bootstrap <path>: overrides the bootstrap binary (default: the tebako-bootstrap next to the tebako binary, else a downloaded release; $TEBAKO_BOOTSTRAP also works). -
--tebako-version <v>: pins the runtime release to press against (default 0.16.9). -
--prefer-local: restores the gem-era bundle install --prefer-local (resolution prefers the runtime’s own gems). Off by default; a no-op with a complete Gemfile.lock. -
tebako install / uninstall: installs a published payload (.tfs) from a registry and registers its shims. -
tebako add-registry / list-registries / update-registries: the registry surface behind tebako install. -
tebako publish: press → sign → upload → registry entry → tap formula (the developer/publisher flow).
What a press produces now
A v1 press gave the user one thing: a monolithic binary with the runtime embedded, large and identical on every machine. A v2 press gives a three-part package (bootstrap + the app as a filesystem image + a trailer naming the runtime), in two flavors. The anatomy is detailed in the concepts page; what matters for the migration is the choice:
- lean (default)
-
The package is the bootstrap (< 3 MB) plus the app image plus the trailer. It is small enough to attach to every CI build. The first run on a machine downloads the runtime once into
~/.tebako(SHA-256-verified), and every tebako app on that machine reuses it from then on. - fat
-
Lean plus the runtime itself as a payload slot. The first run installs the payload into the same cache, verified, with zero network. This is the v1 monolith’s distribution shape: one file, runs anywhere, sized like the v1 binary.
How to choose. Pick fat when the binary goes to machines outside the
developer’s control, when the first run must work air-gapped, or when
"download it and it just runs" is the contract (that was v1’s contract). Pick
lean when artifact size matters, when several shipped apps can share one
Ruby, or when first-run network access is acceptable. Either way the runtime
lands in the same cache, and a machine that already ran any tebako app runs
a lean package offline. TEBAKO_OFFLINE=1 forces cache-only resolution;
TEBAKO_RUNTIME_MIRROR redirects downloads to the developer’s own mirror;
TEBAKO_HOME moves the cache root.
One mental shift remains: in v1 the runtime was the developer’s own, built by the developer’s setup and embedded in the developer’s binary. In v2 the runtime is a shared, versioned system resource, built by the tebako-runtime-ruby pipeline, fetched once per machine, and referred to by every package. v1 binaries keep their embedded runtime forever, and nothing about v2 reaches back into them.
CI migration: the three shapes everyone runs
The three v1 CI shapes below cover nearly every gem-era workflow in the
wild. Each "after" was executed end-to-end in a clean container (build the
CLI, press, run the package), and the run: lines shown are the exact lines
executed.
The install-tebako block is identical in all three workflows and is inlined
once, in pair A; copy it verbatim into B and C where marked. It builds from
source, which remains a supported route; since the binary releases began
shipping, the same install also collapses to a two-line download of
tebako and tebako-bootstrap from the releases page.
A · Press in CI (a script app, no Gemfile)
Before — v1 gem:
name: package
on: [push]
jobs:
press:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
- name: Install tebako
run: gem install tebako -v 0.13.4
- name: Cache the packaging environment
uses: actions/cache@v4
with:
path: ~/.tebako
key: tebako-v1-{{ runner.os }}-0.13.4
- name: Build the packaging environment
# cache miss = a from-source Ruby build, up to 1 h
run: tebako setup
- name: Press
run: tebako press -r . -e start.rb -o dist/myapp
- name: Smoke-test the package
run: ./dist/myapp
- uses: actions/upload-artifact@v4
with:
name: myapp-linux
path: dist/myapp
After — v2 Rust CLI:
name: package
on: [push]
jobs:
press:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
path: app # keep the app root clean of build trees
# --- begin: install tebako v2 (source build, until the
# first binary release — then two download lines) ---
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential cmake ninja-build pkg-config git \
autoconf automake autoconf-archive libtool \
curl zip unzip tar ca-certificates python3 clang libclang-dev \
zlib1g-dev libbz2-dev
- uses: dtolnay/rust-toolchain@1.94.1
- name: Check out the tebako v2 sources
uses: actions/checkout@v4
with:
repository: tamatebako/tebako
path: tebako
- name: Check out dwarfs-rs (sibling path dependency)
uses: actions/checkout@v4
with:
repository: tamatebako/dwarfs-rs
path: dwarfs-rs
submodules: recursive
- name: Set up vcpkg
run: |
git clone https://github.com/microsoft/vcpkg "$RUNNER_TEMP/vcpkg"
git -C "$RUNNER_TEMP/vcpkg" checkout \
f14401ca0f2754347c3864da7488a9b955b4e47a
"$RUNNER_TEMP/vcpkg/bootstrap-vcpkg.sh" -disableMetrics
- name: Pre-install squashfs-tools-ng
working-directory: tebako
run: |
TRIPLET=x64-linux-static
[ "$(uname -m)" = "aarch64" ] && TRIPLET=arm64-linux-static
"$RUNNER_TEMP/vcpkg/vcpkg" install \
--vcpkg-root "$RUNNER_TEMP/vcpkg" --x-wait-for-lock \
--x-manifest-root crates/sqfs-sys \
--x-install-root "$RUNNER_TEMP/.sqfs-installed" \
--triplet "$TRIPLET" \
--overlay-triplets crates/sqfs-sys/vcpkg_triplets \
--overlay-ports crates/sqfs-sys/vcpkg_ports
echo "SQFS_SYS_VCPKG_INSTALLED_DIR=$RUNNER_TEMP/.sqfs-installed/$TRIPLET" \
>> "$GITHUB_ENV"
- name: Build the tebako CLI
working-directory: tebako
env:
DWARFS_RS_VCPKG_ROOT: {{ runner.temp }}/vcpkg
run: |
# Cargo.lock is untracked upstream; fresh resolves pull a
# broken rnp-rs 0.1.9 — pin the version CI builds with:
cargo update -p rnp-rs --precise 0.1.7
# static Botan for the OpenPGP signer (rnp-rs links -lbotan-3):
TRIPLET=x64-linux-static
[ "$(uname -m)" = "aarch64" ] && TRIPLET=arm64-linux-static
"$RUNNER_TEMP/vcpkg/vcpkg" install botan \
--vcpkg-root "$RUNNER_TEMP/vcpkg" --x-wait-for-lock \
--x-install-root "$RUNNER_TEMP/.crypto-vcpkg" \
--triplet "$TRIPLET" \
--overlay-triplets ../dwarfs-rs/dwarfs-t/vcpkg_triplets
mkdir -p .crypto-static
cp "$RUNNER_TEMP/.crypto-vcpkg/$TRIPLET/lib/libbotan-3.a" .crypto-static/
RUSTFLAGS="-L $PWD/.crypto-static" \
cargo build --release -p tebako-cli -p tebako-bootstrap
- name: Put tebako on PATH
run: echo "$GITHUB_WORKSPACE/tebako/target/release" >> "$GITHUB_PATH"
# --- end: install tebako v2 ---
- name: Press (lean — the default; add -m fat for the v1 shape)
run: tebako press -r app -e start.rb -o dist/myapp
- name: Smoke-test the package
run: ./dist/myapp
- uses: actions/upload-artifact@v4
with:
name: myapp-linux
path: dist/myapp
What fell away is the Ruby install, the gem install, the ~/.tebako cache
dance, and the setup hour. What appeared is a one-time CLI build (cache
~/.cache/vcpkg and tebako/target when this runs often) and a press-time
runtime download that replaces the setup hour with a minute.
B · Press from a Gemfile (a bundled app — the fontist fixture)
Before — v1 gem:
name: package
on: [push]
jobs:
press:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4 # Gemfile + Gemfile.lock + main.rb
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
bundler-cache: true
- name: Install tebako
run: gem install tebako -v 0.13.4
- name: Cache the packaging environment
uses: actions/cache@v4
with:
path: ~/.tebako
key: tebako-v1-{{ runner.os }}-0.13.4
- name: Build the packaging environment
run: tebako setup -R 3.3.7
- name: Press (the Gemfile is picked up automatically)
run: tebako press -r . -e main.rb -o dist/fontist-app -R 3.3.7
- name: Smoke-test the package
run: ./dist/fontist-app # => fontist 3.0.10
- uses: actions/upload-artifact@v4
with:
name: fontist-app-linux
path: dist/fontist-app
After — v2 Rust CLI:
name: package
on: [push]
jobs:
press:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
path: app
# … the identical "install tebako v2" steps — copy them
# from pair A above …
- name: Prefer clang as the system cc
# the prebuilt runtimes are compiled with clang, and a press that
# builds a native gem from source replays their build flags
# against the host cc — gcc rejects -fdeclspec and the press dies
# in mkmf. This lockfile pins ruby-platform-only natives
# (json, bigdecimal, brotli), so clang is required here.
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 50
- name: Press (the Gemfile is still picked up automatically)
run: tebako press -r app -e main.rb -o dist/fontist-app
# the Ruby version comes from the Gemfile's ruby directive
# (or -R); no host Ruby is involved at all
- name: Smoke-test the package
run: ./dist/fontist-app # => fontist 3.0.10
- uses: actions/upload-artifact@v4
with:
name: fontist-app-linux
path: dist/fontist-app
Verified against the
fontist fixture
(fontist 3.0.10 on Ruby 3.3.7; the package prints fontist 3.0.10). Bundler
runs inside the target runtime during the press, so the host’s Ruby and
bundler versions are irrelevant, and the ruby/setup-ruby and bundler-cache
steps can be deleted. Commit the Gemfile.lock: locked specs install
exactly as resolved, and the lockfile pins the bundler version too.
C · Native-extension gems (nokogiri, ffi & co.)
Before — v1 gem:
name: package
on: [push]
jobs:
press:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
bundler-cache: true
- name: Install build tools
# v1 built every native extension from source
# (force_ruby_platform) — a C toolchain is required
run: |
sudo apt-get update
sudo apt-get install -y build-essential
- name: Install tebako
run: gem install tebako -v 0.13.4
- name: Cache the packaging environment
uses: actions/cache@v4
with:
path: ~/.tebako
key: tebako-v1-{{ runner.os }}-0.13.4
- name: Build the packaging environment
run: tebako setup -R 3.3.7
- name: Press
run: tebako press -r . -e main.rb -o dist/nokogiri-app -R 3.3.7
- name: Smoke-test the package
run: ./dist/nokogiri-app
- uses: actions/upload-artifact@v4
with:
name: nokogiri-app-linux
path: dist/nokogiri-app
After — v2 Rust CLI:
name: package
on: [push]
jobs:
press:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
path: app
# … the identical "install tebako v2" steps — copy them
# from pair A above …
- name: Prefer clang as the system cc
# no lockfile here: resolution pulls the newest racc, which only
# ships as a ruby-platform source gem — its press-time build
# needs clang as cc (gcc rejects the replayed -fdeclspec flag).
# Alternatives: commit a Gemfile.lock with precompiled pins, or
# press with --prefer-local to use the runtime's bundled racc.
run: sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 50
- name: Press
run: tebako press -r app -e main.rb -o dist/nokogiri-app
# nokogiri installs as the PRECOMPILED platform gem —
# no compiler, no source build, no extra flags
- name: Smoke-test the package
run: ./dist/nokogiri-app
# => Hello from nokogiri app with nokogiri 1.19.4
- uses: actions/upload-artifact@v4
with:
name: nokogiri-app-linux
path: dist/nokogiri-app
The inversion is the following: v1 forced every native gem into a source
build inside the packaging environment, while v2 resolves through the modern
compact index and installs precompiled platform gems (nokogiri, ffi) as-is,
so the C toolchain usually disappears from CI. Gems that exist only as
Ruby-platform sources build during the press against an auto-provisioned
Runtime SDK (a network fetch; mirror it with TEBAKO_SDK_SRC_MIRROR, where
file:// works offline). --prefer-local is the escape hatch when the
runtime’s own bundled/default gems should be used in place.
Behavior differences that matter
The flags look the same, and these are the places where the ground moved under them.
01 — Press-time network.
A v2 press downloads the target runtime into the shared cache on a cold
machine, and the deploy (the bundle install) runs inside that runtime,
not under the host Ruby. A first press on a fresh CI runner needs network
(or a warm ~/.tebako, or TEBAKO_RUNTIME_MIRROR). The packaged binary
then downloads the runtime again on its own first run unless the press was
fat or the machine’s cache is warm.
02 — The image era.
The app ships as a .tfs image with dwarfs-t-native (FlatBuffers) metadata,
built in-process: no mkdwarfs binary anywhere, no FUSE, and no extraction.
The runtime is transitioning to the same form: image-era releases carry it
as a .tfs with .sha256/.origin trust markers, and where a release
still ships the classic package, the cache keeps an extracted layout
instead. Both forms share the same cache, and the same packages run against
either. Details appear on the
runtime as image page.
03 — .tebako.yml is not read.
v1 auto-loaded $PWD/.tebako.yml and merged its options: map under the
CLI flags, an invisible configuration layer. v2 does not read it, and
-t/--tebafile exits with an explicit error. The migration is to paste the
tebafile’s options into the press command line (or the CI step) directly.
04 — Exit codes.
The packaging error table is unchanged: 106 (entry point missing), 107 (root
missing), 120–125 (runtime resolution), 133 (the removed runtime mode), and
the Tebako script failed: <message> [<code>] line that CI may grep for.
Usage errors exit 1 in both worlds. New codes that may appear are 134 (fat
mode needs a payload-capable bootstrap) and 135 (Runtime SDK provisioning
failed). tebako cache list --json is a machine contract: stdout is the
JSON document alone, and the banner moves to stderr.
05 — Bundler behavior, modernized.
v1 passed --prefer-local unconditionally, which could silently degrade a
remote resolution (fontist 3.0.10 came out as the dependency-free 0.1.0),
and it forced force_ruby_platform=true, which doomed precompiled gems to
source builds. v2 resolves through the compact index, installs precompiled
platform gems, and keeps --prefer-local as an opt-in flag (a no-op with a
complete lockfile). When a v1 press "worked" but produced a strangely old
gem set, this is the reason, and v2 fixes it.
06 — Platform reality check.
Rubies: v2 presses only the rubies the pinned runtime release carries
(3.1.6–4.0.6 at v0.16.9), because the 2.7/3.0 lines were v1
source-build-only. Windows: shipped, since the v2 loader and runtime
releases cover the full platform axis, windows-ucrt64 included. Gem/gemspec
press scenarios (packaging a gem itself) are not yet ported. On macOS the
deploy re-signs ad-hoc after stripping, so precompiled .bundle files keep
loading.
07 — On Linux, source builds need clang as cc.
The prebuilt runtimes are compiled with clang. When a press must build a
native gem from source (a gem that only ships as a ruby-platform source:
json, bigdecimal, and brotli in the verified fixture), the Runtime SDK
replays the runtime’s recorded build flags against the host cc, and gcc
rejects -fdeclspec, dying in mkmf with "The compiler failed to generate an
executable file". macOS is unaffected, because cc is clang there. On Linux,
point cc at clang before pressing:
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 50.
Verification: with clang as cc, the fontist fixture builds json 2.7.2,
bigdecimal 3.1.5, and brotli 0.8.0 from source inside the press and runs.
08 — Every run of an unsigned package warns. v1 binaries start silently. The v2 bootstrap prints a two-line warning on every run of a package whose trailer is unsigned (the v1-era legacy form):
tebako-bootstrap: WARNING: ./myapp.tebako carries an unsigned v1 (legacy) tpkg trailer
— accepted for compatibility; re-bundle the package for integrity protection
The warning goes to stderr, the exit code is unaffected, and unsigned packages stay first-class; users will see it, so the release notes should say so (or the package should be signed; see the chain of trust).
New capabilities in v2
Suites: one package, N commands.
v1 pressed one entry point per package. v2’s --suite presses several apps,
each with its own root, entry point, and even its own runtime, into one file
whose binary name selects the command (shim/symlink dispatch):
# suite.yaml
name: myproduct
entries:
- name: app
root: ./app
entry: bin/app
- name: worker
root: ./worker
entry: bin/worker
tebako press --suite suite.yaml -o myproduct (lean only; -r/-e come
from the suite file). Verified: the package runs entries[0] by default, and
a symlink named after an entry runs that entry.
Jails: host-access policy.
v1 packages saw the host filesystem with no policy at all. v2 has the
enforcement engine in the VFS layer (docker -v semantics; denied paths
fail EPERM, writes to read-only grants fail EROFS, and
tfs exec <image> --jail <spec> — <cmd> is the shipped surface for native
binaries under the preload shim):
# spec grammar: default open|deny ; host:mount:ro|rw grants ; @file = read-only arg file
tfs exec app.tfs:/__tebako_memfs__ --jail 'deny;/home/u/src:/work:rw' -- /bin/tool in.csv
The scope is honest, with two limits that a v1 user should know today. The
jail is filesystem-only (no network confinement). It also does not reach
pressed packages yet: a pressed app ignores TEBAKO_JAIL (verified; the
package reads the host unaffected by it), and in the verification Linux
container the shim’s mount of a dwarfs-t app image answered ENOTSUP. The
dispatch-surface and manifest integration are the planned next step, so
treat jails as in-flight rather than as a migration blocker.
Staying on v1, and what its status is
-
The gem is not going away. Every v1 release stays published on rubygems.org; nothing is yanked and nothing is sabotaged.
gem install tebakokeeps working, and already-pressed binaries keep running (they are self-contained; 0.15.9 is the final v1 release). -
The v1 codebase is archived. It lives on as tamatebako/tebako-v1, read-only; the history, tags, and issues remain readable, and development happens only in tamatebako/tebako.
-
Support status, plainly: v1 is unmaintained, with no further releases, fixes, or reviewed PRs. When a v1 setup works, it keeps working; migrate when what v2 adds is needed (shared runtimes, suites, jails, signing, active support) or when a future Ruby/platform change leaves the v1 pipeline behind.
See also: the normative spec set · the manual. When this page misses a migration case, open an issue; the guide is maintained with v2.