Skip to content

feat: admin dashboard, key management, rate limits, and cleanup#136

Open
realsc0t wants to merge 3 commits intorynfar:mainfrom
realsc0t:feat/admin-dashboard-and-cleanup
Open

feat: admin dashboard, key management, rate limits, and cleanup#136
realsc0t wants to merge 3 commits intorynfar:mainfrom
realsc0t:feat/admin-dashboard-and-cleanup

Conversation

@realsc0t
Copy link

feat: admin dashboard, key management, rate limits, and cleanup

Changes

Admin Dashboard (/admin)

  • API key management (create, delete, enable/disable, copy)
  • Per-key usage tracking with per-model token breakdown
  • Per-key rate limits (6h and weekly windows) with enforcement (429 on exceed)
  • Configurable settings: max concurrent sessions, passthrough mode, global limits
  • Session-based auth persistence (survives page refresh, logout button)
  • Available models table

Auth Overhaul

  • Removed ANTHROPIC_API_KEY as auth method — managed keys only
  • /admin/* protected by CLAUDE_PROXY_MASTER_KEY
  • /health protected by CLAUDE_PROXY_MASTER_KEY
  • All proxy endpoints (/v1/messages, /v1/models, etc.) require managed API key
  • / remains public

Port Configuration

  • Port now parsed from ANTHROPIC_BASE_URL (e.g., http://localhost:4567)

Model Catalog

  • Fixed context windows: 1M for opus/sonnet (matching SDK [1m] suffix), 200K for haiku
  • Trimmed to 3 confirmed Claude Max models: opus-4-6, sonnet-4-6, haiku-4-5
  • Aliases still work for request resolution

Remote Proxy Fix

  • Fixed ENOENT crash when remote clients (e.g., Windows) send working directories that don't exist on the Linux proxy server
  • The SDK spawn cwd now falls back to server defaults when client CWD doesn't exist locally
  • Client's real CWD is preserved in the system prompt for Claude to reference
  • Moved lazy Claude executable resolution before the stream/non-stream branch (fixes first-request failures on streaming)

Cleanup

  • Removed telemetry module (in-memory ring buffer replaced by persistent admin stats)
  • Removed unused imports, stale comments, excessive blank lines
  • Updated startup message with ANTHROPIC_BASE_URL and CLAUDE_PROXY_MASTER_KEY

please merge URGENTLY before any change.

/sc0t

@rynfar
Copy link
Owner

rynfar commented Mar 25, 2026

Hey @realsc0t — thanks for putting this together, there's some genuinely useful thinking in here. I can see the vision: admin dashboard, key management, rate limits, OpenAI compat layer. Some of these are features I've been considering.

That said, I'm not able to merge this as-is. A few things I need to work through with you:

Breaking changes without migration path

  • Removing CLAUDE_PROXY_PORT/CLAUDE_PROXY_HOST in favor of parsing from ANTHROPIC_BASE_URL breaks every existing deployment. These need to coexist (new behavior opt-in, old env vars still work).
  • Requiring managed API keys on all endpoints means every existing user's api_key: "dummy" config stops working immediately. This needs a migration story — maybe auth is opt-in when CLAUDE_PROXY_MASTER_KEY is set?

Telemetry removal
The entire telemetry module (dashboard, store, routes, log store, types) and both test files are deleted. The admin stats only track per-key token counts — we lose request latency percentiles, TTFB, waterfall timing, error rates, and diagnostic logs. I'd rather see the admin dashboard alongside telemetry, not replacing it.

Test coverage
The PR removes ~77 test assertions and adds 1. The new code (key store, admin routes, OpenAI compat layer, rate limiting, settings persistence) has no tests. I'd need at least basic coverage for the key store CRUD, rate limit enforcement, and the OpenAI transcoding before merging.

Security concerns

  • API key secrets are stored in plaintext JSON on disk (~/.claude-proxy/keys.json). At minimum these should be hashed (store the hash, compare on auth). The full secret only needs to be shown once at creation time.
  • The master key gets printed to stdout on startup — in Docker/CI this ends up in log aggregators.
  • flush() (sync disk write) is called on every request via recordUsage — this will block the event loop under load. Consider batching or async writes.

What I'd suggest
Rather than one large PR, could we break this into smaller pieces? For example:

  1. The remote proxy ENOENT fix (that's a real bug, I'd merge that standalone today)
  2. OpenAI compat layer (with tests)
  3. Admin dashboard + key management (with tests, alongside existing telemetry)
  4. Auth middleware (opt-in, backward compatible)

Happy to discuss the approach — I think there's good stuff here that can land incrementally. What do you think?

@realsc0t
Copy link
Author

hey @rynfar
i'm going to do another commit to address your points. all should be fine except hashiing keys, it doesn't work that way. api_keys are never hashed, because you can copy it again for users ( that's how it works in many api provider.)
I can add an encryption layer (not a hashing) but it will add some latency. i suggest to keep it as it is.

@rynfar
Copy link
Owner

rynfar commented Mar 25, 2026

Excellent, thank you! I am off to bed but I can get your revamp merged first thing in the morning. Thank you for working on that and helping improve the app!

@realsc0t realsc0t force-pushed the feat/admin-dashboard-and-cleanup branch from d04027f to c955e65 Compare March 25, 2026 10:29
@realsc0t
Copy link
Author

realsc0t commented Mar 25, 2026

the full description:

feat: admin dashboard, key management, rate limits, and Meridian rebrand

Summary

Adds a full-featured admin dashboard with JWT-based authentication, API key management with per-key rate limiting, OpenAI-compatible API layer, and Meridian rebrand — all backward compatible with existing deployments.


Commits

  1. feat: admin dashboard, key management, rate limits, and cleanup — Core admin dashboard, key store, OpenAI compat layer, model catalog, settings persistence, remote proxy CWD fix
  2. fix: restore telemetry, hash API keys, mask master key in logs — Restore telemetry under /admin/telemetry, JWT-based admin auth, master key hashing, debounced flush
  3. fix: backward compat, restore ANTHROPIC_API_KEY, add tests — Backward compat for all env vars, MERIDIAN_* prefix support, comprehensive test suite

Admin Dashboard (/admin)

Two-tab integrated dashboard:

Keys & Settings tab:

  • API key CRUD (create, delete, enable/disable, copy)
  • Per-key usage tracking with per-model token breakdown (expandable rows)
  • Per-key rate limits (6h and weekly rolling windows) with visual progress bars
  • Editable settings with Edit/Save/Cancel flow: max concurrent sessions, passthrough mode, global token limits
  • Summary cards: total keys, active keys, total requests, input/output/total tokens

Telemetry tab:

  • Request latency percentiles (p50/p95/p99) for queue, proxy overhead, TTFB, upstream, total
  • Recent requests table with waterfall visualization
  • Auto-refresh every 5 seconds
  • Time window selector (5min / 15min / 1h / 24h)

Authentication

Admin auth (JWT-based):

  • MERIDIAN_MASTER_KEY (or CLAUDE_PROXY_MASTER_KEY) → SHA-256 hash stored in ~/.claude-proxy/admin.json
  • Plaintext master key is never persisted to disk
  • POST /admin/login — verify master key hash, return JWT (24h expiry)
  • JWT signed with HMAC-SHA256 using the master key hash as secret
  • JWT stored in browser localStorage (never in URLs or browser history)
  • Auto-logout on token expiry (401 triggers redirect to login)
  • Changing the master key automatically invalidates all existing JWTs

LLM endpoint auth:

  • Managed API keys created via admin dashboard (stored in ~/.claude-proxy/keys.json)
  • ANTHROPIC_API_KEY static key still supported for backward compat
  • Rate limit enforcement returns 429 with clear error message

Auth is opt-in:

  • Without MERIDIAN_MASTER_KEY → open access (existing behavior, no breaking change)
  • With MERIDIAN_MASTER_KEY → all LLM endpoints require managed key or ANTHROPIC_API_KEY
Endpoint Auth
/ Public
/admin (HTML) No auth (login page)
/admin/login No auth (POST master key)
/admin/* (API) JWT
/health JWT
/v1/messages, /messages Managed key or ANTHROPIC_API_KEY
/v1/models, /v1/chat/completions Managed key or ANTHROPIC_API_KEY

Meridian Rebrand

All env vars accept both MERIDIAN_* (preferred) and CLAUDE_PROXY_* (backward compat):

New Legacy (still works) Purpose
MERIDIAN_MASTER_KEY CLAUDE_PROXY_MASTER_KEY Admin authentication
MERIDIAN_PORT CLAUDE_PROXY_PORT Listen port
MERIDIAN_HOST CLAUDE_PROXY_HOST Listen host
MERIDIAN_PASSTHROUGH CLAUDE_PROXY_PASSTHROUGH Tool passthrough mode
MERIDIAN_MAX_CONCURRENT CLAUDE_PROXY_MAX_CONCURRENT Max concurrent SDK sessions
MERIDIAN_WORKDIR CLAUDE_PROXY_WORKDIR Working directory
MERIDIAN_DEBUG CLAUDE_PROXY_DEBUG Debug logging
MERIDIAN_KEYS_FILE CLAUDE_PROXY_KEYS_FILE Key store path
MERIDIAN_SETTINGS_FILE CLAUDE_PROXY_SETTINGS_FILE Settings file path
MERIDIAN_ADMIN_FILE CLAUDE_PROXY_ADMIN_FILE Admin config path
MERIDIAN_SESSION_DIR CLAUDE_PROXY_SESSION_DIR Session storage dir
MERIDIAN_MAX_SESSIONS CLAUDE_PROXY_MAX_SESSIONS Max cached sessions
MERIDIAN_TELEMETRY_SIZE CLAUDE_PROXY_TELEMETRY_SIZE Telemetry buffer size
MERIDIAN_IDLE_TIMEOUT_SECONDS CLAUDE_PROXY_IDLE_TIMEOUT_SECONDS Idle timeout

User-facing strings rebranded to "Meridian". Internal paths (~/.claude-proxy/) unchanged.


New Features

OpenAI-compatible API:

  • POST /v1/chat/completions — streaming and non-streaming
  • POST /v1/completions — legacy text completions
  • POST /v1/responses — Responses API
  • GET /v1/models / GET /v1/models/:id — model catalog
  • Full request/response transcoding (OpenAI ↔ Anthropic format)

Model catalog:

  • 3 confirmed Claude Max models with correct context windows
  • claude-opus-4-6 (1M context), claude-sonnet-4-6 (1M), claude-haiku-4-5 (200K)
  • Aliases still work for request resolution (e.g., opus, sonnet, haiku)

Per-key rate limits:

  • 6-hour and weekly rolling window token limits
  • Configurable per key via admin dashboard
  • Enforced at middleware level (429 on exceed)
  • Usage log entries auto-pruned after 7 days

Settings persistence:

  • ~/.claude-proxy/settings.json — survives restarts
  • Max concurrent sessions, passthrough mode, global limits
  • Editable via dashboard with immediate effect (no restart needed)

Port configuration:

  • ANTHROPIC_BASE_URL parsing (e.g., http://localhost:4567)
  • MERIDIAN_PORT/MERIDIAN_HOST override ANTHROPIC_BASE_URL
  • CLAUDE_PROXY_PORT/CLAUDE_PROXY_HOST still work

Bug Fixes

  • Remote proxy ENOENT crash: Windows clients send paths like C:\Users\... as working directory. The SDK tried to spawn with that as cwd on Linux → ENOENT. Fixed by falling back to server CWD when client path doesn't exist locally. Client's real CWD is preserved in the system prompt for Claude to reference.
  • Streaming executable resolution: claudeExecutable was only lazily resolved inside the non-stream path. First streaming request would pass empty string to SDK. Moved resolution before the stream/non-stream branch.
  • Fixed landing page test: Updated for JSON-only root endpoint (removed HTML landing page dependency).

Performance

  • Debounced flush(): Key store writes batched with 100ms debounce via setTimeout. CRUD operations use flushNow() (sync). recordUsage() (called on every request) uses debounced flush to avoid blocking the event loop.
  • env() helper: Single function call per env var lookup with MERIDIAN_*CLAUDE_PROXY_* fallback.

Test Coverage

63 new tests, 149 assertions, 0 failures:

File Tests Coverage
auth.test.ts 7 JWT generation, verification, expiry, master key validation, tamper detection
admin-routes.test.ts 8 Login flow, JWT auth, invalid JWT rejection, models endpoint, settings endpoint
key-store.test.ts 14 CRUD, persistence, enable/disable, reveal, rate limits, per-model tracking, usage windows
env.test.ts 5 MERIDIAN_/CLAUDE_PROXY_ priority, fallback, undefined handling
settings.test.ts 5 Defaults, update, clamping (1-100), passthrough toggle, global limits
openai-compat.test.ts 4 Model catalog, context windows (1M/200K), required fields, aliases
telemetry-routes.test.ts 7 Dashboard HTML, requests/summary/logs endpoints, filtering, limits
telemetry-store.test.ts 13 Ring buffer, eviction, filtering, percentiles, error counting, model/mode breakdown

@realsc0t realsc0t force-pushed the feat/admin-dashboard-and-cleanup branch from c955e65 to 413440b Compare March 25, 2026 11:13
@realsc0t
Copy link
Author

realsc0t commented Mar 25, 2026

sonnet context fixed

@realsc0t realsc0t force-pushed the feat/admin-dashboard-and-cleanup branch 2 times, most recently from 490bab5 to a166f76 Compare March 25, 2026 17:03
@realsc0t
Copy link
Author

realsc0t commented Mar 25, 2026

there is no sonnet[1m] even for max, I got max x20 and sonnet[1m] doesn't work.
Waiting for your merge
@rynfar

@rynfar
Copy link
Owner

rynfar commented Mar 25, 2026

I was battling some other bugs - latest release shoud fix the sonnet issue. I am reviewing this pull request. I am sorry for the delay.

@realsc0t realsc0t force-pushed the feat/admin-dashboard-and-cleanup branch from 3a5ec7b to fda375f Compare March 25, 2026 20:40
@rynfar
Copy link
Owner

rynfar commented Mar 25, 2026

Hey @realsc0t realsc0t thanks for putting this together — I can tell a lot of thought and work went into it, and I appreciate you wanting to help improve the project!

A couple of things:

The sonnet bug is already fixed. We merged subscription-aware model selection in #139 — the proxy now detects your subscription type via claude auth status and picks sonnet[1m] for Max subscribers and plain sonnet for team/pro. There's also
a CLAUDE_PROXY_SONNET_MODEL env var override if you need to force it. So you shouldn't need the hardcoded return "sonnet" change anymore.

On the PR itself — there's a lot of really interesting stuff in here (admin dashboard, key management, OpenAI compat, CWD fix, token usage tracking), but the scope is too broad for me to safely review and merge as a single PR. With 2600+
additions across 30 files touching the core proxy loop, auth, new modules, deleted docs, and env var renaming all at once, it's really hard to evaluate each piece on its own merits and make sure nothing breaks.

I'd love to consider these as smaller, focused PRs — for example:

  • CWD existence check for remote clients (that's a real bug fix, would happily take it)
  • Token usage passthrough (returning real input_tokens/output_tokens instead of 0)
  • Admin dashboard / key management as its own feature branch
  • OpenAI compatibility layer separately

For some of the bigger features (key management, auth overhaul, env var renaming), it'd be great to start with a feature request issue so we can discuss the direction before you invest time coding it — some of these might not align with
where I'm taking the project, and I don't want your effort to go to waste.

If you can break these down into smaller PR's we can look at getting them merged!

Thanks again for contributing — really do appreciate it! 🙏

@rynfar
Copy link
Owner

rynfar commented Mar 25, 2026

@realsc0t dont want to discourage your help. I just have to start being more careful, I have a lot more people using this than I expected to when i published it. I want to move forward in a stable way to try and reduce any of the bug churn we have going on. Much of it is my own doing! Just trying to pace things a little better.

@realsc0t
Copy link
Author

@rynfar
no issue i might keep it in my branch. I wanted to merge fast because i'm using it in our production and wanted to use your npm directly. I do understand your concerns.

/sc0t

@realsc0t realsc0t force-pushed the feat/admin-dashboard-and-cleanup branch from fda375f to e9b4430 Compare March 25, 2026 20:54
@rynfar
Copy link
Owner

rynfar commented Mar 25, 2026

@rynfar no issue i might keep it in my branch. I wanted to merge fast because i'm using it in our production and wanted to use your npm directly. I do understand your concerns.

/sc0t

I totally get it, I may start cherry picking some of your changes and bringing them in. I do want the admin panel with better logging i love it. I also understand the need for auth so I want to get that resolved quickly also.

I am happy to take smaller prs, but i will also be mindful of the need and see if i can move some of these long with your ideas as a great starting point.

Thank you!

- Remove PROXY_PORT/CLAUDE_PROXY_PORT, parse port from ANTHROPIC_BASE_URL
- Remove ANTHROPIC_API_KEY auth, use managed keys only
- Add admin dashboard with master key auth (CLAUDE_PROXY_MASTER_KEY)
- Add API key management (create, delete, enable/disable, per-key usage)
- Add per-model token tracking per key
- Add per-key rate limits (6h and weekly windows) with enforcement
- Add configurable settings (max concurrent, passthrough mode, global limits)
- Fix model catalog: 1M context for opus/sonnet, 200K for haiku
- Add OpenAI-compatible API layer (/v1/chat/completions, /v1/models)
- Remove telemetry module (replaced by persistent admin stats)
- Clean up startup message
- Restore telemetry module under /admin/telemetry (protected by master key)
- Add telemetry button to admin dashboard
- Hash API keys at rest (SHA-256) — plaintext never stored on disk
- Auto-migrate existing plaintext keys to hashed on first load
- Remove reveal endpoint (hashed keys can't be revealed)
- Mask master key in startup logs (show first 4 chars only)
- Debounce flush() to avoid blocking event loop on rapid writes
- Restore diagnosticLog calls in lineage.ts and telemetryStore.record() in server.ts
- Restore CLAUDE_PROXY_PORT/CLAUDE_PROXY_HOST (override ANTHROPIC_BASE_URL)
- Make auth opt-in: only enforced when CLAUDE_PROXY_MASTER_KEY is set
- Restore ANTHROPIC_API_KEY as static key auth (backward compatible)
- Without CLAUDE_PROXY_MASTER_KEY, proxy runs in open-access mode
- Restore telemetry test files
- Add KeyStore tests: CRUD, hashing, rate limits, per-model tracking (12 tests)
- Add admin routes tests: auth middleware, models, settings (7 tests)
- Add OpenAI compat tests: model catalog, context windows, settings (4 tests)
@realsc0t realsc0t force-pushed the feat/admin-dashboard-and-cleanup branch from e9b4430 to 82c8c90 Compare March 25, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants