A single idempotent bash script that turns a fresh Linux host into a ready-to-use Claude Code and Codex agent environment. Built for Brev VMs but works on any Ubuntu/Debian host.
- Claude Code via the official native installer, configured for unattended use with
bypassPermissions, sandbox mode, debug logging, skipped onboarding, and pre-approved first-party API-key fingerprints when provided. - Codex CLI via OpenAI's standalone installer, configured with
approval_policy = "never",sandbox_mode = "danger-full-access", trusted project roots, live web search, shell env inheritance, service tier, reasoning effort, and[agents].max_threads. - AAB env file at
~/.aab/.envfor provider selection, model names, and credentials. The bootstrap keeps these out of~/.bashrcand/etc/environment. - Wrapper families in
~/.local/bin, with the selectedclaudeentrypoint installed as a regular launcher file in~/.local/aab-bin(kept ahead of~/.local/binon PATH so the native auto-updater, which owns~/.local/bin/claude, cannot shadow the wrapper):~/.local/aab-bin/claudeplus explicitclaude-first-party,claude-third-party-anthropic,claude-third-party-deepseek, andclaude-third-party-nemotronlauncherscodexplus explicitcodex-first-partyandcodex-third-party-openailaunchers
- Brev CLI, with optional
brev login --api-key ... --org-id ...whenAAB_BREV_API_KEYandAAB_BREV_ORG_IDare set. - gh CLI, installed from the official
cli.github.comapt repo. - git, with optional author identity, GitHub credential helper, SSH auth key, and SSH signing key.
- Global git-identity enforcement — an agent rule in every harness's global instruction file plus a global git hook that rejects commits whose identity does not match the configured git config. See Git Identity Enforcement.
- Agent plugins listed in
agent_plugins.txt, installed into both Claude Code and Codex.
- Ubuntu/Debian host with
bashandapt-get - Passwordless
sudo, or run as root - A bare
ubuntu:22.04image is valid; the bootstrap installscurl,python3,git,tar,gawk,ripgrep,sudo,ca-certificates, andgh
Create a config file with the settings you want, then run the bootstrap:
cat > /tmp/aab.conf <<'CONF'
AAB_CLAUDE_CODE_INFERENCE_PROVIDER=first-party
AAB_CLAUDE_CODE_FIRST_PARTY_MODEL=claude-opus-4-7
AAB_CLAUDE_CODE_EFFORT=max
AAB_CLAUDE_CODE_FIRST_PARTY_API_KEY=...
AAB_CODEX_INFERENCE_PROVIDER=first-party
AAB_CODEX_FIRST_PARTY_MODEL=gpt-5.5
AAB_CODEX_FIRST_PARTY_API_KEY=...
AAB_CODEX_EFFORT=xhigh
AAB_CODEX_SERVICE_TIER=priority
AAB_CODEX_AGENT_MAX_THREADS=16
AAB_BREV_API_KEY=...
AAB_BREV_ORG_ID=...
AAB_GH_TOKEN=...
AAB_GIT_AUTHOR_NAME=Your Name
AAB_GIT_AUTHOR_EMAIL=you@example.com
CONF
curl -fsSL https://raw.githubusercontent.com/brycelelbach/autonomous-agent-bootstrap/main/bootstrap.bash | bash -s -- /tmp/aab.conf
source ~/.bashrc
claude -p "Say hello from Claude Code"
codex exec "Say hello from Codex"You can also pass the same keys as exported environment variables or pipe config on stdin. The file is sourced as bash, so quote values containing shell metacharacters.
AAB_CLAUDE_CODE_INFERENCE_PROVIDER controls which wrapper behavior the unqualified claude launcher uses:
first-partythird-party-anthropicthird-party-deepseekthird-party-nemotron
AAB_CODEX_INFERENCE_PROVIDER controls which wrapper behavior the unqualified codex launcher uses:
first-partythird-party-openai
The explicit wrappers are always installed, so you can run claude-third-party-deepseek or codex-third-party-openai directly regardless of the default launcher. To change the unqualified claude or codex command, update your config and re-run the bootstrap. AAB writes the unqualified commands as regular executable wrapper files, not shell aliases, so type claude should resolve to ~/.local/aab-bin/claude and type codex to ~/.local/bin/codex.
AAB_CLAUDE_CODE_INFERENCE_PROVIDER=third-party-anthropic
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_BASE_URL=https://gateway.example.com
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_API_KEY=...
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_MODEL=aws/anthropic/bedrock-claude-opus-4-7
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_HAIKU_MODEL=aws/anthropic/claude-haiku-4-5-v1
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_SONNET_MODEL=aws/anthropic/bedrock-claude-sonnet-4-6
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_OPUS_MODEL=aws/anthropic/bedrock-claude-opus-4-7AAB_CLAUDE_CODE_INFERENCE_PROVIDER=third-party-deepseek
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_BASE_URL=https://deepseek-gateway.example.com
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_API_KEY=...
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_MODEL=deepseek-reasoner
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_HAIKU_MODEL=deepseek-chat
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_SONNET_MODEL=deepseek-chat
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_OPUS_MODEL=deepseek-reasonerAAB_CLAUDE_CODE_INFERENCE_PROVIDER=third-party-nemotron
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_BASE_URL=https://inference-api.nvidia.com
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_API_KEY=...
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_MODEL=nvidia/nvidia/nemotron-3-ultra
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_HAIKU_MODEL=nvidia/nvidia/nemotron-3-ultra
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_SONNET_MODEL=nvidia/nvidia/nemotron-3-ultra
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_OPUS_MODEL=nvidia/nvidia/nemotron-3-ultraAAB_CODEX_INFERENCE_PROVIDER=third-party-openai
AAB_CODEX_THIRD_PARTY_OPENAI_BASE_URL=https://inference-api.nvidia.com/v1
AAB_CODEX_THIRD_PARTY_OPENAI_API_KEY=...
AAB_CODEX_THIRD_PARTY_OPENAI_MODEL=openai/openai/gpt-5.5All variables are optional unless you select a provider that needs its credential.
| Variable | Effect |
|---|---|
AAB_CLAUDE_CODE_INFERENCE_PROVIDER |
first-party, third-party-anthropic, third-party-deepseek, or third-party-nemotron. Selects the unqualified claude launcher behavior. Defaults to first-party. |
AAB_CLAUDE_CODE_FIRST_PARTY_API_KEY |
Anthropic first-party API key. Stored in ~/.aab/.env; mapped to ANTHROPIC_API_KEY by claude-first-party. |
AAB_CLAUDE_CODE_FIRST_PARTY_MODEL |
Claude first-party model. Defaults to claude-opus-4-7. |
AAB_CLAUDE_CODE_FIRST_PARTY_HAIKU_MODEL |
First-party haiku-tier model. Defaults to claude-haiku-4-5. |
AAB_CLAUDE_CODE_FIRST_PARTY_SONNET_MODEL |
First-party sonnet-tier model. Defaults to claude-sonnet-4-6. |
AAB_CLAUDE_CODE_FIRST_PARTY_OPUS_MODEL |
First-party opus-tier model. Defaults to claude-opus-4-7. |
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_BASE_URL |
Anthropic-compatible third-party base URL for Claude. |
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_API_KEY |
Third-party Anthropic-compatible API key. Mapped to ANTHROPIC_AUTH_TOKEN. |
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_MODEL |
Third-party Anthropic-compatible default model. |
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_HAIKU_MODEL |
Third-party Anthropic-compatible haiku-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_SONNET_MODEL |
Third-party Anthropic-compatible sonnet-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_ANTHROPIC_OPUS_MODEL |
Third-party Anthropic-compatible opus-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_BASE_URL |
DeepSeek gateway base URL for Claude. |
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_API_KEY |
DeepSeek gateway API key. Mapped to ANTHROPIC_AUTH_TOKEN. |
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_MODEL |
DeepSeek default model. |
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_HAIKU_MODEL |
DeepSeek haiku-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_SONNET_MODEL |
DeepSeek sonnet-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_DEEPSEEK_OPUS_MODEL |
DeepSeek opus-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_BASE_URL |
Nemotron gateway base URL for Claude. |
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_API_KEY |
Nemotron gateway API key. Mapped to ANTHROPIC_AUTH_TOKEN. |
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_MODEL |
Nemotron default model. |
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_HAIKU_MODEL |
Nemotron haiku-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_SONNET_MODEL |
Nemotron sonnet-tier model. |
AAB_CLAUDE_CODE_THIRD_PARTY_NEMOTRON_OPUS_MODEL |
Nemotron opus-tier model. |
AAB_CLAUDE_CODE_EFFORT |
Claude Code effort level. Defaults to max. |
AAB_CODEX_INFERENCE_PROVIDER |
first-party or third-party-openai. Selects the unqualified codex launcher behavior. Defaults to first-party. |
AAB_CODEX_FIRST_PARTY_API_KEY |
OpenAI API key for first-party Codex. Stored in ~/.aab/.env, mapped to OPENAI_API_KEY by codex-first-party, and used for codex login --with-api-key. |
AAB_CODEX_FIRST_PARTY_MODEL |
Codex first-party model. Defaults to gpt-5.5. |
AAB_CODEX_THIRD_PARTY_OPENAI_BASE_URL |
OpenAI-compatible third-party base URL for Codex. Defaults to https://inference-api.nvidia.com/v1. |
AAB_CODEX_THIRD_PARTY_OPENAI_API_KEY |
OpenAI-compatible third-party API key for Codex. |
AAB_CODEX_THIRD_PARTY_OPENAI_MODEL |
Codex third-party OpenAI-compatible model. Defaults to openai/openai/gpt-5.5. |
AAB_CODEX_EFFORT |
Codex reasoning effort: minimal, low, medium, high, or xhigh. Defaults to xhigh. |
AAB_CODEX_SERVICE_TIER |
Codex service tier: priority, flex, default, or fast as an alias for priority. Defaults to priority. |
AAB_CODEX_AGENT_MAX_THREADS |
Maximum number of concurrently open Codex subagent threads. Defaults to 16. |
AAB_BREV_API_KEY |
Brev organization-scoped API key. Used with AAB_BREV_ORG_ID. |
AAB_BREV_ORG_ID |
Brev organization ID paired with AAB_BREV_API_KEY. |
AAB_GH_TOKEN |
GitHub token. Stored in ~/.aab/.env; wrappers map it to GH_TOKEN for agent subprocesses. |
AAB_GIT_AUTHOR_NAME |
git config --global user.name. |
AAB_GIT_AUTHOR_EMAIL |
git config --global user.email. |
AAB_GH_AUTH_SSH_PRIVATE_KEY_B64 |
Base64-encoded OpenSSH private key for GitHub SSH auth. |
AAB_GIT_SSH_SIGNING_PRIVATE_KEY_B64 |
Base64-encoded OpenSSH private key for git commit/tag signing. |
AAB_AGENT_PLUGINS_FILE |
Path to a local plugin marketplace list. |
AAB_AGENT_PLUGINS_URL |
URL for the plugin marketplace list when no local file is used. |
| Path | How |
|---|---|
~/.aab/.env |
Rewritten with provider config, model names, and credentials. Mode 0600; parent directory mode 0700. |
~/.local/aab-bin/claude |
Claude launcher file for the selected provider; on PATH ahead of ~/.local/bin. |
~/.local/bin/claude |
Left as the native installer's binary so the auto-updater can repoint it. |
~/.local/bin/claude-first-party |
Claude wrapper for first-party Anthropic. |
~/.local/bin/claude-third-party-anthropic |
Claude wrapper for Anthropic-compatible third-party gateways. |
~/.local/bin/claude-third-party-deepseek |
Claude wrapper for DeepSeek gateways. |
~/.local/bin/claude-third-party-nemotron |
Claude wrapper for Nemotron gateways. |
~/.local/bin/claude-aab-real |
Symlink to ~/.local/bin/claude, so wrappers exec whatever the updater installs. |
~/.local/bin/codex |
Codex launcher file with the selected provider behavior. |
~/.local/bin/codex-first-party |
Codex wrapper for first-party OpenAI. |
~/.local/bin/codex-third-party-openai |
Codex wrapper for OpenAI-compatible third-party gateways. |
~/.local/bin/codex-aab-real |
Link or moved copy of the real Codex binary. |
~/.claude/settings.json |
Rewritten with unattended Claude defaults and plugin entries; existing file is backed up. |
~/.claude.json |
Merged with onboarding and optional API-key approval state; existing file is backed up. |
~/.codex/config.toml |
Rewritten with unattended Codex defaults and selected provider config while preserving Codex plugin tables; existing file is backed up. |
~/.codex/auth.json |
Written by codex login --with-api-key when first-party Codex API-key auth is configured. |
~/.bashrc |
Managed block for PATH and non-secret unattended-mode exports only. |
~/.profile |
Managed block that keeps ~/.local/aab-bin ahead of ~/.local/bin for login shells. |
/etc/environment |
Existing AAB managed blocks are removed so credentials do not remain there. |
~/.brev/credentials.json |
Written by brev login --api-key ... --org-id ... when Brev credentials are configured. |
~/.brev/onboarding_step.json |
Written to skip the Brev tutorial. |
~/.gitconfig |
git identity, GitHub credential helper, core.hooksPath for identity enforcement, and optional SSH signing config. |
~/.ssh/id_aab_auth, ~/.ssh/config |
Written only when AAB_GH_AUTH_SSH_PRIVATE_KEY_B64 is set. |
~/.ssh/id_aab_signing |
Written only when AAB_GIT_SSH_SIGNING_PRIVATE_KEY_B64 is set. |
~/.aab/git-hooks/ |
Global git hook dispatcher and per-hook-name symlinks that enforce the configured commit identity. core.hooksPath points here. See Git Identity Enforcement. |
~/.claude/CLAUDE.md, ~/.codex/AGENTS.md |
Managed block carrying the git-identity agent rule; other content is preserved. |
The bootstrap handles two independent optional SSH-key variables:
| Env var | Role |
|---|---|
AAB_GH_AUTH_SSH_PRIVATE_KEY_B64 |
GitHub authentication for clone/push/pull over SSH. |
AAB_GIT_SSH_SIGNING_PRIVATE_KEY_B64 |
git commit and tag signing only. |
Generate and encode a key:
ssh-keygen -t ed25519 -C "you@example.com" -f ~/.ssh/new_key -N ""
base64 -w0 < ~/.ssh/new_keySet the encoded private key on the relevant AAB variable, and upload the public key to GitHub as either an authentication key, a signing key, or both.
The bootstrap configures a global git author, email, and (optionally) a commit-signing key, but unattended agents routinely commit under their own identity anyway — via git -c user.email=..., git commit --author=..., GIT_AUTHOR_* / GIT_COMMITTER_* environment variables, or a repo-local git config user.email. Two layers keep commits on the configured identity:
- An agent rule is written to each harness's global instruction file —
~/.claude/CLAUDE.mdfor Claude Code and~/.codex/AGENTS.mdfor Codex — both of which are loaded in every repository. The rule tells the agent to always commit with the configured identity and to leave the global git config alone. The rule lives inside a managed block (# >>> autonomous-agent-bootstrap >>>…# <<< autonomous-agent-bootstrap <<<), so re-running the bootstrap replaces it in place and any other content in those files is preserved. - A global git hook makes the rule non-optional. The bootstrap installs a dispatcher at
~/.aab/git-hooks/aab-git-hook, symlinks it under each managed hook name, and pointscore.hooksPathat that directory. Onpre-committhe dispatcher compares the commit's resolved author and committer identity (git var GIT_AUTHOR_IDENT/GIT_COMMITTER_IDENT) against the globaluser.name/user.email, and rejects the commit on a mismatch. When the global config requires signing (commit.gpgsign=true), it also rejects commits that disable signing via config or swap the signing key. The expected values are read from--global, which a per-invocation-c, an environment variable, or a repo-local config cannot override.
Because a global core.hooksPath replaces — rather than supplements — a repository's own .git/hooks, the dispatcher chains through to the repo's hook of the same name after its own checks pass, so projects that ship their own hooks (Husky, pre-commit, lint-staged, …) keep working.
The enforcement is intentionally scoped to identity and signing. It does not try to defeat the deliberate per-commit escape hatches git provides — git commit --no-verify skips all hooks, and --no-gpg-sign skips signing — which remain available for the rare legitimate case.
If no global user.name / user.email is configured, the hook is a no-op: there is nothing to enforce against, so all commits pass.
All tests are driven by ./test.bash.
./test.bash # lint + unit
./test.bash --lint # bash -n + shellcheck
./test.bash --unit # bats suite
./test.bash --e2e # destructive host e2e
./test.bash --docker # e2e in a fresh ubuntu:22.04 container
./test.bash --smoke # live Claude + Codex inference smoke
./test.bash --secrets # gitleaks scan
./test.bash --all # lint + unit + e2e + secrets--e2e modifies the current $HOME and should only be run on a disposable machine. --docker is the safe bare-image check. --smoke spends real inference using the credentials in the current environment or the AAB wrapper configuration.