feat+docs: D60 — Phase 4 charter (ADR 0010) + default OLP_PORT 3456 → 4567 (#40)

* feat+docs: D60 — Phase 4 charter (ADR 0010) + default OLP_PORT 3456 → 4567

Opens Phase 4 (Operator + Client UX) end-to-end. Per release_kit.phase_rolling_mode,
the version bump fires at Phase 4 close (v0.4.0), not at this D-day; D60 only
ships governance + the port default change.

## ADR 0010 — Phase 4 Charter

Phase 4 scope = 5 D-day groups (~13 D-days total):
- D60 (this commit): charter + default port
- D61-D63: SSE heartbeat + recentErrors[20] ring + /status combined endpoint
- D64-D67: olp Node-based CLI scaffold + olp doctor next_action framework
- D68-D70: olp-connect zero-config IDE setup + /health.anonymousKey + ADR 0011
- D71-D73: olp-plugin/ OpenClaw gateway plugin + docs/integrations/*.md bundle

Charter records the EXPLICIT DECISION to DEFER /v1/messages (Anthropic-shape
entry surface) on the rationale: under ADR 0009 P0 failure it provides no
billing benefit AND degrades worse on fallback than OpenAI-shape clients
(because OpenAI tool schema is the cross-provider lingua franca; Anthropic-
specific features cache_control / computer_use / text_editor / thinking
blocks have no clean fallback mapping). Re-open trigger: (a) ADR 0009 P0
success AND (b) maintainer-named family CC user.

README posture updated: Claude Code listed as NOT SUPPORTED as an OLP
client; recommended alternative is "Cline + OLP" (same fallback chain
available, better cross-provider compatibility).

## Default port 3456 → 4567

server.mjs:74 default value moves so OLP and OCP (which stays on 3456) can
co-host on the same machine without OLP_PORT env override. Existing
deployments wanting the pre-D60 default can set OLP_PORT=3456 in launchd
plist / shell env.

Verified port-change invariants:
- All test-features.mjs suites use port: 0 (ephemeral) — 0 test-surface impact
- Cache / fallback / provider plugins port-agnostic
- Dashboard 30s poll + management endpoints use relative paths
- 623/623 tests pass on D60 branch HEAD

## ADR amendments

- ADR 0001 § "Decision" port-conflict paragraph: struck + amended (co-host
  is now possible via 3456 → 4567 + launchd labels dev.olp.proxy vs
  dev.ocp.proxy)
- ADR 0008 § 6.6 default-port reference updated
- docs/adr/README.md index gains ADR 0010 row

## Authority

- ADR 0010 (this commit)
- ADR 0009 (interactive-mode placeholder — /v1/messages defer rationale)
- 2026-05-26 brainstorm: OCP comprehensive feature audit (subagent output)
  + multi-provider proxy / IDE integration prior-art survey (subagent
  output, both this session)
- docs/v1x-roadmap.md (Phase 4 was the named destination)
- CLAUDE.md release_kit.phase_rolling_mode (current_phase already Phase 4;
  this charter formalizes contents)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: D60 reviewer P2-1 — soften ADR 0001 launchd-label assertion to forward-tense

Reviewer flagged the ADR 0001 amendment's launchd-label collision claim
("avoided via dev.olp.proxy vs dev.ocp.proxy") as present-tense fact when
the OLP plist generator hasn't shipped yet (lands D64-D70 per ADR 0010).
Soften to forward-tense with explicit cross-reference. The factual
claim ("co-host is possible") still stands because plist label is
controlled by the OLP project anyway; this is precision, not correction.

P2-2 (README "since v0.4.0" forward-dated branding) explicitly accepted
as prior-art-consistent with D44+ Phase 2 mid-window doc conventions —
no change.

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>
This commit is contained in:
dtzp555-max
2026-05-26 07:59:11 +10:00
committed by GitHub
co-authored by taodeng Claude Opus 4.7
parent ba69a3c13b
commit 0048481764
7 changed files with 189 additions and 10 deletions
+12 -1
View File
@@ -4,7 +4,18 @@ All notable changes to OLP land here. Per `CLAUDE.md` release_kit overlay, this
## Unreleased ## Unreleased
(empty — Phase 4 entries land here once Phase 4 opens) ### D60 — Phase 4 charter (ADR 0010) + default port 3456 → 4567
Opens Phase 4 (Operator + Client UX). No functional code change beyond the default port value; the substantive D-day work lands D61 onward. Per `release_kit.phase_rolling_mode`, the version bump fires at Phase 4 close (v0.4.0), not at this D-day.
- **Default `OLP_PORT` changed from `3456` to `4567`.** Rationale: OCP defaults to 3456; OLP and OCP can now co-host on the same machine without `OLP_PORT` env override. Existing deployments wanting the pre-D60 default can set `OLP_PORT=3456` in launchd plist / shell env. Tests use `port: 0` (ephemeral) — no test-surface impact. Cache / fallback / provider plugins are port-agnostic. Dashboard uses relative paths.
- **ADR 0010 (Phase 4 charter) ratified.** Records: (a) Phase 4 scope as 5 D-day groups (~13 D-days total) culminating in v0.4.0; (b) explicit DEFER of `/v1/messages` Anthropic-shape entry surface with re-open trigger gated on ADR 0009 P0 success + maintainer-named family CC user; (c) opportunistic Phase 4 micro-additions and Phase 5+ deferrals.
- **ADR 0001 amendment.** § "Decision" paragraph about port conflict struck and amended: the OCP-OLP port-conflict assumption is lifted by the 3456 → 4567 default move + per-project launchd labels (`dev.ocp.proxy` vs `dev.olp.proxy`).
- **ADR 0008 amendment.** § 6.6 default-port reference updated.
- **README quick start + Environment Variables table + Migration from OCP § note** updated for the new default.
- **Authority:** ADR 0010 (Phase 4 charter), ADR 0009 (interactive-mode placeholder — `/v1/messages` defer rationale), 2026-05-26 brainstorm (OCP feature audit + multi-provider proxy / IDE integration prior-art survey).
## v0.3.2 — 2026-05-25 ## v0.3.2 — 2026-05-25
+7 -5
View File
@@ -31,10 +31,12 @@ npm install -g @dtzp555-max/olp
# run setup (writes ~/.olp/config.json, asks which providers to enable) # run setup (writes ~/.olp/config.json, asks which providers to enable)
olp setup olp setup
# start the proxy (default port 3456 — same as OCP if you migrate) # start the proxy (default port 4567 since v0.4.0 — moved off OCP's 3456 so
# OLP and OCP can co-host on the same machine. Set OLP_PORT=3456 if you have
# no OCP on the machine and want the old default.)
olp start olp start
# point your IDE at http://localhost:3456/v1/chat/completions with the OLP API key from `olp keys list`. # point your IDE at http://localhost:4567/v1/chat/completions with the OLP API key from `olp keys list`.
``` ```
--- ---
@@ -112,7 +114,7 @@ _placeholder — full table lands per-phase as variables are introduced._
| Variable | Default | Description | | Variable | Default | Description |
|---|---|---| |---|---|---|
| `OLP_PORT` | `3456` | HTTP listener port. | | `OLP_PORT` | `4567` | HTTP listener port. Moved off `3456` at D60 / v0.4.0 to co-host with OCP — set `OLP_PORT=3456` to restore the pre-D60 default. |
| `OLP_CLAUDE_BIN` | `claude` (from PATH) | Override path to the `claude` binary (Anthropic provider). Useful when multiple `claude` installs are present. | | `OLP_CLAUDE_BIN` | `claude` (from PATH) | Override path to the `claude` binary (Anthropic provider). Useful when multiple `claude` installs are present. |
| `OLP_CODEX_BIN` | `codex` (from PATH) | Override path to the `codex` binary (OpenAI provider). | | `OLP_CODEX_BIN` | `codex` (from PATH) | Override path to the `codex` binary (OpenAI provider). |
| `OLP_VIBE_BIN` | `vibe` (from PATH) | Override path to the `vibe` binary (Mistral provider). | | `OLP_VIBE_BIN` | `vibe` (from PATH) | Override path to the `vibe` binary (Mistral provider). |
@@ -216,7 +218,7 @@ Behaviors that work correctly at personal/family scale but have ratified follow-
npm start npm start
# 4. Validate the key works (substitute the captured plaintext token) # 4. Validate the key works (substitute the captured plaintext token)
curl -H "Authorization: Bearer olp_..." http://localhost:3456/health curl -H "Authorization: Bearer olp_..." http://localhost:4567/health
``` ```
**Recovery if owner token is lost:** `npx olp-keys keygen --owner --force` revokes the previous owner key + creates a fresh one (plaintext printed once). **Recovery if owner token is lost:** `npx olp-keys keygen --owner --force` revokes the previous owner key + creates a fresh one (plaintext printed once).
@@ -270,7 +272,7 @@ Anticipated user-facing flow (target: <5 minutes):
1. Stop OCP (`launchctl bootout` the OCP service or `ocp stop`). 1. Stop OCP (`launchctl bootout` the OCP service or `ocp stop`).
2. Install OLP. 2. Install OLP.
3. Run `olp migrate-from-ocp` — copies `~/.ocp/keys/` to `~/.olp/keys/` and points provider plugins at OCP's existing auth artifacts where applicable. 3. Run `olp migrate-from-ocp` — copies `~/.ocp/keys/` to `~/.olp/keys/` and points provider plugins at OCP's existing auth artifacts where applicable.
4. Start OLP. Clients pointing at port 3456 keep working; their existing OLP API keys remain valid. 4. Start OLP. Clients pointing at port 4567 (or 3456 with `OLP_PORT=3456`) keep working; their existing OLP API keys remain valid. **Note (v0.4.0+):** default port moved from 3456 → 4567 so OCP and OLP can co-host during migration; set `OLP_PORT=3456` if you want the pre-D60 default.
OCP's cache directory is *not* migrated: OLP's cache key format includes provider+model and warms cold naturally. OCP enters maintenance mode (stability fixes only) when OLP v0.1 ships; new development happens in OLP. OCP's cache directory is *not* migrated: OLP's cache key format includes provider+model and warms cold naturally. OCP enters maintenance mode (stability fixes only) when OLP v0.1 ships; new development happens in OLP.
+1 -1
View File
@@ -40,7 +40,7 @@ What OLP **does NOT inherit** from ADR 0005 (and where ADR 0005's reasoning does
- ADR 0005's separate-project recommendation came with two qualifiers that OLP rejects: "BYOK from day one" and "no `cli.js` spawn." Both qualifiers were appropriate for the *commercial* path ADR 0005 was contemplating. OLP is not commercial — it is personal- and family-scale, shares the maintainer's own subscription quota across family clients, and explicitly spawns provider CLIs (it is precisely the spawn-binary architecture that delivers the "subscription quota maximization" value proposition spec §1 names). - ADR 0005's separate-project recommendation came with two qualifiers that OLP rejects: "BYOK from day one" and "no `cli.js` spawn." Both qualifiers were appropriate for the *commercial* path ADR 0005 was contemplating. OLP is not commercial — it is personal- and family-scale, shares the maintainer's own subscription quota across family clients, and explicitly spawns provider CLIs (it is precisely the spawn-binary architecture that delivers the "subscription quota maximization" value proposition spec §1 names).
- OLP is therefore not the commercial pivot ADR 0005 endorsed. It is a personal-use re-architecture of the proxy-CLI pattern, which ADR 0005 did not contemplate. The supersession is honest about this gap. - OLP is therefore not the commercial pivot ADR 0005 endorsed. It is a personal-use re-architecture of the proxy-CLI pattern, which ADR 0005 did not contemplate. The supersession is honest about this gap.
OCP itself is not deleted. Per spec §7, OCP enters maintenance mode when OLP v0.1 ships. The two projects do not parallel-run in production (port-3456 conflict, single launchd service slot, one set of credentials per machine). OCP itself is not deleted. Per spec §7, OCP enters maintenance mode when OLP v0.1 ships. ~~The two projects do not parallel-run in production (port-3456 conflict, single launchd service slot, one set of credentials per machine).~~ **Amended at D60 (2026-05-26, ADR 0010 Phase 4 charter):** the port-conflict assumption is lifted. OLP's default port moved `3456 → 4567` at v0.4.0 so OCP (which stays on 3456) and OLP can co-host on the same machine during a transition window. Launchd label collision **will be** avoided via `dev.olp.proxy` (OLP plist generation lands at Phase 4 close per ADR 0010 D64D70; not on disk at D60) vs `dev.ocp.proxy` (OCP, already shipped). Credentials remain per-project (`~/.ocp/` vs `~/.olp/`). Co-host is explicit-opt-in, not the recommended steady state.
OCP ADR 0005 receives a header amendment on merge of this ADR: *"Superseded in part by OLP — see https://github.com/dtzp555-max/olp ADR 0001 for the narrow scope of the supersession (single-provider-sufficiency premise only; ADR 0005's commercial / BYOK / no-spawn recommendations are not adopted)."* The body of ADR 0005 is otherwise untouched. Future readers should see the original reasoning intact and the supersession marker scoped explicitly. OCP ADR 0005 receives a header amendment on merge of this ADR: *"Superseded in part by OLP — see https://github.com/dtzp555-max/olp ADR 0001 for the narrow scope of the supersession (single-provider-sufficiency premise only; ADR 0005's commercial / BYOK / no-spawn recommendations are not adopted)."* The body of ADR 0005 is otherwise untouched. Future readers should see the original reasoning intact and the supersession marker scoped explicitly.
+1 -1
View File
@@ -195,7 +195,7 @@ The dashboard sets a 30s `setInterval` that calls `fetch('/v0/management/dashboa
### 6.6 Localhost-bound by default ### 6.6 Localhost-bound by default
The dashboard is served from the existing OLP HTTP port (default 3456) which is already bound to `127.0.0.1` per `server.mjs` startup (`server.listen(PORT, '127.0.0.1', ...)`). No additional binding logic. Remote operators access via SSH tunnel; ADR 0007 § 7 owner-only auth provides the per-request gate. The dashboard is served from the existing OLP HTTP port (default 4567 since v0.4.0 / D60; 3456 pre-v0.4.0) which is already bound to `127.0.0.1` per `server.mjs` startup (`server.listen(PORT, '127.0.0.1', ...)`). No additional binding logic. Remote operators access via SSH tunnel; ADR 0007 § 7 owner-only auth provides the per-request gate.
--- ---
@@ -0,0 +1,162 @@
# ADR 0010 — Phase 4 Charter: Operator + Client UX
**Status:** Accepted (Phase 4 open as of 2026-05-26)
**Date:** 2026-05-26
**D-day:** D60 (charter + default port change)
---
## Context
Phases 1 — 3 shipped OLP's structural core: HTTP entry surface, IR, provider plugins, fallback engine, content-addressed cache (including streaming-path singleflight at D57+D58 → v0.3.2), multi-key auth + audit ndjson + daily rotation, owner-only management endpoints + dashboard. v1.x roadmap items #1 / #2 / #4 / #7 are closed. Items #3 / #5 / #6 remain trigger-gated.
Two complementary brainstorm passes (2026-05-26) — a comprehensive OCP feature audit + a multi-provider proxy / IDE integration prior-art survey — converged on a clear gap: **OLP's operator and client surfaces are 0% inherited from OCP**. Today OLP has `bin/olp-keys` and `bin/olp-audit-rotate` as the entire operator CLI, no `olp doctor` / no `olp-connect`, no Telegram/Discord integration, no SSE heartbeat for long-running streams behind reverse proxies. Family members get OLP API keys via out-of-band paste, point their IDEs at OLP via the README's one-line example, and discover failure modes via curl. OCP's UX worked because of a load-bearing combination: README `paste-this-prompt-to-Claude-Code` instructions + machine-readable `ocp doctor next_action.ai_executable[]` + `ocp-connect` zero-config LAN setup + `/health.anonymousKey` self-advertising token + `/ocp` Telegram slash commands. **Phase 4 brings these forward as OLP-native primitives.**
A separate strategic decision — should OLP add `/v1/messages` (Anthropic-shape entry surface) for Claude Code support — was considered and **rejected for Phase 4** (see § "Out of Phase 4 scope" below). The decision is recorded with an explicit re-open trigger.
---
## Decision
Phase 4 scope is **Operator + Client UX**. The phase opens 2026-05-26 with D60 (this charter + default port change). Phase 4 close ships v0.4.0; per `CLAUDE.md release_kit.phase_rolling_mode`, the close PR is maintainer-triggered.
### In scope — Phase 4 D-day plan (~13 D-days)
| D-day | Deliverable | Authority | Estimate |
|---|---|---|---|
| **D60** | Default port `3456 → 4567` + this ADR 0010 charter + README / CHANGELOG / ADR 0001 + ADR 0008 amendments | This charter | 0.5d |
| **D61 — D63** | SSE heartbeat (opt-in via `streaming.heartbeat_interval_ms` config; eager-headers-post-spawn; `X-Accel-Buffering: no` constant) + `recentErrors[20]` ring buffer + `/status` combined endpoint | Port OCP `server.mjs:660-685` + `301-358` + `1151-1188`; OCP `docs/superpowers/specs/2026-04-25-47-sse-heartbeat-design.md` | 2.5d |
| **D64 — D67** | `olp` Node-based CLI scaffold (subcommands `status / health / usage / models / logs / cache / providers / chain show / restart / doctor`) + `olp doctor` machine-readable `next_action.ai_executable[]` framework + one fix-template per shipped provider plugin | Port OCP `ocp` bash wrapper (translated to Node — bash dep on python3 is a known fragile point) + OCP `scripts/doctor.mjs` framework | 4d |
| **D68 — D70** | `olp-connect <ip>` client-side IDE auto-config (Cline / Continue.dev / Cursor / Aider / Claude Code / OpenClaw detection) + `/health.anonymousKey` field (opt-in via `auth.advertise_anonymous_key` config; default off) + ADR 0011 (anonymous-key deployment-context limits — trusted-LAN-only invariant explicit) | Port OCP `ocp-connect` + `server.mjs:1454,1488` | 3d |
| **D71 — D73** | `olp-plugin/` (OpenClaw gateway plugin for `/olp` Telegram/Discord slash commands; subcommand parity with `olp` CLI minus mutations) + `docs/integrations/{continue.md,cline.md,cursor.md,aider.md,claude-code.md,openclaw.md}` IDE setup docs | Port OCP `ocp-plugin/index.js`; cross-ref Prior-Art § 3 + § 4 | 3d |
| **close** | v0.4.0 release PR — `package.json` bump, CHANGELOG promotion, `release_kit.phase_rolling_mode` advance to Phase 5 pre-release identifier | `CLAUDE.md release_kit overlay` | maintainer-triggered |
### Out of Phase 4 scope (with explicit triggers)
#### `/v1/messages` — Anthropic-shape entry surface
**Status:** Deferred. Re-enable strictly gated on ADR 0009 P0 success.
**Value matrix (decisive):**
| Scenario | Without `/v1/messages` | With `/v1/messages` |
|---|---|---|
| Maintainer's own Claude Code usage | Direct via Anthropic OAuth → subscription (today) or Agent SDK pool (post-2026-06-15) | Same — maintainer never routes own CC through OLP per stated workflow |
| Family member wanting CC access | Not supported (OAuth is full-account; OLP CLI tokens are scoped) | CC via `ANTHROPIC_BASE_URL=http://olp:4567` + `olp_*` token |
| **P0 succeeds** (ADR 0009 interactive-mode bills as subscription) | OpenAI-shape IDE clients (Cline/Continue/Cursor) all benefit automatically via OLP's anthropic plugin | CC users additionally benefit; both subscription-billed |
| **P0 fails** (interactive-mode bills as Agent SDK same as `-p`) | OpenAI-shape clients still work; no billing change | CC users get same billing as direct OAuth; **fallback to codex/mistral degrades Anthropic-specific features (tool_use schema mismatch / cache_control drop / computer_use no-op / thinking-block drop)** more severely than OpenAI-shape clients which speak the multi-provider lingua franca |
**Rationale.** Under P0 failure, `/v1/messages` provides no billing benefit AND degrades worse on fallback than OpenAI-shape clients (because OpenAI tool schema is the cross-provider standard). The security benefit (no OAuth exposure) is equally achievable via Cline/Continue/Cursor. **Net non-positive under P0 failure.**
**Re-open condition.** (a) ADR 0009 P0 confirms interactive-mode billing classification as subscription (≥ 2026-07-15) AND (b) maintainer explicitly opens Phase 5 "Anthropic-shape hub" scope with the name of at least one family member who wants CC access. If only (a) fires without (b), `/v1/messages` is reconsidered at the start of whichever phase covers it but is not auto-opened.
**README posture (Phase 4).** README § Supported Clients explicitly lists OpenAI-compatible clients (Cline, Continue.dev, Cursor, Aider, OpenClaw bots). Claude Code is listed as **Not supported as an OLP client**, with the explicit alternative "Cline + OLP" (same fallback chain available, better cross-provider compatibility). README links to this ADR for the reasoning.
#### Other deferred items
- **v1.x roadmap #3 (soft trigger reactivation)**, **#5 (provider `cacheKeyFields` mask)**, **#6 (streaming SPAWN_FAILED salvage)** — trigger conditions per `docs/v1x-roadmap.md` have not fired. Not in Phase 4.
- **Anthropic / codex billing audits** — date-gated (`anthropic.mjs:53, 416, 441` say 2026-06-16; `codex.mjs:572` post-D7 E2E audit). Not in Phase 4.
- **`context_window_exceeded` fallback trigger** (LiteLLM prior-art) — small ADR amendment + trigger taxonomy add; opportunistically in Phase 5 unless trigger fires sooner.
- **`X-OLP-Cost-USD` per-request response header** — depends on provider-cost weights table (Phase 5 prerequisite).
- **per-(provider, model) live stats Map** (replacing audit-query scan for dashboard 30s poll) — current scan latency adequate; Phase 5+.
- **OpenTelemetry GenAI span emission** — `npm` dep + ~150 LOC; family-scale ROI marginal. Phase 6+ unless Langfuse self-host requested.
- **Intent-based routing**, **stackable transformer plugin model** — explicit non-goals per Prior-Art § 8 anti-patterns.
### Opportunistic Phase 4 micro-additions (not blocking)
Items small enough to land alongside a planned D-day without scope creep, if encountered:
- Env-var deny-list before provider plugin `spawn` (per OCP `server.mjs:531-534`; each plugin declares its own list)
- 5 MB request body cap with HTTP 413 (per OCP `server.mjs:1270,1278-1281`)
- Error-response path-sanitization (per OCP `server.mjs:1395`)
- Stable node-path resolution in launchd plist (Homebrew `/Cellar/<ver>/``/opt/` rewrite; per OCP `setup.mjs:344-351`)
- Legacy model alias resolution in `models-registry.json` (`aliases:` field; per OCP `legacyAliases`)
### Exit gate — v0.4.0 close criteria
1. D60 — D73 all merged with fresh-context opus reviewer APPROVE per Iron Rule 10.
2. CI green on every D-day merge commit and on the v0.4.0 release commit head.
3. README § Operator CLI + § IDE Setup + § Telegram/Discord Usage sections present.
4. ADR 0010 (this charter) + ADR 0011 (anonymous-key deployment-context limits) on disk.
5. `CHANGELOG.md "Unreleased"` promoted to `"## v0.4.0 — <date>"` with D60 — D73 entries.
6. `package.json` bumped to `0.4.0`.
7. `CLAUDE.md release_kit.phase_rolling_mode.current_phase` advances `Phase 4 → Phase 5`; `current_pre_release_identifier` advances `0.4.0-phase4 → 0.5.0-phase5`.
8. Standing autopilot grant covers D-day-by-D-day execution; v0.4.0 close PR is maintainer-triggered.
---
## Default port change (D60 specific)
The default `OLP_PORT` value moves `3456 → 4567` at this D-day. Rationale:
- OCP defaults to 3456 and the maintainer's existing OCP installs stay on 3456 indefinitely.
- A standard `olp` install on the same host without overriding `OLP_PORT` collides at bind time.
- Setting `OLP_PORT=4567` as the default makes co-host the recommended steady state during the migration window (and beyond — there is no enforced deprecation of OCP).
- Existing OLP deployments wanting the pre-D60 default can set `OLP_PORT=3456` in the launchd plist / shell env.
**Tested invariants preserved by the port change:**
- All `test-features.mjs` suites use `port: 0` (ephemeral assigned port) — no test depends on the default value. Verified via `grep -nE '\\b3456\\b' test-features.mjs` returning empty.
- All cache / fallback / provider plugin code is port-agnostic.
- Dashboard 30s poll uses relative paths — no port change required in `dashboard.html`.
- `/v0/management/*` endpoints use relative paths — no client-side update required.
**Files amended at D60:**
- `server.mjs:17` — env-var doc comment
- `server.mjs:74` — default value
- `README.md` quick start + Environment Variables table + Migration from OCP § note
- `docs/adr/0001-project-founding.md` § "Decision" paragraph about port conflict (struck and amended)
- `docs/adr/0008-dashboard-and-audit-query.md` § 6.6 port reference
- `CHANGELOG.md` Unreleased entry
- This ADR
---
## Consequences
**Positive.**
- Family member onboarding goes from "maintainer texts API key + edits IDE config" to `curl -fsSL .../olp-connect | bash -s -- <ip>`.
- `paste-this-prompt-to-Claude-Code` self-installation pattern unlocks AI-driven setup / upgrade / repair, eliminating the maintainer's Tier-1 support role.
- Long-reasoning streams behind nginx / Cloudflare / Tailscale Funnel no longer 502 at 60s idle.
- `/olp` Telegram slash commands enable "is OLP up?" / "show usage" / "rotate key" from anywhere with chat access.
- OCP and OLP co-host on the same workstation, lowering the maintainer's cost of running both.
**Negative.**
- Phase 4 is the first phase whose scope is primarily about *operator experience* rather than functional capability. The work doesn't unlock new requests OLP can serve; it makes OLP's existing capability survive contact with real users.
- The `olp-connect` IDE auto-detect logic accumulates IDE-specific quirks (Cline base-URL UI regressions per their issue #7128; Cursor's malformed-request-when-OpenRouter behavior; etc.). Maintenance burden grows.
- README size grows substantially with Operator CLI + IDE Setup + Telegram/Discord sections. Discoverability of the existing technical reference (ADRs, environment variables) may degrade unless the navigation is refactored.
**Neutral.**
- Phase 4 deliberately spends 0 D-days on `/v1/messages`. If ADR 0009 P0 succeeds in Q3 2026, Phase 5 "Anthropic-shape hub" becomes the natural next phase, with the prerequisite IR work that Phase 4 surfaces (every IDE doc page is a test of which IR fields actually flow through). If P0 fails, `/v1/messages` shelves indefinitely and the README simply documents CC as out-of-scope.
---
## Alternatives considered
1. **Phase 4 = `/v1/messages` first, operator UX later.** Rejected. The brainstorm matrix demonstrated `/v1/messages` is value-positive only if ADR 0009 P0 succeeds, and operator UX gains accrue regardless. Building speculative infrastructure ahead of P0 risks 5-7 D-days of work shelving.
2. **Phase 4 = operator + client UX + `/v1/messages` together (full kitchen sink).** Rejected. ~20 D-days lengthens the Phase 4 close window unnecessarily; the natural review chunks blur; maintainer review fatigue is real.
3. **Phase 4 = just D60 + opportunistic SSE heartbeat, no CLI / no plugin / no docs bundle.** Rejected. Each of the operator-UX items individually has small ROI; the value compounds when they ship together (CLI surfaces data → `/status` exposes shape → Telegram plugin renders → IDE docs reference → `olp-connect` automates). Splitting them across phases loses the compounding.
4. **Defer Phase 4 entirely; jump to Phase 5 Anthropic-shape hub when P0 lands.** Rejected. Operator UX is needed now (this session is itself evidence — the maintainer spent ~30 minutes confirming OCP feature inheritance because there's no `olp doctor` answer). Waiting for P0 stalls progress on independently-valuable work.
---
## Authority
- `docs/v1x-roadmap.md` — Phase 4 was named as the canonical destination for the post-cleanup batch since v0.3.0 close.
- `CLAUDE.md release_kit.phase_rolling_mode``current_phase: Phase 4` already; this charter formalizes the contents.
- OCP comprehensive feature audit (2026-05-26 subagent output, summarized in `~/.cc-rules/memory/auto/MEMORY.md` and in this session's transcript).
- Multi-provider proxy / IDE integration prior-art survey (2026-05-26 subagent output).
- ADR 0009 (Anthropic interactive-mode path placeholder) — establishes the gate for `/v1/messages` re-consideration.
- ADR 0001 (project founding) — § "Decision" paragraph about port conflict, amended at this D-day.
- ADR 0008 (dashboard + audit query) — § 6.6 default-port reference, amended at this D-day.
- `~/.cc-rules/memory/auto/standing_autopilot_phase_2.md` — standing autopilot grant covering D-day-by-D-day execution; v0.4.0 close PR is maintainer-triggered per `release_kit.phase_close_trigger`.
---
## Procedural mechanism
CC 开发铁律 v1.6 § 5.5 (release-kit overlay drives Phase boundaries) + § 10 (independent reviewer on every implementation D-day) + § 11 (minimum reviewable unit per PR — this charter ships as D60 PR alongside the default port change because both are governance-class and small).
+1
View File
@@ -23,6 +23,7 @@ New ADRs increment from the highest existing number. Filenames are `NNNN-<short-
| [0007](0007-multi-key-auth.md) | Multi-Key Auth (`lib/keys.mjs`) | Phase 2 design ADR (D43-B, 2026-05-25). Option 2 (filesystem manifest at `~/.olp/keys/<key-id>/manifest.json`) + opaque `olp_<32-byte>` token + SHA-256 hash. Owner / guest / anonymous tier gating with explicit `config.json auth.allow_anonymous` (default false). Bootstrap keygen command surface + `OLP_OWNER_TOKEN` env override with stable synthetic `key_id`. Audit ndjson append-only at `~/.olp/logs/audit.ndjson`, warn+1-retry on append failure. Rejects direct SQLite port at v0.2.0 due to Node baseline (`engines >=18` + CI 20/24 vs `node:sqlite` added 22.5.0 / RC); Option 3 hybrid documented as forward path when Phase 3+ Dashboard / SQL-aggregate quota arrives. | | [0007](0007-multi-key-auth.md) | Multi-Key Auth (`lib/keys.mjs`) | Phase 2 design ADR (D43-B, 2026-05-25). Option 2 (filesystem manifest at `~/.olp/keys/<key-id>/manifest.json`) + opaque `olp_<32-byte>` token + SHA-256 hash. Owner / guest / anonymous tier gating with explicit `config.json auth.allow_anonymous` (default false). Bootstrap keygen command surface + `OLP_OWNER_TOKEN` env override with stable synthetic `key_id`. Audit ndjson append-only at `~/.olp/logs/audit.ndjson`, warn+1-retry on append failure. Rejects direct SQLite port at v0.2.0 due to Node baseline (`engines >=18` + CI 20/24 vs `node:sqlite` added 22.5.0 / RC); Option 3 hybrid documented as forward path when Phase 3+ Dashboard / SQL-aggregate quota arrives. |
| [0008](0008-dashboard-and-audit-query.md) | Dashboard + Audit Query Layer | Phase 3 design ADR (D48, 2026-05-25). Static HTML dashboard + vanilla JS + fetch (no build step). In-memory ndjson scan for aggregate queries (O(N) per call; family-scale acceptable; defers SQLite migration to Option 3 hybrid trigger). Daily audit rotation `audit-YYYY-MM-DD.ndjson` on first append after UTC midnight; cross-file query layer for rolling 30-day windows. Owner-only gating on `/dashboard` + 3 `/v0/management/*` JSON endpoints reusing ADR 0007 § 7 auth model. 30s page poll (no SSE infra). Panels: per-provider quota / 24h request+cache+fallback / 30d spend trend / top-N fallback chains per spec § 4.6. Opens ADR 0007 § 12 Phase 3 deferral (Dashboard + audit query + rotation). | | [0008](0008-dashboard-and-audit-query.md) | Dashboard + Audit Query Layer | Phase 3 design ADR (D48, 2026-05-25). Static HTML dashboard + vanilla JS + fetch (no build step). In-memory ndjson scan for aggregate queries (O(N) per call; family-scale acceptable; defers SQLite migration to Option 3 hybrid trigger). Daily audit rotation `audit-YYYY-MM-DD.ndjson` on first append after UTC midnight; cross-file query layer for rolling 30-day windows. Owner-only gating on `/dashboard` + 3 `/v0/management/*` JSON endpoints reusing ADR 0007 § 7 auth model. 30s page poll (no SSE infra). Panels: per-provider quota / 24h request+cache+fallback / 30d spend trend / top-N fallback chains per spec § 4.6. Opens ADR 0007 § 12 Phase 3 deferral (Dashboard + audit query + rotation). |
| [0009](0009-interactive-mode-path-placeholder.md) | Anthropic Interactive-Mode Path (Placeholder) | Placeholder ADR (2026-05-25, Draft) — blocked on OCP ADR 0007 P0 experiment outcome. Records the maintainer's "wait + port" decision: do NOT independently implement; ride OCP's P0 result. If P0 confirms Transport A (stdio NDJSON) or B (PTY) bills as subscription rather than Agent SDK credit, port to OLP `lib/providers/anthropic.mjs` (Option 1 parallel impl, or Option 2 OCP-as-backend; decision deferred to P0-resolution time). If P0 fails on both, shelve. No Phase 4 D-day scheduled until P0 lands AND maintainer issues explicit "go" naming this ADR. | | [0009](0009-interactive-mode-path-placeholder.md) | Anthropic Interactive-Mode Path (Placeholder) | Placeholder ADR (2026-05-25, Draft) — blocked on OCP ADR 0007 P0 experiment outcome. Records the maintainer's "wait + port" decision: do NOT independently implement; ride OCP's P0 result. If P0 confirms Transport A (stdio NDJSON) or B (PTY) bills as subscription rather than Agent SDK credit, port to OLP `lib/providers/anthropic.mjs` (Option 1 parallel impl, or Option 2 OCP-as-backend; decision deferred to P0-resolution time). If P0 fails on both, shelve. No Phase 4 D-day scheduled until P0 lands AND maintainer issues explicit "go" naming this ADR. |
| [0010](0010-phase-4-charter-operator-and-client-ux.md) | Phase 4 Charter — Operator + Client UX | Phase 4 scope ratification (2026-05-26, Accepted). Phase 4 = operator + client UX (SSE heartbeat / `olp` CLI + doctor / `olp-connect` zero-config + Telegram-Discord plugin + IDE docs bundle). ~13 D-days, D60 → v0.4.0. Records the explicit decision to DEFER `/v1/messages` (Anthropic-shape entry surface) on the rationale that under ADR 0009 P0 failure it provides no billing benefit AND degrades worse on fallback than OpenAI-shape clients. Re-open trigger: ADR 0009 P0 success + maintainer-named family CC user. Also closes the OCP-OLP port co-host ambiguity from ADR 0001 (default `OLP_PORT` 3456 → 4567). |
## When to write a new ADR ## When to write a new ADR
+5 -2
View File
@@ -14,7 +14,10 @@
* - Zero runtime npm dependencies in the proxy core * - Zero runtime npm dependencies in the proxy core
* *
* Env vars: * Env vars:
* OLP_PORT — listen port (default: 3456) * OLP_PORT — listen port (default: 4567 since D60 / v0.4.0 — moved off 3456
* so OLP can co-host with OCP for migration windows. ADR 0010 §
* Default port. Set OLP_PORT=3456 explicitly to restore the
* pre-D60 default when not co-hosting with OCP.)
*/ */
import { createServer } from 'node:http'; import { createServer } from 'node:http';
@@ -71,7 +74,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8')); const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
const VERSION = pkg.version; const VERSION = pkg.version;
const PORT = parseInt(process.env.OLP_PORT ?? '3456', 10); const PORT = parseInt(process.env.OLP_PORT ?? '4567', 10);
const BODY_LIMIT = 5 * 1024 * 1024; // 5 MB const BODY_LIMIT = 5 * 1024 * 1024; // 5 MB
// ── Logging ─────────────────────────────────────────────────────────────── // ── Logging ───────────────────────────────────────────────────────────────