local C launcher · pinned artifacts · shell out of the trust boundary

Bondage. Agent Bondage.

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.

bondage v0.1.0 static fallback
envchain-xtra v1.2.0 static fallback

Why this exists

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.

  • you do not want agents running loose with live API keys and other secrets
  • you cannot assume the dependency tree behind a familiar command is clean or stable
  • you do not want broad ambient environment access by default
  • you want to lean on OS-level primitives like code-signing identity and Keychain instead of rebuilding them in shell glue

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.

Install

Keep the package simple. Install the launcher from Homebrew. Bring your own nono, optional envchain-xtra, and your own pinned tool tree.

Launcher

brew tap nvk/tap
brew install nvk/tap/agent-bondage

The formula name is agent-bondage. The installed command is bondage.

Companion secret layer

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.

1. Pin exact tool artifacts

Versioned directories. Absolute paths. No PATH-sensitive trust decisions.

2. Write local policy

~/.config/bondage/bondage.conf defines profiles, hashes, optional envchain, optional nono, optional Touch ID.

3. Keep wrappers thin

codex() { bondage exec codex ~/.config/bondage/bondage.conf -- "$@"; }

5-minute quickstart

This is the shortest clean path: install the launcher, point it at one real tool, verify the exact argv, then add a thin wrapper.

Do this first

  1. Install bondage and optionally envchain-xtra.
  2. Create ~/.config/bondage/bondage.conf from the sample config.
  3. Pin the exact target path and fill in real hashes with bondage hash-file.
  4. Run bondage verify, then bondage argv, then bondage exec.
  5. Only then add a shell wrapper that does nothing except call bondage exec.

Minimal smoke test

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 argv codex ~/.config/bondage/bondage.conf -- --help
bondage exec codex ~/.config/bondage/bondage.conf -- --help

codex() { bondage exec codex ~/.config/bondage/bondage.conf -- "$@"; }

The stack

01

shell name

Convenience only. Names, tab colors, tiny prompt shaping. Not policy.

02

bondage

Verifies exact paths and exact hashes, expands the chosen profile, builds the final argv, and execs.

03

envchain-xtra

Optional secret-release layer. Namespace-scoped. Only used for profiles that truly need secrets.

04

nono

Optional sandbox layer. The profile that Bondage selects still decides what the process can read, write, and reach.

What it actually does

bondage verify codex ~/.config/bondage/bondage.conf
bondage argv 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

Common objections

Why not keep it in shell?

Because aliases and shell functions are fine for convenience, terrible for a narrow auditable trust boundary.

  • shell state is ambient and messy
  • PATH lookup leaks into the launch decision
  • functions quietly accumulate policy logic
  • reviewing shell glue is a bad substitute for a small launcher

Why not trust npm globals?

Because launch-time controls are only half the story. Mutable global installs are a poor foundation for anything you plan to bless with secrets.

  • the shim name is not the real trust target
  • the interpreter matters
  • the whole package tree matters
  • if you pinned a poisoned tree, Bondage will faithfully run the poisoned tree

Security assumptions

What this defends against

  • PATH drift and surprise binary replacement
  • shell wrappers quietly becoming the policy engine
  • mutable launch chains for JS-based agents
  • accidental secret release to the wrong target
  • profile confusion between sandboxed and rawdog paths

What this does not solve

  • blessing a bad npm tree in the first place
  • host compromise before launch
  • a malicious tool you explicitly pinned and approved
  • supply-chain review for dependencies you never audited
  • human judgment failures about what should get secrets

npm reality

Bondage 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.

Preferred JS shape

Immutable versioned bundles. Pinned interpreter. Pinned entrypoint. Whole-tree hash. No trust in mutable global installs.

Live from GitHub

Static site, live repo state. If GitHub is unavailable, the page falls back to the baked-in values.

bondage repo open →

v0.1.0

tag fallback

Small local C launcher. Exact paths. Exact hashes. Optional envchain-xtra. Optional nono.

envchain-xtra repo open →

v1.2.0

tag fallback

Companion secret-release layer. Modernized macOS backend, public tap install, and compatibility shell guards if you still need them.

FAQ

Why C?

Small trusted computing base. Explicit file-descriptor discipline. No new crate ecosystem to escape one package ecosystem.

Why not just patch envchain forever?

Because the main problem is launch policy and artifact verification, not just secret storage. envchain-xtra stays focused on secret release.

Why not keep it all in shell?

Because aliases and shell functions are fine for convenience, terrible for a narrow auditable trust boundary.

What is rawdog?

An explicit no-nono escape hatch. It should be obvious, ugly, and deliberate.