Alpha release — designed for self-hosters and tinkerers who want to test this early. Not yet recommended for production use.
Subumbra is a security proxy that sits between your apps (like LiteLLM, OpenWebUI, AnythingLLM, n8n, etc.) and providers (like OpenAI or Anthropic). Instead of pasting your API keys directly into each app — where they can be leaked in logs, config files, or breaches — Subumbra holds them encrypted and hands them out only to apps you explicitly authorize, one request at a time.
In plain terms: your apps never see your real API keys. They talk to Subumbra, Subumbra talks to OpenAI (or whoever), and your keys stay locked away.
→ How it works under the hood · Planned features
You'll need:
- A Linux server (VPS or homelab) with Docker installed. Anything with Docker should work, but I have only tested on Ubuntu 24.04 LTS.
- A Cloudflare account with the Workers Paid plan ($5/month) — this is where your keys are held encrypted. It may work with a free account, but this is not guaranteed.
- A Cloudflare API token (created at dash.cloudflare.com/profile/api-tokens — use "Edit Cloudflare Workers" template, add
Workers KV Storage: Edit) - Your Cloudflare Account ID (visible in the URL when you're logged into Cloudflare or after creating your API token)
- At least one provider API key (e.g. an OpenAI key starting with
sk-...)
Don't have Docker yet? Follow the full install guide first.
The example below uses /opt/subumbra — you can use any path you like, just replace it consistently:
git clone https://github.com/polysemic/Subumbra.git /opt/subumbra
cd /opt/subumbraIf
/optis restricted on your system, you may needsudo mkdir -p /opt/subumbra && sudo chown -R "$USER":"$USER" /opt/subumbrafirst, then run the clone.
This lets your Dockerized apps talk to Subumbra by name. Run this once:
docker network create subumbra-netSubumbra reads a file called subumbra.yaml to know which providers you want to use. This file is never committed to git — it lives only on your server.
Start from the minimal template, which includes all supported providers (just comment out the ones you don't use):
cp subumbra.minimal.yaml subumbra.yamlNow open subumbra.yaml in a text editor and:
- Delete or comment out providers you don't have keys for (put a
#at the start of any line to disable it) - Set
adaptersto the names of your apps — for example[litellm, openwebui]or[universal]to use one adapter token for every app. This is to only allow certain apps to use certain keys.
The secret_ref values (like OPENAI_KEY, ANTHROPIC_KEY) are just labels — you'll enter the actual key values in the next step, not here.
Want full control over policies or custom providers? Use
cp subumbra.example.yaml subumbra.yamlinstead. That file documents every available option. See docs/provider-templates.md.
Optional automation: You can also use an automation file. Copy
.env.bootstrap.exampleto.env.bootstrap, fill in your keys and Cloudflare credentials, then run./bootstrap.sh. The file is automatically deleted after a successful run. Thesecret_refvalues fromsubumbra.yamlmust match the environment variable names in.env.bootstrap.
./bootstrap.shThe wizard will ask you for:
- Your Cloudflare API token — paste it in (this isn't stored anywhere and is only used for the initial setup and to update the worker)
- Your Cloudflare Account ID — paste it in (this isn't stored anywhere and is only used for the initial setup and to update the worker)
- A Worker name — just press Enter to use the default (
subumbra-proxy) - Your API keys — for each provider in your
subumbra.yaml, it will ask for the key
Automated Alternative: If you filled .env.bootstrap, all prompts will be skipped and the wizard will use the values from the file.
That's it. The wizard automatically:
- Deploys a Cloudflare Worker (your encrypted key vault lives here)
- Generates a fresh RSA key pair — the private key is generated inside Cloudflare and never touches your server
- Encrypts your API keys and stores them
- Writes all the access tokens your apps will need into
.env - Starts the Subumbra services
# Check all three services are up
docker compose ps
# Check the proxy is healthy and connected to Cloudflare
curl -sS http://127.0.0.1:10199/healthYou should see something like:
{"status": "ok", "worker_auth": "ok"}If worker_auth says ok, you're live. 🎉
After bootstrap, your .env file contains tokens for each app. Check them:
grep SUBUMBRA_TOKEN .envYou'll see lines like:
SUBUMBRA_TOKEN_LITELLM=3fbe4c3f...
SUBUMBRA_TOKEN_OPENWEBUI=19d1262d...
Each app gets its own token (so you can revoke one without affecting others) and points to Subumbra instead of directly to OpenAI:
| Setting | Value |
|---|---|
api_base / base URL |
http://subumbra-proxy:8090/t/<key_id>/... (from inside Docker) |
api_base / base URL |
http://127.0.0.1:10199/t/<key_id>/... (from the host or outside Docker) |
api_key |
Your app's adapter token (e.g. SUBUMBRA_TOKEN_LITELLM from .env) |
Where <key_id> is the identifier from your subumbra.yaml — for example openai_prod, anthropic_prod.
App-specific setup guides:
- LiteLLM: docs/apps/litellm/install.md
- OpenWebUI: docs/apps/openwebui/install.md
- AnythingLLM: docs/apps/anythingllm/install.md
- Bifrost / LibreChat / n8n: docs/apps/
Here's what Subumbra actually protects and what it doesn't:
| What it protects | How |
|---|---|
| API keys at rest on your server | Keys are encrypted immediately and only stored as ciphertext |
| API keys in app configs | Apps only ever see a short-lived proxy token, not your real key |
| Per-app access control | Each app has its own token — revoke one without touching others |
| Policy enforcement | You define which paths and methods each key is allowed to serve |
| What it does not protect | Notes |
|---|---|
| Cloudflare itself | The private key lives in Cloudflare — Cloudflare is in the trust boundary |
| Your server if fully compromised | An attacker with root on your server can read running container memory |
| Billing/rate limits | Subumbra doesn't cap spend — set limits at the provider level |
A read-only dashboard is available at http://127.0.0.1:6563 showing your active keys, usage stats, and audit log.
To access it:
| Setup | Configuration |
|---|---|
| Cloudflare Tunnel + Access (recommended for remote access) | Leave UI_USERNAME and UI_PASSWORD unset in .env |
| Simple password on localhost | Set UI_USERNAME and UI_PASSWORD in .env, then docker compose up -d --force-recreate |
subumbra/
├── docker-compose.yml ← starts the three local services
├── .env.example ← template for optional config
├── .env.bootstrap.example ← template for automation bootstrap
├── subumbra.minimal.yaml ← copy this to subumbra.yaml to get started
├── subumbra.example.yaml ← full reference with every option documented
├── bootstrap/ ← setup wizard and encryption logic
├── subumbra-keys/ ← stores encrypted key records locally
├── subumbra-proxy/ ← the transparent proxy your apps talk to
├── ui/ ← the read-only dashboard
├── worker/ ← the Cloudflare Worker (key vault + decrypt)
└── docs/ ← all documentation
- Full install guide (Docker from scratch)
- Provider templates reference
- Integration recipes (curl examples per provider)
- Operator guide (day-2 operations, recovery)
- Architecture deep-dive
- Developer / council guide
This project handles sensitive credentials (API keys, tokens, and certificates).
- Do not commit
.envfiles or any files containing secrets to version control. - Always back up your secrets in a secure location before running bootstrap scripts.
- Understand the risks before using this software in production.
For detailed security guidance, see:
This project was built with AI coding tools ($20/mo plans). I am not a security expert, a coder, or a software engineer. I am not great at git or github, so the development history, branches, and releases may be a bit clunky. I like to build and tinker with things; this is a project I built for myself to try and solve a problem I kept reading about almost every day. If you find it useful, feel free to use it. If you do not, that is fine too.