* feat+test+docs: D45 — server.mjs auth integration + lib/audit.mjs (Phase 2 wire-up) Second Phase 2 implementation D-day. Wires the D44 lib/keys.mjs identity layer into the request flow + lands lib/audit.mjs per ADR 0007 § 6.2 + § 8. Closes ADR § 10 acceptance criteria #1 (per-key cache isolation), #2 (anonymous prod-default off), #3 (anonymous dev-mode on), #6 (post-revoke 401 within next request — full coverage with D45), #8 (audit ndjson round-trip), #10 (OLP_OWNER_TOKEN env override — full server-side coverage), #11 (providers_enabled 403 scope). Owner-vs-guest gating for /health + X-OLP-Fallback-Detail (criteria #4, #5) remains in D46 scope. NEW lib/audit.mjs (~110 lines): - appendAuditEvent(event, opts): one JSON event per line to ~/.olp/logs/audit.ndjson (file 0600, dir 0700). § 6.2 retry: warn + 1 retry; per-process drop counter + warn on second failure; NEVER throws. Per-call OLP_HOME env resolution (matches lib/keys.mjs). - getAuditDropCount(): for future /health surface. lib/keys.mjs extended: - loadAuthConfigSync({ olpHome }): reads auth block from ~/.olp/config.json with ADR § 7.2 defaults (allow_anonymous: false, owner_only_endpoints: ['/health'], fallback_detail_header_policy: 'owner_only'). Never throws; missing file / malformed JSON falls back to defaults. - _resolveOlpHome(opts): precedence opts.olpHome → process.env.OLP_HOME → ~/.olp. Per-call resolution so tests + operator deployments can redirect without code edits. server.mjs auth middleware integration: - extractToken(req): parses Authorization Bearer / x-api-key. - authenticate(req): validateKey + 401 paths (auth_required vs invalid_or_revoked_key). - isProviderEnabled(olpIdentity, providerKey): '*' = all; else array allowlist. - _authConfig loaded at startup; warn auth_allow_anonymous_enabled when true. Test seams __setAuthConfig / __resetAuthConfig. - handleChatCompletions + handleModels both gated by authenticate at top. Audit ctx built throughout; res.on('finish') appends row + fires touchLastUsed async. - IDENTITY-VS-CREDENTIALS SEPARATION: olpIdentity (new validated identity) consumed for cache namespacing + providers_enabled + audit; authContext passed to provider.spawn() REMAINS null so providers continue their own credential discovery (env / keychain / file). Per-provider per-key credential mapping is Phase 3+ per ADR § 12. - handleChatCompletions chain filtered by isProviderEnabled; empty result returns 403 key_no_provider_access. - keyId = olpIdentity.keyId (replacing hardcoded '__anonymous__'). - Audit captures fields throughout: post-auth, post-IR, post-chain (success or exhausted). Status + latency populated on res.on('finish'). TESTS — Suite 20, +15 (499 → 514): 20a-d: header parsing + valid key happy paths (Bearer / x-api-key / invalid → 401) 20e: revoked key 401 (criterion #6 end-to-end) 20f: OLP_OWNER_TOKEN env override returns 200 (criterion #10 full) 20g: allow_anonymous=true + no header returns 200 (criterion #3) 20h + 20h-extra: providers_enabled=['mistral'] for anthropic model → 403; '*' baseline returns 200 (criterion #11) 20i: per-key cache namespace isolation (criterion #1 end-to-end) 20j + 20j-401: audit.ndjson written with § 8 schema fields + PII guard; 401 path also appends (criterion #8) 20k: filesystem key last_used_at populated post-request (D45 touch wire) 20l + 20l-200: /v1/models also enforces auth TEST-MODE SETUP (test-features.mjs): - process.env.OLP_HOME = mkdtempSync(...) at module load so audit + key writes don't pollute ~/.olp/. - __setAuthConfig({ allow_anonymous: true }) after server.mjs imports so pre-D45 HTTP integration tests (Suite 18 etc.) continue to pass. - Suite 20 explicitly overrides __setAuthConfig per-case to exercise production-default-off coverage. DOCUMENTATION: - AGENTS.md: lib/keys.mjs 🟡 marker updated + NEW lib/audit.mjs entry; Implementation-status-note + shipped-set updated. - README.md: Implementation Status table gains lib/audit.mjs row + lib/keys.mjs row updated; Known limitations Multi-key auth note rewritten to reflect D45 ship + D46 follow-up; new env-vars (OLP_HOME, OLP_OWNER_TOKEN) and auth config block surfaced. - CHANGELOG.md: D45 entry under Unreleased per release_kit overlay phase_rolling_mode discipline. AUTHORITY: - ADR 0007 (multi-key auth — §§ 5/6.2/7/9.4 implementation contracts + § 10 criteria #1/#2/#3/#6/#8/#10/#11 covered). - CLAUDE.md release_kit overlay phase_rolling_mode — under Unreleased. - Phase 2 kickoff handoff (~/.cc-rules/memory/handoffs/ 2026-05-25-phase-2-kickoff.md in cc-rules d9da966). - Standing autopilot grant (~/.cc-rules/memory/auto/ standing_autopilot_phase_2.md in cc-rules bf0ed9a). Verified: 514/514 pass via npm test (no regression in 499 existing tests; 15 new Suite 20 tests all green). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix+test+docs: D45 fold-in — CI fail recovery + opus reviewer P1/P2/P3 Fresh-context opus reviewer (PR #21) returned APPROVE_WITH_MINOR with 4 findings; CI Node 24 separately reported 9 Suite 20 failures (all 200-expecting tests). Root cause of CI: Suite 20 setup did not stub CLAUDE_CODE_OAUTH_TOKEN before mock spawn, so anthropic.mjs AUTH_MISSING pre-check fired and tests 502'd. (Local Node 22 had the env from the maintainer's claude install — masked the gap.) CI FIX — Suite 20 OAuth env stub Added ensureSuite20FakeOAuth / restoreSuite20OAuth helpers in makeSuite20Server / teardownSuite20. Matches the existing pattern in Suite 9 line ~2154 (test-fake-oauth-token-for-cache-tests). P1 — Real-streaming path audit fidelity Single-hop streaming success (server.mjs ~L1050, the most common deployed shape) did not populate auditCtx.provider / tried_providers / cache_status. Audit rows for streaming requests carried provider: null. Fixed by stamping these at the top of the streaming branch and amending error_code on the two streaming failure exit paths (streaming_error_after_first_chunk + streaming_error_before_first_chunk). New regression test 20j-stream: streaming request asserts the audit row's provider, cache_status, and tried_providers fields are populated. P2 — Global test tmpdir cleanup process.env.OLP_HOME = mkdtempSync(...) at test-features.mjs module load left /var/folders/.../olp-test-home-* leak per npm test run. Fixed by process.on('exit', () => rmSync(_GLOBAL_TEST_OLP_HOME)). Best-effort; swallows errors so exit handler never throws. P3 — handleModels 401 lacks OLP diagnostic headers handleChatCompletions 401 passes olpErrorHeaders({ startMs }); handleModels 401 did not. Aligned. DEFERRED — P2 tried_providers semantics on 403 Reviewer noted that key_no_provider_access 403 stamps original chain in tried_providers, but the field name implies hops actually dispatched. Either ADR § 8 amendment or D46+ semantic fix. Marked in CHANGELOG; not in this fold-in scope. Test count: 514 → 515 (+1 streaming-audit regression test 20j-stream; 14 existing Suite 20 tests still pass). Verified locally via npm test. CI Node 24 recovery via the OAuth env stub. Authority: PR #21 fresh-context opus reviewer findings; CI Node 24 run 26382758946 failure logs; CLAUDE.md release_kit overlay phase_rolling_mode — under Unreleased. 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>
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.
Status: v0.1.1 shipped (2026-05-25) — Phase 1 multi-provider proxy core + cleanup. Phase 2 (multi-key auth,
lib/keys.mjs) is currently in progress per ADR 0007. Sections marked placeholder land alongside the relevant phase of work (see phase plan).
Why OLP
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, 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 for OLP's constitution and docs/adr/ for the founding ADRs.
Quick Start
placeholder — lands with Phase 1.
Anticipated shape:
# install
npm install -g @dtzp555-max/olp
# run setup (writes ~/.olp/config.json, asks which providers to enable)
olp setup
# start the proxy (default port 3456 — same as OCP if you migrate)
olp start
# point your IDE at http://localhost:3456/v1/chat/completions with the OLP API key from `olp keys list`.
Supported Providers
Source of truth: models-registry.json. This table is regenerated from the registry per the release_kit overlay; do not edit it out of sync.
OLP distinguishes Candidate Providers (declared as intended, not yet pinned) from Enabled Providers (authority pin filled + plugin landed + Phase audit passed). The v0.1 founding commit ships zero Enabled Providers — enablement is a Phase audit deliverable, not a bootstrap claim. See ALIGNMENT.md § Provider Inventory for the transition gate.
Candidate Providers
| Provider key | CLI | Subscription / auth | Anticipated Tier | Anticipated Phase |
|---|---|---|---|---|
anthropic |
claude -p |
Pro / Max OAuth (pre-2026-06-15); Agent SDK Credit pool after | D (re-eval post-2026-06-15) | Phase 1 |
openai |
codex exec --json |
ChatGPT Pro OAuth or API key | D | Phase 2 |
mistral |
vibe --prompt --output json |
Le Chat Pro API key | D | Phase 3 |
grok |
grok -p --output-format streaming-json |
xAI Build xai-... API key |
C | Phase 8+ |
kimi |
kimi -p --output-format stream-json |
Moonshot Kimi API key | C | Phase 8+ |
minimax |
TBD | MiniMax Token Plan (¥29+/mo) | B | Phase 8+ |
glm |
TBD | Zhipu Coding Plan ($10+/mo) | B | Phase 8+ |
qwen |
TBD | Alibaba Coding Plan ($50/mo) | B | Phase 8+ |
Risk tier guide. D = permissive / safe (eligible for default-enabled); C = tightening signal, no enforcement history (opt-in); B = service-level key revocation risk (opt-in + consent); A = excluded by default (cannot be opt-in enabled). Tier B providers prompt for explicit consent on first enable and record consent in ~/.olp/config.json. See ALIGNMENT.md § Risk Tier Framework.
Excluded by default (Tier A — evidence-backed, pending primary-source pin). Google Antigravity. See ADR 0006 for the named-prohibition + no-cost-advantage + reinstatement-friction rationale, and for the primary-source pinning follow-up that may force a Tier reconsideration if the Google FAQ language cannot be sourced within 90 days of 2026-05-23.
Configuration
placeholder — full configuration reference lands with Phase 4 (fallback engine).
OLP reads its config from ~/.olp/config.json. The minimum useful shape:
{
"routing": {
"chains": {
"<requested-model>": [
{ "provider": "<key>", "model": "<provider-model-id>" },
{ "provider": "<key>", "model": "<provider-model-id>" }
]
},
"soft_triggers": {
"<provider-key>": { "<trigger>": <threshold> }
}
}
}
Note:
routing.soft_triggersthresholds 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. The evaluation logic exists and is tested; only the production data ingestion path is deferred.
Trigger types, fallback safety, idempotency rules, and the full example config land here when Phase 4 ships. See ADR 0004 (Fallback Engine Semantics & Safety) for the design.
API Endpoints
placeholder — full table lands as each endpoint lands.
| Endpoint | Method | Phase | Status | Description |
|---|---|---|---|---|
/v1/chat/completions |
POST | 1 | ✅ Shipped | OpenAI-compatible Chat Completions entry. Internally normalized to IR, dispatched to a provider plugin, response shape converted back. |
/v1/models |
GET | 1 | ✅ Shipped | Lists models from models-registry.json. |
/health |
GET | 1 | ✅ Shipped | Per-provider health snapshot (owner-only). |
/cache/stats |
GET | 5 | 📋 Planned | Cache hit rate, by-provider breakdown. |
/v0/management/quota |
GET | 6 | 📋 Planned | Per-provider quota / credit pool status (best-effort). |
/dashboard |
GET | 6 | 📋 Planned | Owner-only dashboard (localhost-bound by default). |
Environment Variables
placeholder — full table lands per-phase as variables are introduced.
| Variable | Default | Description |
|---|---|---|
OLP_PORT |
3456 |
HTTP listener port. |
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). |
Per-provider auth env vars
These variables configure credential discovery for each provider plugin. Setting the correct one for your provider is usually required for OLP to make successful requests.
Anthropic (claude -p)
| Variable | Default behavior | Description |
|---|---|---|
CLAUDE_CODE_OAUTH_TOKEN |
Searches ~/.claude/.credentials.json first, then macOS keychain (darwin only) |
Directly supplies the OAuth access token. Highest-precedence override; bypasses all credential-discovery logic. Useful in CI/headless environments where the keychain is unavailable. |
OpenAI Codex (codex exec --json)
| Variable | Default behavior | Description |
|---|---|---|
OPENAI_CODEX_AUTH_PATH |
~/.codex/auth.json (or $CODEX_HOME/auth.json) |
Overrides the full path to the Codex auth artifact. When set, no other path is tried; missing or malformed file returns null (no auth). Useful for CI test fixtures. |
CODEX_HOME |
~/.codex |
Overrides the Codex home directory. $CODEX_HOME/auth.json becomes the default auth path. Ignored when OPENAI_CODEX_AUTH_PATH is set explicitly. |
Mistral Vibe (vibe --prompt)
| Variable | Default behavior | Description |
|---|---|---|
MISTRAL_API_KEY |
Reads $VIBE_HOME/.env (default ~/.vibe/.env) |
Directly supplies the Mistral API key. Highest-precedence override per DOCS-2; bypasses .env file lookup. |
MISTRAL_VIBE_AUTH_PATH |
~/.vibe/.env (or $VIBE_HOME/.env) |
Overrides the full path to the Vibe .env auth file. Evaluated only when MISTRAL_API_KEY is not set. When set, no other path is tried; missing or malformed file returns null (no auth). |
VIBE_HOME |
~/.vibe |
Overrides the Vibe home directory. $VIBE_HOME/.env becomes the default auth path. Ignored when MISTRAL_API_KEY or MISTRAL_VIBE_AUTH_PATH is set explicitly. |
📋 Planned (Phase 2) — not yet read by the codebase:
OLP_HOME(~/.olp) — Config, providers, keys, cache, logs root. Currently hardcoded to~/.olp/config.jsoninloadFallbackConfigSync; the env override path is a Phase 2 config-layer deliverable.OLP_LOG_LEVEL(info) — Log level filter (error/warn/info/debug).logEventcurrently writes unconditionally; level filtering is a Phase 2 observability deliverable.
See also the Implementation status table.
Response Headers
Every response served through OLP carries:
X-OLP-Provider-Used: <provider-key>— which provider's plugin served the request. On a chain-exhausted response, this identifies the chain's configured primary entry (chain[0]), not necessarily the first hop wherespawn()was invoked — see ADR 0004 Amendment 6 for the v0.1 chain-origin semantics and the v1.x soft-trigger reactivation note.X-OLP-Model-Used: <model-id>— which model the served provider used.X-OLP-Fallback-Hops: <n>— number of fallback hops (0if served by the primary chain entry).X-OLP-Cache: hit | miss | bypass— cache layer outcome.X-OLP-Latency-Ms: <ms>— end-to-end latency observed at the proxy.
If a fallback chain is exhausted, X-OLP-Fallback-Exhausted lists the tried providers in order.
Implementation status (as of 2026-05-25)
Phase 1 is closed at v0.1.1 (multi-provider proxy core + pre-Phase-2 cleanup). Phase 2 (multi-key auth, lib/keys.mjs) is in progress per ADR 0007. This table reflects what is currently shipped vs. what is designed for later phases.
| File / artifact | Status | Notes |
|---|---|---|
server.mjs |
✅ Shipped | HTTP listener + dispatcher |
lib/ir/ |
✅ Shipped | IR definition + serializers (ADR 0003) |
lib/providers/anthropic.mjs |
✅ Shipped | claude -p spawn-binary plugin |
lib/providers/codex.mjs |
✅ Shipped | codex exec --json plugin |
lib/providers/mistral.mjs |
✅ Shipped | vibe --prompt plugin |
lib/cache/keys.mjs |
✅ Shipped | Content-addressed key computation |
lib/cache/store.mjs |
✅ Shipped | In-memory Map (file-backed layout: 📋 Phase 2 storage adapter) |
lib/fallback/engine.mjs |
✅ Shipped | Trigger evaluation + chain advancement (ADR 0004) |
Soft trigger data path (quotaStatus() polling) |
📋 Planned (v1.x) | Evaluation logic shipped + tested; data ingestion deferred per ADR 0004 Amendment 2 |
models-registry.json |
✅ Shipped | SPOT for (provider, model) metadata |
test-features.mjs |
✅ Shipped | Comprehensive test suite covering IR, cache, fallback, and integration paths (CI: test.yml) |
lib/keys.mjs |
🟡 Phase 2 — core + server integration (D44 + D45) | Multi-key auth core (createKey / validateKey / listKeys / revokeKey / touchLastUsed) per ADR 0007 §§ 5/6.1/6.3/6.3.5/6.4/9.4 + loadAuthConfigSync for auth.allow_anonymous (default false). Server wires validateKey per request, filters chain by providers_enabled, fires touchLastUsed post-response. D46 will add owner-vs-guest gating for /health + X-OLP-Fallback-Detail. |
lib/audit.mjs |
🟡 Phase 2 — shipped at D45 | Append-only ndjson audit at ~/.olp/logs/audit.ndjson per ADR 0007 § 6.2 + § 8. appendAuditEvent fires for every /v1/* request (success, 401, 403, 5xx). Warn + 1 retry on append failure; no memory buffer at Phase 2 (forward path). PII excluded (no message / response content). OLP_HOME env override supported for test/operator isolation. |
dashboard.html |
📋 Planned (Phase 6) | Owner-only multi-provider dashboard |
docs/provider-caveats.md |
📋 Planned (Phase 3+) | Lossy-translation reference; for now documented inline in each plugin header |
docs/openai-spec-pin.md |
✅ Shipped (D30) | OpenAI spec snapshot for annual audit; v0.1 baseline pinned 2026-05-24 |
docs/alignment-audits/ |
📋 Planned | Output directory for annual alignment audits (first audit: 2027-05-14) |
scripts/migrate-from-ocp.mjs |
📋 Planned (Phase 7) | OCP → OLP migration tool |
setup.mjs |
📋 Planned | Setup wizard / initial config |
Known limitations
Behaviors that work correctly at personal/family scale but have ratified follow-ups for a v1.x sprint. Single landing page: docs/v1x-roadmap.md.
-
Streaming-path singleflight not implemented. The cache layer's D4 singleflight (one spawn per identical concurrent request) is fully wired on the buffered path but NOT on the streaming path. N concurrent identical streaming requests at v0.1 will each spawn their own CLI process. Design ratified in ADR 0005 Amendment 8; implementation tracked via issue #16 and v1.x roadmap #1. At family scale this is observably fine — every caller still receives the correct response; the cost is N CLI processes instead of one.
-
Soft triggers configured but inert.
routing.soft_triggersin~/.olp/config.jsonis honored by the engine's evaluation logic butquotaStatus()polling is not wired (ADR 0004 Amendment 2). A startup warning fires if the field is non-empty so the inert state is visible. -
Multi-key auth shipped through server integration (D45); owner gating pending D46.
lib/keys.mjscore (createKey / validateKey / listKeys / revokeKey / touchLastUsed) shipped at D44;server.mjsnow invokesvalidateKeyper request, filters the chain byproviders_enabled, firestouchLastUsedpost-response, and appends an audit row to~/.olp/logs/audit.ndjsonfor each/v1/*request. The owner-vs-guest gating for/healthpayload trimming andX-OLP-Fallback-Detailheader suppression is scheduled D46. Phase 2 in progress through D46 (owner gating), D47+ (keygen CLI surface, docs), Phase 2 close → v0.2.0. Tracked in v1.x roadmap #2.New env vars consumed at D45:
OLP_HOME(override~/.olp/location, used by tests + operator deployments);OLP_OWNER_TOKEN(synthetic env-owner identity for headless / CI deployments — bypasses filesystem manifest lookup with stable__env_owner__keyId).New config block consumed at D45:
config.json auth.{ allow_anonymous, owner_only_endpoints, fallback_detail_header_policy }. Defaultallow_anonymous: false(production-off); set true to accept requests without an OLP API key (development / single-user dev mode). Startup emits a warn whenallow_anonymous: trueso the relaxed posture is observable. -
Provider-level
cacheKeyFieldsmask not implemented. Cache keys include every IR field including ones individual plugins drop at spawn (e.g., Anthropic plugin dropstemperature). Spurious cache misses possible (extra spawn cost; never spurious hits). Conservative posture documented in ADR 0005 Amendment 7. Tracked in v1.x roadmap #5.
Architecture
OLP is a Node.js (ESM, .mjs) HTTP proxy with no build step and minimal dependencies. The high-level shape:
- Entry surface —
server.mjshandles/v1/chat/completionsand the administrative endpoints. Governed by OpenAI's/v1/chat/completionsspecification as the wire authority. SeeALIGNMENT.md§ Authorities. - Intermediate Representation (IR) —
lib/ir/normalizes between the entry surface and provider-native shapes. The IR is the lingua franca; any extension is an ADR 0003 amendment. - Provider plugins —
lib/providers/<name>.mjs. Each plugin implements the contract in ADR 0002 (Plugin Architecture for Providers), spawns its CLI, and translates between IR and provider-native IO. - Cache layer —
lib/cache/is a content-addressed cache keyed on the 11-field tuple defined in ADR 0005 § "Cache key composition (v1.0)" (provider, model, messages, tools, temperature, response_format, cache_control, max_tokens, top_p, stop, tool_choice — as of Amendment 2/D15). Per-key isolation, prompt-caching bypass, chunked stream replay, and singleflight. See ADR 0005 (Cache Layer Cross-Provider Design). Current backing store is an in-memory Map; file-backed storage is planned for Phase 2. - Fallback engine —
lib/fallback/advances a configured chain one provider at a time on configured triggers, never retrying after the first response chunk has been emitted to the client. See ADR 0004. - Multi-key auth —
lib/keys.mjs(📋 planned, Phase 2) will carry OCP's per-OLP-key namespace isolation forward. Each OLP API key will have independent quota, cache namespace, and audit log; each key declares which providers it can access.
Read the ADRs in docs/adr/ in order before proposing structural changes.
Phase plan
OLP lands in phases. Each phase has its own PR series and Iron-Rule-10 reviewer; this README's placeholders are filled per-phase via the release_kit overlay.
The original v0.1 spec (in ~/.cc-rules/memory/projects/olp_v0_1_spec.md on the maintainer's workstations) planned one provider plugin per phase. The actual Phase 1 execution bundled the three Tier-D provider plugins + cache layer + fallback engine into a single shipped milestone (v0.1.0) followed by a cleanup batch (v0.1.1). The phase numbering below reflects what was actually shipped, not the original per-plugin partition.
- Phase 0 — Repo bootstrap,
ALIGNMENT.md, founding ADRs, CI workflows, PR template. ✅ Shipped (2026-05-23). - Phase 1 — Multi-provider proxy core:
server.mjs, IR, three Tier-D provider plugins (Anthropic / OpenAI Codex / Mistral Vibe), cache (D1+D4) + cleanup (D2 bypass / D3 chunked replay / D23 size cap), fallback engine with first-chunk safety + hard triggers + per-hop log observability, IR↔OpenAI translation under Rule 2(b). ✅ Shipped — v0.1.0 (2026-05-24) + v0.1.1 cleanup (2026-05-25, D35–D42). - Phase 2 — Multi-key auth (
lib/keys.mjs) per ADR 0007: opaque OLP API keys, per-key cache namespacing (the data model already supports this — only thekeyIdsource is missing), owner-vs-guest tier for header gating, audit ndjson, owner-only gating for/health+X-OLP-Fallback-Detail(D40 follow-up). (current — ADR drafting at D43-B). - Phase 3 — Dashboard (
dashboard.html) + audit query layer (deferred from Phase 2). Owner-only multi-provider quota panels, fallback rate, cache hit rate; localhost-bound by default. - Phase 4+ — v1.x roadmap items as triggered. Full deferred-work tracker:
docs/v1x-roadmap.md. Includes streaming-path singleflight (issue #16 + ADR 0005 Amendment 8 design ratified), soft-trigger reactivation (ADR 0004 Amendment 2),/healthactiveSpawns integration, provider-levelcacheKeyFieldsmask, streaming-path SPAWN_FAILED salvage. - Phase N (opt-in) — Tier-2 / Tier-C provider plugins (Grok / Kimi / MiniMax / GLM / Qwen) per ADR 0006; provider-native protocol endpoints; deterministic triggers. Triggered by tier-2 demand, not on the bootstrap path.
Full spec (decision rationale, open questions, risks): ~/.cc-rules/memory/projects/olp_v0_1_spec.md on the maintainer's workstations. Phase 2 kickoff handoff: ~/.cc-rules/memory/handoffs/2026-05-25-phase-2-kickoff.md.
Migration from OCP
placeholder — scripts/migrate-from-ocp.mjs lands with Phase 7 (📋 planned, not yet authored).
Anticipated user-facing flow (target: <5 minutes):
- Stop OCP (
launchctl bootoutthe OCP service orocp stop). - Install OLP.
- Run
olp migrate-from-ocp— copies~/.ocp/keys/to~/.olp/keys/and points provider plugins at OCP's existing auth artifacts where applicable. - Start OLP. Clients pointing at 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.
License
MIT.
Acknowledgements
OLP evolved from OCP (Open Claude Proxy). OCP's per-key isolation model, cache-layer design (D1–D4), dashboard, and alignment-constitution discipline are all carried forward. The structural generalization from single-CLI to multi-provider is what makes this a new project rather than an OCP minor version — see ALIGNMENT.md § Reference: How OCP's cli.js discipline maps to OLP.
Authors: project maintainer (with AI drafting assistance).