Skip to content

feat(zdr): support zero data retention via CMD_ZDR and provider headers - #194

Open
mohemohe wants to merge 1 commit into
BrainerVirus:mainfrom
mohemohe:feature/zdr
Open

mohemohe wants to merge 1 commit into
BrainerVirus:mainfrom
mohemohe:feature/zdr

Conversation

@mohemohe

Copy link
Copy Markdown

Summary

Adds opt-in zero data retention (ZDR) for the Command Code provider: with CMD_ZDR=1 (or zdr: true in the plugin
config) every request carries x-cmd-zdr: 1, the same opt-in the Command Code CLI exposes.

Reference: Provider API — Zero data retention

The plugin talks to the Provider API directly (/provider/v1/...), so the CLI-only CMD_ZDR variable had no effect here before. The header routes the request through ZDR-capable upstreams only and fails with 422 cmd_zdr_no_providers instead of falling back to a non-ZDR provider, so it is worth making it explicitly configurable.

Changes

  • src/zdr.ts (new) — single implementation of the ZDR decision and header
    • CMD_ZDR counts as on for 1/true/yes/on (trimmed, case-insensitive)
    • zdrEnabled() = config zdr: true OR the env var
    • withZdrHeaders() merges x-cmd-zdr: 1 without discarding existing headers, and never creates a headers key
      when ZDR is off
  • plugin.ts
    • V1 server(): writes the header into provider.commandcode.options.headers. V1 merges provider options into
      every model of the provider, whichever provider.npm a model overrides, so one map reaches the Chat Completions,
      Messages, and Responses routes. Custom npm transports keep owning their options and are left untouched
    • V2 setup(): writes the header into the registered provider's info.headers on add, and merges it into the
      existing provider on update
  • index.ts — the createCommandCode SDK factory honors CMD_ZDR too (an explicit headers value wins)
  • src/schemas.ts — PluginFileConfigSchema gains zdr: z.boolean().default(false)
  • Docs — README.md (config table + ZDR section) and docs/specs/2026-10-14-zdr-request-header.md (new spec)
  • Tests — tests/unit/zdr.test.ts (new) plus V1/V2/SDK/schema unit additions, and
    tests/integration/zdr.integration.test.ts (new, live)

Behavior

  • Precedence: an explicit user value always wins ("x-cmd-zdr": "0" stays as-is), and other headers in the same map
    survive untouched. With ZDR off the generated config is identical to before
  • API behavior: routing goes through ZDR-capable upstreams only; a model without one fails with
    422 cmd_zdr_no_providers rather than falling back. The upstream can change, so cost can change with it
  • Anthropic models on /messages are already zero-data-retention at the account level; the header is accepted there
    for parity and does not change routing
  • Usage: CMD_ZDR=1 opencode, or { "zdr": true } in ~/.config/opencode/opencode-commandcode.json

Verification

Unit gate (offline, runs in CI):

bun run check
# 250 pass / 0 fail (oxlint --deny-warnings + oxfmt --check + bun test tests/unit/ + tsc --noEmit)

Live verification (manual, outside bun run check; skipped when COMMANDCODE_API_KEY is unset):

COMMANDCODE_API_KEY=... bun run test:integration

Observed while preparing this PR (command-code@1.82.1, live Provider API):

ZDR probe: 18/78 chat models probed, 9 served, 1 refused
ZDR-capable sample: gpt-5.6-sol, gpt-6-luna, gpt-5.6-luna
ZDR-refused sample: deepseek/deepseek-v4.1-flash-fast

11 pass / 0 fail

Add a zero-data-retention opt-in that sends `x-cmd-zdr: 1` on every
Command Code request. Enable it with env `CMD_ZDR=1` (1/true/yes/on)
or `zdr: true` in the plugin config file.

V1 merges the header into provider options.headers, V2 registers it
on the commandcode provider, and createCommandCode honors CMD_ZDR.
Explicit user headers always win, and custom npm transports are left
untouched.
@BrainerVirus

BrainerVirus commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Thanks for contributing this! ZDR support is a useful addition. Lint, formatting, all 250 unit tests, typechecking, and the local package check passed. Review found a few things to address before approval:

1. Header casing

In src/zdr.ts, an existing X-Cmd-Zdr: 0 gets combined with the default as 1, 0, rather than preserving the user’s value.

Please check existing header names case-insensitively before adding the default, and add mixed-case regression tests.

2. Config fallback

With { "zdr": true, "debugStartupLogs": "false" }, the invalid logging field causes the entire config to fall back to defaults—including zdr: false.

Please preserve valid settings or reject invalid config rather than silently disabling ZDR. Add coverage through both plugin hooks.

3. SDK enforcement

The SDK calls /alpha/generate, but the linked ZDR documentation covers /provider/v1/*. The current SDK test checks a successful response, which doesn’t prove the endpoint enforces ZDR.

Please establish that endpoint’s ZDR support and test enforcement, or limit the SDK claims to sending the header until support is confirmed.

4. Live-test cancellation

Test timeouts don’t cancel the underlying requests, so paid probes may continue afterward. Please add request cancellation and stop scheduling probes when the deadline expires.

Small cleanup and CI

  • The test at tests/integration/zdr.integration.test.ts:216 only logs a message; it can be removed.
  • Please sync with main before final checks.
  • CI appears to be waiting for maintainer permission to run—we’ll handle that separately.

We haven’t verified live ZDR enforcement or real OpenCode V1/V2 requests yet. Happy to help with the remaining changes if useful. Thanks again for the contribution!

@BrainerVirus

BrainerVirus commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

We have now checked real requests using the local PR code at ed0834a on OpenCode V1.19.3 in Docker and V2.0.27 in an isolated local process, without using the installed npm plugin for either test.

Both versions used the free glyph-cluster:free model on /provider/v1/chat/completions:

  • ZDR on: observed x-cmd-zdr: 1 on outgoing requests; the API refused with 422 cmd_zdr_no_providers.
  • ZDR off: the same model and prompt, with no ZDR header, returned 200 and OK. Some control requests hit rate limits before succeeding.

This confirms header propagation and the API’s refusal behavior for the tested Chat Completions path on both generations. Thanks for adding this! The fixes in the main review still apply. SDK /alpha/generate, Messages, and Responses remain unverified; these tests do not independently audit server-side data retention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants