Skip to main content
Version: 1.8.0

Govern Claude Code with DVARA

Claude Code sends every model call to Anthropic directly. Your security team sees none of it: no policy, no PII check, no budget, no audit record. From 1.8.5 DVARA serves the Anthropic Messages API at POST /v1/messages, so you can point Claude Code at the DVARA AI governance platform. Every model call it makes is then governed and billed as one DVARA workspace.

/v1/messages is in DVARA Open Source and the Enterprise platform from 1.8.5. It runs without a licence key. On the Enterprise platform, workspace budgets, IP access rules and the Flightdeck views apply to it too.

Point Claude Code at DVARA​

You need three things:

  • A running DVARA 1.8.5 Gateway that Claude Code can reach.
  • An Anthropic provider on that Gateway (ANTHROPIC_API_KEY set on the Gateway), or a route for each model id Claude Code asks for.
  • A DVARA workspace API key. On the Enterprise platform, create it in Flightdeck. In DVARA Open Source, generate it with --generate-key. If the key has scopes, it needs completions:write.

Set these where Claude Code runs. Replace <dvara-workspace-key> with your DVARA key, not an Anthropic key:

export ANTHROPIC_BASE_URL=https://gateway.acme.example.com # the Gateway, no /v1
export ANTHROPIC_API_KEY=<dvara-workspace-key> # sent as x-api-key
# or: export ANTHROPIC_AUTH_TOKEN=<dvara-workspace-key> # sent as Authorization: Bearer
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 # optional: no telemetry or update calls
  • ANTHROPIC_BASE_URL has no /v1. Claude Code adds /v1/messages itself.
  • Either key variable works. DVARA reads the key from Authorization: Bearer or, when that header is absent, from x-api-key. It is the same key either way and has the same rights. With ANTHROPIC_API_KEY, an interactive session asks once to approve the key.
  • The DVARA key wins over a saved login. Set it even if Claude Code is signed in to a Claude subscription. See What DVARA does not govern.

Then start Claude Code as usual and ask it something.

Make sure the model ids route​

Claude Code asks for Anthropic model ids. It also asks for a small background model. Each id must be one the Gateway can route: an id served by its Anthropic provider (any model that starts with claude), or an id with its own route. If not, the call is refused with 400 no_provider.

Pin the ids Claude Code uses to ones your Gateway serves:

export ANTHROPIC_MODEL=<main-model-id>
export ANTHROPIC_DEFAULT_OPUS_MODEL=<opus-model-id>
export ANTHROPIC_DEFAULT_SONNET_MODEL=<sonnet-model-id>
export ANTHROPIC_DEFAULT_HAIKU_MODEL=<haiku-model-id> # the background model
Known issue in 1.8.5: large Claude Sonnet 5.5 and Opus 5.5 requests

The context window check uses one limit per provider, 200,000 tokens for Anthropic, not the model's own window. With the default hard threshold of 90%, a request over about 180,000 estimated tokens is refused with 400 context_window_exceeded, or trimmed if the workspace sets a pruning strategy. Long Claude Code sessions can reach this. Partial workaround: set the workspace's context Hard Threshold to 100%. That serves requests up to 200,000 tokens and no further. 1.8.6 fixes it. See Releases and upgrading.

What happens to a request on an Anthropic route?​

On a route to Anthropic, DVARA sends the request on as Claude Code sent it: every field, message role, content block and the anthropic-beta header. It changes the request only where governance changes it, such as redacted text or the routed model. Anthropic's reply, plain or streamed, comes back the same way.

  • Extended thinking works. The thinking settings and signed thinking blocks pass through unchanged. Thinking is output, so PII rules and guardrails read it.
  • anthropic-beta passes through. DVARA also writes it to the GATEWAY_RESPONSE audit record as anthropic_beta.
  • Usage and cost come from Anthropic's own usage block, including cache reads, cache writes and reasoning tokens.
  • A content block type DVARA does not read is passed on and counted in gateway_anthropic_opaque_blocks_total.

On a route to another provider, DVARA translates the request. A request with extended thinking is refused with 400 unsupported_capability, and the message names the provider. Fields that only tune Anthropic, such as cache_control or a field DVARA does not model, are left out and counted in gateway_anthropic_fields_dropped_total. If your route sends Claude Code to another provider, turn thinking off with CLAUDE_CODE_DISABLE_THINKING=1. Some of the newest models do not allow that.

