OpenClaw + SpendGuard (base-URL recipe)
OpenClaw supports custom provider entries under models.providers. D40a uses
that surface as a base-URL recipe: OpenClaw sends OpenAI-compatible chat
traffic to the SpendGuard egress proxy, and SpendGuard enforces the budget in
the egress proxy and sidecar before the provider call.
D40a is a configuration recipe. SpendGuard enforcement happens in the egress proxy and sidecar before the provider call. OpenClaw is not modified, and no OpenClaw plugin is installed.
Prerequisites
Section titled “Prerequisites”- OpenClaw with file-based config available. D40a pinned the upstream package
metadata at
openclaw/openclaw@d4819948f37d45fe8f1428401316eaae456cdf16and npm packageopenclawversion2026.6.2. - A running SpendGuard egress proxy reachable from the OpenClaw process.
Local host:
http://localhost:9000/v1. Docker network:http://egress-proxy:9000/v1. - A provider credential configured on the SpendGuard proxy path. The
OpenClaw-side
apiKeyonly needs to be non-empty for the local proxy route.
Configure the custom provider
Section titled “Configure the custom provider”Add a custom provider under models.providers. The trailing /v1 is
mandatory.
{ "agents": { "defaults": { "model": { "primary": "spendguard/gpt-4o-mini" }, "models": { "spendguard/gpt-4o-mini": { "alias": "SpendGuard-gated GPT-4o mini" } } } }, "models": { "mode": "merge", "providers": { "spendguard": { "baseUrl": "http://localhost:9000/v1", "apiKey": "non-secret-placeholder-do-not-use-real-key", "api": "openai-completions", "timeoutSeconds": 120, "models": [ { "id": "gpt-4o-mini", "name": "GPT-4o mini via SpendGuard", "reasoning": false, "input": ["text"], "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }, "contextWindow": 128000, "maxTokens": 8192 } ] } } }}The apiKey value above is a non-secret placeholder used only to satisfy
OpenClaw’s non-empty provider-field requirement. Do not put a real upstream
provider key in the OpenClaw config for local smoke tests or production use of
this recipe; the SpendGuard egress proxy owns upstream credentials and
substitutes the outbound authorization header.
Set the provider id and model id to match your upstream. The OpenClaw model
ref is provider/model, so the example primary model is
spendguard/gpt-4o-mini.
Verify locally
Section titled “Verify locally”make demo-downmake demo-up DEMO_MODE=openclaw_base_urlmake -C deploy/demo demo-verify-openclaw-base-urlThe demo is local and keyless. It validates the committed OpenClaw config
fixture, routes requests through http://egress-proxy:9000/v1, points the
SpendGuard proxy upstream at a counting stub, and proves three flows:
| Step | Gate | |---|---| | ALLOW | Provider counter increments once and ledger records reserve + commit. | | DENY | SpendGuard returns a block before provider dispatch; provider counter is unchanged. | | STREAM | SSE frames include usage and SpendGuard commits after stream close. |
Successful runner line:
[demo] openclaw_base_url ALL 3 steps PASS (ALLOW + DENY + STREAM)Full example files live in
examples/openclaw-base-url/.
Deployment notes
Section titled “Deployment notes”Local desktop
Section titled “Local desktop”Use http://localhost:9000/v1 when OpenClaw and SpendGuard run on the same
host.
Docker compose
Section titled “Docker compose”Use the service DNS name reachable from the OpenClaw container, for example:
{ "models": { "providers": { "spendguard": { "baseUrl": "http://spendguard-egress-proxy:9000/v1" } } }}Hosted proxy
Section titled “Hosted proxy”Use the public HTTPS endpoint for your SpendGuard egress proxy:
{ "models": { "providers": { "spendguard": { "baseUrl": "https://spendguard.example.com/v1" } } }}Gotchas
Section titled “Gotchas”- The trailing
/v1is mandatory. OpenClaw’s OpenAI-compatible adapter appends the chat-completions path behind the configured base URL. - Use
api: "openai-completions". D40a covers OpenAI-compatible chat completions. Anthropic-compatible custom providers are a separate path. - The OpenClaw-side key is not the upstream key. SpendGuard receives the inbound authorization header and uses its own provider credential path.
- This is not plugin-level coverage. D40b owns the OpenClaw provider plugin package and in-process adapter work.
- Private-network trust follows OpenClaw’s provider policy. Treat the
configured
baseUrlorigin as the trusted SpendGuard proxy endpoint and consult OpenClaw’s provider policy before allowing other private destinations.
What works today
Section titled “What works today”| Surface | Status |
|---|---|
| Custom provider baseUrl to SpendGuard egress proxy | Verified by D40a fixture demo |
| Non-streaming OpenAI-compatible chat | Verified by D40a |
| Streaming SSE chat completions | Verified by D40a |
| Deny-before-provider dispatch | Verified by D40a counting-stub gate |
| OpenClaw provider plugin | D40b, not D40a |
| Non-OpenAI-compatible provider APIs | Out of scope |
What next
Section titled “What next”Maintainer docs: OpenClaw model providers