Launcher
brew tap nvk/tap
brew install nvk/tap/agent-bondage
The formula name is agent-bondage. The installed command is bondage.
local C launcher · pinned artifacts · shell out of the trust boundary
______ _____ _ _______ ___ _____ _____
| ___ \| _ | \ | | _ \/ _ \| __ \| ___|
| |_/ /| | | | \| | | | / /_\ \ | \/| |__
| ___ \| | | | . ` | | | | _ | | __ | __|
| |_/ /\ \_/ / |\ | |/ /| | | | |_\ \| |___
\____/ \___/\_| \_/___/ \_| |_/\____/\____/
Coding agents are useful, but you cannot let them run loose with live keys,
weak dependency provenance, and broad ambient environment access. Exact paths.
Exact hashes. Optional envchain-xtra. Optional nono.
Your shell gets names. Bondage gets trust.
On macOS, if you use Claude Code with the normal Claude.ai/OAuth login, do not remove Keychain access from the default Claude profile unless you are deliberately moving to an API-key-only or experimental path.
Bondage is a small local C launcher for coding agents such as Codex, Claude Code,
OpenCode, and Pi. It exists because coding agents should not run loose with live
keys, mutable dependency trees, and broad ambient environment access. Instead of
trusting a shell alias or PATH lookup, Bondage verifies exact absolute paths and
exact hashes, optionally releases secrets through envchain-xtra,
optionally applies nono sandbox profiles,
and then execs the exact target. The design leans on operating-system primitives
where they are strongest: Keychain for secret storage and code-signing identity
where available for approval and drift signals. Bondage narrows the launch boundary.
It does not make an unaudited dependency tree trustworthy.
In practice, that means the shell keeps short names and convenience, while Bondage owns the trust decision about what exact thing is about to receive secrets and run.
Agent launch stacks drift. Shell wrappers grow policy logic. Global npm installs mutate under your feet. PATH resolution quietly becomes part of the trust boundary whether you intended it or not.
Bondage is the correction: move the security decisions into one small local launcher, pin the actual artifacts, and demote the shell back to naming sugar instead of policy.
Keep the package simple. Install the launcher from Homebrew. Bring your own
nono, optional envchain-xtra, and your own pinned tool tree.
brew tap nvk/tap
brew install nvk/tap/agent-bondage
The formula name is agent-bondage. The installed command is bondage.
brew tap nvk/tap
brew install nvk/tap/envchain-xtra
envchain-xtra is optional per profile. Use it only when a profile actually needs secret release.
Versioned directories. Absolute paths. No PATH-sensitive trust decisions.
~/.config/bondage/bondage.conf defines profiles, hashes, optional envchain, optional nono, optional Touch ID.
codex() { bondage exec codex ~/.config/bondage/bondage.conf -- "$@"; }
This is the shortest clean path: install the launcher, point it at one real tool, inspect the launch chain, then add a thin wrapper.
bondage and optionally envchain-xtra.~/.config/bondage/bondage.conf from the sample config.bondage hash-file.bondage verify, then bondage chain, then bondage exec.bondage exec.brew tap nvk/tap
brew install nvk/tap/agent-bondage
mkdir -p ~/.config/bondage
cp /path/to/bondage.conf.example ~/.config/bondage/bondage.conf
bondage hash-file /absolute/path/to/codex
bondage verify codex ~/.config/bondage/bondage.conf
bondage chain codex ~/.config/bondage/bondage.conf -- --help
bondage exec codex ~/.config/bondage/bondage.conf -- --help
codex() { bondage exec codex ~/.config/bondage/bondage.conf -- "$@"; }
Convenience only. Names, tab colors, tiny prompt shaping. Not policy.
Verifies exact paths and exact hashes, expands the chosen profile, prints a compact launch summary, builds the final chain, and execs.
Optional secret-release layer. Namespace-scoped. Only used for profiles that truly need secrets.
Optional sandbox layer. The profile that Bondage selects still decides what the process can read, write, and reach.
bondage verify codex ~/.config/bondage/bondage.conf
bondage chain codex ~/.config/bondage/bondage.conf -- --help
bondage exec codex ~/.config/bondage/bondage.conf -- --help
bondage hash-file /absolute/path/to/tool
bondage hash-tree /absolute/path/to/package-root
Because aliases and shell functions are fine for convenience, terrible for a narrow auditable trust boundary.
Because launch-time controls are only half the story. Mutable global installs are a poor foundation for anything you plan to bless with secrets.
envchain-xtra is usednono remains the enforcement layer for sandbox policy when enabledBondage is a launch-time control, not a procurement miracle. If you install compromised garbage and then pin it, Bondage will faithfully protect the compromised garbage.
Immutable versioned bundles. Pinned interpreter. Pinned entrypoint. Whole-tree hash. No trust in mutable global installs.
Fix sandbox denials in managed profiles and launcher config. If a hook reports a denial, keep it factual and short. Do not inject repair workflows that tell the agent to branch the conversation, wait, stop, or create new profile policy.
Static site, live repo state. If GitHub is unavailable, the page falls back to the baked-in values.
tag fallback
Small local C launcher. Exact paths. Exact hashes. Optional envchain-xtra. Optional nono.
tag fallback
Companion secret-release layer. Modernized macOS backend, public tap install, and compatibility shell guards if you still need them.
Treat package-manager upgrades as launcher-policy changes. The current public
baseline is agent-bondage 0.2.7, envchain-xtra
1.3.1, nono 0.61.1, and pinned nono packs for
codex 0.0.12, claude 0.0.16, and
opencode 0.0.5.
export BONDAGE_CONF="${BONDAGE_CONF:-$HOME/.config/bondage/bondage.conf}"
export NONO_PROFILE="${NONO_PROFILE:-codex}"
brew upgrade nono
brew cleanup nono
bondage --config "$BONDAGE_CONF" repin-globals
bondage --config "$BONDAGE_CONF" doctor
nono pull always-further/codex@0.0.12 --force
nono pull always-further/claude@0.0.16 --force
nono pull always-further/opencode@0.0.5 --force
nono pin always-further/codex
nono pin always-further/claude
nono pin always-further/opencode
nono list --installed
nono profile show "$NONO_PROFILE" >/dev/null
bondage --config "$BONDAGE_CONF" verify codex
bondage --config "$BONDAGE_CONF" chain codex -- --help
export NONO_PROFILE="${NONO_PROFILE:-codex}"
for path in \
"$HOME/.ssh" \
"$HOME/.npmrc" \
"$HOME/.aws" \
"$HOME/Library/Keychains"
do
nono why --profile "$NONO_PROFILE" --path "$path" --op read
done
nono why --profile "$NONO_PROFILE" --path "$HOME/.config/nono/profiles" --op write
nono why --profile "$NONO_PROFILE" --path "$HOME/.config/nono/profile-drafts" --op write
Expected shape: credential paths are denied, active profile writes are denied,
and profile-drafts is writable only for profiles that intentionally
support draft-and-promote profile edits. Update nono before pulling
newer packs; old nono builds may reject the newer profile schema.
Small trusted computing base. Explicit file-descriptor discipline. No new crate ecosystem to escape one package ecosystem.
Because the main problem is launch policy and artifact verification, not just secret storage. envchain-xtra stays focused on secret release.
Because aliases and shell functions are fine for convenience, terrible for a narrow auditable trust boundary.
An explicit no-nono escape hatch. It should be obvious, ugly, and deliberate.