Docs / Quickstart

Bridgekeeper is self-hosted: you run the proxy image, with your own database, your own LLM keys, and your own master key. At onboarding you receive a service-account key (for the image pull + signed-library reads) and the exact image reference. The rest is configuration.

1. Authenticate and pull

Log Docker into the registry with the JSON key you were issued, then pull the image reference from your onboarding email.

cat your-key.json | docker login -u _json_key --password-stdin https://us-central1-docker.pkg.dev
docker pull <image-ref-from-onboarding>

2. Configure

Bridgekeeper does not manage these — you provide them via environment variables:

VariablePurpose
LITELLM_MASTER_KEYYour admin key for minting per-end-user virtual keys (you generate it).
DATABASE_URLYour Postgres for virtual-key storage.
ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEYAt least one upstream provider key you want to use.
OLLAMA_BASE_URLOptional — point at your Ollama host to use local open-source models.
GOOGLE_APPLICATION_CREDENTIALSPath to the mounted service-account JSON (the signed-library poller's identity).
BRIDGEKEEPER_POLL_INTERVAL_SECONDSHow often to check for a new signed protection bundle (default 300).

3. Run it in front of your proxy

docker run -d --name guardllm-proxy -p 4000:4000 \
  -v "$PWD/your-key.json:/secrets/sa-key.json:ro" \
  -e GOOGLE_APPLICATION_CREDENTIALS=/secrets/sa-key.json \
  -e LITELLM_MASTER_KEY="sk-admin-..." \
  -e DATABASE_URL="postgresql://litellm:PW@host:5432/litellm" \
  -e ANTHROPIC_API_KEY="sk-ant-..." \
  <image-ref-from-onboarding>

Where it attaches

Bridgekeeper attaches at the pre/post-call seam. Today it ships first-class for LiteLLM and OpenRouter (gateways) and any OpenAI-compatible model server you front (Ollama, vLLM, LM Studio, TGI, llama.cpp). Portkey, Kong AI Gateway, Helicone, and framework wrappers (LangChain, LlamaIndex) are rolling out.

4. Smoke-test

curl http://localhost:4000/health/liveliness                       # -> "I'm alive!"
curl -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  http://localhost:4000/guardllm/library-info | jq .                 # current signed policy version
curl http://localhost:4000/v1/chat/completions \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}],"max_tokens":20}'

Policy fields

Outbound DLP behavior is governed by the signed policy and overridable via PolicyConfig:

Logging schema

The handler emits structured [GuardLLM] log lines. The outbound decision records the channel checked, the verdict, the reason, and signals:

[GuardLLM] inbound sanitized; warnings=[]
[GuardLLM] inbound wrapped content: '<untrusted_content source="..." trust="untrusted"> ... </untrusted_content>'
[GuardLLM] async_post_call_success_hook fired
[GuardLLM] outbound[content] checked; allowed=False; reason=Verbatim overlap (13 chars) with ingested sensitive content; secrets_found=[]; overlap_pct=0.0; provenance_blocked=False
[GuardLLM] library v2 loaded (sha=617a817156…); policy hot-swapped

Air-gapped update mode

The protection feed is a signed bundle: the in-process poller fetches it, verifies its SHA + ECDSA signature against the public key baked into the image, and hot-swaps the policy — rules flow in; nothing about your traffic flows out. For fully air-gapped operation, mirror the signed bundle into an internal location the container can reach and point the poller at it; verification is offline against the embedded public key, so no internet egress is required.

Failure modes

False-positive behavior

Lowering the overlap threshold catches shorter sensitive strings but raises the chance of over-triggering on benign text that happens to share a substring with your system prompt. Tune dlp_sensitive_lcs_min to your content. A measured false-positive rate against a benign corpus is in progress and will be published here when available.