feat+docs+release(v0.4.3): D76 — README install-path overhaul + OLP_BIND env + AI-install prompt + ADR 0011 amendment (#48)

10 README install gaps catalogued and fixed in one D-day after v0.4.2's
PI231 E2E session exposed that the v0.4.0-v0.4.2 README Quick Start was
fictional (npm package isn't published; olp setup/start commands don't
exist). F5 (OLP_BIND env) ships in the same patch so the documented LAN
onboarding flow actually works. AI-driven install prompt added per Phase 4
charter brainstorm Top-5 inheritance candidate #2 (D64-D67 built the
doctor framework; D76 closes the README half).

## G1-G7: README Quick Start now real

Rewrote § "Manual install" from placeholder text to the empirically-verified
sequence:
- Prerequisites (Node >= 18 + provider CLI install matrix)
- git clone + npm test verify
- olp-keys keygen --owner FIRST (allow_anonymous=false default needs a key)
- Per-provider OAuth (claude setup-token / codex login --device-auth /
  MISTRAL_API_KEY)
- ~/.olp/config.json with the minimum that actually serves traffic
- npm start
- Smoke-test via curl + olp doctor
- IDE pointing

## G8 / F5: OLP_BIND env shipped

server.mjs:
- const BIND = process.env.OLP_BIND ?? '127.0.0.1' (safe default unchanged)
- server.listen(PORT, BIND, ...) replaces hard-coded '127.0.0.1'
- New startup warn anonymous_key_advertised_with_lan_bind fires when
  OLP_BIND is non-loopback AND auth.advertise_anonymous_key: true
  (operator visibility into trust-context overlap)

Pre-D76 the server only accepted loopback connections, so the documented
olp-connect <ip> family-onboarding flow was unreachable from LAN without
SSH tunneling. F5 makes ADR 0011 operational instead of aspirational.

## G10: AI-driven install prompt

README § "Install with your AI (the fast path)" — verbatim prompt the
operator pastes into Claude Code / Cursor / Copilot / Aider. The AI
follows README + uses olp doctor --json next_action.ai_executable[] for
self-repair, stopping only when human_required[] is non-empty (provider
OAuth dances). Closes the Phase 4 brainstorm #2 inheritance candidate.

## Opening compressed

§ "Why OLP" (3 paragraphs of Anthropic 2026-06-15 billing history)
removed from the top. The OCP-trigger context moved to § "Migration
from OCP" at the bottom, condensed into a single paragraph. New users
land on value-prop + § "What you get" + install paths without needing
to digest 2026-05-14 billing history first. OCP users get a one-line
pointer at the top.

## Configuration + Env Variables sections updated

- § Configuration: placeholder replaced with full ~/.olp/config.json
  schema documentation, every field cross-referenced to its ADR
- § Environment Variables: added OLP_BIND, OLP_API_KEY, OLP_OWNER_TOKEN,
  OLP_PROXY_URL rows that were used in the manual-install flow but
  previously undocumented

## ADR 0011 § Deployment configurations amendment

Codifies the three deployment trust contexts:
- 127.0.0.1 (loopback only) — safe with any auth posture
- RFC1918 / tailnet / specific LAN IP — anonymous_key OK (the documented
  trusted-LAN zero-config family onboarding flow)
- Public IP — incompatible with advertise_anonymous_key: true

Documents the new anonymous_key_advertised_with_lan_bind startup warn
event. Closes ADR 0011's pre-D76 dangling reference to a non-existent
BIND_ADDRESS concept.

## Test count

714 (v0.4.2) -> 717 (v0.4.3). +3 D76 regression tests in Suite 36
(36s/36t/36u) pinning OLP_BIND wiring + safety warn + ADR amendment.

## Process learning

Every D-day reviewer rubric should add "open README §-Quick-Start and
verify the commands literally exist + work in the current repo" — would
have caught G1-G7 at v0.4.0 close. Combined with D74 (review-against-spec)
+ D75 (review-without-deployment), D76 (review-without-following-README)
codifies the third tier of review discipline.

## Out of D76 scope (deferred)

- F6 (doctor client-side vs server-side check separation) — needs design
  ADR for --remote mode. Phase 5.
- D75 reviewer P2-1 (ADR 0004 per-hop schema amendment) + P2-2 (defensive
  typeof hopModel === 'string') — both genuine follow-ups, neither
  blocking.
- scripts/migrate-from-ocp.mjs — Phase 7.

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 13:32:02 +10:00
committed by GitHub
co-authored by taodeng Claude Opus 4.7
parent 6edf6e0b94
commit 6605b7b14a
6 changed files with 327 additions and 41 deletions
+217 -39
View File
@@ -1,55 +1,204 @@
# OLP — Open LLM Proxy
A personal- and family-scale multi-provider LLM proxy. One HTTP endpoint, many subscriptions behind it, automatic routing, automatic fallback, content-addressed caching — so your IDEs and family clients keep working as long as *any* of your subscriptions has quota left.
A personal- and family-scale multi-provider LLM proxy. One HTTP endpoint, many subscriptions behind it, automatic routing + fallback + content-addressed caching. Your IDEs and family clients keep working as long as **any** of your subscriptions has quota left.
> **Status:** v0.4.0 shipped (2026-05-26) — Phase 1 multi-provider proxy core (v0.1.0 + v0.1.1) + Phase 2 multi-key auth + audit + owner gating + keygen CLI (v0.2.0) + Phase 3 Dashboard + audit query layer + daily audit rotation (v0.3.0) + Phase 4 Operator + Client UX (v0.4.0): SSE heartbeat / `olp` Node CLI + `olp doctor` framework / `olp-connect` zero-config LAN setup / `/health.anonymousKey` opt-in / `/olp` Telegram-Discord plugin / 6-IDE integration docs. Phase 5 scope is open — candidates per ADR 0010 § Out-of-Phase-4-scope: `/v1/messages` (gated on ADR 0009 P0 outcome + named family CC user), context-window-exceeded fallback trigger, per-(provider, model) live stats. Sections marked _placeholder_ land alongside the relevant phase of work (see [phase plan](#phase-plan)).
> **Status:** v0.4.3 shipped, 714+ tests. Phase 4 (Operator + Client UX) closed; Phase 5 scope is open. Coming from [OCP](https://github.com/dtzp555-max/ocp)? See [§ Migration from OCP](#migration-from-ocp).
---
## Why OLP
## What you get
On 2026-05-14, Anthropic announced (effective 2026-06-15) that `claude -p`, the Agent SDK, and third-party agent traffic move out of the Pro/Max subscription pool into a separate fixed monthly Agent SDK Credit pool. [OCP](https://github.com/dtzp555-max/ocp), OLP's predecessor, was a proxy around a single CLI — its core assumption was *"subscription = unlimited within rate limits"*. That assumption breaks for Anthropic on the effective date.
The structural response is to stop relying on one provider's subscription terms remaining favourable. OLP spreads risk across multiple providers whose subscriptions still include CLI/programmatic use, routes intelligently between them, and caches aggressively so every request that does spawn a CLI counts.
OLP is **not**: a commercial multi-tenant SaaS; an enterprise gateway competing with LiteLLM / OpenCode / CLIProxyAPI on breadth; a model-capability router ("route to the smartest model" — you pick the model); a conversation-state store (your client handles that).
See [`ALIGNMENT.md`](./ALIGNMENT.md) for OLP's constitution and [`docs/adr/`](./docs/adr/) for the founding ADRs.
- **OpenAI-compatible** `/v1/chat/completions` endpoint — any IDE that speaks OpenAI (Cline / Continue.dev / Cursor / Aider) plugs in
- **Multi-provider chain** — primary fails / quota dies → automatically falls back to the next provider (anthropic ↔ codex ↔ mistral by default; risk-tier framework guards which ones get enabled)
- **Content-addressed cache** — repeat requests don't re-spawn the CLI; streaming requests dedup via singleflight tee
- **Multi-key auth** — owner key with full visibility, family-member keys with per-key audit log + per-provider scoping
- **Telegram / Discord** `/olp` slash commands (read-only — for "is OLP up?" checks from anywhere)
- **AI-driven self-repair** — `olp doctor --json` emits machine-readable `next_action.ai_executable[]` so a Claude Code / Cursor / Copilot session can fix install issues for you (see [§ Install with your AI](#install-with-your-ai-the-fast-path))
- **Observability** — owner-only `/dashboard` (quota / 24h stats / 30d spend trend / top fallback chains)
---
## Quick Start
## Install with your AI (the fast path)
_placeholder — lands with Phase 1._
If the manual steps feel like a lot, paste this verbatim into your AI coding assistant (Claude Code / Cursor / Copilot / Aider). It walks you through everything:
Anticipated shape:
```
I want to install OLP (Open LLM Proxy) on this machine. The repo is at
https://github.com/dtzp555-max/olp and ships these tools that you can use:
```bash
# install
npm install -g @dtzp555-max/olp
1. `git clone` the repo to ~/olp
2. README.md § "Manual install" has the sequenced steps. Follow them in order.
3. After EACH step, run `node ~/olp/bin/olp.mjs doctor --json` (NOT `npx olp`
— the package isn't on npm). Parse the JSON. The `kind` field tells you
the state: noop / fresh_install / fix_server / fix_oauth / fix_provider /
fix_config.
4. If `kind != noop`, run the commands listed in `next_action.ai_executable[]`
verbatim. Then re-run doctor to verify.
5. STOP and ask me only when `next_action.human_required[]` is non-empty.
That's where I need to do a browser OAuth flow you can't do for me.
# run setup (writes ~/.olp/config.json, asks which providers to enable)
olp setup
The provider CLIs OLP spawns (claude / codex / vibe) need their own one-time
OAuth — those are the only steps I personally have to do (Claude.ai login,
ChatGPT login, Mistral API key). Everything else (clone, npm install of the
provider CLIs, owner-key generation, config.json bootstrap, server start) is
in your `ai_executable[]` and you should run it without asking.
# 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
# point your IDE at http://localhost:4567/v1/chat/completions with the OLP API key from `olp keys list`.
Begin.
```
**Family-on-LAN onboarding (D68-D70).** For other devices on the same network, run on the client device:
Then sit back and respond when it asks for OAuth confirmation. This pattern works because `olp doctor` is purpose-built for AI consumption — every failure mode has a shell-executable repair command AND a human-required step listed separately.
---
## Manual install (5-10 min)
### 0. Prerequisites
- **Node.js ≥ 18.** Verify: `node --version`
- **The provider CLIs you want OLP to spawn.** Install whichever you'll actually use:
| Provider | Install | Subscription |
|---|---|---|
| `anthropic` (`claude -p`) | `npm install -g @anthropic-ai/claude-code` | Claude Pro/Max (OAuth) |
| `openai` (`codex exec`) | `npm install -g @openai/codex` | ChatGPT Plus/Pro (OAuth) or OpenAI API key |
| `mistral` (`vibe --prompt`) | follow the `vibe` install docs | Le Chat Pro API key |
You only need to install the ones you'll route to. Single-provider OLP works fine.
### 1. Clone and verify the test suite
```bash
# Detects Cline / Continue.dev / Cursor / Aider / OpenClaw installed locally
# and writes per-tool config pointing at the OLP host. Requires `python3`.
olp-connect <olp-host-ip>
git clone https://github.com/dtzp555-max/olp.git ~/olp
cd ~/olp
npm test # 714+ tests, ~5s, no external deps
```
If the OLP host has `auth.advertise_anonymous_key: true` AND a key was created with `olp-keys keygen --anonymous --advertise`, `olp-connect` picks up the token from `/health.anonymousKey` — zero out-of-band token paste required. See [ADR 0011](./docs/adr/0011-anonymous-key-deployment-context.md) for the trusted-LAN-only invariant.
(If `npm test` fails here, stop — that means your Node version or the repo state is broken. Don't proceed to step 2.)
Per-IDE setup details: [`docs/integrations/`](./docs/integrations/README.md).
### 2. Bootstrap the owner key
The owner key is what you (and `olp-connect`) use to authenticate to OLP. Default config has `auth.allow_anonymous: false`, so you need a key BEFORE the server starts accepting requests.
```bash
node ~/olp/bin/olp-keys.mjs keygen --owner --name=$(whoami)-laptop
# Prints the plaintext token ONCE. Copy it now — you can't recover it later.
# Example: olp_l23-PN46tDljmPATV94-KfOgOBO0Ed8theVjTdAgQoY
```
Export it so the CLI subcommands can use it:
```bash
export OLP_API_KEY=olp_l23-PN46... # paste your real token
```
(Add to `~/.bashrc` / `~/.zshrc` to persist.)
### 3. Authenticate the providers (one-time OAuth)
Run each provider's own login flow. OLP's anthropic / openai / mistral plugins spawn these CLIs and reuse their cached credentials — OLP itself never touches the OAuth dance.
```bash
# Anthropic (Claude Pro/Max subscription)
claude setup-token
# Opens a TUI / prints a URL. Authorize in browser. Paste the returned code.
# Result: ~/.claude/.credentials.json
# OpenAI (ChatGPT subscription)
codex login --device-auth
# Prints a https://auth.openai.com/codex/device URL + 10-char code.
# Open URL in browser, enter code, authorize.
# Result: ~/.codex/auth.json
# Mistral (Le Chat API key)
export MISTRAL_API_KEY=sk-... # add to ~/.bashrc to persist
```
### 4. Write a minimum config
`~/.olp/config.json`:
```json
{
"auth": {
"allow_anonymous": false,
"owner_only_endpoints": [
"/health",
"/v0/management/dashboard-data",
"/v0/management/quota",
"/v0/management/status",
"/cache/stats",
"/dashboard"
],
"fallback_detail_header_policy": "owner_only"
},
"providers": {
"enabled": { "anthropic": true, "openai": true }
},
"routing": {
"chains": {
"claude-sonnet-4-6": [
{ "provider": "anthropic", "model": "claude-sonnet-4-6" },
{ "provider": "openai", "model": "gpt-5.5" }
],
"gpt-5.5": [{ "provider": "openai", "model": "gpt-5.5" }]
}
},
"streaming": { "heartbeat_interval_ms": 15000 }
}
```
(Enable only the providers you actually authenticated in step 3. Chains map `<your-IDE's-requested-model>` → ordered list of `{provider, model}` hops; the chain's per-hop `model` is what gets passed to that provider's CLI.)
### 5. Start the server
```bash
cd ~/olp
npm start
# OLP v0.4.3 listening on :4567 (2 providers enabled)
```
### 6. Smoke-test
```bash
curl -H "Authorization: Bearer $OLP_API_KEY" http://localhost:4567/health | jq
# Expect: {ok: true, providers: {enabled: 2, status: {anthropic: {ok: true...}, openai: {ok: true...}}}}
node ~/olp/bin/olp.mjs doctor
# Expect: "9 of 9 checks passed", kind=noop
```
### 7. Point your IDE at OLP
```
OPENAI_BASE_URL=http://localhost:4567/v1
OPENAI_API_KEY=$OLP_API_KEY
```
Per-IDE configuration details: [`docs/integrations/`](./docs/integrations/README.md).
---
## Family / LAN setup
To let other devices on your home network use the same OLP server, you need TWO things:
1. **Bind to the LAN interface** (not just loopback). On the SERVER:
```bash
OLP_BIND=0.0.0.0 npm start # or your specific LAN IP, e.g. 192.168.1.10
```
Default is `127.0.0.1` (loopback only). See [ADR 0011 § Deployment configurations](./docs/adr/0011-anonymous-key-deployment-context.md#deployment-configurations-d76-amendment-2026-05-26) for the trust-context table — **never set `OLP_BIND=0.0.0.0` on a public-internet-facing host** (use a tunnel like Tailscale instead).
2. **Onboard each family member's device** from THEIR machine:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/dtzp555-max/olp/main/bin/olp-connect) <olp-host-ip>
```
Detects Cline / Continue.dev / Cursor / Aider / OpenClaw locally and writes per-tool config pointing at your OLP host. Requires `python3` on the client. Prompts for the OLP API key — OR, if the server has `auth.advertise_anonymous_key: true` AND a key was created with `olp-keys keygen --anonymous --advertise`, picks the token up from `/health.anonymousKey` (zero out-of-band paste). See [ADR 0011](./docs/adr/0011-anonymous-key-deployment-context.md) for the trusted-LAN-only invariant.
Per-IDE setup details: [`docs/integrations/`](./docs/integrations/README.md). Telegram / Discord `/olp` slash command setup: [§ Telegram / Discord Usage](#telegram--discord-usage).
---
@@ -80,12 +229,19 @@ OLP distinguishes **Candidate Providers** (declared as intended, not yet pinned)
## Configuration
_placeholder — full configuration reference lands with Phase 4 (fallback engine)._
OLP reads its config from `~/.olp/config.json`. The minimum useful shape:
OLP reads `~/.olp/config.json` at startup. § "[Manual install § Step 4](#4-write-a-minimum-config)" above has a working minimum example. The full schema:
```json
{
"auth": {
"allow_anonymous": false,
"owner_only_endpoints": ["/health", "/dashboard", "/v0/management/..."],
"advertise_anonymous_key": false,
"fallback_detail_header_policy": "owner_only"
},
"providers": {
"enabled": { "<provider-key>": true }
},
"routing": {
"chains": {
"<requested-model>": [
@@ -96,13 +252,25 @@ OLP reads its config from `~/.olp/config.json`. The minimum useful shape:
"soft_triggers": {
"<provider-key>": { "<trigger>": <threshold> }
}
},
"streaming": {
"heartbeat_interval_ms": 0
}
}
```
> **Note:** `routing.soft_triggers` thresholds are parsed and stored but have **no runtime effect at v0.1** — the quota polling path (`quotaStatus()` per hop) is deferred to v1.x per [ADR 0004 Amendment 2](./docs/adr/0004-fallback-engine.md#amendment-2--2026-05-24-soft-triggers-deferred-to-v1x-d22). The evaluation logic exists and is tested; only the production data ingestion path is deferred.
Field guide:
Trigger types, fallback safety, idempotency rules, and the full example config land here when Phase 4 ships. See [ADR 0004 (Fallback Engine Semantics & Safety)](./docs/adr/0004-fallback-engine.md) for the design.
- **`auth.allow_anonymous`** — default `false`. When false, every request needs a Bearer token; when true, anonymous-tier requests succeed (ADR 0007 § 7). Production posture is `false`.
- **`auth.owner_only_endpoints`** — list of endpoints that REQUIRE owner-tier auth (non-owner returns 401). The defaults above are minimum sane for production.
- **`auth.advertise_anonymous_key`** — default `false`. When true (+ `allow_anonymous: true` + a key created with `olp-keys keygen --anonymous --advertise`), `/health.anonymousKey` exposes the plaintext token so `olp-connect <ip>` is zero-config. **Trusted-LAN only** — see [ADR 0011](./docs/adr/0011-anonymous-key-deployment-context.md).
- **`auth.fallback_detail_header_policy`** — controls `X-OLP-Fallback-Detail` response header emission. `owner_only` (default) only shows tuples to owner identity; debug surface to LAN family without leaking to anonymous.
- **`providers.enabled`** — flip a provider plugin on. Only enable providers whose CLI you've authenticated; OLP doesn't do its own OAuth.
- **`routing.chains`** — keyed by the model name your IDE / client requests. Each entry is an ordered list of fallback hops; each hop's `model` is what gets passed to that provider's CLI. F7 fix (D75) — the hop-level `model` field finally overrides the IR's request model during cross-provider fallback.
- **`routing.soft_triggers`** — parsed and stored but **inert at v0.4.x** — the `quotaStatus()` polling data path is deferred to v1.x per [ADR 0004 Amendment 2](./docs/adr/0004-fallback-engine.md#amendment-2--2026-05-24-soft-triggers-deferred-to-v1x-d22). Startup emits a warn if non-empty so the inert state is visible.
- **`streaming.heartbeat_interval_ms`** — default `0` (disabled). Set > 0 (e.g. `15000`) to emit SSE keepalive frames during silent windows. Required behind reverse proxies (nginx / Cloudflare Tunnel / Tailscale Funnel) with 60s idle aborts.
See [ADR 0004 (Fallback Engine)](./docs/adr/0004-fallback-engine.md), [ADR 0007 (Multi-key auth)](./docs/adr/0007-multi-key-auth.md), [ADR 0010 (Phase 4 charter)](./docs/adr/0010-phase-4-charter-operator-and-client-ux.md), [ADR 0011 (Anonymous-key deployment)](./docs/adr/0011-anonymous-key-deployment-context.md).
---
@@ -127,6 +295,10 @@ _placeholder — full table lands per-phase as variables are introduced._
| Variable | Default | Description |
|---|---|---|
| `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_BIND` | `127.0.0.1` | HTTP listener bind address. **Set to `0.0.0.0` or your LAN IP to accept LAN connections** (required for `olp-connect <ip>` to actually reach the server). Default loopback-only is the secure default. See [ADR 0011 § Deployment configurations](./docs/adr/0011-anonymous-key-deployment-context.md#deployment-configurations-d76-amendment-2026-05-26) for the trust-context table — never bind to a public-internet IP. |
| `OLP_API_KEY` | (none) | Owner-tier OLP API key (the `olp_...` plaintext from `olp-keys keygen --owner`) used by `olp` CLI subcommands as the bearer for management endpoints. |
| `OLP_OWNER_TOKEN` | (none) | Fallback used by `olp` CLI if `OLP_API_KEY` is absent. |
| `OLP_PROXY_URL` | `http://127.0.0.1:$OLP_PORT` | Override target URL for `olp` CLI subcommands (so the same binary works against a remote OLP via SSH tunnel or direct LAN). |
| `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_VIBE_BIN` | `vibe` (from PATH) | Override path to the `vibe` binary (Mistral provider). |
@@ -353,16 +525,22 @@ Full spec (decision rationale, open questions, risks): `~/.cc-rules/memory/proje
## Migration from OCP
OLP is OCP's successor. The trigger was Anthropic's 2026-05-14 announcement (effective 2026-06-15) splitting `claude -p` / Agent SDK / third-party agent traffic out of the Pro/Max subscription pool into a separate fixed $100/month Agent SDK credit pool — invalidating OCP's foundational assumption (*"subscription = unlimited within rate limits"*) for its only provider. OLP's structural response is to spread risk across multiple subscriptions whose CLI/programmatic use remains in their main subscription pool, with intelligent fallback when one runs out.
Beyond the billing trigger, OLP is intentionally NOT a commercial multi-tenant SaaS (LiteLLM / OpenRouter / Portkey already serve that market with funding + SOC2), NOT an enterprise gateway competing on provider breadth, NOT a model-capability router ("route to the smartest model" — you pick the model in `routing.chains`), and NOT a conversation-state store (your client manages its own context). See [ADR 0001](./docs/adr/0001-project-founding.md) for the founding decision and [`ALIGNMENT.md`](./ALIGNMENT.md) for the constitution that governs every plugin / IR / entry-surface change.
### Migrating an existing OCP install
_placeholder — `scripts/migrate-from-ocp.mjs` lands with Phase 7 (📋 planned, not yet authored)._
Anticipated user-facing flow (target: <5 minutes):
1. Stop OCP (`launchctl bootout` the OCP service or `ocp stop`).
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.
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.
2. Install OLP (per [§ Manual install](#manual-install-5-10-min) above).
3. Run `olp migrate-from-ocp` — will copy `~/.ocp/keys/` to `~/.olp/keys/` and point provider plugins at OCP's existing auth artifacts where applicable.
4. Start OLP. Clients pointing at port 4567 (or 3456 with `OLP_PORT=3456`) keep working; their existing OLP API keys remain valid.
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.
**Default port moved 3456 → 4567 at v0.4.0** so OCP and OLP can co-host on the same machine during the migration window — 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.
---