On every route, DVARA refuses with 400 unsupported_capability anything the provider would act on outside the Gateway: mcp_servers, container, server tools such as web search or code execution, and metadata keys other than user_id.

Send a test call​

You can call the endpoint yourself to check the setup. The anthropic-version: 2023-06-01 header is required, and so is max_tokens:

curl -s https://gateway.acme.example.com/v1/messages \
-H "x-api-key: <dvara-workspace-key>" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "<sonnet-model-id>",
"max_tokens": 200,
"messages": [{"role": "user", "content": "Reply with one word: ready."}]
}'

A successful call returns Anthropic's message shape:

{
"id": "msg_01H8kQ3nV2pX7rT4yW9bZ6cD",
"type": "message",
"role": "assistant",
"model": "<sonnet-model-id>",
"content": [{"type": "text", "text": "Ready."}],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {"input_tokens": 14, "output_tokens": 4}
}

The response carries an X-Trace-ID header. Look it up in Flightdeck's audit view: the call has a GATEWAY_RESPONSE record, and its tokens and cost show on the Token Usage and Cost pages under the key's workspace.

To count tokens without calling a model, send the same body to POST /v1/messages/count_tokens. It answers {"input_tokens": 14}. On an Anthropic route the number is Anthropic's count; otherwise it is DVARA's estimate. A count is not billed, books no usage and is not counted as a call against the unlicensed call band. It still runs workspace status, policy, prompt templates, PII and guardrails, and the rate limit counts it as a request.

What does a refusal look like?​

On /v1/messages and /v1/messages/count_tokens, every refusal comes back in Anthropic's error envelope, so Claude Code can show it. The status and the code are the same as on /v1/chat/completions. A wrong key:

{
"type": "error",
"error": {
"type": "authentication_error",
"message": "Invalid API key.",
"code": "invalid_api_key"
},
"request_id": "a3f1c9e2-5b7d-4e8a-9c1f-2d6b8e4a7f30"
}
StatusCodeWhat it meansWhat to do
400unsupported_api_versionThe anthropic-version header is missing or is not 2023-06-01Send anthropic-version: 2023-06-01. Claude Code does.
400no_providerNo provider or route serves the model idPin the model variables above, or add a route
400unsupported_capabilityThinking on a non-Anthropic route, or a field DVARA refuses on every routeTurn thinking off, or route the model to Anthropic
400context_window_exceededThe request is over the provider's context limitSee the known issue above
401invalid_api_keyThe key is not a DVARA key, or a Claude subscription credential reached DVARASet a DVARA workspace key
403api_key_scopeThe key has scopes and lacks completions:writeAdd the scope, or use another key
403session_killedOn the Enterprise platform, someone killed this agent sessionStart a new session

Policy, PII BLOCK, guardrail and budget refusals use the codes in Error handling.

What does DVARA govern for Claude Code?​

DVARA governs the model calls. Each one runs the same controls as /v1/chat/completions: policy, PII rules, guardrails (on the system text too), budgets, rate limits, call caps, cost records and the signed audit chain. On the Enterprise platform, a workspace's IP allowlist or denylist applies to a call that sends its key in x-api-key, as it does for a bearer key.

PII detection fires on every Claude Code call

Claude Code's own prompt carries an email address, so the email detector matches on every request. With the default PII action, LOG, that is an audit entry and nothing else. A workspace whose action is REDACT or TOKENIZE edits Claude Code's own prompt before it reaches the model. BLOCK refuses every call. For a workspace that runs Claude Code, keep the email action at LOG, or expect that edit. See PII detection.

What does DVARA not govern?​

  • Claude Code's local actions. File edits, shell commands and git run on the developer's machine and never reach DVARA.
  • MCP servers Claude Code calls directly. Point them at the DVARA MCP Gateway to govern them.
  • A Claude subscription. Claude Code signed in to a Claude subscription, with no DVARA key set, uses its own credential. DVARA cannot govern that use; if that credential reaches the Gateway, it is refused with 401. Set a DVARA key as shown above.
  • Claude Code on Amazon Bedrock or Google Vertex AI. With CLAUDE_CODE_USE_BEDROCK or CLAUDE_CODE_USE_VERTEX, Claude Code does not use /v1/messages, so it cannot be pointed at DVARA.

Where to go next​