storozhenko98/bailout

The harness meant to be deleted. Bootstrap a fresh machine or repair your broken coding setup, then get back to your usual tools.

Python

0

74 commits

updated Sep 18, 2026

See the code

See what people are saying (1)

README

bailout

The harness meant to be deleted.

You have a fresh VM or a new Mac. No GitHub CLI, no configured coding agent, no API key handy. Or your usual agent broke, and you need a working one to fix it.

Bailout gets you a small, independent harness in one command. Use it to bootstrap the machine or repair your setup, get back to your normal tools, then delete it.

curl -fsSL https://bailout.dev/install.sh | bash
bailout

No account, local API key, Git, Node, Python, or existing agent required. Apple Silicon macOS, x64 Linux, and ARM64 Linux. Bring Bash, curl, internet access, and the usual base utilities (including tar and a SHA-256 utility).

Website · Setup & recovery guide · Releases

OpenCode will not start. Bailout repairs its config, then is deleted.

Watch the full-size video. A prepared config failure, repaired by real public Auto inference in an Ubuntu ARM64 container. No local API key. Other settings and the original config backup are checked; OpenCode answers after Bailout is removed. Waits shortened; terminal output unchanged. Reproduce it.

When you need it

A clean slate. Boot an EC2 instance, a GCP VM, or a new Mac. Ask bailout to inspect the machine, install missing tools, help you sign in to GitHub, clone your repo, and get Codex, Claude Code, Pi, or OpenCode ready to use.

A broken setup. Your normal agent no longer starts. A config is malformed, a runtime moved, or an update broke something. Use an independent harness to inspect what changed, back up the config, repair it, and verify that your main tool works.

A quick exit. The useful outcome is your usual setup working again. Bailout has no daemon, account setup, persistent conversation store, or project scaffolding. It is one native binary: 638.6 KB on Apple Silicon in v0.7.3. Every release stays under 6,000,000 bytes. Linux releases are statically linked with musl.

$ bailout

  bailout v0.7.3
  ~

  › auto · free models · full access
  Fresh machine? Broken setup? Tell me what needs to work.
  /shell local terminal   /new fresh conversation   /help

  › this is a fresh Ubuntu VM. help me set up gh and OpenCode

Other starting points:

bailout 'pi stopped launching after a config change. diagnose and repair it'
bailout 'check what is missing before I can use my usual dev tools here'

Bash is the only model tool. Commands run automatically with your account's permissions, including file changes, package installation, and network access. This is not a sandbox. The agent is instructed to inspect first, preserve working setup, back up configuration before repair, and verify the result.

Sign in locally

Bailout provides its own free inference. The tools you set up still use your own accounts and credentials.

For sign-in, sudo, or key entry, the Bash tool can hand you the real terminal with interactive: true. That command's input and output are not captured for the model; only its exit status is returned. You can also enter /shell for a local Bash session, then type exit to return. Don't paste passwords or API keys into chat. Normal Bash output is captured, so never ask the agent to print credential files.

For example, GitHub CLI's login flow can run in the local terminal handoff. Authentication still needs your participation. Bailout cannot manufacture an account or recover an unavailable secret.

Done? Delete it.

bailout uninstall

This removes only the running bailout binary. The tools you installed, repositories you cloned, and configurations you repaired remain in place. It does not remove other tools, credentials, or directories. Reinstall with the same curl command whenever you need it again.

Controls

In a sessionAction
/shellOpen local Bash for private or interactive setup; exit returns
/lastExpand the last command's captured output
/newStart a fresh conversation
/help / /exitShow help or quit

Ctrl-C stops a model request or Bash process group. At the prompt, it clears a draft or exits when empty. Up/Down browses history; Ctrl-J or Alt-Enter inserts a newline. Replies stream as they arrive. Questions can be answered directly, without a Bash call. Commands execute only after their complete tool call is validated.

Each Bash call starts a fresh shell in the session directory unless workdir is specified. Shell state does not persist between calls. Ordinary commands default to a two-minute timeout; interactive commands default to ten minutes. Models can request up to thirty minutes. --max-steps N changes the default 50 model steps. Routing is automatic. NO_COLOR=1 disables colors.

Bailout's Bash calls skip shell startup files, and its curl transport ignores .curlrc, so those customizations do not have to work before bailout can help.

Free means zero

The hosted FastAPI service on Cloudflare Python Workers owns provider keys. The client never receives them. The OpenRouter adapter applies these checks: For every model request, including subsequent agent steps and fallback attempts:

  1. Fetch a fresh OpenRouter catalog with cache bypass; require an explicit :free model, native tool support, and exactly zero in every reported pricing field.
  2. Fetch that model's current endpoints. Require matching model IDs, zero endpoint prices, tool support, an operational status, and at least 95% reported uptime over 30 minutes (and over 5 minutes when reported). Missing prices or unknown health fail closed.
  3. Send only the exact verified provider endpoints with allow_fallbacks: false, require_parameters: true, and a hard zero max_price for prompt, completion, request, and image charges. This cap also covers the gap between checking and sending.
  4. Auto recovers from provider failures, timeouts, broken streams and invalid responses with at most four metered attempts within 120 seconds. Temporary 429s get one delayed retry before switching. The new terminal uses Auto only; legacy pinned API requests stay pinned. No paid inference fallback exists.
  5. Reject arbitrary routing overrides, provider keys, plugins, multimodal content, and tools other than Bash. Audit returned costs and reject unexpected nonzero costs.

The $1 key limit alone would still permit paid requests. The checks and upstream zero-price cap are what enforce this app's free-only policy. Like any client, the app depends on OpenRouter honoring its published prices and routing contract.

The router qualifies models using Bailout's own setup-and-repair benchmark. Models need at least 80% success on a complete ten-task run, native Bash calls, and no critical failures. Repeat runs refresh that evidence; reproduced live regressions can hold a model out even when its synthetic score passes. Unrecognized models remain outside production. Rankings combine measured task success with aggregate route health; context length, model names and marketing copy do not earn intelligence points. The nightly workflow publishes versioned results atomically, retaining the last valid snapshot on failure. See qualification, context handling and staged rollout. The hosted service admits only models with genuine passing qualification evidence.

Auto remembers the working model for the terminal session and avoids failed routes. The backend shares cooldowns across users and reserves request/token capacity for every attempt, including retries. Recovery preserves completed Bash results and never replays commands. Partial tool calls are discarded. A known account quota can use a separately enabled free provider; policy refusals and the hosting cutoff stop recovery. No prompt logs or installation IDs are introduced. The terminal explains temporary capacity waits and retries with backoff for up to five minutes, with at most eight HTTP attempts. Ctrl-C cancels the wait. Every attempt is metered; daily exhaustion and hosting-budget refusals stop promptly. See recovery and the API protocol.

Adapters cover OpenRouter, Groq, Mistral, Z.AI and Vercel AI Gateway. Groq and Mistral require expiring attestations of verified Free accounts with paid billing and top-ups disabled. Z.AI requires fresh, explicit Free pricing in all four published token-price columns. Vercel requires zero input/output/other fees, native tools, a free capability, and providerOptions.gateway.has: ["free"] on every inference. Credit balances and spending limits never make a paid model eligible. Unknown or changed eligibility skips that route. See operator setup.

The client preserves history instead of silently removing old turns. Every route must support at least 32K context and fit this conversation plus answer space and headroom. Near a limit, Auto announces a switch to a fitting qualified free model. If none fits, it returns context_exhausted; /new starts a fresh task. The first implementation uses a conservative byte-based token estimate and can switch early.

Free capacity is shared and best effort. OpenRouter's account quota and provider availability still apply. A final HTTP 429 means wait for the indicated capacity reset; the app never pays to bypass it. The public gateway enforces 60 requests/minute, 600/hour and 2,000/day per IP, plus a globally shared allowance. IPv6 /64s and users behind a NAT share limits. At the hosting cutoff, HTTP 503 budget_exhausted explains when capacity returns; the CLI displays it without automatically retrying. See limits and error codes.

Source contracts: OpenRouter provider routing, OpenRouter limits, Cloudflare Workers pricing.

Privacy

Prompts, model-selected file contents, and captured Bash output are sent through the hosted Worker to the selected configured provider (OpenRouter and its upstream provider, or an enabled Groq, Mistral, Z.AI or Vercel route). Provider data policies apply; free does not mean zero data retention. The Worker does not store conversations or log request bodies. The gateway retains short-lived daily IP hashes, quota reservations, cooldowns, and aggregate model availability/latency; see retention details. Worker observability is disabled. Do not include credentials in prompts or ask the agent to read secret files.

The homepage publishes aggregate download and model-request totals. Downloads come from GitHub's public binary-asset counts (including updates and repeats). The request counter starts when enabled; it counts hosted chat requests accepted by the backend, including streams that may fail later. There are no analytics cookies, installation IDs, location analytics, or additional CLI telemetry calls. See how the public counters work.

Measured release sizes

From the published v0.7.3 assets, verified against SHA-256 checksums:

PlatformNative binary (uncompressed)Download (.tar.gz)
macOS ARM64638,592 bytes318,119 bytes
Linux x64844,576 bytes417,262 bytes
Linux ARM64790,784 bytes397,031 bytes

Build and test

cargo test --locked
cargo clippy --locked --all-targets -- -D warnings
cargo build --release --locked
python3 scripts/check-size.py target/release/bailout
python3 scripts/smoke.py target/release/bailout
python3 scripts/test-installer.py
(cd api && uv sync && uv run pytest)
(cd worker && npm ci && npm test && npm run build:site)
python3 bench/test_bench.py

Optional live file-write verification (uses shared free quota): python3 scripts/live-smoke.py target/release/bailout. For the recovery use case: python3 scripts/live-recovery.py target/release/bailout diagnoses a broken fixture agent, backs up its configuration, and verifies repair.

Three direct Rust dependencies: serde_json, libc, and rustyline. System curl handles TLS; Bash handles everything the model does. The API is FastAPI on Cloudflare Python Workers. A separate Worker serves the static landing page and forwards legacy API URLs through the budget gateway. The Python service is private; a SQLite Durable Object reserves capacity atomically before forwarding requests.

The smoke test drives a real controlling terminal. It verifies Ctrl-C as a keypress while editing, waiting on a model, running a child process, and at the empty prompt, plus history, multiline input, Auto routing, local interactive authentication without credential capture, shell handoff, uninstall, and recovery after interruption.

CI runs the tests and real binary smoke checks on all three supported platforms. Tagging vX.Y.Z builds native release assets, tests them, enforces the size ceiling, and publishes SHA-256 checksums. The installer pins one resolved release version, verifies the archive checksum, checks its contents and binary size, then installs atomically. Set BAILOUT_VERSION=v0.7.3 or BAILOUT_INSTALL_DIR=/your/bin to override. It prefers an existing writable PATH location and never uses sudo or modifies shell rc files.

Starting in v0.4.0, each launch checks the official GitHub stable release. A newer release is downloaded, SHA-256 verified, installed atomically, and restarted with the same arguments, working directory, input and backend setting. Failed checks keep the existing binary. Use bailout update to check manually or BAILOUT_NO_UPDATE=1 to disable automatic updates. Custom-backend builds manage their own updates. Older releases need the installer once to gain this feature.

Website and API

Worker and browser sources use strict TypeScript. npm run build:site in worker emits plain JavaScript and copies static assets to dist/site; Wrangler runs that build automatically for the website. The Rust binary and Python API need no JS runtime.

The site uses self-hosted Space Grotesk and IBM Plex Mono, with their OFL licenses included. No analytics, external fonts, or frontend framework. The visual language is inspired by neobrutalism.dev.

Host your own router

Use Python 3.13+, uv 0.12.3+, and Node (for Wrangler). Choose your own Worker name in api/wrangler.jsonc before deploying. Keep Python private. In worker/gateway.wrangler.jsonc, choose your gateway name and domain and point its API binding at your Python Worker. Keep workers.dev and preview URLs disabled. Bind the Python Worker’s CAPACITY namespace to BudgetGuard in that gateway. For a new deployment, first deploy the gateway with an empty services array and SERVICE_PAUSED=true so its Durable Object exists. Then deploy Python with the CAPACITY binding, restore the gateway’s API service binding, and unpause it. For an existing deployment, use the staged qualification rollout before switching the public backend. An empty registry fails closed.

The Python API needs Workers Paid on Cloudflare. Its JSON processing and streaming exceed the Free plan's 10 ms CPU allowance; requests can otherwise be terminated midway through a reply. Paid starts at $5/month plus usage. This is hosting cost, separate from the strict zero-cost model routing. See Cloudflare's current pricing and CPU limits. You can also run the same FastAPI app on an existing server with Uvicorn.

cd api
uv sync
uv run pywrangler login
uv run pywrangler deploy
uv run pywrangler secret put OPENROUTER_API_KEY

Enter the OpenRouter key at the secret prompt. Never put it in source or config. For edge development, place it in a gitignored .dev.vars file and run uv run pywrangler dev. For ordinary local FastAPI protocol development, run uv run uvicorn app:app --app-dir src --reload. Production qualification and quotas require the Durable Object binding; local mode has no qualified models by default. The API exposes /health, /v1/models, /v1/chat, /docs, and /openapi.json. Chat accepts {model, messages, stream} and optional Auto-only preferred_model and avoid_models session hints. With stream: true, it returns NDJSON model, text, and done events, or an error event. Only the done event contains a validated message that is safe to pass to the tool dispatcher. An additive retry: true field on a model event marks recovery or a preemptive switch; clients should display its notice and separate any partial text from the new response. The complete conversation message comes only from done, never concatenated text deltas.

Deploy the guard with npm ci && npx wrangler deploy --config gateway.wrangler.jsonc in worker. To deploy the site, point the service binding in worker/wrangler.jsonc at your gateway Worker, then run npm run deploy. Set your own domains in both configs. The public gateway additionally exposes /v1/status.

export BAILOUT_API_URL=https://api.your-domain.example
bailout

BAILOUT_DEFAULT_API at compile time changes the binary's built-in backend URL. Runtime API overrides require HTTPS, except localhost for development. The public service intentionally requires no login, so use your own Worker and key if you need a separate quota. Cloudflare hosting costs and limits are separate from model prices.

MIT licensed. Inspired by Pi and fx.

Contributors

storozhenko98

74 commits

storozhenko98/bailout

The harness meant to be deleted. Bootstrap a fresh machine or repair your broken coding setup, then get back to your usual tools.

Python

0

74 commits

updated Sep 18, 2026

See the code

See what people are saying (1)

README

bailout

The harness meant to be deleted.

You have a fresh VM or a new Mac. No GitHub CLI, no configured coding agent, no API key handy. Or your usual agent broke, and you need a working one to fix it.

Bailout gets you a small, independent harness in one command. Use it to bootstrap the machine or repair your setup, get back to your normal tools, then delete it.

curl -fsSL https://bailout.dev/install.sh | bash
bailout

No account, local API key, Git, Node, Python, or existing agent required. Apple Silicon macOS, x64 Linux, and ARM64 Linux. Bring Bash, curl, internet access, and the usual base utilities (including tar and a SHA-256 utility).

Website · Setup & recovery guide · Releases

OpenCode will not start. Bailout repairs its config, then is deleted.

Watch the full-size video. A prepared config failure, repaired by real public Auto inference in an Ubuntu ARM64 container. No local API key. Other settings and the original config backup are checked; OpenCode answers after Bailout is removed. Waits shortened; terminal output unchanged. Reproduce it.

When you need it

A clean slate. Boot an EC2 instance, a GCP VM, or a new Mac. Ask bailout to inspect the machine, install missing tools, help you sign in to GitHub, clone your repo, and get Codex, Claude Code, Pi, or OpenCode ready to use.

A broken setup. Your normal agent no longer starts. A config is malformed, a runtime moved, or an update broke something. Use an independent harness to inspect what changed, back up the config, repair it, and verify that your main tool works.

A quick exit. The useful outcome is your usual setup working again. Bailout has no daemon, account setup, persistent conversation store, or project scaffolding. It is one native binary: 638.6 KB on Apple Silicon in v0.7.3. Every release stays under 6,000,000 bytes. Linux releases are statically linked with musl.

$ bailout

  bailout v0.7.3
  ~

  › auto · free models · full access
  Fresh machine? Broken setup? Tell me what needs to work.
  /shell local terminal   /new fresh conversation   /help

  › this is a fresh Ubuntu VM. help me set up gh and OpenCode

Other starting points:

bailout 'pi stopped launching after a config change. diagnose and repair it'
bailout 'check what is missing before I can use my usual dev tools here'

Bash is the only model tool. Commands run automatically with your account's permissions, including file changes, package installation, and network access. This is not a sandbox. The agent is instructed to inspect first, preserve working setup, back up configuration before repair, and verify the result.

Sign in locally

Bailout provides its own free inference. The tools you set up still use your own accounts and credentials.

For sign-in, sudo, or key entry, the Bash tool can hand you the real terminal with interactive: true. That command's input and output are not captured for the model; only its exit status is returned. You can also enter /shell for a local Bash session, then type exit to return. Don't paste passwords or API keys into chat. Normal Bash output is captured, so never ask the agent to print credential files.

For example, GitHub CLI's login flow can run in the local terminal handoff. Authentication still needs your participation. Bailout cannot manufacture an account or recover an unavailable secret.

Done? Delete it.

bailout uninstall

This removes only the running bailout binary. The tools you installed, repositories you cloned, and configurations you repaired remain in place. It does not remove other tools, credentials, or directories. Reinstall with the same curl command whenever you need it again.

Controls

In a sessionAction
/shellOpen local Bash for private or interactive setup; exit returns
/lastExpand the last command's captured output
/newStart a fresh conversation
/help / /exitShow help or quit

Ctrl-C stops a model request or Bash process group. At the prompt, it clears a draft or exits when empty. Up/Down browses history; Ctrl-J or Alt-Enter inserts a newline. Replies stream as they arrive. Questions can be answered directly, without a Bash call. Commands execute only after their complete tool call is validated.

Each Bash call starts a fresh shell in the session directory unless workdir is specified. Shell state does not persist between calls. Ordinary commands default to a two-minute timeout; interactive commands default to ten minutes. Models can request up to thirty minutes. --max-steps N changes the default 50 model steps. Routing is automatic. NO_COLOR=1 disables colors.

Bailout's Bash calls skip shell startup files, and its curl transport ignores .curlrc, so those customizations do not have to work before bailout can help.

Free means zero

The hosted FastAPI service on Cloudflare Python Workers owns provider keys. The client never receives them. The OpenRouter adapter applies these checks: For every model request, including subsequent agent steps and fallback attempts:

  1. Fetch a fresh OpenRouter catalog with cache bypass; require an explicit :free model, native tool support, and exactly zero in every reported pricing field.
  2. Fetch that model's current endpoints. Require matching model IDs, zero endpoint prices, tool support, an operational status, and at least 95% reported uptime over 30 minutes (and over 5 minutes when reported). Missing prices or unknown health fail closed.
  3. Send only the exact verified provider endpoints with allow_fallbacks: false, require_parameters: true, and a hard zero max_price for prompt, completion, request, and image charges. This cap also covers the gap between checking and sending.
  4. Auto recovers from provider failures, timeouts, broken streams and invalid responses with at most four metered attempts within 120 seconds. Temporary 429s get one delayed retry before switching. The new terminal uses Auto only; legacy pinned API requests stay pinned. No paid inference fallback exists.
  5. Reject arbitrary routing overrides, provider keys, plugins, multimodal content, and tools other than Bash. Audit returned costs and reject unexpected nonzero costs.

The $1 key limit alone would still permit paid requests. The checks and upstream zero-price cap are what enforce this app's free-only policy. Like any client, the app depends on OpenRouter honoring its published prices and routing contract.

The router qualifies models using Bailout's own setup-and-repair benchmark. Models need at least 80% success on a complete ten-task run, native Bash calls, and no critical failures. Repeat runs refresh that evidence; reproduced live regressions can hold a model out even when its synthetic score passes. Unrecognized models remain outside production. Rankings combine measured task success with aggregate route health; context length, model names and marketing copy do not earn intelligence points. The nightly workflow publishes versioned results atomically, retaining the last valid snapshot on failure. See qualification, context handling and staged rollout. The hosted service admits only models with genuine passing qualification evidence.

Auto remembers the working model for the terminal session and avoids failed routes. The backend shares cooldowns across users and reserves request/token capacity for every attempt, including retries. Recovery preserves completed Bash results and never replays commands. Partial tool calls are discarded. A known account quota can use a separately enabled free provider; policy refusals and the hosting cutoff stop recovery. No prompt logs or installation IDs are introduced. The terminal explains temporary capacity waits and retries with backoff for up to five minutes, with at most eight HTTP attempts. Ctrl-C cancels the wait. Every attempt is metered; daily exhaustion and hosting-budget refusals stop promptly. See recovery and the API protocol.

Adapters cover OpenRouter, Groq, Mistral, Z.AI and Vercel AI Gateway. Groq and Mistral require expiring attestations of verified Free accounts with paid billing and top-ups disabled. Z.AI requires fresh, explicit Free pricing in all four published token-price columns. Vercel requires zero input/output/other fees, native tools, a free capability, and providerOptions.gateway.has: ["free"] on every inference. Credit balances and spending limits never make a paid model eligible. Unknown or changed eligibility skips that route. See operator setup.

The client preserves history instead of silently removing old turns. Every route must support at least 32K context and fit this conversation plus answer space and headroom. Near a limit, Auto announces a switch to a fitting qualified free model. If none fits, it returns context_exhausted; /new starts a fresh task. The first implementation uses a conservative byte-based token estimate and can switch early.

Free capacity is shared and best effort. OpenRouter's account quota and provider availability still apply. A final HTTP 429 means wait for the indicated capacity reset; the app never pays to bypass it. The public gateway enforces 60 requests/minute, 600/hour and 2,000/day per IP, plus a globally shared allowance. IPv6 /64s and users behind a NAT share limits. At the hosting cutoff, HTTP 503 budget_exhausted explains when capacity returns; the CLI displays it without automatically retrying. See limits and error codes.

Source contracts: OpenRouter provider routing, OpenRouter limits, Cloudflare Workers pricing.

Privacy

Prompts, model-selected file contents, and captured Bash output are sent through the hosted Worker to the selected configured provider (OpenRouter and its upstream provider, or an enabled Groq, Mistral, Z.AI or Vercel route). Provider data policies apply; free does not mean zero data retention. The Worker does not store conversations or log request bodies. The gateway retains short-lived daily IP hashes, quota reservations, cooldowns, and aggregate model availability/latency; see retention details. Worker observability is disabled. Do not include credentials in prompts or ask the agent to read secret files.

The homepage publishes aggregate download and model-request totals. Downloads come from GitHub's public binary-asset counts (including updates and repeats). The request counter starts when enabled; it counts hosted chat requests accepted by the backend, including streams that may fail later. There are no analytics cookies, installation IDs, location analytics, or additional CLI telemetry calls. See how the public counters work.

Measured release sizes

From the published v0.7.3 assets, verified against SHA-256 checksums:

PlatformNative binary (uncompressed)Download (.tar.gz)
macOS ARM64638,592 bytes318,119 bytes
Linux x64844,576 bytes417,262 bytes
Linux ARM64790,784 bytes397,031 bytes

Build and test

cargo test --locked
cargo clippy --locked --all-targets -- -D warnings
cargo build --release --locked
python3 scripts/check-size.py target/release/bailout
python3 scripts/smoke.py target/release/bailout
python3 scripts/test-installer.py
(cd api && uv sync && uv run pytest)
(cd worker && npm ci && npm test && npm run build:site)
python3 bench/test_bench.py

Optional live file-write verification (uses shared free quota): python3 scripts/live-smoke.py target/release/bailout. For the recovery use case: python3 scripts/live-recovery.py target/release/bailout diagnoses a broken fixture agent, backs up its configuration, and verifies repair.

Three direct Rust dependencies: serde_json, libc, and rustyline. System curl handles TLS; Bash handles everything the model does. The API is FastAPI on Cloudflare Python Workers. A separate Worker serves the static landing page and forwards legacy API URLs through the budget gateway. The Python service is private; a SQLite Durable Object reserves capacity atomically before forwarding requests.

The smoke test drives a real controlling terminal. It verifies Ctrl-C as a keypress while editing, waiting on a model, running a child process, and at the empty prompt, plus history, multiline input, Auto routing, local interactive authentication without credential capture, shell handoff, uninstall, and recovery after interruption.

