Skip to content

chore(audit): write entitlements to Upstash only in production - #2563

Merged
kk-agent merged 2 commits into
mainfrom
cursor/entitlements-upstash-only-0f95
Nov 4, 2026
Merged

kk-agent merged 2 commits into
mainfrom
cursor/entitlements-upstash-only-0f95

Conversation

@kk-agent

@kk-agent kk-agent commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Outcome

Production entitlement saves reach Upstash. When Upstash credentials are set, saveEntitlement writes redis.set and does not touch .data/entitlements.json, so a read-only deploy filesystem cannot skip the Upstash write. A Redis write error now propagates.

Findings

  • Done: saveEntitlement updated the in-memory map, awaited writeEntitlementToFile (mkdir + writeFile in entitlement-file-store.ts, not caught), and only then called redis.set. rg writeEntitlementToFile on current main hits entitlement-file-store.ts, entitlement-store.ts, and entitlement-file-store.test.ts. The file store stays for the local no-Redis path.
  • Done: removed the empty catch around redis.set.
  • Not touched: billing/paywall-bypass.ts and Stripe/checkout code.

Scope

  • Included: apps/web/src/lib/billing/entitlement-store.ts and apps/web/src/lib/billing/__tests__/entitlement-store.test.ts.
  • Explicitly excluded: paywall bypass, Stripe, checkout, and the file store used when Redis is not configured.

Risk

  • Risk level: low
  • Failure mode: a Redis write error now fails the save instead of being ignored. Local dev without Upstash still writes the file.
  • Rollback: revert this commit.

Verification

Reproduced before the code change. npm --workspace=apps/web exec vitest run src/lib/billing/__tests__/entitlement-store.test.ts failed both cases:

  • With Redis configured, a real mkdir throw (EEXIST: file already exists) from writeEntitlementToFile aborted saveEntitlement before redis.set.
  • With redis.set rejecting, the save resolved the record. The mock recorded one set call and the empty catch swallowed upstash unavailable.

After the change, that file passed (2 tests). The Redis client in the test is a vi.mock of @upstash/redis so the assertion can see set without a live Upstash account. The file-write failure in the reproduction was a real filesystem error.

Local commands:

  • npm ci completed
  • npm --workspace=apps/web run lint passed
  • npm run build:web passed, including TypeScript
  • npm --workspace=apps/web test: 1562 passed, 2 failed, 1 skipped. Both failures are src/lib/__tests__/home-sell-surface.test.ts

CI on head d1371ccb9 (https://github.com/groupthinking/EventRelay/actions/runs/41670537706):

  • build passed (type-check, ESLint, web build, Studio preflight, production image)
  • lint-frontend, guards, Python 3.11 and 3.12 tests passed
  • test-frontend failed. The log shows entitlement-store.test.ts passed (2 tests). The only failures are the two home-sell-surface.test.ts cases, the same failure already on main run 41659258955. Left unchanged.

Production evidence

Not applicable. No deploy was performed. The change is the entitlement write path, covered by the unit test above. CI did not call a live Upstash account.

Issue link check

The Closing keyword to open issue check failed, as expected: https://github.com/groupthinking/EventRelay/actions/runs/41670537564/job/126037389310

Open in Web Open in Cursor 

Related: #2328

Closes #2581

saveEntitlement wrote entitlements.json before redis.set, so a read-only
filesystem throw skipped Upstash, and an empty catch hid Redis errors.
When Upstash is configured, write Redis only and let set failures propagate.

Co-authored-by: KK <kk-agent@users.noreply.github.com>
@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
v0-uvai Ready Ready Preview, v0 Nov 4, 2026 7:12am UTC

@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-03T16:06:30.639260Z d1371cc PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ Repository reconciliation: this PR does not reference exactly one canonical issue.

Please add a Closes #<issue> reference in the PR description so it can be tracked against the delivery plan.

See governance: #898

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

✅ E2E Test Results: ALL TESTS PASSED

Metric Value
Status 🟢 GREEN
Total Tests
Passed 16
Failed
Deployment https://v0-uvai-r9rjnyxd0-garv1.vercel.app
Test Output
ass on every request without persisting it�[32m 1�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mTemplate Gallery�[2m > �[22mhomepage returns 200 with HTML�[32m 93�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mTemplate Gallery�[2m > �[22mfeatures page contains template/workflow markup�[32m 142�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mTemplate Gallery�[2m > �[22mfeatures page surfaces at least 5 workflow/template indicators�[32m 99�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mVideo Pack Pipeline�[2m > �[22mPOST /api/video/pack accepts a YouTube URL without hanging�[33m 2168�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mVideo Pack Pipeline�[2m > �[22mGET /api/video/pack resolves to a ready pack with evidence lineage�[33m 598�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mError Handling�[2m > �[22mmissing URL returns 400, not a hang�[32m 74�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mError Handling�[2m > �[22minvalid URL returns 400 quickly, not a hang�[32m 74�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mError Handling�[2m > �[22mPOST /api/video/pack with no body returns 400�[32m 64�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mDashboard�[2m > �[22m/dashboard returns 200�[32m 144�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mDashboard�[2m > �[22m/dashboard contains agent or pipeline visualization markup�[32m 84�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mAPI Health�[2m > �[22mGET /api returns a response (not 404)�[32m 232�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mAPI Health�[2m > �[22mretired /api/pipeline routes are gone (no pipeline served)�[32m 82�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mStatic Assets�[2m > �[22mhomepage has proper meta tags�[32m 78�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mStatic Assets�[2m > �[22m/features page returns 200�[32m 100�[2mms�[22m�[39m

�[2m Test Files �[22m �[1m�[32m1 passed�[39m�[22m�[90m (1)�[39m
�[2m      Tests �[22m �[1m�[32m16 passed�[39m�[22m�[90m (16)�[39m
�[2m   Start at �[22m 16:09:38
�[2m   Duration �[22m 5.66s�[2m (transform 55ms, setup 0ms, import 69ms, tests 5.46s, environment 0ms)�[22m


@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

✅ E2E Test Results: ALL TESTS PASSED

Metric Value
Status 🟢 GREEN
Total Tests
Passed 16
Failed
Deployment https://v0-uvai-k84arap3g-garv1.vercel.app
Test Output
y request without persisting it�[32m 1�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mTemplate Gallery�[2m > �[22mhomepage returns 200 with HTML�[33m 336�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mTemplate Gallery�[2m > �[22mfeatures page contains template/workflow markup�[32m 248�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mTemplate Gallery�[2m > �[22mfeatures page surfaces at least 5 workflow/template indicators�[32m 242�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mVideo Pack Pipeline�[2m > �[22mPOST /api/video/pack accepts a YouTube URL without hanging�[33m 2849�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mVideo Pack Pipeline�[2m > �[22mGET /api/video/pack resolves to a ready pack with evidence lineage�[33m 896�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mError Handling�[2m > �[22mmissing URL returns 400, not a hang�[33m 534�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mError Handling�[2m > �[22minvalid URL returns 400 quickly, not a hang�[33m 357�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mError Handling�[2m > �[22mPOST /api/video/pack with no body returns 400�[32m 200�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mDashboard�[2m > �[22m/dashboard returns 200�[33m 303�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mDashboard�[2m > �[22m/dashboard contains agent or pipeline visualization markup�[32m 254�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mAPI Health�[2m > �[22mGET /api returns a response (not 404)�[33m 1157�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mAPI Health�[2m > �[22mretired /api/pipeline routes are gone (no pipeline served)�[32m 114�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mStatic Assets�[2m > �[22mhomepage has proper meta tags�[32m 221�[2mms�[22m�[39m
 �[32m✓�[39m tests/e2e/pipeline.test.ts�[2m > �[22mEventRelay E2E — Live Deployment�[2m > �[22mStatic Assets�[2m > �[22m/features page returns 200�[32m 256�[2mms�[22m�[39m

�[2m Test Files �[22m �[1m�[32m1 passed�[39m�[22m�[90m (1)�[39m
�[2m      Tests �[22m �[1m�[32m16 passed�[39m�[22m�[90m (16)�[39m
�[2m   Start at �[22m 07:19:47
�[2m   Duration �[22m 11.37s�[2m (transform 54ms, setup 0ms, import 69ms, tests 11.18s, environment 0ms)�[22m


@kk-agent
kk-agent merged commit 02f4aff into main Nov 4, 2026
32 of 33 checks passed
@kk-agent
kk-agent deleted the cursor/entitlements-upstash-only-0f95 branch November 4, 2026 08:58

This branch was successfully deployed

1 active deployment
Preview — 57abb3f8 Deployed Nov 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update javascript code tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit cleanup: chore(audit): write entitlements to Upstash only in production

3 participants