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.
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.
- Anthropic — Legal and compliance
anthropic-screws-opencode.ts— reproduction script- HN: subscription block · OpenCode block · legal action
On a request that passes the activation gates:
- Sanitizes the opencode system prompt — drops the
You are OpenCodeidentity paragraph and any paragraph containing an opencode URL, rewrites two classifier-trigger phrases, and keeps everything else. Block order and metadata (includingcache_control) are preserved;messagesis never touched. - 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 |
All of the following must hold. Otherwise the request passes through untouched and native cloaks it normally.
- Source format and target format are both
claude. system[0]is not already a billing header (the transform is idempotent).- It is not a real Claude Code request (canonical Claude Code UA and the Claude Code prompt).
- The model does not start with
claude-3-5-haiku(native skips cloaking there too, so the plugin must not strip the prompt). - Positive opencode evidence: the client User-Agent matches
opencode_ua_regex, or a system paragraph contains the exact textYou are OpenCode. - 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.
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.
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/pluginsNo 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.
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.
Everything below is optional and fully configurable in the management panel — no YAML editing required.
| 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.
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_agentIf 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.
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.
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.