Bump version 3.13.0 → 3.14.0. No functional code change in this PR;
all three security fixes are already merged in main via PRs #86, #87, #88.
This PR covers only metadata + docs:
- package.json: version bump to 3.14.0
- CHANGELOG.md: v3.14.0 entry with Features / Behavior changes / Verification /
Governance sections
- README.md: /api/usage self-scope note in Auth Modes §, API Endpoints table
row update, file-mode bullet in Important Notes §
cli.js citation N/A: this release PR modifies no server.mjs / setup.mjs / keys.mjs.
The three underlying security PRs (#86, #87, #88) each carry their own
cli.js-citation-not-applicable disclaimer per PR #75 pattern, as they are
OCP-internal access-control, session-state, and file-permission changes with
no corresponding cli.js operation.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The Chinese blog post linked from these two places was taken down by
the 知乎 platform shortly after publication. Both pointers now resolve
to a "content removed" warning page, which is a worse signal to
visitors than no link at all. Removing both before they reach more
README readers.
Reverts the additions from #81 specifically:
- Top badges row: drop the "blog · engineering story" shield
- §Why OCP? closing blockquote: drop the line pointing at the article
The 知乎 article URL is no longer reachable, so retaining the references
would route incoming traffic to a dead page that suggests the project
is itself problematic. Better to leave that section silent until a
working canonical write-up exists somewhere.
Doc-only change. server.mjs not touched. ALIGNMENT.md Rule 5 (cli.js
citation) does not apply.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Adds two pointers from README to a Chinese-language engineering blog
post (https://zhuanlan.zhihu.com/p/2036388634207770402) that walks
through OCP's cli.js-alignment philosophy, the 2026-04-11 drift
incident (the 9-day hallucinated /api/oauth/usage endpoint), the
three-tier guardrail design, and the recent fresh-state E2E pass
that produced PRs #74-#78.
Two minimal touches to README only:
1. Badges row: a "blog · engineering story" shield linking to the
article. Slot fits between the existing Release badge and the
Buy Me a Coffee badge so the row's visual rhythm is preserved.
2. New blockquote line at the end of §Why OCP? (between the
single-maintainer / pre-1.0 disclosure and §Supported Tools).
Brief, factual, no marketing voice.
Why this PR
The 知乎 piece is a self-contained engineering narrative about
maintaining a single-maintainer LLM-assisted proxy without endpoint
drift. README is the project's primary surface; pointing at the
narrative lets readers who land on the repo decide if the project's
discipline matters to them before they invest in install. Reverse
direction: GitHub readers who follow the link bring some traffic
back to the article, validating that engineering content has a
home.
Doc-only change. server.mjs untouched. ALIGNMENT.md Rule 5 (cli.js
citation) does not apply. Same pattern as #68 / #69 / #71 / #78 / #79.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* docs(install): remove false symlink claim, fix Anonymous mode startup command
Three doc inaccuracies surfaced during fresh-state install testing on Pi231 +
MacBook (Round 1+2):
1. README §Server Setup claimed setup.mjs "Symlink `ocp` to /usr/local/bin for
CLI access" — false. setup.mjs writes start.sh + plist/systemd unit but
creates no PATH symlink. Removed the line; added a short PATH tip showing
the user's options (manual symlink to ~/.local/bin or /usr/local/bin, or
shell alias) right after the install summary.
2. README §Anonymous Access told users to run `ocp start` to enable the
feature — there is no `ocp start` subcommand. Available commands are
restart / stop / status / logs / keys / usage / update / lan / health /
clear / settings (verified via `~/ocp/ocp` enumeration). Replaced with
the correct flow: export PROXY_ANONYMOUS_KEY, then `node setup.mjs
--bind 0.0.0.0 --auth-mode multi`.
3. The same paragraph implied that exporting PROXY_ANONYMOUS_KEY in an
interactive shell is enough to enable anonymous access — but the running
proxy is auto-started by launchd/systemd from the service unit's own
env, not from the user's shell. Spelled this out and noted that if OCP
was installed before exporting the env var, the user must re-run
setup.mjs (idempotent) so the service unit env is refreshed, then
`ocp restart`.
The PROXY_ANONYMOUS_KEY mechanism described becomes 100% accurate when
PR B (`fix/setup-inject-service-env`, sibling PR) lands; current setup.mjs
on main does not yet inject this env into the service unit.
Doc-only — no `server.mjs` change, no version bump, no `cli.js` citation
required.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(readme): add restrained star + issue CTA below tagline
A single italic line under the existing personal-note italic, asking
readers to ⭐ the repo if they get value, and to file issues — framed
explicitly as "issues are even more useful than stars" so the CTA reads
as feedback-seeking rather than vanity-metric chasing.
Tone matches the rest of README: low-key, single-maintainer self-deprecating,
no marketing voice. No "save money / free / $0" verbiage. Coexists with the
existing buy-me-a-coffee personal-note line above it (different ask:
funding vs. social-proof).
This is doc-only. ALIGNMENT.md Rule 5 (cli.js citation) does not apply.
Same pattern as #68 / #69 / #71 / #78.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Three doc inaccuracies surfaced during fresh-state install testing on Pi231 +
MacBook (Round 1+2):
1. README §Server Setup claimed setup.mjs "Symlink `ocp` to /usr/local/bin for
CLI access" — false. setup.mjs writes start.sh + plist/systemd unit but
creates no PATH symlink. Removed the line; added a short PATH tip showing
the user's options (manual symlink to ~/.local/bin or /usr/local/bin, or
shell alias) right after the install summary.
2. README §Anonymous Access told users to run `ocp start` to enable the
feature — there is no `ocp start` subcommand. Available commands are
restart / stop / status / logs / keys / usage / update / lan / health /
clear / settings (verified via `~/ocp/ocp` enumeration). Replaced with
the correct flow: export PROXY_ANONYMOUS_KEY, then `node setup.mjs
--bind 0.0.0.0 --auth-mode multi`.
3. The same paragraph implied that exporting PROXY_ANONYMOUS_KEY in an
interactive shell is enough to enable anonymous access — but the running
proxy is auto-started by launchd/systemd from the service unit's own
env, not from the user's shell. Spelled this out and noted that if OCP
was installed before exporting the env var, the user must re-run
setup.mjs (idempotent) so the service unit env is refreshed, then
`ocp restart`.
The PROXY_ANONYMOUS_KEY mechanism described becomes 100% accurate when
PR B (`fix/setup-inject-service-env`, sibling PR) lands; current setup.mjs
on main does not yet inject this env into the service unit.
Doc-only — no `server.mjs` change, no version bump, no `cli.js` citation
required.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* docs(install): add §Quick install with AI assistance + plug five new-user pitfalls
Context: a returning user observed that letting an AI assistant follow the
README to install OCP would mostly work but stumble on a handful of small
gaps — missing OS qualifier, no admin-key generation hint, server IP
discovery buried, and a few common setup errors not in Troubleshooting.
This patch closes those gaps and adds a copy-paste prompt section for
new users who'd rather have an AI walk them through the install.
Five additive changes (README only, server.mjs untouched):
1. **§Server Setup prerequisites** — add the macOS/Linux qualifier
("Windows is not supported — setup.mjs installs launchd / systemd")
and `git`, both of which were implicit before.
2. **OCP_ADMIN_KEY generation hint** — replace the placeholder
`your-secret-admin-key` with a one-line `openssl rand -base64 32`
example, plus a reminder to add the export to ~/.zshrc / ~/.bashrc
so it survives shells.
3. **§Client Setup** — add an inline note pointing readers to run
`ocp lan` on the server to discover the server's LAN IP. Previously
`ocp lan` was mentioned only in §Server Setup, leaving client-side
readers to guess.
4. **§Troubleshooting** — three new entries for setup-time errors
(claude not found / EADDRINUSE 3456 / node version), with the
specific recovery commands. Existing entries left unchanged.
5. **§Installation → new ###Quick install with AI assistance subsection**
— three copy-paste prompts (single-machine, LAN server, client) that
pin the AI to the right README path, name the verification step, and
forbid silent retries. Includes a pointer to the manual handbook
sections for readers who prefer that path.
Doc-only change. server.mjs not modified. ALIGNMENT.md Rule 5 (cli.js
citation requirement) does not apply. Same pattern as #68, #69, #70.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(install): add Claude CLI install command + ####Headless install notes
Live Pi231 (RPi4 / Debian Bookworm) test of PR #71's "Quick install with AI
assistance → LAN server" prompt surfaced two more new-user pitfalls in the
README's Prerequisites + Server Setup flow:
1. **Claude CLI install command was missing.** Previous text said "Claude CLI
installed and authenticated" with a docs link — but the actual install
command (`npm install -g @anthropic-ai/claude-code`) appeared nowhere.
An AI assistant following the prompt has to fetch external docs to
guess at the install path. Now inlined.
2. **Headless servers (Pi / NAS / VPS) had no auth guidance.** OCP's main
deployment targets are always-on headless devices. `claude auth login`
actually works headless (prints URL + code, OAuth completes on any
browser-capable device), and `claude setup-token` provides a long-lived
token — but neither was documented. New ####Headless install notes
subsection explains both paths.
Test trail (Pi231):
- ✅ Linux (aarch64 Debian Bookworm), Node v22.22.2, git 2.39.5 — prereqs
satisfied except Claude CLI.
- ❌ `claude` not on PATH (expected — fresh Pi). README didn't tell the
user how to install it. → fix#1 above.
- ⚠️ Even after AI fetches the install command, headless OAuth was a
documentation gap. → fix#2 above.
Doc-only change. server.mjs not modified. ALIGNMENT.md Rule 5 does not
apply. Extends PR #71 (same install-UX layer per Iron Rule 11 IDR).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
The "Why OCP?" section previously led with technical/governance bullets
(SSE heartbeat, alignment, models.json) and buried the most relatable
selling points (LAN multi-user keys, ocp-connect IDE auto-config, cache).
First-time readers stopped reading before reaching the bullets that would
have sold them on the project.
Changes (README.md only, line 19-26):
1. Reorder: human-relatable benefits first, governance discipline last.
New order: LAN multi-user → ocp-connect → cache → quota → SSE heartbeat
→ alignment → models.json. Quota is split out from the old combined
"quota + cache" bullet so each gets its own one-liner.
2. Soften the alignment bullet's framing. The previous prose flagged
"Other Claude proxies have shipped exactly that" and was deleted —
no need to call out competitors. Replaced with a measured "LLM-assisted
code drifts easily — it's tempting to invent plausible-looking endpoints
that cli.js doesn't actually use" plus a deliberately understated
payoff: "your setup keeps working when cli.js ships its next minor."
3. Add visceral hooks where bullets benefit from them:
- SSE heartbeat: "If you've ever watched your IDE die at the 60s idle
mark during a long Claude tool-use pause — that's nginx/Cloudflare
default behavior" (frames the problem in user-felt terms before
describing the fix).
- Per-key quota: concrete example "set a kid's iPad to 20/day, a
partner's laptop to 100/week" (replaces abstract "limits per key").
4. Cache bullet now explicitly states the per-key isolation guarantee:
"cross-user pollution is impossible by hash construction, not by
application logic" — this addresses the most common pre-adoption
concern (and reflects the v3.13.0 D1 design).
5. Total length compressed ~20% despite added context — old bullets had
redundant "what" descriptions; new bullets lead with the "what for".
Doc-only change. server.mjs not touched. ALIGNMENT.md Rule 5 (cli.js
citation requirement) does not apply. Same pattern as #68, #69.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Context: Buy Me a Coffee + Stripe onboarding now fully live (verified
buymeacoffee.com/dtzp555 returns og:type=profile with Support CTA, default
$3 price tier, membership tier active). Previous §Support OCP at line 709
was effectively buried — last section before License, missed by most readers.
Changes (README.md only, server.mjs untouched):
1. Top of file — three shields.io badges (License MIT, latest release,
Buy Me a Coffee) under H1, before tagline. Standard OSS pattern
(Vue / Vite / Tailwind), high visibility, doesn't disrupt prose.
2. Just under tagline — one-line italic personal note pointing to
§Support OCP, with inline ☕ link as fallback for users who don't
scroll. Quotes the spirit of the longer section without duplicating it.
3. §Support OCP expanded — adds the "open source from day one" framing
(not freemium, not commercial-turned-open), the "my family uses it
daily" angle, and an explicit feedback / issues invitation. The
debugging-history paragraph is preserved verbatim.
Doc-only change. ALIGNMENT.md Rule 5 (cli.js citation) does not apply —
server.mjs is not modified. Same pattern as #68.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- .github/FUNDING.yml — enables GitHub's native "Sponsor" button on the
repo page, pointing to buymeacoffee.com/dtzp555. Other platforms
(GitHub Sponsors, Ko-fi) are commented out and can be enabled later
by uncommenting + filling in handles.
- README.md § Support OCP — new section just before License. States the
free-and-open-source commitment, lists the kinds of work that don't
show up in commits (multi-machine debugging, IDE validation, drift
incidents, concurrency leaks), and offers a single ☕ link for users
who want to support continued maintenance. Explicitly disclaims paid
tiers / premium features so the open-source posture stays unambiguous.
server.mjs is not modified; this commit is doc-only and therefore exempt
from the cli.js citation requirement (ALIGNMENT.md Rule 5 applies only
to commits that touch server.mjs).
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Per release_kit overlay (CLAUDE.md § Iron Rule 5.5):
- package.json bumped 3.12.0 → 3.13.0
- CHANGELOG.md updated with v3.13.0 entry
- README.md § Response Cache updated to document the four hardening features
This is a release preparation commit. Tag push to v3.13.0 will trigger
.github/workflows/release.yml which auto-creates the GitHub Release.
cli.js does not perform proxy-layer response caching, stampede protection,
or replay; this release only ships internal cache-layer correctness and
concurrency improvements that do not change the OpenAI-compatible wire
surface visible to clients. No client-observable wire shape change.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
OCP uses Node.js built-in SQLite (`node:sqlite`) in keys.mjs:3 for the
LAN-mode key store. The `node:sqlite` module is only available in:
- Node 22.5.0+ behind --experimental-sqlite flag
- Node 23.0.0+ without any flag (fully stable)
The previous `engines: ">=18"` was inaccurate and would have caused
opaque "Cannot find module 'node:sqlite'" failures for users on
Node 18-22.4 the moment LAN mode (multi-key) was enabled.
Changes:
- package.json: engines.node ">=18" → ">=22.5"
- README.md: prerequisite "Node.js 18+" → "Node.js 22.5+ (Node 23+ recommended …)"
with a one-line note explaining the flag distinction so users on 22.x know
they may need --experimental-sqlite.
Verification:
- Source usage of node:sqlite confirmed via grep: keys.mjs:3
(`import { DatabaseSync } from "node:sqlite";`).
- Local node --version = v25.8.0 (well above 22.5); `npm install` exit 0
with no engine-mismatch warning.
- No other source file imports node:sqlite (single point of usage).
Refs: audit side-finding 2 of 4.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Multiple documentation polish items rolled into one PR (one layer:
"docs alignment with current state").
### README.md
- **Uninstall section** added between Server Setup and Client Setup
(was missing — `node uninstall.mjs` exists but went undocumented).
- **OpenClaw definition** added as a footnote on first README mention
(the architecture diagram and Supported Tools table both reference
OpenClaw without ever defining it).
- **Repository Layout section** added before Security — table of
top-level files (server.mjs, setup.mjs, uninstall.mjs, keys.mjs,
models.json, ocp/ocp-connect, dashboard.html, scripts/, .claude/skills/,
ocp-plugin/, docs/adr/, ALIGNMENT.md, AGENTS.md, CLAUDE.md) so a new
contributor knows what each file does.
- **LICENSE link** added to the License section footer.
### docs/adr/README.md (new)
- Index of the three published ADRs (0002, 0003, 0004) with a one-line
description each.
- Explains the `0001` placeholder (early internal proposal that was
superseded; numbering deliberately starts at `0002`).
- Guidance on when to write a new ADR vs. when a commit message suffices.
### Spec/plan housekeeping
- `specs/.gitkeep` removed (the empty `specs/` placeholder confused the
picture; canonical paths are `docs/superpowers/plans/` for active plans
and `docs/superpowers/specs/` for long-lived design docs that other
code references).
- Shipped plans moved to `docs/superpowers/plans/shipped/`:
- `2026-04-10-lan-mode.md` (shipped: README LAN mode section)
- `2026-04-25-47-sse-heartbeat-plan.md` (shipped: v3.12.0 per CHANGELOG)
- `2026-04-25-47-sse-heartbeat-design.md` left in `docs/superpowers/specs/`
unchanged because both `server.mjs:565` and `CHANGELOG.md:7` link to
that exact path; moving it would require a `server.mjs` edit, which
needs `cli.js` citation per ALIGNMENT.md Rule 1.
### AGENTS.md
- Updated "Key files to know" to add `docs/adr/README.md`,
`docs/superpowers/plans/`, and `memory/constitution.md`.
- Note explaining `memory/constitution.md` is spec-kit's standard
location, distinct from `~/.cc-rules/memory/` and `ALIGNMENT.md`.
- Updated "Handoff expectations" item 5 from `docs/superpowers/specs/*/tasks.md`
(which never matched anything — there were no `tasks.md` files there)
to `docs/superpowers/plans/` (excluding `shipped/`).
### Coordination with PR #53
PR #53 is open and adds "Why OCP?", "Comparison", and "Governance"
sections to README. This PR deliberately avoids those areas — only edits
the Supported Tools table footnote, inserts Uninstall before Client Setup,
inserts Repository Layout before Security, and updates the License footer.
No expected merge conflict.
Refs: audit findings M6, M8, M9, M11, L2, L3, L6.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Adds three positioning sections to make the README convert clones-to-stars
better:
1. "Why OCP?" near the top — 6 differentiator bullets with evidence links
(SSE heartbeat / ALIGNMENT.md / models.json SPOT / multi-key /
per-key quota / ocp-connect).
2. "Comparison" subsection — honest table vs claude-code-router and
anthropic-proxy. Acknowledges CCR's larger ecosystem; positions OCP
as cli.js-aligned + subscription-multiplexing focused.
3. "Governance" section near the bottom — links to ALIGNMENT.md, AGENTS.md,
ADRs, alignment.yml. Consolidates the governance-link surface in one
place rather than scattered.
Net diff +43 -0. No content removed. No anchors broken. No code changes.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* docs(spec): design for #47 SSE heartbeat on streaming path
Draft spec for an opt-in idle-watchdog SSE heartbeat covering both
pre-first-byte and mid-stream silent windows. Default disabled,
controlled by CLAUDE_HEARTBEAT_INTERVAL. Targets ~40 LOC.
Decisions captured: D1 whole-stream reset-on-byte; D2 SSE comment
frame; D3 default disabled; D4 relocate ensureHeaders() to post-spawn;
D5 X-Accel-Buffering: no on both SSE header sites; D6 single log line
per affected request.
Scope-locked: does not touch CLAUDE_TIMEOUT semantics, the separate
server.mjs:480-489 dangling-client bug, issues #41/#42, or the
non-streaming path.
Includes privacy preflight and cloud-testing plan per maintainer
feedback.
Refs: #47
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(plan): implementation plan for #47 SSE heartbeat
6 phases: pre-work (file 480-bug), implementation (5 tasks on
feat/47-sse-heartbeat), opus fresh-context review, cloud verification
on Mac rig, privacy preflight, PR+release.
Each implementation task carries concrete code, syntax check, and a
scoped commit message. LOC budget enforced in Task 1.6 gate (~45
server.mjs lines max). Reviewer checklist scopes scope-lock, ALIGNMENT,
privacy, and heartbeat-cannot-abort discipline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(server): add startHeartbeat helper + HEARTBEAT_INTERVAL env var
Per design doc (refs #47). Helper is a per-request idle watchdog that
emits `: keepalive\n\n` SSE comment frames; returns a {reset, stop} handle.
No wiring yet — helper is unused, safe to commit in isolation.
cli.js citation: N/A — SSE response shaping is an OCP-owned translation
layer, not a cli.js operation. See AGENTS.md and ALIGNMENT.md Rule 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(server): eagerly send SSE headers post-spawn (D4, refs #47)
Moves the ensureHeaders() call from "on first stdout byte" to
"immediately after successful spawn." This is a prerequisite for the
heartbeat covering the pre-first-byte silent window (the 'processing
large contexts' failure mode in #47).
Behavioral consequence: the narrow "spawn succeeded but subprocess died
before any byte" branch at server.mjs:610-611 becomes effectively dead
in the common case. The post-headers SSE-stop path (612-619) handles
it instead. The branch remains defensively for the client-closed-before-
ensureHeaders race.
Isolated commit per design doc §D4 so reviewer can focus on this one
behavior change.
cli.js citation: N/A — SSE header emission is OCP response-shaping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(server): wire heartbeat into streaming path (refs #47)
- sendSSE() accepts optional hb handle and calls hb.reset() before write
- callClaudeStreaming starts heartbeat after ensureHeaders() and passes
hb to the three streaming sendSSE call sites
- All three exit paths (proc close, proc error, res close) call
hb.stop() to guarantee timer cleanup; no-op handle when disabled
means zero runtime cost when CLAUDE_HEARTBEAT_INTERVAL=0
Heartbeat never aborts — only writes comment frames and re-arms. Aligns
with v3.3 timeout discipline (single CLAUDE_TIMEOUT, no secondary
client-killing timers).
cli.js citation: N/A — SSE response shaping is OCP translation layer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(server): add X-Accel-Buffering: no to SSE response headers (refs #47)
nginx (and many LBs / Cloudflare) default to proxy_buffering=on, which
would buffer heartbeat comment frames indefinitely and defeat the
feature silently. This header hints no-buffering; other stacks ignore
it. Applied at both SSE header sites (real streaming + cache-hit).
cli.js citation: N/A — response header shaping is OCP translation layer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(release): v3.12.0 — streaming heartbeat (refs #47)
Bundles the release-kit companion files per Iron Rule 5.2 / 11 example:
version bump across package.json + ocp-plugin + openclaw.plugin.json,
CHANGELOG v3.12.0 section, README env var row + "Streaming heartbeat"
explainer.
Tag push to v3.12.0 triggers .github/workflows/release.yml to create
the GitHub Release automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(server): ensureHeaders returns true when already sent (refs #47)
Phase 3 smoke test revealed every content chunk was being dropped
after the D4 eager ensureHeaders() call: the stdout.on('data') guard
`if (!ensureHeaders()) return;` early-returned on every chunk because
ensureHeaders returned false for the already-sent case (conflated with
the dead-connection case).
Split the two conditions explicitly: return false only when res is
ended/destroyed; return true when headers are (already or now) sent.
This also fixes a latent multi-chunk bug on main that was masked
because claude CLI typically outputs in one stdout chunk.
Verified: node -c server.mjs; subsequent re-run of Phase 3 smoke test
now sees streaming content chunks + heartbeat frames.
cli.js citation: N/A — SSE response shaping is OCP translation layer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three-part follow-up from the 2026-04-22 privacy postmortem:
1. OAUTH_CLIENT_ID verified as public Claude Code constant (not a secret).
Added gitleaks allowlist entry. The value 9d1c250a-e61b-44d9-88ed-5944d1962f5e
is the public PKCE client ID used by the Claude Code CLI — public clients in
PKCE flows have no client secret, so the ID itself carries no secret value.
Introduced in commit b87992f (the 2026-04-11 drift incident); the constant
mirrors what cli.js embeds for its OAuth token-refresh flow.
2. README.md API key example made clearly fake (ocp_example12345abcde...) to
avoid gitleaks false-positives and clarify to readers it is not real.
The ocp_ prefix IS the real prefix (keys.mjs line 80: randomBytes(24).base64url),
so the prior example could be mistaken for a real truncated key.
3. PR template: added Privacy self-check section for PUBLIC repos below the
existing 5.3 user-visible change self-check section.
4. .gitleaks.toml: new file with allowlist for the three confirmed non-issues
(OAUTH_CLIENT_ID constant, README placeholder regex, old plan doc path).
No code or behavior change beyond the docs, README, and new config file.
Closes part of ocp#44.
Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Backfills user-visible feature documentation for v3.11.0 that was missing
from PR #33 (which only fixed stale references). Per Tao's review: shipping
a feature without README documentation is a worse failure than stale
references, since users don't know the capability exists.
Added:
- "Self-Update" section: explain the full ocp update pipeline order
- New "OpenClaw Auto-Sync (v3.11.0+)" section: when it triggers, what
gets synced (with strict scope boundaries), safety guarantees, manual
invocation, opt-out, bootstrap caveat, behavior for non-OpenClaw IDEs
- "Available Models" expanded: explain models.json as SPOT, contributor
workflow for adding a new model
- "Troubleshooting" two new entries:
- "Startup log warns OpenClaw registry out of sync"
- "OpenClaw shows old models after ocp update (v3.10→v3.11 only)"
No code changes. README only.
Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Verify-curl example: 3 stale ids → 4 current ids
- Connect-script output sample: v3.10.0 → v3.11.0, "3 models" → "4 models"
- OpenClaw setup output: add ocp/claude-opus-4-7 to the example
- Available Models table: add opus-4-7, retain opus-4-6 for pinning, mark default aliases, link to models.json as canonical source
- OpenClaw Integration section: add bullet for ocp update auto-sync (v3.11.0+)
No code changes. README only.
Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Minor bump. User-visible new feature: Claude Opus 4.7 model is now
selectable via OCP.
- New model id 'claude-opus-4-7' in MODEL_MAP and /v1/models
- Short aliases 'opus' and 'claude-opus-4' now route to 4.7
- 'claude-opus-4-6' remains explicit for pinned usage
Evidence chain (see PR #27):
- Anthropic /v1/models: claude-opus-4-7 (display 'Claude Opus 4.7')
- claude.exe v2.1.114 strings: claude-opus-4-7 present
- Live probe verified 2026-04-20
No wire-format breaking changes.
Co-authored-by: Oracle Public Cloud User <opc@instance-20230820-1333.subnet07301351.vcn07301351.oraclevcn.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Remove "Status: Stable — Feature-complete, bug fixes only" tagline
- Add Per-Key Quota section with curl examples and 429 response format
- Add Response Cache section with enable/management instructions
- Update API Endpoints table with new quota + cache endpoints
- Add CLAUDE_CACHE_TTL to Environment Variables table
- Update version references from v3.7.0 to v3.8.0
Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Brings README up to date with the four PRs merged on 2026-04-12:
* #13 ocp-connect v1.1.0 — per-agent auth-profiles write
* #14 ocp-connect v1.2.0 — IDE detection rewrite + hint density
* #15 OCP server v3.7.0 — PROXY_ANONYMOUS_KEY allowlist
* #16 ocp-connect v1.3.0 — auto-discover anonymous key from /health
## Changes
1. Banner updated `v3.6.0` to `v3.7.0`.
2. New "Zero-config" paragraph in Client Setup showing
`./ocp-connect <server-ip>` works without `--key` when the
server admin has set PROXY_ANONYMOUS_KEY.
3. Client Setup example output replaced to reflect v1.3.0 actual
behavior: `OCP Connect v1.3.0` banner, `Remote OCP v3.7.0`,
new `ⓘ Using server-advertised anonymous key` block, new
`Per-agent auth profile seeded (2)` line with both main and
macbook_bot agentDir paths, new three-line smoke test caveat,
and correct `claude-haiku-4-5-20251001` model ID.
4. "The script automatically" list expanded from 3 bullets to 5:
added auto-discovery note (v1.3.0+/v3.7.0+), added OpenClaw
per-agent profile note, corrected IDE hint bullet to list
Cline/Continue.dev/Cursor/opencode explicitly as "manual
configuration required" rather than the earlier inaccurate
"configures" wording.
5. Available Models table updated `claude-haiku-4` to
`claude-haiku-4-5-20251001` to match the actual `/v1/models`
response.
6. Environment Variables table: new `PROXY_ANONYMOUS_KEY` row
added after `PROXY_API_KEY`, with a link to the existing
"Anonymous Access (optional)" section.
## Not changed
* `package.json` already at 3.7.0 from #15.
* ocp-connect script unchanged (v1.3.0 from #16).
* server.mjs unchanged (v3.7.0 from #15).
* No behavioral changes. README text only.
## Test
`git diff --stat main -- README.md` = +24 -7 on one file. Markdown
spot-checked for broken table alignment and link targets. The
anonymous key section anchor `#anonymous-access-optional` resolves
to the existing heading at line 234.
Co-authored-by: taodeng <taodeng@Taos-MBP>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements issue #12 section 14 Path A. Lets OCP admin designate a
single well-known "anonymous" key that bypasses validateKey() while
keeping AUTH_MODE=multi. Gives OpenClaw multi-agent clients (which
MUST send a non-empty Authorization header per their per-agent
auth-profiles schema) a way to connect without every user needing
a personal key.
## Background
After PR-1 (issue #12), we know OpenClaw multi-agent setups
require a per-agent auth-profiles.json with a non-empty `key`
field. OCP multi-mode rejects any non-empty Bearer token that
isn't in the keys database (server.mjs line 1152), which creates
a deadlock: the only way for OpenClaw to use OCP is with a real
admin-issued key.
Path A resolves this by letting the admin opt in to a public
anonymous key that clients can auto-discover via /health. OpenClaw
writes this key into its agent profiles just like a real key; OCP
server short-circuits validateKey when the key matches the
allowlist. No per-user coordination needed, admin still controls
the policy (can rotate, can unset, can rate-limit).
## Changes
### server.mjs
* Line 100-103: new `PROXY_ANONYMOUS_KEY` env var + warning if set
while AUTH_MODE is not multi.
* Line 1127-1138 (localhost branch): anonymous allowlist short-circuit
before validateKey so localhost clients using the anon key are
labeled "anonymous" instead of "local" in stats.
* Line 1153-1163 (multi branch): the same allowlist check between
the ADMIN_KEY check and validateKey. Uses timingSafeEqual with
explicit length check (consistent with the admin and shared key
patterns above).
* Line 1221 (/health response): new `anonymousKey` field, null when
not set, the actual value when set. Admins opted into public
access when they set the env var, so exposing the key here is
intentional and lets ocp-connect auto-discover it without
out-of-band coordination.
### package.json
Version 3.6.0 to 3.7.0 (per dev iron rule 5, version bump before push).
### README.md
New "Anonymous Access (optional)" subsection under Auth Modes:
* Enable example (export PROXY_ANONYMOUS_KEY=...)
* Client-side /health discovery explanation
* Security note: opt-in to public access, rate-limit warning
* "Not a secret" note: /health is unauthenticated, the key is
publicly readable by design; treat it as a convenience handle,
not as an access credential.
## Test evidence
Offline tests on macOS 13, local server on 0.0.0.0:8889 with
PROXY_ANONYMOUS_KEY=ocp_public_anon_TEST, CLAUDE_AUTH_MODE=multi,
CLAUDE=/bin/echo (mock):
* GET /health ->
authMode: multi
anonymousKey: ocp_public_anon_TEST
(pass, field exposed correctly)
* POST /v1/chat/completions with Bearer ocp_WRONG_KEY from
172.16.2.29 (non-localhost) -> HTTP 401 in 15ms
body: Unauthorized: invalid or revoked API key
(pass, validateKey still rejects unknown keys)
* POST /v1/chat/completions with Bearer ocp_public_anon_TEST from
172.16.2.29 -> curl hangs at 3s after auth middleware
(pass, auth passed, request reached Claude handler which hangs
because CLAUDE=/bin/echo cant serve a real chat; the point is
the auth middleware accepted the key, confirmed by no 401 return)
* POST /v1/chat/completions with no Authorization from 172.16.2.29
-> curl hangs at 3s after auth middleware
(pass, pre-existing empty-token anonymous path not regressed)
node --check server.mjs: syntax OK.
## Code review
One independent opus reviewer. Verdict: PASS WITH CONCERNS, zero
blockers. Two strong-recommend items addressed in this commit:
1. Localhost branch was not covered in the first draft. Reviewer
pointed out that a localhost client using the anon key would be
labeled "local" instead of "anonymous" in stats, making operations
visibility worse. Fixed by applying the same anonymous allowlist
check in the localhost branch at line 1127-1138.
2. README security note was missing the explicit "not a secret"
framing. Added a paragraph clarifying that because /health is
unauthenticated, the anonymous key is publicly readable by
anyone who can reach the server, which is intentional.
Reviewer nits (tokenBuf3 naming, stats subcategory for header-less
vs anon-key anonymous) are deferred as they do not affect
correctness.
## Upstream dependencies on this commit
After this PR is merged and the OCP instance at 172.16.2.30 is
restarted with `PROXY_ANONYMOUS_KEY` set in the environment, a
follow-up PR can add anonymous key auto-discovery to ocp-connect:
* On startup, ocp-connect calls GET /health
* If anonymousKey field is non-null and --key was not provided,
ocp-connect automatically uses that key to seed per-agent
auth-profiles for OpenClaw
* User gets zero-config OCP connectivity for OpenClaw multi-agent
setups, no admin coordination, no --key flag
That follow-up is NOT in this PR. This PR is server-only.
Co-authored-by: taodeng <taodeng@Taos-MBP>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed v3.0.x migration guide, old idleTimeoutSeconds workaround
(auto-handled by setup.mjs since v3.2.1), and manual session cleanup
instructions. Replaced with concise troubleshooting for common issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ocp-connect now detects installed IDEs and offers to configure them
automatically. OpenClaw gets full interactive setup (provider name,
primary/backup priority, model aliases). Other IDEs (Cline, Continue.dev,
Cursor) get manual config instructions printed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Writes to both .bashrc and .zshrc on macOS (covers both shells)
- macOS: launchctl setenv for GUI apps and daemons
- Linux: ~/.config/environment.d/ocp.conf for systemd user services
- Ensures IDEs and daemons can discover OCP models
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds CLAUDE_NO_CONTEXT=true env var that passes
CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 and CLAUDE_CODE_DISABLE_AUTO_MEMORY=1
to Claude CLI child processes. This suppresses CLAUDE.md and auto-memory
injection while preserving OAuth auth — needed for third-party agents
like Hermes that have their own memory systems.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In zero-config auth mode, the server allows anonymous access even in
multi mode. The script now probes /v1/models first — if it succeeds,
no key is needed. Also updated README examples and version to 3.5.1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clarify that /ocp commands are for Telegram/Discord via gateway plugin,
while terminal CLI uses ocp (without slash).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add LAN CLI commands (keys, lan, usage --by-key), new API endpoints
(dashboard, keys, usage), LAN env vars (CLAUDE_BIND, CLAUDE_AUTH_MODE,
OCP_ADMIN_KEY), and expanded security section.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove firstByteTimeout, adaptive tier algorithm, and MODEL_TIMEOUT_TIERS.
Claude tool-use causes 30s-5min pauses in token streams, making fine-grained
timeouts unreliable — they repeatedly killed valid requests. A single generous
timeout (10 min, matching LiteLLM/OpenAI SDK defaults) is simpler and correct.
Breaking: CLAUDE_FIRST_BYTE_TIMEOUT env var removed, default timeout changed
from 300s to 600s. Bump to v3.3.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setup.mjs now sets agents.defaults.llm.idleTimeoutSeconds=0 in
openclaw.json during installation. Without this, OpenClaw's default
60s idle timeout kills Claude connections during tool use (Bash, Read,
etc.), causing exit 143 errors and stuck sessions.
Also adds troubleshooting section to README. Bump to v3.2.1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Renamed from OpenClaw Control Plane to Open Claude Proxy. README
rewritten to position OCP as a universal proxy for any IDE that
speaks the OpenAI protocol, not just OpenClaw.
Added: supported tools table (Cline, OpenCode, Aider, Continue.dev),
simplified Quick Start, architecture diagram showing multi-IDE flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documented the intermittent "Unknown skill: ocp" issue caused by
OpenClaw gateway session routing bug (#26895). Workaround: /new
followed by /ocp restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The slash session workaround was incomplete — root cause still under
investigation. Removed to avoid misleading users. Will add back with
accurate info once the issue is fully resolved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document OpenClaw gateway bug (#26895, #54485) where telegram:slash
sessions interfere with registerCommand plugin routing. Include
workaround script and warning not to add ocp to agent skills lists.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Common issue: ocp not in PATH after clone. Added explicit symlink
instructions with absolute paths and a troubleshooting note.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- README: add "Status: Stable" badge, rename title to OCP — OpenClaw Control Plane
- package.json: update description and repo URL to dtzp555-max/ocp
- No code changes. Feature-complete, bug fixes only going forward.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Explains removing old skills/ocp/SKILL.md to avoid conflict
with the new plugin-based command handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the 2026-03-22 multi-agent cascading timeout incident, root cause
analysis, and all new/changed defaults. Update env vars table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major architectural upgrade:
- Replace pool system with on-demand spawning (eliminates crash loops,
DEGRADED states, and stdin timeout errors from v1.x)
- Add session management with --resume support (reduces token waste on
multi-turn conversations)
- Expand default allowed tools (Bash, Read, Write, Edit, Glob, Grep,
WebSearch, WebFetch, Agent) with configurable CLAUDE_ALLOWED_TOOLS
- Add system prompt pass-through (CLAUDE_SYSTEM_PROMPT)
- Add MCP config support (CLAUDE_MCP_CONFIG)
- Add concurrency control (CLAUDE_MAX_CONCURRENT, default 5)
- Add periodic auth health monitoring
- Add session API endpoints (GET/DELETE /sessions)
- Improve /health with full diagnostics (stats, sessions, errors, config)
- Fix timeout race condition (graceful SIGTERM → SIGKILL)
- Fix ERR_HTTP_HEADERS_SENT by checking headersSent in all response helpers
- Document coexistence with Claude Code interactive mode (Telegram, IDE)
- No conflict with CC: different ports, protocols, and process models
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- setup.mjs: install launchd plist (macOS) or systemd user service (Linux) after proxy starts; logs to ~/.openclaw/logs/proxy.log
- Add uninstall.mjs to stop and remove the auto-start entry
- README: Quick Start (Node.js) first, Security section, Docker moved to Server/Advanced
- Bump version to 1.5.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>