v0.0.1-beta.3
Pre-release
Pre-release
·
264 commits
to main
since this release
[v0.0.1-beta.3] - 2026-08-17
Added
- Client IP capture in usage and error logs (
client_ipcolumn on both tables, exposed via the admin/user log APIs):proxy.behind_cdn(default false) enables vendor header detection —CF-Connecting-IP→True-Client-IP→X-Real-IPin order,RemoteAddrfallback; off =RemoteAddronly. Zero-allocation extraction on the request path. - Pagination (
limit/offset) for the redemption-code usage audit endpoint — previously silently truncated at 20 rows. - Masked admin key listing (
GET /admin/keys) with name/user/group filters and pagination. - WS upgrade handshake timeout (15 s) — a black-hole upstream can no longer pin concurrency slots forever.
- Service-side search for the logs filters and a unified ScrollArea scrollbar across the admin UI.
Changed
- Forwarding pipeline unified into a single shared skeleton:
handleFormat/HandleSearch/HandleResponsesWSnow share one guard stage (auth → quota → balance → concurrency gate → rate limit) and one failover loop, with per-format differences confined to two narrow interfaces (attempt + sink). Warn wording, per-format prechecks and the WS exhaustion frame stay format-specific. - WS relay (responses-ws and codex variants) unified onto a 5-method transport interface — the two 200-line concurrent state machines are now one skeleton plus thin adapters.
- Key/group lifecycle semantics hardened: soft-deleted keys can no longer be resurrected through update/rotate (404), soft-deleted groups reject key creation and assignment (404), group deletion validates account membership (409, including the batch path), key updates are patch-based (only provided fields are written), and assignment replacement is transactional.
- Admin role revocation takes effect immediately: the admin auth path now trusts the snapshot role (fail-closed when the snapshot is missing) instead of the 24 h JWT claim.
- WS relay goroutines are panic-contained per connection (log + orderly teardown) instead of crashing the whole process; the panic log now includes a stack trace and no longer writes a 500 body into an already-started SSE stream.
- Admin/user list endpoints clamp
limitto 200; strict JSON decoding rejects unknown fields and trailing garbage with a 400. - Config validation is fail-fast for
proxy.max_body_size(≥ 1) andupstream.idle_conn_timeout/dial_timeout(≥ 1 ms). - User-visible error frames never carry SDK/connection internals: codex fatal/4xx paths use fixed gateway messages, the aiclient 4xx fallback keeps internal text in logs only, and images SSE error frames use a fixed message.
- JSON processing family converged: single-pass top-level extraction (4 full-document scans → 2), byte-level sjson rewrites (preserving >2^53 integer precision), byte-anchored event-type detection, and a usage pre-filter on the chat path — all pinned by zero-allocation assertions.
- Web frontend: poll failures keep stale data with a warning bar instead of replacing the whole page; log filter inputs are debounced (300 ms); settings render unknown keys in a fallback card.
Fixed
- SSE long-line truncation zeroing billing/quota on long responses (line-continuation state machine).
- WS gateway-credential passthrough:
X-Api-Keyis now stripped from the upstream handshake likeAuthorization. - WS path ignoring
service_tier— tier extraction, strip/reject policy and billing tier are now applied on the WS first frame. - Converted-path
tt = 0never deducting quota — all three exit points mirror the nativett = it + ot. failover_attempts = 0leaking concurrency slots permanently (validated ≥ 1, plus a defensive release on the exhaust path).- Scheduler resurrection race:
apply/FailAccountare now copy-on-write CAS with a disabled absorbing state. - Anthropic SDK validation errors classified as 4xx instead of network errors.
- Test flakes: stats-agg first-round assertion timing, and the user-key lifecycle list assertion depending on fake-store map iteration order.