CI runs the tests and real binary smoke checks on all three supported platforms. Tagging vX.Y.Z builds native release assets, tests them, enforces the size ceiling, and publishes SHA-256 checksums. The installer pins one resolved release version, verifies the archive checksum, checks its contents and binary size, then installs atomically. Set BAILOUT_VERSION=v0.7.3 or BAILOUT_INSTALL_DIR=/your/bin to override. It prefers an existing writable PATH location and never uses sudo or modifies shell rc files.

Starting in v0.4.0, each launch checks the official GitHub stable release. A newer release is downloaded, SHA-256 verified, installed atomically, and restarted with the same arguments, working directory, input and backend setting. Failed checks keep the existing binary. Use bailout update to check manually or BAILOUT_NO_UPDATE=1 to disable automatic updates. Custom-backend builds manage their own updates. Older releases need the installer once to gain this feature.

Website and API

Worker and browser sources use strict TypeScript. npm run build:site in worker emits plain JavaScript and copies static assets to dist/site; Wrangler runs that build automatically for the website. The Rust binary and Python API need no JS runtime.

The site uses self-hosted Space Grotesk and IBM Plex Mono, with their OFL licenses included. No analytics, external fonts, or frontend framework. The visual language is inspired by neobrutalism.dev.

Host your own router

Use Python 3.13+, uv 0.12.3+, and Node (for Wrangler). Choose your own Worker name in api/wrangler.jsonc before deploying. Keep Python private. In worker/gateway.wrangler.jsonc, choose your gateway name and domain and point its API binding at your Python Worker. Keep workers.dev and preview URLs disabled. Bind the Python Worker’s CAPACITY namespace to BudgetGuard in that gateway. For a new deployment, first deploy the gateway with an empty services array and SERVICE_PAUSED=true so its Durable Object exists. Then deploy Python with the CAPACITY binding, restore the gateway’s API service binding, and unpause it. For an existing deployment, use the staged qualification rollout before switching the public backend. An empty registry fails closed.

The Python API needs Workers Paid on Cloudflare. Its JSON processing and streaming exceed the Free plan's 10 ms CPU allowance; requests can otherwise be terminated midway through a reply. Paid starts at $5/month plus usage. This is hosting cost, separate from the strict zero-cost model routing. See Cloudflare's current pricing and CPU limits. You can also run the same FastAPI app on an existing server with Uvicorn.

cd api
uv sync
uv run pywrangler login
uv run pywrangler deploy
uv run pywrangler secret put OPENROUTER_API_KEY

Enter the OpenRouter key at the secret prompt. Never put it in source or config. For edge development, place it in a gitignored .dev.vars file and run uv run pywrangler dev. For ordinary local FastAPI protocol development, run uv run uvicorn app:app --app-dir src --reload. Production qualification and quotas require the Durable Object binding; local mode has no qualified models by default. The API exposes /health, /v1/models, /v1/chat, /docs, and /openapi.json. Chat accepts {model, messages, stream} and optional Auto-only preferred_model and avoid_models session hints. With stream: true, it returns NDJSON model, text, and done events, or an error event. Only the done event contains a validated message that is safe to pass to the tool dispatcher. An additive retry: true field on a model event marks recovery or a preemptive switch; clients should display its notice and separate any partial text from the new response. The complete conversation message comes only from done, never concatenated text deltas.

Deploy the guard with npm ci && npx wrangler deploy --config gateway.wrangler.jsonc in worker. To deploy the site, point the service binding in worker/wrangler.jsonc at your gateway Worker, then run npm run deploy. Set your own domains in both configs. The public gateway additionally exposes /v1/status.

export BAILOUT_API_URL=https://api.your-domain.example
bailout

BAILOUT_DEFAULT_API at compile time changes the binary's built-in backend URL. Runtime API overrides require HTTPS, except localhost for development. The public service intentionally requires no login, so use your own Worker and key if you need a separate quota. Cloudflare hosting costs and limits are separate from model prices.

MIT licensed. Inspired by Pi and fx.

Contributors

storozhenko98

74 commits

Languages

Python

55.0%

TypeScript

23.0%

Rust

12.6%

HTML

8.5%