Plugin Store
Everything you can plug into Busbar.
Busbar ships small and complete: a memory store, token auth, and env/file secrets in the box. Everything heavier is something you add without recompiling the core: where state lives (stores), how requests are trusted (auth), where a config secret value resolves from (secrets), your own code on the request path (hooks), and where telemetry goes (exporters). As of busbar 1.5.0 that is a working system, not a roadmap: a plugin is one signed archive you download, drop in a directory, and the binary you already run verifies and loads it. How it works is below the grid. Browse them, and vote for what you want built next.
GitHub
Developers sign in with GitHub for a budgeted key.
Developers self-serve their own budgeted busbar key by signing in with GitHub. No portal to build. busbar hosts token-exchange itself: a dev logs in with GitHub at /auth/token and gets back a static, self-scoped key for their AI tools (Cursor, Claude Code, VSCode BYOK), with their own budget, tracked per dev. Org membership becomes groups, so team access follows GitHub.
HashiCorp Vault
Resolve secrets from Vault KV v2.
Read config secret values (provider API keys, database passwords) straight out of a HashiCorp Vault KV v2 mount instead of environment variables or files on disk.
Headroom
Context compression, on the path.
A rewrite gate that trims and compacts a request’s context before it ships, so a long conversation stays under the window without your app rebuilding the prompt. Written once, it works against every protocol and provider Busbar speaks. Measured: about 50% fewer input tokens on noisy histories for sub-millisecond overhead; short chats pass through untouched.
Powered by the open-source Headroom project.
LDAP / Active Directory
Directory username + password → a budgeted key.
On-prem LDAP / Active Directory as the way developers get a busbar key: they sign in with their directory username and password, busbar binds against your directory, reads their group memberships, and hands back a static, self-scoped key with its own budget, tracked per dev. For shops with no cloud identity provider, this runs against the directory you already have.
MySQL
Shared state on the MySQL/MariaDB you already run.
A durable, cluster-shared store for the MySQL, MariaDB, or Aurora MySQL you already operate: the same shared-state story as Postgres and Valkey, on the MySQL-protocol database many fleets already run.
OIDC
Developers self-serve a budgeted key via SSO.
Developers self-serve their own budgeted API key by signing in with your IdP; no portal to build. busbar hosts token-exchange itself: a dev logs in via SSO (or presents an OIDC token they already hold) and gets back a static, self-scoped key for their AI tools (Cursor, Claude Code, VSCode BYOK), with their own budget, tracked per dev.
Postgres
Shared, durable, multi-node.
One Postgres behind a cluster of Busbar nodes: virtual keys, usage, and audit shared across the fleet. Every node enforces from memory on the hot path and reconciles its spend into the shared record behind it.
SQLite
Durable local persistence, one file.
Keys, budgets, and usage survive restarts in a single SQLite file, no external service to run. Write-behind by design: enforcement stays in memory on the hot path, and SQLite is the durable mirror flushed behind it. The single-node durability answer.
Valkey
Shared state on the Valkey you already run.
Point Busbar’s governance at the Valkey you already operate: virtual keys, usage, and audit shared across nodes without standing up a database. Same discipline as the other stores: enforcement stays in memory on the hot path, Valkey is the shared record behind it.
Webrequest
Run your hook logic out-of-process.
A transparent forwarder that POSTs each hook op envelope to an operator-configured HTTPS URL and returns the reply verbatim: a signed, trusted relay to your own out-of-process gate logic. SSRF-guarded, redirects disabled, tight timeouts.
Env / File
The zero-setup default.
The default secret resolution: read a value from an environment variable or a file on disk. Zero dependencies, in the core. Point any config field at { env: VAR_NAME } or { file: /path }.
Built into the core: the default secret resolution.
Memory
RAM store, zero setup, ephemeral.
The default. Virtual keys, budgets, rate limits, and usage all live in memory, nothing to install, nothing to provision. Ideal for a single node, a dev box, or a stateless tier where durability lives elsewhere. A restart clears it; that’s the trade for zero moving parts.
Built into the core: the default when no store is configured.
OTLP traces
Ship spans to your OpenTelemetry collector.
One instance installs an OTLP/HTTP trace exporter that ships spans straight to the collector you already run. SSRF-guarded, credentials moved out of the URL into an Authorization header before anything is logged, and traces flushed on graceful shutdown.
Built into the core: configured under export:, never downloaded.
Prometheus
Scrape /metrics, bounded cardinality.
Name a `prometheus` exporter and Busbar installs the recorder and mounts GET /metrics for your scraper: breaker state per (pool, lane), failover counts, fault classes, latency, and per-key spend. Leave it out and there is no recorder, no route, and every emit site is a genuine no-op.
Built into the core: configured under export:, never downloaded.
Request-log file
Append every request line as JSONL.
Appends the same per-request JSON line to a file on disk, one object per line, for the agent that already tails your logs. Optional size-based rotation. Bounded the same way the webhook sink is: on a full disk or a hung mount, lines are shed and counted, never queued without limit.
Built into the core: configured under export:, never downloaded.
Request-log webhook
POST every request line to a URL you run.
Pushes one JSON line per finished request to an HTTPS endpoint you run. Name it twice with two URLs and you get two independent sinks (app logs and a SIEM), each with its own auth header, timeout, and in-flight cap. Fire-and-forget: a slow or dead collector never touches the request path.
Built into the core: configured under export:, never downloaded.
Tokens
Static bearer tokens, constant-time.
The default client auth: a constant-time allowlist of bearer tokens, checked on every request across both header and bearer carriers. Zero dependencies, in the core. Pair it with the admin token that guards the management API.
Built into the core: the default request auth.
Smart Router
Route by cost, latency, and live load.
A worked example, not a finished product: a routing gate that picks the backend per request from real signals: each member’s cost, latency, live concurrency, and rate headroom. It’s a template to read, fork, and shape into the router you actually want.
Read the write-up: the smart router you want is a hook, a pre-1.3 post, so its route: / policy: / target: config is historical; the current shape is a named hooks: entry (module: smart-router-hook, kind: gate, on_error: weighted) attached by name. See Hooks.
Your auth
Trust requests your own way?
HMAC-signed requests, a bespoke token service, a SASO scheme of your own: the auth contract is small and stackable. Tell us how you need requests trusted and we’ll help you wire it in.
Contact us, or open a PR against the repo.
Your hook
Built something worth sharing?
A guardrail, a router, a cost meter, an audit sink: anything that runs on the path. Tell us what it does and where it lives, and we’ll list it here and link straight to your repo.
Contact us, or open a PR against the repo.
Your secret backend
Have a secrets manager Busbar should speak?
AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, 1Password: the secret contract is one method, resolve a reference to bytes. Tell us what you need and we’ll help you build it.
Contact us, or open a PR against the repo.
Your store
Have a backend Busbar should speak?
The store is a small, sync contract: keys, budgets, usage, metering. If you want Busbar’s state in DynamoDB, Mongo, or your own service, the contract is the whole job. Tell us what you need and we’ll help you build it.
Contact us, or open a PR against the repo.
How a plugin loads
A plugin is a plugin. Stores, identity providers, secret modules, and hooks share one format, one
loader, and one trust model; the kind field in the manifest is the only thing that
tells them apart. Distribution is one signed .tar.gz per plugin and architecture: download it,
drop it in plugins/, done. Busbar unpacks and verifies the archive entirely in
memory (on Linux the library loads from an anonymous memfd, so nothing ever touches disk), and
the bytes that were verified are exactly the bytes that load.
It is safe by default. The loader is off until you set plugins.enabled: true, so a
tarball dropped next to a gateway that hasn't opted in is inert. Busbar's own plugins verify
against a release key embedded in the binary: trusted with zero configuration. Unsigned or
third-party plugins are logged and skipped unless you explicitly allow them
(allow_unsigned, allow_third_party, and a per-publisher key
allowlist), and version floors stop an old plugin from being loaded beside a newer binary.
And it is inspectable before it carries traffic. busbar --list-plugins prints every
plugin in the directory with its signature state and exactly why it will or won't load, without
executing any of them. busbar --validate checks the config and every plugin
manifest through the same path a real boot uses: if it passes, boot succeeds; if anything is
wrong (a bad config, a malformed manifest, two plugins claiming one name), Busbar names it and
refuses to start rather than booting degraded.
The point of the whole design: the default binary stays lean (~12 MB, with the memory store and token auth built in), and durable governance (SQLite, Postgres, Valkey, and MySQL) is a download you drop in, not a recompile.
Exporters are configured, not downloaded
One honest exception, so nothing on this page over-promises: the exporters above
ship inside the binary. You turn one on by naming an instance under the top-level
export: map (<name>: { module: …, settings: … }, the same
named-definition shape hooks: and identity-providers: use), and there is
no tarball to fetch, no signature to trust, and no plugins.enabled to flip. Name none
and Busbar emits nothing: no recorder, no /metrics route, no tracer, no request-log
payload built at all.
Each instance also declares its own projection: streams: names what
that sink is entitled to, drawn from a frozen vocabulary of
metrics, logs, traces, costs,
decisions, events, identity, prompts, and
completions. It is not a filter applied on the way out: Busbar builds each payload
to the projection, so a stream an instance did not subscribe to is never serialized and
never crosses the plugin boundary. Subscribing to something this release cannot deliver, a stream
with no producer yet or one the module cannot carry, is a boot error naming the instance rather
than a sink that validates and quietly receives nothing. See
the projection.
export is nonetheless a real plugin kind alongside store,
secret, auth, and hook: the same neutral C symbols, plus two
operations of its own (streams, asked once at load, and deliver). What has
not shipped is the config seam, so module: takes the built-in names only and today the
exporters are the set you see here. The export docs cover every module,
its settings, and the observability: / metrics: migration.
Hooks or exporters?
Two of the kinds above put your own logic in the request's path, and it's easy to reach for the wrong one. The line is simple: hooks can stop the request. Exporters tell you what happened.
| Hooks | Exporters | |
|---|---|---|
| Timing | Real time, mid-request | After the fact, delayed by milliseconds or seconds |
| Power | Preventative and modifying: can block, reject, restrict, rewrite | Never preventative; observe and emit only |
| Purpose | Change what happens | Record what happened |
| Scope | Bound to pools, fire at declared phases | Engine-wide |
| Sees | Requests that reach them | Everything the engine knows, including events that are not requests |
| What you want | Which kind | Why |
|---|---|---|
| Prompt compression | Hook | must happen before the request leaves |
| PII redaction | Hook | the entire point is that the PII does not go |
| Alert when someone sends credentials to a model | Hook | an exporter alerting 200 ms later is too late. The credential has already reached the provider. Alerting without the power to stop is not a control, it is notification of a breach. |
| A record of what everyone did | Exporter | after the fact, must be complete, must never block a request |
| Dashboards, metrics, traces | Exporter | observation, never on the critical path |
Worth stating plainly: an auditor written as a hook is structurally incomplete. Hooks fire at request phases and are bound to pools, so a hook-based auditor cannot see requests refused before it ran (admission shedding, auth failure at the door, budget exhaustion), admin/config changes, or any pool it is not bound to. "Show me every attempt, including the denied ones" is a question only an exporter can answer.
New to the request path? Start with the hooks docs for the config shape, the tap/gate lifecycle, and how a hook falls back safely without ever blocking a request. Stores and auth are set in configuration; telemetry egress, and the data boundary an exporter never crosses, is export.