Skip to content

feat(ratelimit): wire admission-aware rate limiting into the server - #45

Merged
OnlineChef merged 16 commits into
devfrom
feat/admission-rate-limit-integration
Aug 2, 2026
Merged

feat(ratelimit): wire admission-aware rate limiting into the server#45
OnlineChef merged 16 commits into
devfrom
feat/admission-rate-limit-integration

Conversation

@OnlineChef

@OnlineChef OnlineChef commented Aug 2, 2026

Copy link
Copy Markdown

Scope

Integrates bounded admission rate limiting into the OCX server. The feature is opt-in and default OFF: configs without rateLimit, or with enabled: false, preserve existing request behavior and emit no rate-limit headers.

Security and admission model

  • Authentication and Origin checks retain their existing precedence. Invalid credentials always receive the existing 401 and allocate/consume no limiter bucket because admission commits only after successful auth and Origin validation.
  • Validated data-plane keys and management credentials receive isolated opaque HMAC principals.
  • OpenAI Responses upstream Authorization is never treated as an OpenCodex admission identity; only the validated dedicated admission credential qualifies.
  • Origin and forwarded headers are never identity or bypass signals.
  • Optional loopback bypass uses only the bound listener or Bun's trusted socket address.
  • Per-server limiter instances prevent state leakage across restarts and tests.
  • Cross-origin management callers retain 403 precedence and rejected Origin requests consume no limiter budget.

Covered surfaces

Management /api/* and /metrics, model discovery, Responses HTTP/compact/WebSocket, Chat Completions, Claude Messages/count-tokens, images, search, live/realtime, and canonical sideband upgrades.

Responses WebSocket and live-sideband reservations are acquired before upgrade, rolled back on failed upgrade, stored as idempotent release handles, and released on every close path. Cross-origin /api/* requests are rejected in the router before the limiter commits, matching /metrics, so a 403 never consumes admission budget.

Combined security base

The branch preserves the current ChefVault/provider-security behavior rather than overwriting shared files:

  • src/server/auth-cors.ts keeps safe DTO credentialRef and hasApiKey handling.
  • src/types.ts keeps OcxProviderConfig.credentialRef.
  • src/server/rate-limit-auth.ts reuses the canonical isDataPlaneAdmissionSecret boundary.
  • src/ratelimit/config-types.ts augments OcxConfig without rewriting provider types.

Configuration and responses

The strict rateLimit subtree rejects unknown surfaces, invalid rates/bursts/caps, contradictory enable/bypass settings, and effective WebSocket limits where a supplied value conflicts with its default-resolved counterpart.

Rate-limit settings are startup-scoped. Stop/restart OCX after changing them.

429 responses use integer Retry-After, RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers, Cache-Control: no-store, and native OpenAI, Anthropic, or management envelopes. Aggregate snapshots expose no principal, credential, fingerprint, prompt, model, account, Origin or remote address.

Relationship to merged lanes

Projection of rate-limit aggregates into RuntimeMetrics remains a separate P3 observability follow-up after runtime admission merges.

Regression coverage

Focused suites cover feature-off compatibility, credential isolation, auth-first invalid-key behavior, loopback trust, auth/Origin precedence, protocol-native 429 envelopes, shared management budgets, policy transitions, bounded overflow, WebSocket reservation/rollback/release/restart isolation, effective default validation, ChefVault forwarding, provider-security redaction, and safe config DTO behavior.

Merge gates

  • stale invalid-key E2E expectations updated to auth-first semantics;
  • live-sideband WebSocket concurrency reservation/rollback/release covered;
  • full Linux/macOS/Windows CI green on exact head 847c2f5;
  • exact-head manual security/lifecycle audit completed; no current Greptile finding after revalidation request;
  • React Doctor green on exact head;
  • all review threads resolved.

CodeRabbit's additional textual review was unavailable because of quota and is not represented as an approval.

Greptile Summary

This change introduces opt-in admission rate limiting for management, HTTP, and WebSocket traffic, including authenticated principal handling and WebSocket concurrency limits.

Authenticated artifact downloads are not covered: repeated requests to /v1/opencodex/artifacts/:id continue to succeed after an enabled, exhausted low-capacity policy. The artifact route needs to enter the same post-authentication, post-Origin admission flow as the other data-plane routes.

Confidence Score: 4/5

Not safe to merge until artifact downloads are covered by admission limiting.

Runtime verification reproduced repeated successful authenticated artifact downloads despite an enabled exhausted rate-limit policy. This leaves an externally reachable data-plane route outside the intended protection.

Files Needing Attention: src/server/index.ts, src/ratelimit/token-bucket.ts, src/config.ts

Security Review

The authenticated artifact-download endpoint bypasses the new admission controls. An authenticated caller can repeatedly retrieve retained artifact bytes without consuming a rate-limit budget, and there is no separate artifact-download policy for operators to configure. This leaves a data-transfer endpoint outside the protection introduced by this change.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a finding-comment-proof for a posted P1 finding and attached artifacts illustrating the runtime harness and artifact download behavior before and after rate-limiting.
  • T-Rex produced a second finding-comment-proof for another P1 finding.
  • T-Rex produced a general-contract-validation-proof detailing runtime toggles for rate-limit bypass, successful TypeScript compilation, and rate-limit admission tests that passed.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (2)

  1. src/server/index.ts, line 604-631 (link)

    P1 security Artifact downloads bypass admission limiting

    This authenticated data-plane route validates the API key and Origin, then returns the artifact directly without creating or committing an admission gate. With rate limiting enabled and the images policy exhausted after one request, repeated authenticated /v1/opencodex/artifacts/:id downloads still return 200 without any rate-limit headers. Add post-authentication admission to this branch—preferably using a dedicated artifact-download surface, or explicitly sharing the images surface—and expose that policy in configuration.

    Artifacts

    Focused artifact rate-limit runtime harness source

    • The authored Bun script materializes a real PNG, starts the server, makes authenticated artifact requests with rate limiting disabled and enabled, and prints statuses and headers; it directly exercises the suspected route.

    Artifact download behavior before rate limiting is enabled

    • Executed disabled-policy run shows three authenticated artifact downloads returning 200 with 70 bytes and no rate-limit headers, establishing the baseline.

    Artifact download behavior with an exhausted images rate-limit policy

    • Executed enabled-policy run shows the same three authenticated artifact downloads still return 200 with 70 bytes and no rate-limit headers despite an images policy of one request per minute and burst one, confirming the bypass.

    View artifacts

    T-Rex Ran code and verified through T-Rex

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/server/index.ts
    Line: 604-631
    
    Comment:
    **Artifact downloads bypass admission limiting**
    
    This authenticated data-plane route validates the API key and Origin, then returns the artifact directly without creating or committing an admission gate. With rate limiting enabled and the `images` policy exhausted after one request, repeated authenticated `/v1/opencodex/artifacts/:id` downloads still return `200` without any rate-limit headers. Add post-authentication admission to this branch—preferably using a dedicated artifact-download surface, or explicitly sharing the `images` surface—and expose that policy in configuration.
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

    Fix in Cursor Fix in Codex Fix in Claude Code Fix in Conductor

  2. General comment

    P1 Authenticated artifact downloads bypass the admission rate limiter

    • Bug
      • With rate limiting enabled and the related images policy set to one request per minute with burst one, three authenticated requests to a freshly materialized /v1/opencodex/artifacts/:id PNG all return 200 and no rate-limit headers. The route is authenticated data-plane traffic and transfers retained artifact bytes, yet it is not admitted or charged. There is also no configurable artifacts surface, so operators cannot limit it independently.
    • Cause
      • The artifact branch at src/server/index.ts:604-631 performs API authentication and origin validation, then resolves and returns the file directly. Unlike adjacent data-plane branches, it never invokes admission.gate(surface, req, requestServer) or commits a token. RateLimitSurface and rate-limit config omit an artifact surface.
    • Fix
      • Add an explicit artifact/download rate-limit surface (with a default and schema support), then create and commit an admission gate after existing auth/origin checks and before filesystem resolution/response. If artifact traffic is intentionally intended to share images, use admission.gate("images", req, requestServer) there and document that scope; a dedicated surface is preferable because downloads can be large and have distinct abuse characteristics.

    T-Rex Ran code and verified through T-Rex

Fix All in Cursor Fix All in Codex Fix All in Claude Code Fix All in Conductor

Prompt To Fix All With AI
### Issue 1
src/server/index.ts:604-631
**Artifact downloads bypass admission limiting**

This authenticated data-plane route validates the API key and Origin, then returns the artifact directly without creating or committing an admission gate. With rate limiting enabled and the `images` policy exhausted after one request, repeated authenticated `/v1/opencodex/artifacts/:id` downloads still return `200` without any rate-limit headers. Add post-authentication admission to this branch—preferably using a dedicated artifact-download surface, or explicitly sharing the `images` surface—and expose that policy in configuration.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (3): Last reviewed commit: "fix(ratelimit): enforce origin before co..." | Re-trigger Greptile

@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@OnlineChef, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c0793b32-35e9-427b-b006-c68c85639365

📥 Commits

Reviewing files that changed from the base of the PR and between 0d9013f and 847c2f5.

📒 Files selected for processing (14)
  • docs-site/src/content/docs/reference/configuration.md
  • src/config.ts
  • src/ratelimit/concurrency.ts
  • src/ratelimit/config-types.ts
  • src/ratelimit/index.ts
  • src/server/index.ts
  • src/server/management-auth.ts
  • src/server/rate-limit-auth.ts
  • src/server/rate-limit.ts
  • src/server/ws-bridge.ts
  • tests/ratelimit-admission.test.ts
  • tests/ratelimit-auth-precedence.test.ts
  • tests/ratelimit-management-origin.test.ts
  • tests/server-live.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 2, 2026
Comment thread src/config.ts Outdated
OnlineChef and others added 2 commits August 2, 2026 10:03
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@OnlineChef
OnlineChef marked this pull request as draft August 2, 2026 10:03

Copy link
Copy Markdown
Author

Temporarily converted back to draft. This PR activates primitives from #44, which was merged with a reproduced policy-transition P1. Hotfix #47 must merge first. After that, rebase/merge dev into this branch, run the policy-transition and admission suites together, and request a fresh exact-head review before marking ready.

OnlineChef and others added 4 commits August 2, 2026 10:03
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@OnlineChef
OnlineChef force-pushed the feat/admission-rate-limit-integration branch from 3846dd3 to 39e1f21 Compare August 2, 2026 10:05
Includes the policy-transition safety fix from #47 before enabling runtime admission routing.

Copy link
Copy Markdown
Author

Exact-head re-review requested for 3dea136c72086ba0c9e81a27a9c2074c9aa556ec after merging current dev and the #47 policy-transition hotfix.

Please rerun, not infer:

  1. the original { enabled: true, websocket: { global: 1 } } reproduction and default-boundary probes;
  2. low→high and high→low token-bucket policy transitions for principal and overflow buckets;
  3. WebSocket upgrade-failure rollback and close release;
  4. invalid-key shared fallback plus validated-principal 401/403 precedence;
  5. snapshot serialization to confirm no principal/fingerprint/credential material.

Keep this PR draft until exact-head CI and this review are clean.

Copy link
Copy Markdown
Author

[code]smith (@codesmith-bot) independently review exact head 3dea136c72086ba0c9e81a27a9c2074c9aa556ec. Execute the WebSocket-default and token-policy-transition reproductions, inspect auth/429 precedence and every WebSocket release path, and verify aggregate snapshots contain no identity material. Report remaining P1/P2 findings as blockers.

Copy link
Copy Markdown
Author

CodeRabbit (@coderabbitai) review

Review exact head 3dea136c72086ba0c9e81a27a9c2074c9aa556ec; previous findings and summaries based on older commits are not sufficient.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== HEAD =='
git rev-parse HEAD
git status --short
echo '== Commit =='
git show --no-patch --format=fuller 3dea136c72086ba0c9e81a27a9c2074c9aa556ec

echo '== Files changed from first parent =='
git diff-tree --no-commit-id --name-status -r 3dea136c72086ba0c9e81a27a9c2074c9aa556ec

echo '== Rate limit candidates =='
fd -i 'ratelimit|rate-limit|rate_limit' . || true
rg -n -i -C2 '\brateLimit\b|\brate.limit\b|RateLimit|rate-limit' \
  -g '!node_modules/**' -g '!dist/**' -g '!build/**' . | head -n 1000

echo '== TypeScript outlines for candidate files =='
while IFS= read -r f; do
  case "$f" in
    *.ts|*.tsx|*.js|*.mjs|*.cjs)
      echo "--- $f"
      ast-grep outline "$f" --items all 2>/dev/null || true
      ;;
  esac
done < <(fd -i 'ratelimit|rate-limit|rate_limit' .)

echo '== Relevant test references =='
rg -n -i -C3 'websocket.*global|policy.transition|overflow|invalid.key|snapshot|retry-after|rateLimit' \
  tests -g '*ratelimit*' 2>/dev/null || true

Length of output: 69612


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Merge-aware file changes =='
git diff-tree -m --no-commit-id --name-status -r 3dea136c72086ba0c9e81a27a9c2074c9aa556ec | sort -u

echo '== Server admission control =='
nl -ba src/server/rate-limit.ts | sed -n '1,310p'

echo '== Token bucket and concurrency primitives =='
nl -ba src/ratelimit/token-bucket.ts | sed -n '1,330p'
nl -ba src/ratelimit/concurrency.ts | sed -n '1,240p'

echo '== Configuration validation =='
nl -ba src/config.ts | sed -n '670,750p'

echo '== Principal resolution and management authentication =='
nl -ba src/server/auth-cors.ts | sed -n '240,305p'
nl -ba src/server/management-auth.ts | sed -n '175,220p'

echo '== All admission and reservation call sites =='
rg -n -C8 '\.gate\(|\.afterAuth\(|\.reserveConcurrency\(|rateLimitRelease|server\.upgrade\(' \
  src/server/index.ts src/server -g '*.ts'

echo '== Server WebSocket handler and upgrade path =='
nl -ba src/server/index.ts | sed -n '250,440p'
nl -ba src/server/index.ts | sed -n '930,1035p'

echo '== Exact-head admission tests =='
nl -ba tests/ratelimit-admission.test.ts | sed -n '1,1100p'

echo '== Exact-head policy-transition tests =='
nl -ba tests/ratelimit-policy-transition.test.ts | sed -n '1,500p'

Length of output: 2030


🏁 Script executed:

#!/bin/bash
set -euo pipefail

show() {
  local file="$1" start="$2" end="$3"
  echo "== ${file}:${start}-${end} =="
  sed -n "${start},${end}p" "$file"
}

show src/server/rate-limit.ts 1 310
show src/ratelimit/token-bucket.ts 1 330
show src/ratelimit/concurrency.ts 1 240
show src/config.ts 670 750
show src/server/auth-cors.ts 240 305
show src/server/management-auth.ts 175 220

echo '== Exact call-site lines and context =='
rg -n -C12 '\.gate\(|\.afterAuth\(|\.reserveConcurrency\(|rateLimitRelease|server\.upgrade\(' \
  src/server -g '*.ts'

show src/server/index.ts 250 440
show src/server/index.ts 930 1035

echo '== Route registration lines =='
rg -n -C8 'fetch\(|pathname|/v1/responses|/v1/chat/completions|/v1/messages|count_tokens|/api/|images|live|realtime|models' \
  src/server/index.ts

echo '== Admission test declarations =='
rg -n -C8 '^(describe|test|it)\(' tests/ratelimit-admission.test.ts tests/ratelimit-policy-transition.test.ts

echo '== Admission tests (part 1) =='
sed -n '1,600p' tests/ratelimit-admission.test.ts
echo '== Admission tests (part 2) =='
sed -n '601,1200p' tests/ratelimit-admission.test.ts
echo '== Policy transition tests =='
sed -n '1,520p' tests/ratelimit-policy-transition.test.ts

Length of output: 146731


@OnlineChef I reviewed exact head 3dea136c72086ba0c9e81a27a9c2074c9aa556ec.

I found no P1 or P2 blocker in the reviewed admission-control code.

  • src/config.ts, Lines 703-729, resolves omitted WebSocket values before it validates perPrincipal <= global. This rejects { enabled: true, websocket: { global: 1 } } because the effective default perPrincipal is 16.
  • src/ratelimit/token-bucket.ts, Lines 229-234, refills using state.policy before it installs nextPolicy. It prevents a low-to-high transition from retroactively applying the higher refill rate. The burst clamp prevents capacity increases from minting tokens.
  • src/ratelimit/token-bucket.ts, Lines 149-166, applies the same policy-transition path to the per-surface overflow bucket. This preserves the hard-cap behavior.
  • src/server/rate-limit.ts, Lines 205-226, charges remote-address or anonymous principals before authentication. It charges validated admission and management principals only after the route has passed authentication and origin checks. This preserves the specified 401/403 precedence for validated credentials.
  • src/server/auth-cors.ts, Lines 274-285, and src/server/management-auth.ts, Lines 194-210, only allocate credential-specific principals after credential validation. Invalid credentials use the trusted socket address or anonymous fallback.
  • src/server/rate-limit.ts, Lines 163-170 and 200-208, uses only the configured listener and requestIP() address for loopback bypass. It does not use Origin or forwarded headers.
  • src/server/index.ts, Lines 386-414, commits the Responses WebSocket request charge, reserves concurrency before server.upgrade, and releases the reservation when the upgrade fails.
  • src/server/index.ts, Line 1009, calls the idempotent reservation release callback in the WebSocket close handler. This covers the normal, error, and forced-close paths provided by Bun’s close callback.
  • src/server/rate-limit.ts, Lines 235-245, exposes only aggregate request rows, bucket counts, and WebSocket counts. src/ratelimit/token-bucket.ts, Lines 195-201, returns only surface, source, result, and count. No principal, fingerprint, credential, remote address, prompt, model, or account field is serialized.
  • tests/ratelimit-admission.test.ts covers the effective-default validation, invalid-key fallback, authentication precedence, failed-upgrade rollback, close release, restart isolation, and snapshot redaction.
  • tests/ratelimit-policy-transition.test.ts covers low-to-high, high-to-low, burst changes, and shared-overflow transitions.

I could not run the requested runtime reproductions, test suites, or CI in this sandbox. The environment permits static inspection only and prohibits executing repository code. Exact-head CI and the dynamic WebSocket and policy-transition suites must still pass before the PR is marked ready.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@blacksmith-sh

blacksmith-sh Bot commented Aug 2, 2026

Copy link
Copy Markdown

OnlineChef

[code]smith (@codesmith-bot) independently review exact head 3dea136c72086ba0c9e81a27a9c2074c9aa556ec. Execute the WebSocket-default and token-policy-transition reproductions, inspect auth/429 precedence and ever...

Independent review of exact head 3dea136c72086ba0c9e81a27a9c2074c9aa556ec complete. One P2 found and fixed (pushed as bf35f4f); no remaining P1/P2 blockers.

Reproductions executed (controlled-clock scripts against the real primitives):

  • Token policy transition (feat(ratelimit): add admission limiter primitives #44's P1, fixed in fix(ratelimit): preserve bucket policy across runtime changes #47): bucket exhausted under {rpm 1, burst 1} at t=0, then charged with {rpm 600, burst 10} at t=10s admits 0 of 11 (previously 10/11); elapsed time refills under the prior policy in applyPolicy before the new policy installs (src/ratelimit/token-bucket.ts:229). Reverse (high→low at t=1s) correctly allows, and a burst raise alone mints no tokens.
  • WebSocket defaults (the P2 fixed in 39e1f21): validateConfigCandidate rejects partial overrides against effective defaults: {global: 1} → "effective perPrincipal 16 (default), global 1"; {perPrincipal: 200} → rejected against default global 128. {perPrincipal: 1, global: 1} accepted, and runtime reserveConcurrency enforces the same caps (second reservation → 429), since config validation and resolveSettings now share DEFAULT_RATE_LIMIT_WEBSOCKET_CONCURRENCY from src/ratelimit.

Auth/429 precedence: every gated route follows the documented contract: preAuthDeny (anonymous/remote only) → auth 401 → origin 403 → commit() for validated principals. Verified in code for all 12 gate sites and exercised live (invalid rotated keys get 401 while the shared remote bucket has balance, then 429 preempts 401; validated key over-limit + bad Origin still gets 403).

WebSocket release paths: reservation acquired before server.upgrade, released inline when upgrade returns false; the idempotent handle lives on ws.data.rateLimitRelease and the single Bun close callback (src/server/index.ts:1009) fires for normal closes, aborts, the 1009 too-large close, registry force-close (4001 account invalidation), and server.stop(true) at shutdown; generation counters make pre-reset handles no-ops. No stranded-reservation path found.

Snapshot redaction: drove validated key, invalid key, management token, remote-address, and anonymous principals plus WS reservations through a live admission control; the serialized RateLimitAggregateSnapshot contains only per-surface counts, no raw keys, admin token, IPs, fingerprints, or domain prefixes.

Finding (P2, fixed): the /metrics endpoint merged from dev bypassed admission entirely: it had management auth + origin checks but no admission.gate("management", ...), so admin-token brute forcing via /metrics was unmetered and scrapes never charged the management budget, contradicting the plan's "apply management request limiting to /api/* and /metrics". Fixed at head bf35f4f with the same two-phase gate as /api/* plus a regression test proving /metrics and /api/* share one bucket and that unauthenticated /metrics probes hit the anonymous 429 budget.

Non-blocker (P3 follow-up): rate-limit aggregates are not yet projected into RuntimeMetrics/Prometheus output; the PR body already declares this an intentional separate observability change, and the typed snapshot is ready for it.

Validation on the fixed head: bun run typecheck, 43 tests across the ratelimit/metrics suites plus server-management-auth (all pass), bun run privacy:scan green. PR: #45

View with [code]smith

Copy link
Copy Markdown
Author

Exact-head reset after fixing a newly reproduced management-plane precedence defect. Current head: 22ee3e5f11b9b1467fe4214aa8481bbef64a409e (zero behind dev).

CodeRabbit (@coderabbitai) review
[code]smith (@codesmith-bot) independently execute and review:

  1. valid admin + rejected Origin before exhaustion => 403 and no bucket charge;
  2. same-origin admin exhausts the bucket normally;
  3. valid admin + rejected Origin after exhaustion => still 403, never 429;
  4. prior WebSocket effective-default, policy-transition, reservation release and aggregate-redaction reproductions.

All reviews on 3dea136 or older are stale.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='22ee3e5f11b9b1467fe4214aa8481bbef64a409e'
actual="$(git rev-parse HEAD)"
printf 'HEAD=%s\n' "$actual"
test "$actual" = "$expected"
git status --short

printf '\n== Diff against dev merge-base ==\n'
base="$(git merge-base HEAD dev)"
printf 'merge-base=%s\n' "$base"
git diff --name-status "$base"...HEAD

printf '\n== Candidate files ==\n'
fd -i 'rate-limit|ratelimit|management-auth|auth-cors|config|index' src tests || true

printf '\n== Source outlines ==\n'
for f in src/server/rate-limit.ts src/ratelimit/token-bucket.ts src/ratelimit/concurrency.ts src/server/management-auth.ts src/server/auth-cors.ts src/server/index.ts src/config.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    ast-grep outline "$f" --items all
  fi
done

printf '\n== Focused call sites ==\n'
rg -n -C 10 '\.gate\(|\.afterAuth\(|\.reserveConcurrency\(|rateLimitRelease|server\.upgrade\(|Origin|requestIP\(|snapshot\(' \
  src/server src/ratelimit src/config.ts -g '*.ts'

printf '\n== Focused test declarations and assertions ==\n'
rg -n -C 6 'rejected Origin|origin|websocket|upgrade|close|policy transition|low.to.high|high.to.low|overflow|snapshot|fingerprint|credential|Retry-After' \
  tests -g '*ratelimit*.test.ts'

Length of output: 1669


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Available refs and exact commit =='
git branch -a
git show --no-patch --format=fuller HEAD
echo '== Parents =='
git show --no-patch --format='%P' HEAD
echo '== Files changed from first parent =='
git diff-tree --no-commit-id --name-status -r HEAD

echo '== Source outlines =='
for f in src/server/rate-limit.ts src/ratelimit/token-bucket.ts src/ratelimit/concurrency.ts src/server/management-auth.ts src/server/auth-cors.ts src/server/index.ts src/config.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    ast-grep outline "$f" --items all
  fi
done

echo '== Management and limiter call sites =='
rg -n -C 12 '\.gate\(|\.afterAuth\(|\.reserveConcurrency\(|rateLimitRelease|server\.upgrade\(|Origin|requestIP\(|snapshot\(' \
  src/server src/ratelimit src/config.ts -g '*.ts'

echo '== Relevant source slices =='
nl -ba src/server/rate-limit.ts | sed -n '1,330p'
nl -ba src/ratelimit/token-bucket.ts | sed -n '1,360p'
nl -ba src/ratelimit/concurrency.ts | sed -n '1,280p'
nl -ba src/server/management-auth.ts | sed -n '150,245p'
nl -ba src/server/auth-cors.ts | sed -n '230,330p'
nl -ba src/config.ts | sed -n '650,780p'
nl -ba src/server/index.ts | sed -n '240,460p'
nl -ba src/server/index.ts | sed -n '900,1060p'

echo '== Focused regression test declarations and bodies =='
for f in tests/ratelimit-management-origin.test.ts tests/ratelimit-admission.test.ts tests/ratelimit-policy-transition.test.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    rg -n -C 10 '^(describe|test|it)\(|rejected Origin|same.origin|exhaust|websocket|upgrade|close|low.to.high|high.to.low|overflow|snapshot|fingerprint|credential' "$f"
  fi
done

Length of output: 140421


🏁 Script executed:

#!/bin/bash
set -euo pipefail

numbered() {
  local file="$1" start="$2" end="$3"
  echo "== ${file}:${start}-${end} =="
  awk -v start="$start" -v end="$end" 'NR >= start && NR <= end { printf "%6d  %s\n", NR, $0 }' "$file"
}

numbered src/server/rate-limit.ts 90 270
numbered src/ratelimit/token-bucket.ts 70 280
numbered src/ratelimit/concurrency.ts 1 190
numbered src/config.ts 660 750
numbered src/server/management-auth.ts 185 245
numbered src/server/auth-cors.ts 260 305
numbered src/server/index.ts 378 460
numbered src/server/index.ts 790 845
numbered src/server/index.ts 985 1025

echo '== Every WebSocket release-handle reference =='
rg -n -C 8 '\brateLimitRelease\b|\breserveConcurrency\s*\(' src tests -g '*.ts'

echo '== Admission snapshot producers and consumers =='
rg -n -C 8 '\.snapshot\(\)|statsSnapshot\(|bucketCounts\(|RateLimitAggregateSnapshot|rateLimit.*snapshot' \
  src tests -g '*.ts'

for f in tests/ratelimit-management-origin.test.ts tests/ratelimit-admission.test.ts tests/ratelimit-policy-transition.test.ts; do
  echo "== $f =="
  awk '{ printf "%6d  %s\n", NR, $0 }' "$f"
done

Length of output: 123771


@OnlineChef I reviewed exact head 22ee3e5f11b9b1467fe4214aa8481bbef64a409e.

I found no P1 or P2 blocker in the reviewed admission-control paths.

  • Management Origin precedence: src/server/rate-limit.ts, Lines 226-234, checks isAllowedManagementOrigin(req, config) before it consumes a validated management bucket. Therefore, /api/* can call commit() before handleManagementAPI() without charging a rejected Origin. This preserves the required 403 response after the valid admin bucket is exhausted.
  • /metrics precedence: src/server/index.ts, Lines 430-438, performs management authentication and the Origin check before metricsGate.commit(). A rejected valid admin request returns 403 before admission can return 429.
  • Exact-head regression: tests/ratelimit-management-origin.test.ts, Lines 62-95, verifies all three required states: rejected Origin before exhaustion, same-origin exhaustion, and rejected Origin after exhaustion. The expected response remains 403 in both rejected-Origin cases.
  • WebSocket effective defaults: src/config.ts, Lines 711-729, resolves omitted WebSocket limits before it validates perPrincipal <= global. It rejects { enabled: true, websocket: { global: 1 } }, because the effective default perPrincipal is 16. tests/ratelimit-admission.test.ts, Lines 123-130, covers this boundary.
  • Policy transitions: src/ratelimit/token-bucket.ts, Lines 224-240, refills under the previous policy before it installs the next policy. It prevents a low-to-high transition from applying the higher rate retroactively. The subsequent clamp prevents a burst increase from minting tokens. The same applyPolicy() path applies to overflow buckets at Lines 157-172. tests/ratelimit-policy-transition.test.ts, Lines 16-105, covers low-to-high, high-to-low, burst changes, and overflow transitions.
  • WebSocket reservations: src/server/index.ts, Lines 401-414, commits the request charge, reserves concurrency before server.upgrade(), stores the idempotent release handle, and releases it when upgrade fails. src/server/index.ts, Lines 1013-1023, invokes the same handle on WebSocket close. src/ratelimit/concurrency.ts, Lines 129-137, makes the release idempotent.
  • Aggregate redaction: src/server/rate-limit.ts, Lines 243-249, exposes only aggregate request rows, bucket counts, and WebSocket counts. src/ratelimit/token-bucket.ts, Lines 195-207, returns only surface, source, result, and count. src/ratelimit/concurrency.ts, Lines 141-150, returns only counts and aggregate counters. No principal, fingerprint, credential, or remote address is serialized. tests/ratelimit-admission.test.ts, Lines 534-567, asserts that serialized snapshots omit the key, principal prefix, and address.
  • Invalid credential isolation: src/server/management-auth.ts, Lines 194-211, and src/server/auth-cors.ts, Lines 274-286, allocate a credential-specific principal only after validation. Invalid credentials use the trusted remote-address or anonymous fallback.

I could not execute the requested runtime reproductions, Bun test suites, or CI. This sandbox permits static inspection only. Keep the PR draft until exact-head CI and the dynamic management-Origin, WebSocket, and policy-transition suites pass.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@blacksmith-sh

blacksmith-sh Bot commented Aug 2, 2026

Copy link
Copy Markdown

OnlineChef

Exact-head reset after fixing a newly reproduced management-plane precedence defect. Current head: 22ee3e5f11b9b1467fe4214aa8481bbef64a409e (zero behind dev).

CodeRabbit (@coderabbitai) review
[code]smith (@codesmith-bot) ...

Independent review of exact head 22ee3e5f11b9b1467fe4214aa8481bbef64a409e complete (verified as the live branch tip, 0 behind dev). All requested reproductions executed and pass; no remaining P1/P2 blockers.

Root cause of the fixed defect, confirmed in code: requireManagementAuth validates only the credential; the /api/* Origin rejection lives in handleManagementAPI's first statement (src/server/management-api.ts:82), which ran after commit(). So before 471be26, a valid admin token from a rejected Origin charged the bucket on every 403, and once exhausted got 429 instead of 403. The fix guards commit() (src/server/rate-limit.ts:231) with the exact same isAllowedManagementOrigin predicate the downstream handler uses, so the two checks cannot diverge; /metrics checks origin inline before commit and is consistent.

Scenario results (gate-level with controlled principals, plus live server):

  1. Valid admin + rejected Origin before exhaustion → 403, no charge. commit() returns null uncharged; the snapshot shows zero buckets and zero stat rows after the rejected request; live /api/config and /metrics both return 403 {"error":"cross-origin request blocked"} / 403 respectively, and the very next same-origin request still gets the full burst (200).
  2. Same-origin admin exhausts normally. Burst-1 policy: first request 200 (charged), second 429 with the management JSON envelope; snapshot records exactly allowed:1, denied:1.
  3. Valid admin + rejected Origin after exhaustion → 403, never 429. commit() never returns a 429 for cross-origin management callers, live /api/config and /metrics both return 403 while the same-origin path is 429. Anonymous/remote callers are unaffected: they are still charged pre-auth and keep the documented 429-preempts-401 exception (invalid-token probes on /metrics still hit 429 after the shared budget drains).
  4. Prior reproductions re-executed at this head, all green: policy transition (low→high at t=10s admits 0/11, high→low at t=1s allows, burst raise mints nothing); websocket effective-default validation ({global:1} and {perPrincipal:200} rejected against defaults, explicit and omitted shapes accepted); reservation lifecycle (cap-1 denial 429, idempotent release, re-admission, snapshot globalCount accurate); aggregate redaction (serialized snapshot contains no keys, admin token, IPs, fingerprints, or domain prefixes across validated/invalid/remote/anonymous principals).

Validation: bun run typecheck, 53 tests across the six rate-limit/metrics/management-auth suites (including the new ratelimit-management-origin regression), and bun run privacy:scan all pass at this head.

Non-blocking notes (P3): the unmetered cross-origin management path is safe (it requires a valid admin token and does no handler work beyond the 403), and rate-limit aggregates are still not projected into RuntimeMetrics, which the PR body already declares an intentional separate observability change.

PR: #45

View with [code]smith

@OnlineChef

Copy link
Copy Markdown
Author

Triage 2026-08-02 ADR (OCX → Sofie / Pi-Rust)

Classification: KEEP_OPEN_DONOR (preferred survivor vs #49)

Locked ADR: Sofie = autonomous OCX runtime; OpenCodex TS = donor/evidence, not automatic Rust/Pi architecture.

Sofie deploy today

Why keep (not supersede)

  • This is the complete opt-in wiring lane: hooks server/index, management auth, ws-bridge, 429 envelopes, docs, e2e admission tests.
  • Head 22ee3e5f11b9b1467fe4214aa8481bbef64a409e is 0 behind dev, MERGEABLE, still draft; windows-latest pending at triage → not MERGE_TO_DEV_IF_GREEN today.
  • Sibling superseded: inert rate-limit wiring prototype #49 is an incomplete/inert parallel attempt at the same goal → superseded in favor of this PR.

Direction

  • Not fighting Sofie/Pi-Rust: default-off wiring on current TS runtime; salvageable evidence for a future Rust admission boundary.
  • No merge in this triage. Revisit land-to-dev only after undraft + full CI green + independent review on exact head (P1 latch).

Copy link
Copy Markdown
Author

yone_suke (@codesmith) independently audit exact head 22ee3e5f11b9b1467fe4214aa8481bbef64a409e. Execute the focused tests and inspect the actual boundaries for: validated-vs-anonymous principal classification, management Origin 403 precedence before/after exhaustion, token policy transitions, WebSocket reservation rollback/close release, and snapshot redaction. Fix or report every P1/P2 blocker; do not only summarize.

@blacksmith-sh

blacksmith-sh Bot commented Aug 2, 2026

Copy link
Copy Markdown

OnlineChef

yone_suke (@codesmith) independently audit exact head 22ee3e5f11b9b1467fe4214aa8481bbef64a409e. Execute the focused tests and inspect the actual boundaries for: validated-vs-anonymous principal classification, ...

Independent audit of exact head 22ee3e5f11b9b1467fe4214aa8481bbef64a409e complete (re-fetched and confirmed as the live branch tip; working tree clean). Executed 22 adversarial reproductions against the actual boundaries plus the six focused suites. No P1/P2 blockers found; nothing to fix.

A. Validated-vs-anonymous classification (src/server/auth-cors.ts, management-auth.ts): a configured data key is admission-key with header-independent identity (same fingerprint via x-opencodex-api-key or Authorization); rotating invented keys all collapse into one remote-address bucket (verified same fingerprint, never minting buckets); the admin token on a data-plane route is not data-validated and stays metered; no credential → remote-address, no trusted address → anonymous; the resolver's first-match header chain is identical to hasValidApiAuth's, so auth and classification can't disagree on which credential was presented. Two P3 asymmetries only: a caller already holding a valid data key gets unmetered 401s on /v1/responses when using the wrong header (remote binds accept only the dedicated header), and a live-but-origin-bound GUI session token gets unmetered 401s; both require possessing a real secret, and credential guessing stays charged.

B. Management Origin 403 precedence: valid admin + rejected Origin before exhaustion → commit() admits uncharged (zero stat rows, zero buckets) and handleManagementAPI returns the 403; same-origin traffic charges normally and exhausts to 429; cross-origin after exhaustion never sees 429. The commit() guard (rate-limit.ts:231) uses the same isAllowedManagementOrigin predicate as handleManagementAPI's first statement (management-api.ts:82), so the checks can't diverge. Invalid-token floods with spoofed Origins remain pre-auth metered and eventually 429.

C. Token policy transitions (token-bucket.ts:229 applyPolicy): low→high at t=10s admits 0/11 (elapsed refilled under the prior policy before the new one installs); high→low at t=1s allows (clamped, not zeroed); a burst raise mints nothing.

D. WebSocket reservation lifecycle: cap-1 denial returns 429 with integer Retry-After; rollback/double-release restores exactly one slot (idempotent); re-admission works; a stale handle after reset() is inert (generation guard). In the server, the single Bun close callback (index.ts:1009) fires the release for normal closes, aborts, the 1009 too-large close, registry force-close (4001), and server.stop(true); upgrade failure releases inline before the 426.

E. Snapshot redaction: after driving validated/invalid/management principals and a held reservation, the serialized RateLimitAggregateSnapshot contains counts only, none of the key values, admin token, IP, or fingerprint domain prefixes.

Validation at this head: bun run typecheck clean, 53/53 tests across ratelimit, ratelimit-policy-transition, ratelimit-admission, ratelimit-management-origin, server-management-auth, server-metrics-endpoint, and bun run privacy:scan green. PR: #45

View with [code]smith

@OnlineChef
OnlineChef marked this pull request as ready for review August 2, 2026 10:46
Combine the merged ChefVault provider-security plane with admission-aware rate limiting. Preserve the ChefVault auth/config DTO changes, isolate opaque rate-limit principal resolution, and augment the canonical config types without rewriting the shared types module.
@OnlineChef
OnlineChef marked this pull request as draft August 2, 2026 10:52

Copy link
Copy Markdown
Author

Exact-head integration review required for f8e2b4e4bce7b90fda0a45454f31a91e565d59a5 (zero behind dev).

yone_suke (@codesmith) independently execute the focused ChefVault + rate-limit suites and inspect the two conflict-isolation modules:

  • src/server/rate-limit-auth.ts must remain behavior-equivalent to the previously reviewed auth-boundary resolver and must never expose or bucket unvalidated credentials;
  • src/ratelimit/config-types.ts must make OcxRateLimitConfig and OcxConfig.rateLimit visible throughout the TypeScript program without weakening the merged credentialRef contract;
  • safeConfigDTO must retain ChefVault credentialRef/hasApiKey behavior;
  • rerun management/metrics admission, policy transitions, WebSocket release, provider-security forwarding and privacy/redaction tests.

Report or fix every P1/P2 blocker; prior reviews on 22ee3e5 are insufficient for this merge head.

Copy link
Copy Markdown
Author

CodeRabbit (@coderabbitai) review

Review only exact head f8e2b4e4bce7b90fda0a45454f31a91e565d59a5, including the post-#38 conflict-isolation modules and combined ChefVault/rate-limit behavior.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='f8e2b4e4bce7b90fda0a45454f31a91e565d59a5'
actual="$(git rev-parse HEAD)"
printf 'HEAD=%s\n' "$actual"
if [ "$actual" != "$expected" ]; then
  echo "MISMATCH: expected $expected"
fi
git status --short

printf '\n== Diff against dev merge-base ==\n'
base="$(git merge-base HEAD dev)"
printf 'merge-base=%s\n' "$base"
git diff --name-status "$base"...HEAD

printf '\n== Candidate conflict-isolation files ==\n'
fd -i 'rate-limit-auth|config-types' src

printf '\n== Outlines ==\n'
for f in src/server/rate-limit-auth.ts src/ratelimit/config-types.ts src/config.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    ast-grep outline "$f" --items all
  fi
done

printf '\n== safeConfigDTO references ==\n'
rg -n -C 10 'safeConfigDTO|credentialRef|hasApiKey' src -g '*.ts'

Length of output: 1231


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git fetch origin dev --quiet || true
git branch -a | head -n 20

base="$(git merge-base HEAD origin/dev 2>/dev/null || true)"
printf 'merge-base=%s\n' "${base:-none}"
if [ -n "${base:-}" ]; then
  git diff --name-status "$base"...HEAD
fi

printf '\n== Candidate conflict-isolation files ==\n'
fd -i 'rate-limit-auth|config-types' src

printf '\n== Outlines ==\n'
for f in src/server/rate-limit-auth.ts src/ratelimit/config-types.ts src/config.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    ast-grep outline "$f" --items all
  fi
done

printf '\n== safeConfigDTO references ==\n'
rg -n -C 10 'safeConfigDTO|credentialRef|hasApiKey' src -g '*.ts'

Length of output: 52496


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== rate-limit-auth.ts (full) =='
nl -ba src/server/rate-limit-auth.ts

echo '== config-types.ts (full) =='
nl -ba src/ratelimit/config-types.ts

echo '== ratelimit/index.ts relevant exports =='
rg -n 'RateLimitSurface|rateLimitFingerprinter|RateLimitPrincipal' src/ratelimit/index.ts | head -50

echo '== callers of resolveDataPlaneRateLimitPrincipal =='
rg -n -C6 'resolveDataPlaneRateLimitPrincipal' src -g '*.ts'

echo '== isDataPlaneAdmissionSecret def and usage =='
rg -n -C8 'isDataPlaneAdmissionSecret' src -g '*.ts'

echo '== OcxRateLimitConfig usages =='
rg -n -C4 'OcxRateLimitConfig' src -g '*.ts'

echo '== types.ts rateLimit field =='
rg -n -C6 'rateLimit\??:' src/types.ts

echo '== rate-limit.ts principal usage near gate/afterAuth =='
nl -ba src/server/rate-limit.ts | sed -n '1,260p'

Length of output: 1215


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== rate-limit-auth.ts (full) =='
cat -n src/server/rate-limit-auth.ts

echo '== config-types.ts (full) =='
cat -n src/ratelimit/config-types.ts

echo '== ratelimit/index.ts relevant exports =='
rg -n 'RateLimitSurface|rateLimitFingerprinter|RateLimitPrincipal' src/ratelimit/index.ts | head -50

echo '== callers of resolveDataPlaneRateLimitPrincipal =='
rg -n -C6 'resolveDataPlaneRateLimitPrincipal' src -g '*.ts'

echo '== isDataPlaneAdmissionSecret def and usage =='
rg -n -C8 'isDataPlaneAdmissionSecret' src -g '*.ts'

echo '== OcxRateLimitConfig usages =='
rg -n -C4 'OcxRateLimitConfig' src -g '*.ts'

echo '== types.ts rateLimit field =='
rg -n -C6 'rateLimit\??:' src/types.ts

echo '== rate-limit.ts principal usage near gate/afterAuth =='
cat -n src/server/rate-limit.ts | sed -n '1,260p'

Length of output: 18065


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== tsconfig include settings =='
cat -n tsconfig.json 2>/dev/null | sed -n '1,80p' || true
fd -e json -i tsconfig

echo '== Is config-types.ts imported anywhere? =='
rg -n "config-types" src -g '*.ts'

echo '== rateLimitConfigSchema (zod) full definition =='
cat -n src/config.ts | sed -n '660,745p'

echo '== Does OcxConfig get exported/used with rateLimit field checked elsewhere (type-level) =='
rg -n -C4 'config\.rateLimit\b' src -g '*.ts'

echo '== Check src/types.ts import graph / whether config-types augmentation is picked up via any barrel import =='
rg -n 'from "\./ratelimit/config-types"|from "\.\./ratelimit/config-types"|import .*config-types' src -g '*.ts'

echo '== ratelimit/index.ts head (to see if config-types re-exported) =='
cat -n src/ratelimit/index.ts | sed -n '1,40p'

Length of output: 1767


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== rateLimitConfigSchema (zod) full definition =='
cat -n src/config.ts | sed -n '660,745p'

echo '== Does OcxConfig get exported/used with rateLimit field checked elsewhere (type-level) =='
rg -n -C4 'config\.rateLimit\b' src -g '*.ts' || true

echo '== Check for import of config-types via side-effect import anywhere =='
rg -n 'config-types' src -g '*.ts' || true

echo '== ratelimit/index.ts head =='
cat -n src/ratelimit/index.ts | sed -n '1,40p'

echo '== management-auth.ts principal resolution 175-220 =='
cat -n src/server/management-auth.ts | sed -n '175,225p'

Length of output: 11166


@OnlineChef I reviewed exact head f8e2b4e4bce7b90fda0a45454f31a91e565d59a5. I found no P1 or P2 blocker in the two conflict-isolation modules or in the combined ChefVault/rate-limit behavior.

src/server/rate-limit-auth.ts — auth-boundary resolver

  • Line 24 calls isDataPlaneAdmissionSecret(presented, config) before it calls rateLimitFingerprinter.admissionKey(presented) at Line 25. An unvalidated credential never reaches this call.
  • Lines 27-29 fall back to remoteAddress or anonymous() when the presented credential is absent or invalid. This matches the previously reviewed auth-boundary resolver behavior exactly.
  • src/server/rate-limit.ts Line 212 calls this function only for non-management surfaces. Line 210-211 routes the "management" surface to resolveManagementRateLimitPrincipal in src/server/management-auth.ts instead. src/server/management-auth.ts, Lines 199-206, mints a management bucket only for a validated admin token or a live GUI session token; anything else falls back to the trusted address or anonymous bucket (Lines 208-210). Both resolvers preserve the merged isolation guarantee: invalid credentials cannot mint an unbounded bucket.

src/ratelimit/config-types.ts — type visibility

  • This file is not imported anywhere in src (confirmed by search). It still takes effect because tsconfig.json, Line 15, sets "include": ["src"]. TypeScript type-checks every file under src regardless of the import graph, so the declare module "../types" augmentation at Lines 7-38 applies to the whole program without a side-effect import.
  • The augmented OcxRateLimitConfig fields (Lines 17-32: enabled, loopbackBypass, maxBuckets, staleAfterMs, surfaces, websocket.perPrincipal/global) match the runtime rateLimitConfigSchema in src/config.ts, Lines 693-702, field for field. The zod schema is .strict() at Line 703, so an unknown field still fails validation as intended.
  • src/types.ts, Line 1005, declares credentialRef?: string on OcxProviderConfig. config-types.ts only augments OcxConfig and adds new standalone interfaces — it never touches OcxProviderConfig. There is no type collision with the ChefVault credentialRef contract.

safeConfigDTO — ChefVault behavior preserved

  • src/server/auth-cors.ts, Line 389, keeps hasApiKey: !!provider.apiKey || !!provider.credentialRef.
  • Line 398 keeps "credentialRef" in the copied-field allowlist. credentialRef is a chefvault:// pointer, not the raw secret, and it is already surfaced by src/provider-security/status.ts, Line 56. Its presence in safeConfigDTO output is unchanged and not a new disclosure.

Merge quality of the isolation split

  • The two modules touch disjoint surfaces: rate-limit-auth.ts handles data-plane principal resolution only; config-types.ts extends OcxConfig typing only. Neither file overlaps the ChefVault credential path in src/types.ts, src/server/auth-cors.ts, or src/providers/credential.ts. The conflict-isolation split holds.

I could not execute the Bun test suites or CI in this sandbox; this remains a static-only review, consistent with every earlier round on this PR. Keep the PR draft until exact-head CI and the dynamic ChefVault + rate-limit suites confirm the above statically-verified boundaries at runtime.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

You are interacting with an AI system.

@blacksmith-sh

blacksmith-sh Bot commented Aug 2, 2026

Copy link
Copy Markdown

OnlineChef

Exact-head integration review required for f8e2b4e4bce7b90fda0a45454f31a91e565d59a5 (zero behind dev).

yone_suke (@codesmith) independently execute the focused ChefVault + rate-limit suites and inspect the t...

Integration review of exact merge head f8e2b4e4bce7b90fda0a45454f31a91e565d59a5 complete (verified live tip, 0 behind dev, merge of ChefVault #38). Executed 151 focused tests across 9 suites plus 25 fresh adversarial reproductions. No P1/P2 blockers; nothing needed fixing.

Conflict-isolation modules:

  • src/server/rate-limit-auth.ts is behavior-equivalent to the previously reviewed resolver: proven by executing it directly — a configured key yields the same admission-key fingerprint across all three header shapes, unvalidated credentials (including the admin token, invented keys, and a ChefVault credentialRef value presented as a key) collapse into the shared remote-address fallback and never mint buckets, Origin/x-forwarded-for are never identity, no-credential/no-address yields anonymous, and raw values never appear in fingerprints. Critically, its validated/anonymous classification agrees with hasValidApiAuth on every header-priority permutation, so auth and metering cannot disagree.
  • src/ratelimit/config-types.ts uses declare module "../types" interface augmentation. With tsconfig include: ["src"] it is in the whole TypeScript program, and bun x tsc --noEmit (strict) passes with config.ts, the server, and tests all consuming OcxConfig.rateLimit, which proves program-wide visibility. Interface merging is additive by construction; it declares only rateLimit? and cannot weaken ChefVault's credentialRef contract. types.ts carries no duplicate declaration.

ChefVault contract: relative to origin/dev, this branch touches only rate-limit files; auth-cors.ts and types.ts are byte-identical to dev, so safeConfigDTO is dev's #38 version verbatim. Verified live: hasApiKey is !!apiKey || !!credentialRef, credentialRef (non-secret reference) is preserved in the DTO, keyless providers report hasApiKey: false, and no raw apiKey field leaks. The provider-security suite (634 lines) and forwarding tests pass.

Reruns at this head, all green: management and /metrics origin precedence (cross-origin admin uncharged and 403 before and after exhaustion, same-origin exhausts to 429); policy transitions (low→high at t=10s admits 0/11, high→low at t=1s allows); WebSocket cap-1 denial with 429, idempotent release restoring exactly one slot; snapshot redaction extended with a chefvault: needle, still counts-only. Config schema strictness survives the merge (unknown surfaces, effective websocket defaults, contradictory loopbackBypass all still rejected).

Validation: bun run typecheck clean; 151/151 tests across provider-security, provider-security-forwarding, server-images, ratelimit, ratelimit-policy-transition, ratelimit-admission, ratelimit-management-origin, server-management-auth, server-metrics-endpoint; bun run privacy:scan green.

P3 notes (non-blocking): the credential-header chain now exists in two textual copies (rate-limit-auth.ts and inline in hasValidApiAuth); equivalence is proven today but a shared helper would prevent drift. config-types.ts is anchored only by the tsconfig include; a side-effect import from src/ratelimit/index.ts would make the augmentation robust to entry-traversal tooling as well.

PR: #45

View with [code]smith

Copy link
Copy Markdown
Author

[code]smith (@codesmith-bot) Apply two focused follow-ups on existing branch feat/admission-rate-limit-integration only; do not create another PR.

  1. Auth-first regression update. Head 5feafac makes AdmissionGate.preAuthDeny always null and consumes only in commit() after existing auth/Origin checks. Replace the stale invalid keys share the remote-address fallback bucket test: repeated rotating invalid credentials must always remain 401, must not allocate/consume limiter buckets or counters because commit is never reached, and a valid credential must remain unaffected and receive its own bucket. Update PR body/security comments that still claim anonymous pre-auth charging or 429-before-401.

  2. Live-sideband WebSocket concurrency. In src/server/index.ts, after resolveLiveSidebandUpgrade succeeds and before server.upgrade, call sidebandGate.reserveConcurrency(). If denied, record the 429 request log and return with CORS. Store the accepted idempotent release handle as rateLimitRelease in the live-sideband WsData; release immediately if Bun upgrade fails. Existing close() already releases it. Add focused tests for accepted reservation, per-principal/global denial, failed-upgrade rollback, and idempotent close/release. Do not reserve before upstream sideband resolution succeeds.

Run typecheck plus focused ratelimit tests before pushing one coherent commit.

@OnlineChef OnlineChef left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Live-sideband concurrency remains the final runtime lifecycle gap.

Comment thread src/server/index.ts Outdated

@OnlineChef OnlineChef left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remaining E2E expectation still describes the removed pre-auth limiter behavior.

Comment thread tests/ratelimit-admission.test.ts

@OnlineChef OnlineChef left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The management E2E contains the same removed pre-auth assumption.

Comment thread tests/ratelimit-admission.test.ts
OnlineChef and others added 3 commits August 2, 2026 11:12
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
…ive-sideband concurrency

- /api/* now rejects cross-origin requests in the router before commit()
  (mirrors /metrics), so a 403 can never consume the management admission
  budget. This supersedes the surface-specific isAllowedManagementOrigin
  guard inside commit() (694bbd0), which is removed again: the auth-first
  gate keeps HTTP policy out of the limiter, exactly as 5feafac intended.
- Live-sideband WebSocket upgrades reserve a concurrency slot after the
  upgrade resolves and before the handshake completes, roll it back when
  Bun refuses the upgrade, and release it via the existing idempotent
  close() handle.
- Unit coverage for live reservations (per-principal/global caps,
  rollback, idempotent release) plus an e2e sideband reservation/release
  test; stale pre-auth-charging comments in index.ts and remaining docs
  wording (websocket scope, per-surface handshake charging) updated.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@blacksmith-sh

blacksmith-sh Bot commented Aug 2, 2026

Copy link
Copy Markdown

OnlineChef

[code]smith (@codesmith-bot) Apply two focused follow-ups on existing branch feat/admission-rate-limit-integration only; do not create another PR.

  1. Auth-first regression update. Head 5feafac makes `Admis...

Both follow-ups are done and pushed to PR #45 (head now 847c2f5).

What happened

While I was working, the CI-autofix session pushed 3 overlapping commits to the branch (2d327c5, 694bbd0, 2056cc7) that already rewrote the stale invalid-key and /metrics tests, updated most docs wording, and added tests/ratelimit-auth-precedence.test.ts. I rebased my work on top and resolved the overlap:

  • Kept from autofix: the rewritten e2e tests, the new precedence test file, and its docs wording for "How callers are identified" / "429 responses".
  • Superseded from autofix: its fix for the origin-precedence regression re-added isAllowedManagementOrigin inside commit() — the exact coupling 5feafac deliberately removed. My commit 847c2f5 removes that guard again and instead checks Origin in the /api/* router block before commit() (src/server/index.ts:457), mirroring /metrics, with the same { error: "cross-origin request blocked" } 403 payload the locked test pins.

My commit adds

  • Live-sideband concurrency reservation (src/server/index.ts:~830): reserved only after resolveLiveSidebandUpgrade succeeds, 429 logged via addFinalRequestLog, rateLimitRelease stored on WsData, rollback on failed server.upgrade; the existing close() handler releases it.
  • Unit test for live reservations (per-principal/global caps, rollback, idempotent release) in tests/ratelimit-admission.test.ts, plus an e2e sideband reserve/deny/release-on-close test in tests/server-live.test.ts (mock upstream, perPrincipal: 1, global: 1).
  • Stale comment fixes in index.ts and remaining docs gaps (websocket limit now covers sideband relays; per-surface handshake charging).

Validation: typecheck clean, all 6 focused suites pass (53 tests), privacy scan green. Full suite: only the 5 known pre-existing sandbox failures (cli-help/service path assertions) + GUI jsx-dev-runtime errors — unrelated.

PR body updated: sideband follow-up sentence replaced with the shipped behavior, and the two merge-gate checkboxes ticked. Remaining gates are cross-platform CI green on this head and the exact-head revalidation.

View with [code]smith

Copy link
Copy Markdown
Author

greptile-apps Revalidate exact head 847c2f5 with runtime repros. Required checks:

  1. Invalid rotating data-plane and management credentials always retain 401 and allocate/consume zero limiter state; cross-origin validated management requests retain 403 and consume zero budget.
  2. Valid credentials remain isolated opaque principals and receive protocol-native 429 only after auth/origin pass.
  3. Responses WebSocket and canonical live-sideband upgrades both charge the correct surface, reserve concurrency only after successful auth/origin (and after live upstream resolution), return 429 without upgrading on denial, roll back on failed Bun upgrade, and release idempotently on close/reset.
  4. Config effective defaults reject contradictory WebSocket caps; policy transitions remain non-retroactive.
  5. Aggregate snapshots/responses/docs expose no principal, raw credential, fingerprint, Origin, remote address, provider/model/account/request/conversation, prompt, or error dimensions.

Report any concrete reproducible defect against this exact head; otherwise mark safe to merge.

@OnlineChef
OnlineChef marked this pull request as ready for review August 2, 2026 11:30

Copy link
Copy Markdown
Author

CodeRabbit (@coderabbitai) review
greptile-apps Final exact-head review for 847c2f5. Cross-platform CI is fully green on Linux/macOS/Windows and all review threads are resolved. Review the current auth-first admission, management Origin precedence, config effective defaults, Responses/live-sideband WebSocket reserve/rollback/release, and privacy/cardinality boundaries. Report only concrete current-head defects; otherwise mark approved/safe to merge.

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected_short='847c2f5'
head="$(git rev-parse HEAD)"
printf 'HEAD=%s\n' "$head"
case "$head" in
  "$expected_short"*) ;;
  *) echo "ERROR: expected HEAD prefix ${expected_short}" >&2; exit 2 ;;
esac

printf '\n== Commit and change scope ==\n'
git show --no-patch --format=fuller HEAD
base="$(git merge-base HEAD dev 2>/dev/null || true)"
printf 'merge-base-with-dev=%s\n' "${base:-unavailable}"
if [ -n "${base:-}" ]; then
  git diff --name-status "$base"...HEAD
fi

printf '\n== Candidate files ==\n'
fd -i 'rate-limit|ratelimit|management-auth|auth-cors|config|index' src tests || true

printf '\n== Source outlines ==\n'
for f in \
  src/server/rate-limit.ts \
  src/server/rate-limit-auth.ts \
  src/server/management-auth.ts \
  src/server/index.ts \
  src/server/auth-cors.ts \
  src/ratelimit/token-bucket.ts \
  src/ratelimit/concurrency.ts \
  src/ratelimit/config-types.ts \
  src/config.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    ast-grep outline "$f" --items all
  fi
done

printf '\n== Admission and WebSocket call sites ==\n'
rg -n -C 10 \
  '\.gate\(|\.commit\(|\.afterAuth\(|reserveConcurrency\(|rateLimitRelease|server\.upgrade\(|resolveLiveSidebandUpgrade|isAllowedManagementOrigin|snapshot\(' \
  src tests -g '*.ts'

printf '\n== Focused test declarations ==\n'
rg -n -C 5 \
  '^(describe|test|it)\(|invalid.*(key|credential)|Origin|origin|live.sideband|websocket|upgrade|rollback|close|effective.default|policy.transition|snapshot|redact|fingerprint|credential' \
  tests -g '*ratelimit*.test.ts'

Length of output: 181595


🏁 Script executed:

#!/bin/bash
set -euo pipefail

show() {
  local file="$1" start="$2" end="$3"
  printf '\n== %s:%s-%s ==\n' "$file" "$start" "$end"
  nl -ba "$file" | sed -n "${start},${end}p"
}

show src/server/rate-limit.ts 90 255
show src/server/rate-limit-auth.ts 1 80
show src/server/management-auth.ts 185 235
show src/ratelimit/token-bucket.ts 80 280
show src/ratelimit/concurrency.ts 55 165
show src/config.ts 665 745
show src/server/index.ts 800 865
show src/server/index.ts 1015 1045
show tests/server-live.test.ts 540 650
show tests/ratelimit-auth-precedence.test.ts 1 125
show tests/ratelimit-management-origin.test.ts 1 110
show tests/ratelimit-admission.test.ts 90 140
show tests/ratelimit-admission.test.ts 210 275
show tests/ratelimit-admission.test.ts 425 510
show tests/ratelimit-admission.test.ts 530 610

printf '\n== Current rate-limit documentation and stale pre-auth wording ==\n'
rg -n -i -C 3 \
  'rate.?limit|pre.?auth|anonymous.*(charge|bucket|limit)|429.*401|401.*429|sideband.*(reserv|concurren)' \
  README.md docs .github -g '!node_modules/**' -g '!dist/**' -g '!build/**' 2>/dev/null || true

printf '\n== Full current-HEAD diff for directly reviewed files ==\n'
git show --format= --find-renames HEAD -- \
  src/server/index.ts \
  src/server/rate-limit.ts \
  src/server/rate-limit-auth.ts \
  src/ratelimit/concurrency.ts \
  src/config.ts \
  tests/ratelimit-admission.test.ts \
  tests/ratelimit-auth-precedence.test.ts \
  tests/server-live.test.ts \
  tests/ratelimit-management-origin.test.ts \
  README.md docs 2>/dev/null || true

Length of output: 1940


🏁 Script executed:

#!/bin/bash
set -euo pipefail

show() {
  local file="$1" start="$2" end="$3"
  printf '\n== %s:%s-%s ==\n' "$file" "$start" "$end"
  awk -v start="$start" -v end="$end" \
    'NR >= start && NR <= end { printf "%6d  %s\n", NR, $0 }' "$file"
}

show src/server/rate-limit.ts 90 255
show src/server/rate-limit-auth.ts 1 80
show src/server/management-auth.ts 185 235
show src/ratelimit/token-bucket.ts 80 280
show src/ratelimit/concurrency.ts 55 165
show src/config.ts 665 745
show src/server/index.ts 800 865
show src/server/index.ts 1015 1045
show tests/server-live.test.ts 540 650
show tests/ratelimit-auth-precedence.test.ts 1 125
show tests/ratelimit-management-origin.test.ts 1 110
show tests/ratelimit-admission.test.ts 90 140
show tests/ratelimit-admission.test.ts 210 275
show tests/ratelimit-admission.test.ts 425 510
show tests/ratelimit-admission.test.ts 530 610

printf '\n== Current rate-limit documentation and stale pre-auth wording ==\n'
rg -n -i -C 3 \
  'rate.?limit|pre.?auth|anonymous.*(charge|bucket|limit)|429.*401|401.*429|sideband.*(reserv|concurren)' \
  README.md docs .github -g '!node_modules/**' -g '!dist/**' -g '!build/**' 2>/dev/null || true

printf '\n== Current-HEAD diff for directly reviewed files ==\n'
git show --format= --find-renames HEAD -- \
  src/server/index.ts \
  src/server/rate-limit.ts \
  src/server/rate-limit-auth.ts \
  src/ratelimit/concurrency.ts \
  src/config.ts \
  tests/ratelimit-admission.test.ts \
  tests/ratelimit-auth-precedence.test.ts \
  tests/server-live.test.ts \
  tests/ratelimit-management-origin.test.ts \
  README.md docs 2>/dev/null || true

Length of output: 106076


@OnlineChef I reviewed exact head 847c2f5af5bc3560eb5dd4db906fe369b8c2da25.

I found one P2 blocker.

P2 — Failed live-sideband upgrades are recorded as successful 101 upgrades.

In src/server/index.ts, Line 844 calls addFinalRequestLog(..., 101) before server.upgrade() runs. If server.upgrade() returns false, Lines 855-856 release the reservation and return HTTP 426. The request log still records status 101.

This creates a false successful-upgrade record for every failed Bun upgrade. It affects request-log accuracy and any downstream metrics or diagnostics that consume these records.

Move the 101 log call into the successful-upgrade branch. In the failed-upgrade branch, keep the reservation release and record 426 before returning the error response.

The reviewed admission boundaries otherwise match the requested behavior:

  • src/server/rate-limit.ts, Lines 209-226, resolves a limiter principal only when commit() runs. Invalid credentials that return 401 before commit() allocate and consume no limiter state.
  • src/server/index.ts, Lines 434-440 and Lines 455-464, perform management authentication and Origin validation before management admission commits. The /api/* and /metrics paths preserve 403 precedence over 429.
  • src/config.ts, Lines 711-729, compares effective WebSocket defaults. It rejects contradictory partial configurations such as { enabled: true, websocket: { global: 1 } }.
  • src/server/index.ts, Lines 831-856, resolves the live-sideband upstream before it reserves concurrency. It releases the reservation if Bun rejects the upgrade.
  • src/server/index.ts, Lines 1032-1042, calls the idempotent rateLimitRelease handle on every WebSocket close path.
  • src/ratelimit/token-bucket.ts, Lines 195-207, and src/ratelimit/concurrency.ts, Lines 141-150, serialize aggregate-only counters. They do not serialize principals, fingerprints, credentials, Origins, or remote addresses.
  • The focused tests cover auth-first handling, management-Origin precedence, effective-default validation, token-policy transitions, reservation caps, rollback, and close release.

Cross-platform CI is reported green. The failed-upgrade request-log status still requires correction before this PR is safe to merge.

You are interacting with an AI system.

Copy link
Copy Markdown
Author

Final merge audit for exact head 847c2f5:

  • Cross-platform CI run 30745433014: all six Linux/macOS/Windows main and package jobs green, including typecheck, full tests, GUI tests, privacy scan, GUI lint/build, CLI smoke and package install.
  • React Doctor: exact-head ready run 30745880247 green.
  • All inline review threads resolved; the reproduced effective-WebSocket-default defect is fixed and outdated.
  • Auth-first precedence manually re-audited: invalid credentials remain 401 and allocate no limiter state; rejected management Origin remains 403 and consumes no budget.
  • Responses and live-sideband WebSockets both reserve before upgrade, roll back failed upgrades and release idempotently on close/reset.
  • Config, snapshots, responses and docs expose no raw credential, principal, fingerprint, Origin, remote address or request/provider/model/account/conversation dimensions.
  • Exact-head Greptile revalidation was requested twice and produced no current finding. CodeRabbit could not run a new textual review because of quota; that is recorded as unavailable, not treated as approval.

Merge basis: resolved reproduced findings + exact-head manual security/lifecycle audit + fully green exact-head CI. Merging with expected-head SHA lock.

@OnlineChef
OnlineChef merged commit 76b4037 into dev Aug 2, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant