Skip to content

Repository files navigation

opencode-cloak

A CLIProxyAPI plugin that makes third-party opencode requests look like Anthropic's official Claude Code — while preserving opencode's own system prompt.

CLIProxyAPI's native cloaking clears the same gate by replacing opencode's system prompt with Claude Code's, which throws away the tool discipline, editing rules, and output conventions that make opencode behave like opencode. This plugin takes the narrower route: strip only the paragraphs that carry the opencode identity, keep the rest.

It is a Go C-ABI plugin with the request_interceptor capability, hooked on request.intercept_after (after auth selection, before the native cloaking step). Transport headers, request-level identity fields, body normalization, and the final cch signature all stay with CLIProxyAPI's native pipeline.

Why this exists

In January 2026 Anthropic stopped honoring Claude Pro/Max subscription OAuth tokens outside its own first-party clients: OAuth is for Anthropic's products, third-party harnesses belong on an API key.

Enforcement was also name-specific. Under a subscription token, a request whose second system block read You are OpenCode was rejected, while You are Cursor, You are Pi, and You are Droid in the same position all succeeded — the discriminator was the identity string sitting in the prompt, not the shape of the request around it. That specific check stopped reproducing in March 2026, but the broader subscription restriction stands, and the episode set this plugin's design constraint.

How it works

On a request that passes the activation gates:

  1. Sanitizes the opencode system prompt — drops the You are OpenCode identity paragraph and any paragraph containing an opencode URL, rewrites two classifier-trigger phrases, and keeps everything else. Block order and metadata (including cache_control) are preserved; messages is never touched.
  2. Prepends the Claude fingerprint blocks, producing:
[ <x-anthropic-billing-header>, "You are Claude Code, Anthropic's official CLI for Claude.", <sanitized original blocks...> ]

A billing header at system[0] makes CLIProxyAPI's native system-prompt replacement step aside. The rest of the native pipeline still runs: it applies the outgoing Claude client profile and re-signs cch over the final upstream body.

Concern Owner
Sanitizing and preserving opencode's system blocks this plugin
Billing-header version, suffix, and entrypoint this plugin
cch signing over the final body native
Outgoing client profile, request-level identity fields, body normalization native

When it activates

All of the following must hold. Otherwise the request passes through untouched and native cloaks it normally.

  1. Source format and target format are both claude.
  2. system[0] is not already a billing header (the transform is idempotent).
  3. It is not a real Claude Code request (canonical Claude Code UA and the Claude Code prompt).
  4. The model does not start with claude-3-5-haiku (native skips cloaking there too, so the plugin must not strip the prompt).
  5. Positive opencode evidence: the client User-Agent matches opencode_ua_regex, or a system paragraph contains the exact text You are OpenCode.
  6. A valid billing header can be derived from the first user message.

Gate 5 is the safety net. Merely "not Claude Code" is not enough, so Cherry Studio, Cline, generic SDKs, and other third-party clients are left alone.

Install

Option A — Plugin store (recommended)

This plugin is listed in the official CLIProxyAPI plugin store, so the management panel's Plugin Store page installs it in a few clicks. The equivalent API call:

curl -X POST \
  "http://127.0.0.1:8080/v0/management/plugin-store/opencode-cloak/install?source=official" \
  -H "Authorization: Bearer $CLIPROXY_MANAGEMENT_KEY"

The store downloads the platform archive from this repo's GitHub Releases, verifies its sha256, drops the library into plugins.dir, writes plugins.configs.opencode-cloak.enabled: true into your config, and hot-reloads the host. The latest release tag is resolved as the version, so re-running the install (or the panel's update action) picks up new releases with no manual download.

Option B — Prebuilt archive

Every release ships a per-platform zip plus a checksums.txt, named the way the store installer expects: opencode-cloak_<version>_{linux,darwin}_{amd64,arm64}.zip and ..._windows_amd64.zip.

Download the one matching your server from the Releases page and unzip it into plugins.dir. The archive holds a single opencode-cloak.<ext> at its root — keep that basename, it becomes the plugin ID.

mkdir -p /opt/cliproxy/plugins
ver=0.2.1   # match the release tag without the leading v
curl -L -o /tmp/opencode-cloak.zip \
  "https://github.com/conversun/cliproxy-plugin-opencode-cloak/releases/download/v${ver}/opencode-cloak_${ver}_linux_amd64.zip"
unzip -o /tmp/opencode-cloak.zip -d /opt/cliproxy/plugins

Option C — Build from source

No local CLIProxyAPI checkout is required: the plugin depends on the public SDK module (github.com/router-for-me/CLIProxyAPI/v7), so a plain build works anywhere (Go 1.26+, CGO enabled).

git clone https://github.com/conversun/cliproxy-plugin-opencode-cloak.git
cd cliproxy-plugin-opencode-cloak
make build   # or: CGO_ENABLED=1 go build -buildmode=c-shared -o bin/opencode-cloak.so .

make build selects the extension per OS (.so Linux, .dylib macOS, .dll Windows). Build on the same OS/arch as the host that loads it; cross-compiling a c-shared library does not work.

Is it usable right after installing?

Yes — no plugin configuration is needed. Two switches have to be on, and the store install handles one of them for you:

Requirement Store install (A) Manual install (B / C)
Library present in plugins.dir done for you copy the file yourself (basename stays opencode-cloak)
plugins.configs.opencode-cloak.enabled: true written automatically toggle Plugins → opencode-cloak → Enabled in the panel, or add it to config.yaml
plugins.enabled: true (global plugin system) not changed — turn it on yourself same

The global switch lives in the management panel under Config Management → Advanced & Experimental → Plugins → Enable Plugin System. While it is off, the Plugin Store page warns that "plugins.enabled is false, so installed plugins will not become effective".

The defaults are already self-consistent: the plugin's built-in Claude Code identity is claude-cli/2.1.63 (external, cli), which is exactly the fingerprint CLIProxyAPI itself falls back to when claude-header-defaults.user-agent is unset — so the billing header in the body and the outgoing HTTP User-Agent agree without you touching anything.

To confirm it is live, open the panel's Plugins page: opencode-cloak should show Registered and Effective.

Optional: advanced settings

Everything below is optional and fully configurable in the management panel — no YAML editing required.

Plugin fields — Plugins → opencode-cloak → Edit config → Config fields

Field Default What it does
claude_code_user_agent claude-cli/2.1.63 (external, cli) The Claude Code identity the plugin encodes into the billing header (cc_version, cc_entrypoint). Must be a canonical UA — claude-cli/<version> (external, cli|sdk-cli) — anything else is ignored and the default is kept.
opencode_ua_regex (?i)^opencode/ Regex that identifies an opencode client by User-Agent (gate 5). An invalid regex logs a warning and falls back to the default.

Host User-Agent — Config Management → Advanced & Experimental → Header Defaults → Claude Header Defaults → User-Agent

Saved as claude-header-defaults.user-agent. This is the actual HTTP User-Agent CLIProxyAPI sends upstream; the plugin never reads it.

The one rule: change both, or neither

claude_code_user_agent (billing header, inside the body) and claude-header-defaults.user-agent (transport header) describe the same client. Set them to the same string, or leave both untouched. A mismatch makes the upstream request contradict itself.

To track a newer Claude Code release, put the same tuple in both places, e.g. claude-cli/2.1.220 (external, sdk-cli) — the plugin then emits cc_version=2.1.220.* and cc_entrypoint=sdk-cli, while native still sends X-App: cli.

The same thing in config.yaml
plugins:
  enabled: true
  dir: "/absolute/path/to/plugins"
  configs:
    opencode-cloak:
      enabled: true
      claude_code_user_agent: &claude_code_ua "claude-cli/2.1.220 (external, sdk-cli)"

claude-header-defaults:
  user-agent: *claude_code_ua   # same value as the plugin's claude_code_user_agent

If you use a YAML anchor, declare it inside plugins.configs.opencode-cloak as shown: CLIProxyAPI serializes the plugin subtree on its own, so an anchor declared under claude-header-defaults will not resolve for the plugin.

Caveats

No entitlement guarantee. This layout improves consistency and follows the current upstream auth-plugin direction, but Anthropic can still classify OpenCode or agent frameworks as third-party traffic. Disable the plugin to fall back to native cloaking if plan-limit compatibility matters more than prompt fidelity.

ABI. The plugin targets CLIProxyAPI plugin ABI v1 (built against SDK v7.2.96) and loads into any host that speaks ABI v1.

⚠️ Terms of Service / Risk

Using this plugin may violate Anthropic's Terms of Service. It is provided for technical research and learning purposes only. You assume all risk, including account bans, service interruption, and any other consequences of use.

About

CLIProxyAPI request_interceptor plugin: cloaks opencode CLI requests as official Claude Code (billing header + surgical system-prompt sanitize)

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages