Toolbox bundle
Back to BundleDock · All bundles
Published bundle
Version: 2026.10.18-b2
Index digest: sha256:c7715f6a2cae2812dcaf847185427080f2ab73097aa392393d427e460afed402
Source: dce172b73a78d3a816dbfbf60129415156ad6a31
Publication: run 39156619187, attempt 1
Pull this published carrier by its immutable index digest:
docker pull quay.io/bundledock/toolbox@sha256:c7715f6a2cae2812dcaf847185427080f2ab73097aa392393d427e460afed402
The source inventory below describes the current lock; this publication retains its exact source revision.
Spec Kit launcher (source)
The source supplies an owned specify launcher for specify-cli==1.1.3,
separate from downloaded catalog tools and absent from older published images.
It uses plain pip in a dedicated writable virtual environment. Supply python3
3.11+ with venv and pip from the consumer’s stock language image; the launcher
rejects virtual environments and recognized uv-managed interpreters, including
UV_PYTHON_INSTALL_DIR. Arbitrarily relocated interpreter provenance cannot be
inferred, so provide the image’s own Python.
specify --help
specify init my-project
First use needs network access to the official PyPI index and compatible prebuilt wheels for the image’s Python version, ABI and platform. Source builds, Python downloads, global installs and project environments are never used. The exact top-level wheel and SHA256 are recorded in bundled requirements; transitive dependency versions remain unfrozen and are resolved on first installation. A missing compatible wheel fails clearly. A completed environment can run offline; pip’s download cache alone is not a completed environment.
Use writable HOME outside the toolbox. Environments live under
${XDG_DATA_HOME:-$HOME/.local/share}/bundledock/tools/specify, keyed by package
metadata, helper source, interpreter and ABI. Downloads use
${XDG_CACHE_HOME:-$HOME/.cache}/bundledock/pip/specify. XDG overrides must be
absolute paths outside the toolbox. Concurrent first uses serialize, interrupted
installs retry safely, and changed metadata or Python creates a separate
environment. Earlier environments remain available. The launcher ignores pip
configuration, clears installer and Python environment overrides, uses only
wheels, and preserves the working directory, arguments and exit status. Explicit
commands such as specify init can create projects and agent configuration.
Official PyPI metadata
records
Python >=3.11 and the universal wheel specify_cli-1.1.3-py3-none-any.whl, SHA256
9cc0712dbdffb8a035f1d3f4b80f678eeeddc14b3cf1437d26056df2c60ee127.
The immutable bundle contains the owned launcher, shared Python helper and
requirements metadata, with no installed Python runtime or packages. Installed
package notices stay in the writable environment. launchers.specify records
the pin separately from downloaded tools; SHA256SUMS covers all launcher
source and requirements. The catalog updater does not change this pin.
Source tests execute stdlib Python and synthetic venv/pip fixtures without network access or package installation. Native consumer smoke and real first-use resolution remain separate checks requiring an approved image build.
tldr launcher (source)
The source supplies tldr through the official Python client
,
version 3.4.4. It requires python3 3.10+ with venv and pip from the
consumer’s stock language image. The launcher uses the same isolated plain-pip
helper as Spec Kit, with a separate tool directory and download cache:
tldr --help
tldr tar
tldr --update
Explicit first use installs exactly three reviewed universal wheels: tldr
3.4.4, shtab 1.12.1 and termcolor 3.3.0. Each wheel’s official URL and
SHA256 are pinned in share/tldr/tldr.requirements.txt; pip installs with
--no-deps and --only-binary=:all:. The base distribution already provides
the CLI; its optional cli extra contains development tools and is excluded.
The immutable carrier retains the unchanged MIT/MPL-2.0 notices and source
record through its license archive. Installed notices remain in the environment.
Environments live under ${XDG_DATA_HOME:-$HOME/.local/share}/bundledock/tools/tldr
and pip downloads under ${XDG_CACHE_HOME:-$HOME/.cache}/bundledock/pip/tldr.
Keep these paths and HOME writable outside the toolbox. First installation
needs network access to the pinned official PyPI files; a completed environment
can launch offline. tldr separately fetches example pages and caches them in
${XDG_CACHE_HOME:-$HOME/.cache}/tldr. Use TLDR_NETWORK_ENABLED=0 with cached
pages for offline lookups; a missing page is unavailable offline. Package pins
do not freeze the community page content. Upstream language, color and page
source configuration remains available.
PyPI release metadata and all three wheel manifests were inspected and hashes verified without installing or executing third-party code. Source fixtures cover first use, reuse, Python compatibility, argument/status forwarding and altered requirements. Real package installation, first page download and native consumer smoke remain unverified until a separately approved image build and runtime check. Older published images do not include this launcher.
Package-installed tool defaults
Python command-line additions use a dedicated per-tool virtual environment and
plain pip, with reviewed requirements shipped beside the owned launcher.
Package installation occurs on explicit first use, outside the read-only bundle.
Stock language images provide Python, venv and pip; BundleDock does not install
an interpreter. See the source-only package installation policy
for dependency pins, npm defaults and the existing-tool inventory.
Retained notices
New bundles store all staged licenses/<package>/... files, including full
license texts, source records and vendored dependency notices, in one
licenses.tar.gz at the bundle root. Original member paths and bytes are
preserved. licenses.index.json remains plain text for offline discovery: it
maps every member to its package, size and SHA256 and records the archive
SHA256. SHA256SUMS covers both files. Payload-internal notices stay with their
unchanged runtime assets. This changes the installed loose notice paths; the
source notice directories in this repository remain unchanged. The published
image above predates this source change.
The existing carrier’s init-volume helper can list the index or print one
complete notice without external gzip/tar tools, network access or volume writes:
docker run --rm --network none "$IMAGE" --list-licenses
docker run --rm --network none "$IMAGE" --license licenses/gh/LICENSE
Use a carrier built from this source change as IMAGE; earlier published
carriers retain their existing notice layout and initializer flags. The helper
lives in the carrier, not the mounted tool volume. A mounted consumer can read
licenses.index.json directly, or read licenses.tar.gz with compatible archive
tools. For a mounted root, the carrier helper accepts --source with that
mounted path. Requests require the complete member path from the index,
avoiding ambiguous basenames across packages. The helper checks the index/archive
checksums, member hashes and sizes, rejects unsafe or duplicate paths, and
bounds expansion before printing bytes. Retaining these notices does not
establish a complete transitive license audit.
Source inventory
The default bundle lives in bundles/toolbox/: packages.txt,
packages.catalog.json, packages.lock.json, its Dockerfile, smoke scripts and licenses.
Shared Go code remains at the repository root. The source toolbox contains 57
downloaded tools plus the two first-use Python launchers listed below. Codex, Antigravity and Claude are supplied by their separate
Codex
, Antigravity
and Claude Code
bundles.
The published toolbox recorded above still includes those agents; this source
removal has not been built or published.
packages.lock.json pins vendor artifact URLs and checksums for Linux amd64
and arm64; builds verify every download. Launcher versions come from their
separate wheel inputs. Homepage and Source links are generated from reviewed
upstream metadata and persist when the native lock is refreshed.
The source catalog also includes xh for HTTP requests, dust for disk usage,
direnv for opt-in directory environments, and SOPS (sops) for encrypted
configuration. All four use official stable prebuilt Linux AMD64/ARM64 releases
with publisher SHA256 pins and retained historical license snapshots. Inclusion
of direnv does not install shell hooks or approve .envrc files; users choose
and trust those separately. Keep configuration, credentials and writable state
outside the read-only toolbox. SOPS key services require the user’s own setup.
The consumer smoke only checks these four tools’ versions, disables SOPS version checks, and runs with networking disabled. Source tests use synthetic commands and do not establish native compatibility. These additions have not been built, published or activated; native consumer smoke awaits an approved image build.
TruffleHog is included alongside Gitleaks, using official stable Linux AMD64 and
ARM64 binaries. Its full AGPL-3.0 license and upstream README are retained under
licenses/trufflehog. For a local scan without update checks or credential
verification requests:
trufflehog filesystem --no-update --no-verification --fail --json /path/to/project
Without --no-verification, TruffleHog can contact providers to verify detected
credentials. Gitleaks configurations and rules do not transfer automatically.
The consumer smoke uses only a synthetic custom detector and clean control,
isolated configuration and HOME, with updates/verification disabled and consumer
networking disabled. Native smoke acceptance remains part of an approved build.
| Tool | Pinned version | Homepage | Source |
|---|---|---|---|
actionlint (actionlint) |
1.7.12 | Homepage | Source |
age (age, age-keygen, age-inspect, plugins) |
1.3.2 | Homepage | Source |
ast-grep (ast-grep) |
0.51.0 | Homepage | Source |
bat (bat) |
0.26.1 | Homepage | Source |
bottom (btm) |
0.14.9 | Homepage | Source |
btop (btop) |
1.4.7 | Homepage | Source |
Cosign (cosign) |
3.1.3 | Homepage | Source |
delta (delta) |
0.20.1 | Homepage | Source |
Difftastic (difft) |
0.71.0 | Homepage | Source |
direnv (direnv) |
2.38.2 | Homepage | Source |
dust (dust) |
1.2.6 | Homepage | Source |
Fastfetch (fastfetch) |
2.70.0 | Homepage | Source |
fd (fd, fdfind) |
10.5.0 | Homepage | Source |
Fish (fish) |
4.9.3 | Homepage | Source |
fzf (fzf) |
0.74.5 | Homepage | Source |
Git LFS (git-lfs) |
3.8.0 | Homepage | Source |
git-cliff (git-cliff) |
2.14.2 | Homepage | Source |
| GitHub CLI | 2.102.0 | Homepage | Source |
GitHub CLI Stacked PRs (gh stack) |
0.2.1 | Homepage | Source |
Gitleaks (gitleaks) |
8.30.1 | Homepage | Source |
Hadolint (hadolint) |
2.15.1 | Homepage | Source |
Helix (hx) |
25.07.1 | Homepage | Source |
Herdr (herdr) |
0.9.5 | Homepage | Source |
Hugo (hugo, standard) |
0.168.0 | Homepage | Source |
hyperfine (hyperfine) |
2.0.0 | Homepage | Source |
| jq | 1.8.2 | Homepage | Source |
jsonschema-cli (jsonschema-cli) |
0.58.5 | Homepage | Source |
Jujutsu (jj) |
0.46.0 | Homepage | Source |
just (just) |
1.59.0 | Homepage | Source |
lazygit (lazygit) |
0.66.2 | Homepage | Source |
Leaf (leaf) |
1.29.1 | Homepage | Source |
lsd (lsd) |
1.2.0 | Homepage | Source |
lychee (lychee) |
0.24.2 | Homepage | Source |
Mike Farah yq (yq) |
4.54.1 | Homepage | Source |
mise (mise) |
2026.10.13 | Homepage | Source |
navi (navi) |
2.24.0 | Homepage | Source |
Nushell (nu) |
0.116.1 | Homepage | Source |
pinact (pinact) |
5.0.0 | Homepage | Source |
regctl (regctl) |
0.11.6 | Homepage | Source |
ripgrep (rg) |
15.2.0 | Homepage | Source |
Ruff (ruff) |
0.17.1 | Homepage | Source |
SCC (scc, wstein fork) |
4.1.1 | Homepage | Source |
ShellCheck (shellcheck) |
0.11.0 | Homepage | Source |
shfmt (shfmt) |
3.14.1 | Homepage | Source |
SOPS (sops) |
3.13.3 | Homepage | Source |
Spec Kit (specify, first-use pip launcher) |
1.1.3 | Homepage | Source |
Starship (starship) |
1.27.0 | Homepage | Source |
Syft (syft) |
1.54.1 | Homepage | Source |
tldr (tldr, first-use pip launcher) |
3.4.4 | Homepage | Source |
tmux (tmux) |
3.7c (official static builds) | Homepage | Source |
Trivy (trivy) |
0.76.0 | Homepage | Source |
TruffleHog (trufflehog) |
3.99.2 | Homepage | Source |
typos (typos) |
1.51.1 | Homepage | Source |
Typst (typst) |
0.15.1 | Homepage | Source |
uv (uv, uvx) |
0.13.1 | Homepage | Source |
watchexec (watchexec) |
2.8.0 | Homepage | Source |
xh (xh) |
0.26.2 | Homepage | Source |
zizmor (zizmor) |
1.31.1 | Homepage | Source |
zoxide (zoxide) |
0.10.0 | Homepage | Source |
Nushell and tmux are opt-in commands. tmux pins the published stable
official static build
,
3.7c, while the upstream source release is 3.8. Its provider resolves that static
build feed; release suffixes compare numerically (3.7a = 3.7.1, 3.7c =
3.7.3, 3.8 = 3.8.0) while lock versions and asset URLs retain the upstream tag.
No plugins or user configuration are installed for either alternative.
Propose upgrades with GitHub Actions
The Propose upgrades workflow with bundle=toolbox (.github/workflows/propose-upgrades.yml)
runs daily at 06:50 UTC from the repository default branch. Scheduled runs
resolve all desired stable tools within packages.txt constraints and open a
reviewable PR only when the lock or its generated inventory changes. GitHub
may delay scheduled runs; the cron time is not a delivery guarantee. The
schedule starts after a human merges this workflow onto the default branch.
For a manual test, open Actions → Propose upgrades → Run workflow,
select the default branch and bundle=toolbox, keep dry_run=true, and optionally set tool=gh
for a bounded first run. An empty tool input resolves every desired tool.
The workflow refuses non-default-branch runs. A new manual workflow must
exist on the default branch before it can be dispatched.
Dry runs use a read-only token, run the existing tools upgrade resolver, and
show the proposed lock and inventory diff in the run summary. Resolution downloads official
prebuilt archives for both AMD64 and ARM64 to verify their checksums. Resolution never
executes upstream code or build images, CLI releases, or documentation. An
upstream error fails the run without creating a PR. No changes produce a
successful no-op. Stale inventory alone is a deliberate repair: it counts as
a change even when the lock versions are already current.
For manual runs, set dry_run=false when you want a reviewable lock and
inventory PR; scheduled runs already propose actual changes. A separate job
requests contents: write and pull-requests: write, transfers only the
resolved lock and generated inventory from that same run attempt, and creates its own
automation/toolbox-upgrade-<run-id>-<attempt> branch. It never force-pushes,
reuses another branch, merges a PR, publishes tools, or activates a consumer
image. Rerunning a changed attempt creates a separate topic; review
existing topics before rerunning. An unsuccessful PR creation may leave its
already-pushed topic branch for inspection.
Dry runs work with default read-only workflow permissions and do not require permission to create pull requests. Before using write mode, an administrator must explicitly allow the requested token write permissions under any repository/organization policy and enable Settings → Actions → General → Workflow permissions → Allow GitHub Actions to create and approve pull requests. The checkbox permits PR creation; this workflow never approves reviews. If policy blocks the push or PR, the job fails with these prerequisites. The workflow does not change settings, install a PAT, or bypass policy.
A PR opened with GITHUB_TOKEN does not automatically start ordinary
pull_request workflows. Run the required source checks and merge the eligible
updater as wstein using the protected human merge path. That actual owner
merge authorizes publication of the exact frozen lock after successful merged
main CI. See the owner-merge publication flow
. Unrelated source
changes cannot enter this path. Source checks do not establish native image
smoke coverage. Initial publication retains explicit full-SHA manual approval,
and publication does not activate a consumer image.
SCC fork release
The source lock includes exactly wstein/scc-fork v4.1.1
,
with scc==4.1.1 preserving the requested fork release. Both Linux AMD64
(scc_Linux_x86_64.tar.gz) and ARM64 (scc_Linux_arm64.tar.gz) archives were
SHA256 checked against the publisher release API digests and checksums.txt on
2026-10-18. Read-only ELF and Go build metadata inspection confirmed both native
architectures, CGO_ENABLED=0 and commit 9bb762717271e08953a01cfe63981af21390d777.
The catalog installs only bin/scc; the MIT license and full vendored dependency
notices are retained from that historical source snapshot.
The isolated consumer smoke uses only scc --version. Source tests use a stub;
no downloaded SCC binary has been executed for this addition. Native consumer
smoke, image build/publication and consumer activation remain pending separate
owner authorization. The published image above retains its recorded source.
Keep the source inventory consistent
The pinned-version table above is managed between the
BEGIN GENERATED TOOL INVENTORY and END GENERATED TOOL INVENTORY comments.
Its versions come from packages.lock.json for both supported architectures;
existing catalog tag prefixes determine the display version, and existing
labels remain presentation metadata. Rows sort alphabetically by the displayed
label, ignoring case. Missing catalog IDs or
architecture version differences fail generation rather than omitting a tool.
After changing a lock locally, run:
python3 scripts/tool-inventory.py # update only the managed table
python3 scripts/tool-inventory.py --check # fail on drift without writing
make check # includes the same source check
The upgrade workflow generates this table after resolution. Before opening a
PR, the write job regenerates it from the checked-out documentation and resolved
lock, then compares the transferred inventory byte for byte. It accepts only
the lock and generated table changes; unrelated prose edits are rejected.
Prose outside the markers, historical verification records, badges and consumer
pins are not generated. These commands edit or check Markdown source; they do
not invoke Hugo or render a documentation site. The existing CI workflow calls
make check, so a lock-only change with a stale table fails source verification.
Helix terminal editor
The toolbox includes official stable Helix 25.07.1
Linux AMD64 and ARM64 prebuilts. hx launches the untouched binary with
HELIX_RUNTIME pointing to the same mounted bundle’s lib/helix/runtime.
The whole runtime, compiled tree-sitter grammars, queries, themes and upstream
notices are retained. User configuration belongs in writable
$XDG_CONFIG_HOME/helix (normally $HOME/.config/helix); no user configuration
is created by bundle initialization. Language servers, formatters and clipboard
helpers are supplied by the consumer or installed in writable home locations.
No grammar download, compilation or plugin installation is needed for the shipped
runtime. Helix’s official GNU/Linux prebuilts require compatible glibc; the
stock Debian consumer remains the integration target.
tools init copies and validates volume content; it cannot change the invoking
shell’s environment. Anchorage owns its consumer shell seeds, which default
EDITOR to hx only when hx is available and EDITOR is unset, preserving
user overrides and VISUAL. Existing custom seeds remain user-owned. Other
consumers can opt in with export EDITOR="${EDITOR-hx}" after placing toolbox
bin on PATH. The existing older pinned consumer image does not gain Helix
until a separately approved build, publication and activation.
Helix uses calendar release tags such as 25.07.1. Its official-repository-only
helix-calendar normalization compares this as 25.7.1; a two-part release
such as 25.07 compares as 25.7.0. Desired requirements use canonical numeric
versions (helix>=25.7.1); the lock and download asset retain the original tag
spelling. Generic SemVer parsing remains strict and prereleases are excluded.
The helix-runtime extraction recipe accepts only a single stable Helix Linux
release root, regular files and directories. Production extraction binds that
root to the verified release pin. It does not permit archive links.
Upgrade the lock
Run upgrades from the repository root:
make run ARGS="tools upgrade" # resolve all tools
make run ARGS="tools upgrade gh" # resolve only GitHub CLI
make run ARGS="tools upgrade --directory bundles/toolbox" # explicit bundle directory
packages.txt contains one desired requirement per line: gh>=2.102.0
or helix>=25.7.1 for a minimum, node>=24.0.0,<25.0.0 for a bounded range, or
gh==2.102.0 for an exact pin. Commas mean AND; lower bounds are inclusive
and upper bounds are exclusive. Versions must be complete stable
major.minor.patch values; prereleases and build metadata are excluded.
Comments start with #. Generic CLI requirements, including Helix, use minimum
bounds so future stable releases remain eligible. The minimum records the
existing accepted baseline; it does not upgrade an installed tool. Node retains its selected LTS
major, Bun its runtime major, and code-server its standalone carrier major.
These major bounds need separate compatibility review before changing. Helix
retains its original calendar tag spelling in the lock and requires a matching
complete runtime and compiled grammars from the same official stable archive.
Provider tag normalization, archive recipes and both-architecture checks still apply to every
candidate; a minimum does not permit unsupported release formats.
Editing this file changes the desired range, not the installed tools.
packages.catalog.json is the trusted declarative package catalog. It names upstream
providers, repositories, tag prefixes, platform aliases, asset templates, supported
raw/tar/ZIP extraction, executable members, regular-file aliases, notices, and the
extraction recipes. Preserved tar recipes can omit exact relative directories
with exclude_directories; names are normalized paths, without glob matching.
Non-preserved tar recipes can retain whole archive directories as notices with
archive_notice_trees (exact normalized relative paths); regular files below them
are kept under licenses/<tool>/ with their relative paths.
Every omitted entry still undergoes path, type, duplicate, payload and stream
integrity validation. Recipes cannot execute shell commands.
Both upgrade and build validate this catalog. Review catalog edits as build inputs. A catalog change requires
an upgrade of all tools so preserved pins cannot acquire a different recipe silently.
The native builder supports tar.xz with the checksum-pinned pure Go
github.com/ulikunitz/xz decoder; no external xz command is needed. XZ inputs
are limited to 64 MiB compressed, 256 MiB decoded, 4096 tar members, and 128 MiB
per member. The decoder validates stream integrity through EOF. XZ headers can
request dictionary memory beyond these limits, so the production installer
verifies the exact trusted upstream checksum before decoding; these limits do
not make the decoder suitable for arbitrary untrusted archives. Decoder notices
are retained in bundles/toolbox/licenses/xz.
Upgrades fetch fresh release metadata and select the numerically newest stable
version within each requirement. GitHub release history is paginated. Optional GITHUB_TOKEN authentication is
sent only to api.github.com; artifact and other provider requests never receive
it. HTTP errors, including API rate limits, abort with the request URL and status.
The standalone Claude and
Antigravity bundles combine official current manifests with historical GitHub releases.
The chosen version must supply both architectures;
missing assets, metadata errors, or checksum failures abort without selecting an
older version. All selected downloads are hashed and verified before the lock is
replaced atomically. A failed resolution leaves the previous lock bytes unchanged;
a targeted upgrade preserves every other tool pin. An all-tool upgrade can create
a missing lock or reconcile catalog additions and removals.
Lock verification provenance distinguishes publisher SHA256/SHA512 checksums from tls-download-sha256: the latter pins bytes downloaded
from official HTTPS when a release has no publisher checksum. It is a reproducibility
pin, not an independent publisher authenticity proof. A present invalid publisher
checksum always fails. Redirects require HTTPS and an official host; GitHub’s release
asset CDN is allowed. The migrated existing hashes and artifact URLs remain pinned.
Canonical SHA256 digests bind the lock to desired requirements and catalog content;
formatting and comments in packages.txt do not change its identity. Builds reject stale
identity, incomplete platforms, unsupported recipes, and unsafe archive members.
tools upgrade writes only packages.lock.json. Review and commit the diff, then run
make bundle-build bundle-smoke bundle-volume to build and initialize the new bundle.
Existing images, volumes, running containers, and desired ranges are unaffected by
resolution. tools volume remains the existing initialization command; no update
alias or other tool-management commands are added.
Optional toolbox utilities
fzf filters lists; for example, printf 'alpha\nbeta\n' | fzf --filter beta
works without an interactive terminal. Shell key bindings and completion are
opt-in through fzf --bash, fzf --zsh or fzf --fish; source the chosen output
in your own shell configuration only if desired. BundleDock never edits dotfiles.
bat includes upstream embedded syntax and theme assets. Use
bat --paging=never --style=plain file for predictable noninteractive output.
Its pager is optional and consumer supplied; no editor or pager default is set.
Upstream completion scripts, manual, README and changelog are retained under
licenses/bat/ for optional use, alongside the decoded upstream syntax/theme
notices and their inventory. Custom configuration and caches belong in writable
HOME, outside the read-only toolbox.
lsd lists directory contents with optional colors, icons and tree views. Use
lsd --ignore-config --color never --icon never --oneline for predictable
noninteractive output without user configuration or icon font requirements.
No ls shell alias is installed automatically; user configuration belongs in
writable $XDG_CONFIG_HOME/lsd or $HOME/.config/lsd, outside the read-only
toolbox. Full upstream Apache-2.0 LICENSE and README.md are retained under
licenses/lsd/ from each official Linux AMD64 and ARM64 musl release archive,
which were downloaded and SHA256 verified against the official release API
digests on 2026-10-16.
regctl is the official registry client only; regbot and regsync are excluded.
Credentials and state belong in writable HOME (~/.regctl/), with optional
REGCTL_CONFIG pointing to a writable config file. Offline smoke uses only
version/help and reference parsing; it never logs in or contacts a registry.
The six Linux AMD64/ARM64 release artifacts were downloaded and SHA256 checked against the official release API digests on 2026-10-12. Both fzf archives contain one regular binary; both bat archives contain only regular files and one completion directory. regctl assets are raw executables. These recipes retain the existing extractor’s rejection of archive links. Source tests use synthetic artifacts and smoke stubs. Native execution, OCI builds/smoke, vulnerability scans and consumer activation remain unverified pending separate approval.
actionlint lints GitHub Actions workflow files statically: actionlint .github/workflows/ci.yml. It runs shellcheck and pyflakes only when they are on PATH. It needs no network access or user configuration.
hadolint lints Dockerfiles: hadolint Dockerfile. Keep a project .hadolint.yaml in the repository. It is an unmodified official GPL-3.0 release binary; the full upstream LICENSE, the upstream ThirdPartyNotices.txt and a corresponding-source record (tarball URL and SHA256) are retained under licenses/hadolint/. The static binary links GPL-3.0-only ShellCheck, GPL-3.0-or-later language-docker and GPL-2.0-only HsYAML. The repository owner accepted shipping this combined GPL mix on 2026-10-17, following the ShellCheck precedent: the binary is unchanged, the full LICENSE and the upstream ThirdPartyNotices.txt are retained, and the upstream source tarball URL and SHA256 are recorded in licenses/hadolint/SOURCE. The license is GPL-3.0-only with the third-party notice mix exactly as reported by upstream ThirdPartyNotices.txt, including GPL-2.0-only HsYAML. As a written offer, BundleDock will give any recipient of the toolbox the corresponding source of hadolint on request, by pointing to that recorded tarball; the offer relies on upstream continuing to host the tagged source. This is not legal advice.
pinact pins GitHub Actions and reusable workflows to commit SHAs. pinact run rewrites workflow files and, unless offline, queries the GitHub API (set GITHUB_TOKEN in the environment); pinact run --check reports without writing. The release archive’s third_party_licenses/ tree (Apache-2.0 cobra and afero, MPL-2.0 go-version, BSD-licensed Go packages and other dependency notices) is retained with its relative paths under licenses/pinact/third_party_licenses/ via the catalog’s archive_notice_trees.
zizmor statically audits GitHub Actions workflows for security issues: zizmor --offline .github/workflows. Online audits query the GitHub API with an optional GH_TOKEN or GITHUB_TOKEN from the environment. The official Linux archives are GNU/glibc builds, and zizmor needs glibc 2.34 or newer on both architectures; Alpine and other musl-based images are unsupported. The archive carries only the executable, so the upstream MIT LICENSE is retained separately.
cosign signs and verifies container images and other artifacts. Verification contacts registries and Sigstore services and signing can open a browser; neither runs at image build or shell startup. Keys, tokens and the TUF cache belong in writable HOME (~/.sigstore), outside the read-only toolbox.
git-cliff generates changelogs from Git history and Conventional Commits: git-cliff --config cliff.toml. Keep the configuration in the repository. Release archives also carry completions, man pages and git-cliff-completions/git-cliff-mangen generators; only the git-cliff executable, both licenses and README are installed.
zoxide is a directory jumper. The shared shell layer initializes it interactively with the official zoxide init <shell> --cmd cd, providing cd and cdi in Bash, Zsh, Fish and Nushell. Set BDOCK_ZOXIDE=0 before startup to keep your own navigation commands. Its database lives in $_ZO_DATA_DIR or the XDG data directory under writable HOME, outside the read-only toolbox. The nested completion README.md is not extracted, to avoid a name clash with the top-level README.
difft (Difftastic) compares files by syntax structure: difft old.go new.go. Use --color never for predictable output. As an optional Git external diff, set GIT_EXTERNAL_DIFF=difft in your own environment; BundleDock sets no Git configuration. The ARM64 release is the official GNU/Linux build (no musl asset exists); the archive carries only the executable, so the upstream MIT LICENSE is retained separately.
jsonschema-cli validates JSON instances against JSON Schema: jsonschema-cli validate schema.json -i instance.json (the older flat form without validate is deprecated and warns). It is selected from the repository’s cli-v releases, which are separate from the library’s rust-v, python-v and ruby-v releases (the repository’s latest-release marker points at a non-CLI release). Remote $ref resolution may use the network when a schema asks for it.
GitHub CLI extension: gh stack
The toolbox carries the official gh stack
extension (MIT, stable release v0.2.1 when added, not a pre-release) as the
unchanged release binary at libexec/gh-stack inside the toolbox. It is not on
PATH. gh finds extensions only in
${XDG_DATA_HOME:-$HOME/.local/share}/gh/extensions/<name>/gh-<name>, and that
directory lives in writable HOME, which BundleDock never edits. A consumer links
the shipped binary into it once. Set toolbox to wherever your environment
mounts the toolbox (the devcontainer uses /opt/bundledock/toolbox):
toolbox=/opt/bundledock/toolbox
src="$toolbox/libexec/gh-stack"
d="${XDG_DATA_HOME:-$HOME/.local/share}/gh/extensions/gh-stack"
if [ ! -x "$src" ]; then
echo "gh-stack not found at $src (older toolbox image or wrong toolbox value); nothing linked" >&2
elif [ -L "$d/gh-stack" ] && [ ! -e "$d/gh-stack" ]; then
echo "dangling symlink at $d/gh-stack; leaving it untouched" >&2
elif [ -e "$d/gh-stack" ] || [ -e "$d/manifest.yml" ]; then
: # an extension is already installed here: leave it untouched
elif [ -L "$d" ] && [ ! -e "$d" ]; then
echo "dangling symlink at $d; leaving it untouched" >&2
else
mkdir -p "$d" && ln -sfn "$src" "$d/gh-stack" # also repairs an empty or partial directory
fi
# Optional check. Run it only if linked: gh offers to install a missing official
# extension from upstream when it finds none, so also disable prompts and stdin.
[ -x "$d/gh-stack" ] && GH_PROMPT_DISABLED=1 gh stack --version </dev/null || true
No manifest.yml is needed: gh treats the directory as a local extension. It
is therefore not upgradable through gh: gh extension upgrade --all prints
[stack]: already up to date and exits 0 without checking, gh extension upgrade stack exits 1 with “unable to retrieve latest version”, and
gh extension list shows no version. The version follows the toolbox lock;
upgrade through BundleDock. The guard leaves an existing install, such as a
user’s own gh extension install github/gh-stack, untouched. A consumer seed
(for example the devcontainer setup script) should perform this
idempotent step. Source tests cover the catalog, lock, notices and smoke
script; native smoke, which also resolves the extension through gh, needs an
approved image build.
Git Large File Storage
The source toolbox includes Git LFS 3.8.0
from official Linux AMD64 and ARM64 prebuilts. git-lfs supplies Git large file
storage commands. Repository setup is opt-in: BundleDock does not run
git lfs install, install hooks, change Git configuration or track file patterns.
The archive’s install.sh is excluded. Consult the upstream documentation
when choosing repository-specific setup.
Both binary archives were SHA256 checked against official release API digests
and the contents of sha256sums.asc on 2026-10-12; its PGP signature was not
verified. The archives omit a license file, so the exact source tag’s full
LICENSE.md, including its Go-derived code notice, is retained locally. The
archive README, changelog and base manual are retained under licenses/git-lfs/.
No separate runtime tree is required by this recipe. Source tests cover stable
resolution, both installation recipes, and license retention. Consumer smoke
uses only git-lfs version in an isolated temporary directory. Native execution,
OCI build/smoke, repository hooks and network transfers remain unverified;
the pinned consumer image is unchanged.
lazygit terminal interface
The source toolbox includes lazygit 0.66.1
from official Linux AMD64 and ARM64 prebuilts. Run lazygit inside a Git repository
to open its terminal interface. User configuration belongs in writable HOME;
BundleDock does not open the interface or change shell/editor defaults.
Both archives were SHA256 checked against the official release API digests and
checksums.txt on 2026-10-12. The full upstream MIT license is retained. Source
tests cover stable resolution and both installation recipes. Consumer smoke
uses only lazygit --version in a private temporary directory with a cleared
environment; no interactive session is started. Native execution and OCI
build/smoke remain unverified until separately authorized. The pinned consumer
image is unchanged.
Jujutsu version control
The source toolbox includes Jujutsu (jj) 0.46.0
from the official Linux AMD64 and ARM64 musl prebuilts. jj provides a Git-backed
version control interface. BundleDock installs bin/jj and the full upstream
Apache-2.0 license; it does not initialize or migrate consumer repositories or
edit user configuration. Keep repository state and user configuration in writable
locations outside the toolbox mount. See the upstream tutorial
for an introduction.
Both archives were downloaded and SHA256 checked against official release API
digests on 2026-10-12. They contain jj, LICENSE and README.md, with no
separate runtime tree. Source tests cover stable release resolution, both
architecture recipes, checksum rejection and exact license retention. The
consumer smoke creates a private temporary Git-backed repository, runs jj new
and checks its description through jj log; inherited Git selectors and Jujutsu
configuration are cleared before tool execution. Source tests exercise this
smoke with synthetic Jujutsu commands and an isolated Git fixture. No upstream
Jujutsu binary was executed during source verification. Native AMD64/ARM64
execution and OCI build/smoke remain unverified until separately authorized;
the pinned consumer image does not acquire Jujutsu from this source change.
Shell, recipe, Python and file utilities
The source catalog also includes ShellCheck, shfmt, just, Ruff, delta,
watchexec, hyperfine and age. ShellCheck checks shell scripts; shfmt formats
shell source; just runs explicitly selected project recipes; Ruff lints and
formats Python. Delta renders diffs, watchexec runs commands when files change,
hyperfine benchmarks commands, and age encrypts files. Age includes the official
age-keygen, age-inspect and bundled age-plugin-* companions. These additions
are source pins and do not change the previously published toolbox receipt.
No shell hooks, Git pager, project recipes, editor settings or user configuration
are enabled automatically. Use delta with --paging=never for noninteractive
output. Ruff caches, watchexec state, hyperfine exports and age keys belong in a
writable project directory or HOME, outside the read-only toolbox. Never store
private encryption keys in the bundle.
All sixteen Linux AMD64/ARM64 assets were SHA256 checked against official stable release API digests and inspected without executing them on 2026-10-12. Shfmt uses raw executables; other additions use official tar archives. Delta uses a musl AMD64 asset and a GNU libc ARM64 asset because upstream supplies no ARM64 musl archive. ARM64 consumers therefore require compatible GNU libc and its runtime libraries; Alpine/musl ARM64 support is not established. This limitation must be rechecked against upstream assets before changing the recipe or claiming broader consumer support. Full upstream license texts, Ruff’s bundled notices, delta’s bat 0.26.1 syntax/theme notices and age’s distributed license are retained.
Source tests exercise synthetic archives and reject incorrect smoke outputs. The offline smoke script checks shell formatting, an explicit temporary just recipe, isolated Ruff formatting, a fixed diff, one watchexec command, a hyperfine JSON export and age encryption/decryption with a temporary key. Native AMD64 and ARM64 execution, OCI builds/smoke and vulnerability scans remain unverified and require separately approved builds before release or consumer activation.
Shared shell layer
The toolbox carries one set of Git, Jujutsu, lsd listing, search, disk and
directory shortcuts (97 in all), four navigation names and a
starship prompt for bash, zsh, fish and Nushell. Every shell gets the same names, and each name
runs the same command with the same arguments: gst is git status, gpsup
pushes the current branch with an upstream, jst is jj status, in all four.
The table is internal/shelllayer/shortcuts.tsv; Git names follow
jhillyerd/plugin-git
(MIT, notice
retained under licenses/shell-layer). The set is curated: no --no-verify,
git-svn, git-flow or forge-specific entries and no irreversible clean or reset
shortcuts. Names use [a-z0-9] only, so gc!-style names are not offered. Command
words are plain, with one exception: a git log row may carry one --pretty= or
--format= value (optionally format: or tformat: first) in single quotes (glol, gloo).
The quoted text is limited to letters, digits, spaces and % ( ) < > . , : @ / = _ [ ] -, so a
format string with spaces stays one word and means the same in all four shells. Only
self-contained options (such as --date=short) or these no-argument ones may come before it:
--all, --graph, --decorate, --stat, --abbrev-commit, --no-merges, --first-parent,
--reverse, -p, --patch; positional words such as {branch} may also come first. git reads the
word after --grep, --author or -n as that option’s argument, after --end-of-options as a
revision and after -- as a path, never as a format. The row may
not also carry another --pretty, --format or --oneline, which would override it. Any other
quoted word, any quote in another command (including git show and git config) and a second
quoted word are rejected. gloo uses format:, which separates entries with newlines but ends
without one, so the prompt follows its last line.
The listing family is read-only and runs lsd
,
which the toolbox ships (lsd in packages.txt; a source test requires every
shortcut command to be a shipped tool or a base command on an explicit,
justified allowlist): ll (lsd -l), la (-A),
lla (-lA), lt (-lt, long, sorted by modification time), ltr (-ltr,
reversed), l1 (-1, one entry per line), ltree (--tree) and llg (-l --group-dirs first), each with --color auto and any extra arguments
appended. ltree is the tree view because lt already means the time-sorted
long listing. Stock ls is left untouched and is never aliased: in Nushell it
is a built-in that returns a table, an alias would replace it for the whole
session, and scripts expect the real ls; the table parser rejects any
shortcut named like a Nushell built-in. The flags come from the lsd 1.2.0
documentation (doc/lsd.md) and were not verified against a running lsd here,
including the sort direction of -t. The family is opted out with the rest of
the shortcuts (BDOCK_SHORTCUTS=0; in Nushell, which cannot define aliases
conditionally, by leaving the toolbox out of XDG_DATA_DIRS, where the aliases
call the external ^lsd).
Everyday shortcuts cover search, disk and memory reports, and directories:
rgi (rg -i), rgl (rg -l), rgf (rg --files), fdh (fd --hidden),
dfh (df -h), duh (du -h -d 1), fr (free -h), md (mkdir -p) and
rd (rmdir). rg and fd are toolbox tools, covered by the packages.txt
test. df, du, free, mkdir and rmdir come from the base image (free from procps, which slim images may omit, so fr needs it) and are on
that test’s explicit allowlist with a reason each: df, du and free only
report, mkdir -p only creates and never overwrites, and rmdir removes only an
empty directory. None of the names is an executable on PATH or in the
toolbox’s bin; a test checks that. cp -i, mv -i and every rm variant are
deliberately absent: the interactive prompts hang agent terminals that cannot
answer, and rm is irreversible. du -d 1 and mkdir -p work in GNU coreutils
and busybox alike.
Navigation names .. (cd ..), ... (cd ../..), .... (cd ../../..) and
- (cd -) are not table rows: they have their own short fixed list in the
generator with strict patterns and per-shell rendering, and validName and
validWord still reject such names and path words. The layer defines a name only
where the shell does not run it natively and accepts the definition; otherwise it
leaves the name alone and says so here. Measured in an interactive
pseudo-terminal, and re-checked by a test that runs the real cd in a temporary
tree in each installed shell and asserts pwd:
| Shell (version) | .. |
... |
.... |
- |
|---|---|---|---|---|
| bash 5.2.37 | defined by the layer | defined by the layer | defined by the layer | defined by the layer |
| zsh 5.9 | defined by the layer | defined by the layer | defined by the layer | unsupported |
| fish 4.9.3 | native | defined by the layer | defined by the layer | defined by the layer |
| Nushell 0.116.1 | native | native | native | unsupported |
bash and zsh define aliases for the dotted names, and bash also for - (alias -- -='cd -'). In zsh - is a built-in precommand modifier (- cmd runs cmd with a leading dash in its name), and an alias would replace it, so the layer leaves it alone; autocd and auto_cd would
only cover .., so they are not used. fish defines abbreviations. Nushell’s
implicit cd works only for a bare path typed alone in the interactive REPL, not
under nu -c, -e or after ;. In Nushell - is unsupported: alias - = cd -
and def "-" are accepted but never run, because the parser treats a lone - as
a string, so the layer defines nothing there. A test requires this table to
match what the generator emits. The navigation names follow the same opt-out as
the shortcuts (BDOCK_SHORTCUTS=0 in bash, zsh and fish).
| Shell | File in the toolbox | How it loads | Shortcuts |
|---|---|---|---|
| fish | share/fish/vendor_conf.d/bdock-shell.fish |
XDG_DATA_DIRS |
abbreviations, expanded in place |
| Nushell | share/nushell/vendor/autoload/bdock-shell.nu |
XDG_DATA_DIRS |
aliases and wrapped commands |
| zsh | share/shell/init.zsh |
one line in the system zshrc | aliases, expanded in place on space |
| bash | share/shell/init.bash |
one line in the system bashrc | aliases |
Nothing is enabled automatically. A consumer opts in by putting the toolbox’s
share first in XDG_DATA_DIRS (fish and Nushell) and sourcing the bash or zsh
file from the image’s system rc file, guarded so a missing toolbox is harmless:
XDG_DATA_DIRS=/opt/bundledock/toolbox/share:/usr/local/share:/usr/share
[ -r /opt/bundledock/toolbox/share/shell/init.zsh ] && . /opt/bundledock/toolbox/share/shell/init.zsh
Shortcuts that need a branch (gpsup, grbm, gswm) are functions in every
shell: they resolve the branch first and stop with bdock: not on a branch (detached HEAD) before running Git, which matters in jj-colocated repositories.
The default branch is origin/HEAD, else main, else master. All files load
only in interactive shells. The layer runs fastfetch once per interactive shell
when the executable is present; repeated sourcing does not repeat the greeting.
It leaves user Fastfetch configuration in control and tolerates a missing or
failing executable. Set BDOCK_FASTFETCH=0 to skip the greeting.
The layer also initializes zoxide
with its official --cmd cd option. cd accepts existing directory paths or
queries the directory database for keywords; cdi uses upstream interactive
selection (which requires fzf). Set BDOCK_ZOXIDE=0 before startup to skip
initialization and preserve existing cd/cdi definitions. This switch is
independent of the shortcut and prompt switches. Noninteractive sourcing never
initializes zoxide. A missing executable or a failed initializer leaves existing
navigation alone; partial output from a failed initializer is never evaluated.
No initializer files or caches are written by the layer, and it preserves
PATH, user configuration and upstream _ZO_* settings.
Bash, Zsh and Fish evaluate successful output directly. Nushell captures the
same official output in memory and installs it through a tagged, one-shot
pre_prompt string hook before the first prompt; this is supported by the
pinned Nushell 0.116.1. The hook removes itself and checks the optout and any
manual initialization again before installing definitions. This preserves
unrelated hooks and avoids repeated initialization when the layer is sourced
again before or after the first prompt. An existing manual zoxide initializer
is left in control in all four shells; keep one initializer, with your desired
command names and options, rather than also aliasing cd to z yourself.
To bypass zoxide, use builtin cd in Bash, Zsh and Fish. Nushell retains the
pre-initialization command as __bdock_native_cd, including native flags such
as __bdock_native_cd --physical /. If you already customized cd, that alias
captures your prior command. Nushell’s alias is created only when the zoxide
initializer actually runs. Scripts retain native cd behavior.
BDOCK_SHORTCUTS=0 skips the shortcuts,
BDOCK_STARSHIP=0 the prompt and BDOCK_ZSH_EXPAND=0 zsh’s expansion on space
(Ctrl+Space inserts a plain space). A prompt already initialized by the user’s
own configuration (STARSHIP_SHELL set) is left alone. Nushell definitions
cannot be conditional, so there the shortcuts are opted out by leaving the
toolbox out of XDG_DATA_DIRS. Nushell’s prompt setup retains a static copy of what
starship init nu produces; its zoxide commands use the string hook described
above.
The generator renders the four files from the table and they are committed.
The layer belongs to the toolbox alone: only the toolbox Dockerfile copies
internal/shelllayer into its build and passes it to tools-bundle --shell-layer,
which copies the files without running any shell. A toolbox build without the
layer fails, any other bundle refuses it, and neither the builder nor the
volume initializer that every bundle image ships imports the layer. After
editing the table, run
go test ./internal/shelllayer -run TestGeneratedFilesAreCurrent -update.
TestShortcutsBehaveTheSameInEveryShell runs every shortcut in each installed
shell against stub commands and requires identical arguments;
TestBranchShortcutsRefuseDetachedHead checks the detached-HEAD refusal.
Verified on 2026-10-15 with bash 5.2.37, zsh 5.9 and the pinned fish 4.9.3,
Nushell 0.116.1 and starship 1.26.0, including starship initialization in all
four shells, Nushell vendor autoload through XDG_DATA_DIRS and zsh’s
expansion in a pseudo-terminal. Zoxide initialization was checked on
2026-10-18 with Bash, Zsh, Fish 4.9.3 and Nushell 0.116.1 using fake executables;
Nushell’s first-prompt command definitions, query arguments, native escape,
optout and hook preservation were exercised in a real pseudo-terminal with
initializer source derived from the official zoxide 0.10.0 template. These are
source checks, not an OCI build or execution of the real zoxide binary.
Zsh is not part of the toolbox: no official upstream prebuilt release exists,
so it comes from the consumer image.
Optional browser editor
The code-server bundle provides a separate optional OCI carrier with its own lock and read-only volume. It does not change the toolbox or start a service automatically.
Leaf Markdown preview
The source toolbox includes Leaf 1.29.0
from the official Linux AMD64 and ARM64 raw binaries, installed as bin/leaf
with the full MIT license from the same upstream tag. Run leaf README.md for
an interactive terminal preview or leaf --inline plain README.md for plain
rendered output. Keep HOME and XDG configuration/cache directories writable
outside the read-only toolbox mount. Upgrade through BundleDock’s lock;
leaf --update writes its executable and is unsuitable for the mounted toolbox.
Both downloaded binary hashes matched the official release API and
checksums.txt on 2026-10-12. Read-only ELF inspection confirmed x86-64 and
AArch64; both binaries require glibc, libgcc_s and libm with their native loaders.
No upstream binary was executed. Source fixtures cover resolution and raw
installation on both architectures and the isolated noninteractive smoke’s
--version and --inline plain rendering. Native runtime compatibility and
image smoke on both architectures remain unverified until an explicitly
approved build and smoke run. The smoke clears caller configuration overrides
and confines Leaf’s HOME, XDG paths and Markdown fixture to a temporary directory.
Optional mise version manager
The source toolbox includes mise 2026.10.8
as bin/mise, using the official stable raw Linux MUSL executables for AMD64
and ARM64. The desired constraint is mise>=2026.10.8; the lock pins both
architectures and retains the full upstream MIT license. Upgrade the executable
through BundleDock’s lock rather than using mise self-update on the read-only
mount.
Use stock language containers as the default source of language toolchains.
Mise is an optional version manager for projects that deliberately choose it.
Adding it to the toolbox does not activate it, bootstrap runtimes, modify shell
configuration, or run mise use or mise install. Any chosen runtime installation
and shell integration is a separate user action. Keep HOME, XDG configuration,
cache, data and state directories writable outside /opt/bdock/toolbox.
On 2026-10-13, the downloaded raw binary hashes matched both the official release
API digests and SHASUMS256.txt. Read-only ELF inspection confirmed x86-64 and
AArch64 and no ELF interpreter; no upstream executable was run. Source fixtures
cover resolution, the minimum version, both architecture installation recipes,
and an isolated, noninteractive mise --version smoke that clears inherited
configuration and runs from a disposable HOME. Native runtime compatibility
and read-only image smoke on both architectures remain deferred until explicit
approval of the exact source revision and intended build/smoke operation.
Navi interactive cheatsheets
The source toolbox includes navi 2.24.0
from official Linux AMD64 musl and ARM64 GNU prebuilts. It uses the bundled
fzf. Cheatsheet commands can execute shell code: choose trusted sheets and
review commands before running them. BundleDock does not fetch cheatsheets,
execute examples, install shell hooks or activate Navi at shell startup.
Keep HOME and any Navi configuration/cache in writable consumer storage.
The release supplies no publisher checksum; the lock records SHA256 observed
from official HTTPS downloads. Both binaries are UPX packed, so read-only ELF
inspection cannot establish the original loader/library requirements. ARM64
consumer compatibility remains unverified until an approved image build and
native smoke run. The smoke probe checks only navi --version, without opening
the interface or fetching sheets. This source addition has not been published.
Typst typesetting
The source toolbox includes Typst 0.15.1
from official Linux AMD64 and ARM64 musl prebuilts, pinned with publisher SHA256
verification. The unmodified binary includes upstream embedded fonts. Its full
archive LICENSE and NOTICE and the version-bound typst-assets NOTICE are
retained, including the fonts’ separate license terms and exceptions.
Keep HOME, XDG_CACHE_HOME and Typst package caches in writable consumer storage
outside the read-only toolbox. Typst can use system fonts and explicit user
font directories via --font-path or TYPST_FONT_PATHS; choose those directories
in writable consumer storage when adding fonts. BundleDock does not download
fonts or render documents automatically. User-requested documents may resolve
Typst packages over the network and cache them under the user’s cache directory.
The smoke probe uses only typst --version; compilation, rendering and native
runtime compatibility remain unverified pending an explicitly approved image
build and native smoke run. This source addition has not been published.