Skip to content

feat(auth): count sign-in code email sends per caller - #8022

Merged
iscekic merged 13 commits into
mainfrom
drain/sign-in-code-send-budgets
Oct 27, 2026
Merged

iscekic merged 13 commits into
mainfrom
drain/sign-in-code-send-budgets

Conversation

@iscekic

@iscekic iscekic commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Evidence

Evidence: demonstrated — production-mode local run of POST /api/auth/native/otp on this VM against local Postgres, with a local HTTPS edge in front of the handler that answers the Vercel rate-limit API contract the firewall client calls. The route returned the documented status for each configured count, and admitted requests created one code row and one suppressed email dispatch each.

Local run

Setup: the route handler module runs with NODE_ENV=production, the mode in which @vercel/firewall performs its rate-limit lookup, against local Postgres. A local HTTPS edge answers /.well-known/vercel/rate-limit-api/<rule> with 204 (counted), 429 (count exhausted) or 404 (rule absent), keyed on x-vercel-rate-limit-key, and supplies the caller address headers.

  • Both rule ids are consulted on the request path, per-address first: the edge recorded one magic-link-email call and one sign-in-code-email-ip call per request.
  • An available count admits the request: 200 {"success":true,"challengeId":…}, one code row, one email dispatch.
  • An exhausted count returns 429 {"success":false,"error":"SIGNUP-RATE-LIMITED"} with no code row and no dispatch.
  • The caller key follows the edge-supplied x-vercel-forwarded-for value; the x-real-ip fallback is covered by the unit suite.
  • A rule that is absent from the edge configuration is skipped and the request is served normally, as the unit suite covers for the report.

Not observed: the web magic-link route needs a Turnstile token in this environment, so only the native OTP route was driven; both routes call the shared checkEmailSignInEligibility. The report for a rule that is not configured is covered by the unit suite, since the local run has no Sentry DSN.

Change

Sign-in code email volume is counted against two Vercel Firewall rules in apps/web/src/lib/auth/email-signin-eligibility.ts:

  • magic-link-email — the existing per-normalized-address rule, same 429 shape as before.
  • sign-in-code-email-ip — new rule keyed by the caller address taken from the edge-set forwarded header (x-vercel-forwarded-for), falling back to x-real-ip then unknown.

Both run inside checkEmailSignInEligibility before the domain checks, so POST /api/auth/native/otp and POST /api/auth/magic-link share the counters. An exhausted count returns the existing 429 SIGNUP-RATE-LIMITED shape; no new response shape is introduced.

A rule that is not present in the project's firewall configuration is skipped, and the gap is reported to Sentry at most once a minute. Both rule ids are documented in ENVIRONMENT.md (Security & Auth) for the firewall configuration review.

The native OTP route keeps its opaque 200 for addresses the domain rules refuse and its existing ordering; only the eligibility module and its tests changed.

Tests

  • apps/web/src/lib/auth/email-signin-eligibility.test.ts — call order and keys, caller key from the edge header plus the x-real-ip fallback, 429 mapping, rule-not-configured path with one report per interval, and ordering before the domain rules.
  • apps/web/src/app/api/auth/native/otp/route.test.ts — unchanged from main; the route passes eligibility outcomes through as before.
  • apps/web/src/app/api/auth/magic-link/route.test.ts — the key assertions now select calls by rule id, since each request makes two firewall calls.
  • apps/web/src/lib/observability/rest-routes-auth.test.ts — unchanged from main.
  • pnpm --filter web run typecheck, oxlint and the suites above pass locally; CI on the head commit is green.

Production prerequisites

  • Create the Vercel Firewall rate-limit rule sign-in-code-email-ip with a chosen threshold, and confirm magic-link-email exists. Both rule ids are listed in ENVIRONMENT.md for the firewall configuration review. Without the new rule the per-caller counter is skipped and each instance reports the missing rule to Sentry once a minute.

Merge notes

  • Merge by human; auth change.

Proxy answers

The owner's proxy gave these answers in the owner's place.

  1. Question: Fail-closed: When a rate-limit rule is missing from the firewall configuration (checkRateLimit error 'not-found'), which rules should fail closed with 503?
    Answer: All rules
    Confidence: 0.65 · Precedent: Fix the root cause and the class of the issue: session 00eb2a1d3e4b, "if anything repeats the same mistake over and over again, fix the class". The brief also says to return 503 whenever a rate-limit rule is unavailable.

  2. Question: 503 shape: What response should the 503 fail-closed case return on the native OTP route?
    Answer: SIGNUP-UNAVAILABLE (Recommended)
    Confidence: 0.70 · Precedent: Prefer existing code to hand-rolled additions: session df491946927a, "we will try and use libraries wherever we can". This reuses an existing error type that clients already map and keeps the response shapes unchanged.

  3. Question: Scope: Where should the new limits apply? The web magic-link route calls the same checkEmailSignInEligibility function.
    Answer: Shared (Recommended)
    Confidence: 0.75 · Precedent: Fix the class on every affected surface: session 75f8833fe8c1, "fix this for all shared services model pickers on all surfaces (web, app)".

  4. Question: main is currently red: every CI job fails at pnpm install --frozen-lockfile with a broken lockfile entry (jest-worker references a package version main's lockfile no longer defines). Two open PRs fix exactly that line (fix(repo): restore the green main checks #8510, fix(deps): resolve jest-worker structured-clone in the lockfile #8511, both green checks, awaiting an owner merge). This PR's new merge commit inherits main's broken lockfile, so its CI cannot go green until one of those lands. How should this PR proceed?
    Answer: Carry the same one-line lockfile repair here (Recommended)
    Confidence: 0.60 · Precedent: Autonomy: never accept blocked or failed as an end state (session 2f71af3a787e, "a work item must converge"); fix gaps even if unrelated (session a247f3c19b1e, "fix the error even though it's unrelated")

  5. Question: Review state: GitHub shows Kilo Code Review completed successfully on exact head 6bbe940 with summary “Code review completed successfully,” and no new comments or review object; pr_loop_state still says “reviewed head no.” How should this be resolved?
    Answer: Treat exact-head success as complete
    Confidence: 0.82 · Precedent: Expect kilobot to post reviews in varied shapes: session df491946927a, “kilobot is inconsistent in how it posts a review”; human ready means kilobot recommends merge or every comment was addressed (session 640cb4aa808a)

Answers 1 and 2 describe the earlier fail-closed design; the review on 2026-10-23 replaced it with a skip-and-report path for a rule that is not configured, and answer 3 still holds: the counters live in the shared eligibility check.

The eligibility check for email sign-in codes now counts each send
against three Vercel Firewall rules: the existing per-address rule, a
new rule keyed by the caller address, and a new rule sharing one
global bucket. Each rule returns 429 SIGNUP-RATE-LIMITED when its
allowance is exhausted, and a rule missing from the firewall
configuration returns 503 SIGNUP-UNAVAILABLE instead of proceeding.
Rule ids are documented in ENVIRONMENT.md for the firewall
configuration, and tests cover the keys, response mappings, and
missing-rule behavior for the native OTP and magic-link routes.
@iscekic iscekic added drain Work item from the findings drain janitor:trust Janitor finding: trust boundary hardening labels Oct 19, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only new commit reverts the global send bucket, the mobile SIGNUP-UNAVAILABLE mapping, and their tests/mocks, leaving the per-address and per-caller firewall checks plus a throttled missing-rule report that matches the repository's existing fail-open firewall wrappers; the updated call-count assertions match the two checkRateLimit calls per send.

Assumptions: [INFERENCE] The PR title, body, evidence, and merge notes still describe a sign-in-code-email-global rule and a 503 fail-closed response, but current HEAD has neither: sign-in-code-email-global is referenced nowhere, and apps/web/src/lib/auth/email-signin-eligibility.ts:93-95 skips an unconfigured rule after a once-per-minute Sentry report. ENVIRONMENT.md matches the code, so operators should follow it rather than the merge notes. Spoof resistance of the per-caller key still depends on Vercel overwriting x-vercel-forwarded-for at the edge, which repository code cannot prove. Read-only review: no tests or typechecks were executed.

Files Reviewed (96 files)
  • ENVIRONMENT.md - documents the two remaining rules and the report-once-per-minute behavior
  • apps/web/src/lib/auth/email-signin-eligibility.ts - per-address/per-caller checks retained; missing rules skip after a throttled Sentry report
  • apps/web/src/lib/auth/email-signin-eligibility.test.ts - missing-rule cases now assert continue-plus-report; global suite removed
  • apps/web/src/app/api/auth/native/otp/route.ts - global budget call removed; comment names the caller limit
  • apps/web/src/app/api/auth/native/otp/route.test.ts - global-budget cases removed; remaining 429/503 cases consistent
  • apps/web/src/app/api/auth/magic-link/route.ts - global budget call removed; eligibility path unchanged
  • apps/web/src/app/api/auth/magic-link/route.test.ts - call counts/indexes updated for two rate-limit checks per request
  • apps/web/src/lib/observability/rest-routes-auth.test.ts - checkGlobalSendBudget stub removed
  • apps/mobile/src/lib/auth/auth-error-messages.ts + apps/mobile/src/i18n/locales/*.json (87 catalogs) - one-line signupUnavailable additions reverted; files match base
Previous Review Summaries (8 snapshots, latest commit e6d1e25)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit e6d1e25)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental change is three test-only edits plus a lockfile snapshot-key repair; the new OTP test matches the route's intentional fall-through for EMAIL-SIGNUP-DISABLED, and the narrowed test helpers fail loudly instead of silently defaulting to an empty body.

Assumptions: [INFERENCE] The per-caller key's spoof resistance still rests on Vercel overwriting x-vercel-forwarded-for at the edge, which is not verifiable from repository code. The new OTP case (counts a signup-disabled delivery against the total send budget) does not re-open the earlier "global bucket spent by requests that never send mail" finding: apps/web/src/app/api/auth/native/otp/route.ts:67 deliberately excludes EMAIL-SIGNUP-DISABLED from the early return, so that path does create a code and send mail before consuming the budget. Read-only review: no tests or typechecks were executed.

Files Reviewed (4 files)
  • apps/web/src/app/api/auth/native/otp/route.test.ts - new signup-disabled budget case matches the route's fall-through at route.ts:67-98 and asserts no code issuance on a 429
  • packages/web-shared/src/lib/ai-gateway/openai-chatgpt/refresh-terminal-race.test.ts - URLSearchParams assertion matches the production body (refresh.ts:136-149 sends URLSearchParams.toString()); non-string body now throws
  • packages/web-shared/src/lib/ai-gateway/providers/upstream-request.aws-bedrock.test.ts - helper narrowing is safe; both callers send JSON.stringify bodies (upstream-request.ts:276-283), and the URL branch covers string/URL/Request
  • pnpm-lock.yaml - generated snapshot-key repair only; not reviewed

Previous review (commit 39e51f6)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental change is comment-only plus a lint-driven test-helper narrowing; the previously carried suggestion about a deleted precedent is now resolved, and the new reference (clientIpFromHeaders) accurately describes that reader's header order.

Assumptions: [INFERENCE] The spoof resistance of the per-caller key still depends on Vercel overwriting x-vercel-forwarded-for at the edge, which is not verifiable from repository code; the docstring now states that edge behavior directly and cites packages/web-shared/src/lib/admin/admin-access-log.ts:361 only for the shared header name, which matches the code (it prefers x-forwarded-for and falls back to x-vercel-forwarded-for). Read-only review: no tests or typechecks were executed.

Files Reviewed (2 files)
  • apps/web/src/lib/auth/email-signin-eligibility.ts - docstring re-pointed at a current file; claim verified against clientIpFromHeaders; no issues
  • packages/web-shared/src/lib/ai-gateway/openai-chatgpt/refresh-terminal-race.test.ts - new body assertion still fails loudly for a non-string body (empty string does not contain the token), and the production exchange sends URLSearchParams.toString(), so no weakened coverage

Previous review (commit 54cf369)

Status: 1 Suggestion Found | Recommendation: Merge

Executive Summary

The incremental change (a lint-driven test-helper rewrite plus the main merge) introduces no new defect; the only open remark is the still-stale precedent cited in the per-caller header rationale.

Assumptions: [INFERENCE] The per-caller key's spoof resistance depends on Vercel overwriting x-vercel-forwarded-for at the edge, which I could not verify against repository code or Vercel documentation; the file the comment cites was deleted, and the current x-vercel-forwarded-for caller (packages/web-shared/src/lib/admin/admin-access-log.ts:361) actually prefers x-forwarded-for, so it is not a precedent for trusting only the Vercel header. Read-only review: repository tests and typechecks were not executed.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/web/src/lib/auth/email-signin-eligibility.ts 42 Still cites the deleted lib/mcp-gateway/oauth-client-service.ts as precedent. Verify against a current caller or state the Vercel edge behavior directly; no current file trusts x-vercel-forwarded-for alone (admin-access-log.ts:361 prefers x-forwarded-for).
Files Reviewed (2 files)
  • packages/web-shared/src/lib/ai-gateway/providers/upstream-request.aws-bedrock.test.ts - incremental change; no new issues (url narrowing covers string/URL/Request, non-string body yields '', and the SigV4 assertions would fail loudly if the body shape were wrong)
  • apps/web/src/lib/auth/email-signin-eligibility.ts - targeted re-check only; 1 carried-forward suggestion

Previous review (commit bb4a2bf)

Status: 1 Suggestion Found | Recommendation: Merge

Executive Summary

The incremental change (the main merge plus the lockfile repair) resolves every previous finding and introduces no functional or security defect; the only remark is a stale source reference in the per-caller header rationale.

Assumptions: [INFERENCE] The per-caller key's spoof resistance rests on the claim that Vercel overwrites x-vercel-forwarded-for at the edge. I could not confirm that against repository code or Vercel documentation, and the file the comment cites as precedent was deleted, so the claim is unverified (see the inline remark). Read-only review: repository tests and typechecks were not executed.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/web/src/lib/auth/email-signin-eligibility.ts 42 Cited precedent lib/mcp-gateway/oauth-client-service.ts was deleted from the repository, so the header-trust rationale cannot be verified against it.
Files Reviewed (10 files)
  • apps/web/src/lib/auth/email-signin-eligibility.ts - 1 suggestion
  • apps/web/src/lib/auth/email-signin-eligibility.test.ts
  • apps/web/src/app/api/auth/native/otp/route.ts
  • apps/web/src/app/api/auth/native/otp/route.test.ts
  • apps/web/src/app/api/auth/magic-link/route.ts
  • apps/web/src/app/api/auth/magic-link/route.test.ts
  • apps/web/src/lib/observability/rest-routes-auth.test.ts
  • apps/mobile/src/lib/auth/auth-error-messages.ts
  • ENVIRONMENT.md
  • pnpm-lock.yaml

Previous review (commit 2718214)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental commit wraps the checkEmailSignInEligibility/checkGlobalSendBudget import onto multiple lines, resolving the only outstanding formatting finding; no new issues were introduced in the changed code.

Files Reviewed (1 file)
  • apps/web/src/lib/observability/rest-routes-auth.test.ts

Previous review (commit d5b36eb)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The single incremental commit only wires the new checkGlobalSendBudget mock into the REST timing suite; it fixes the failing test, but the added import line exceeds the repo's 100-column printWidth and breaks format:check.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/observability/rest-routes-auth.test.ts 81 Import line is 105 chars against printWidth: 100; oxfmt --list-different . in the format-check CI job will fail.
Files Reviewed (1 file)
  • apps/web/src/lib/observability/rest-routes-auth.test.ts - 1 issue

Fix these issues in Kilo Cloud

Previous review (commit efce6de)

Status: No Issues Found | Recommendation: Merge

Executive Summary

I reviewed the incremental changes since 63bb4e6f — the trusted edge-IP key, the send-time global budget, the throttled missing-rule Sentry report, and the mobile SIGNUP-UNAVAILABLE mapping with all 87 catalogs — and all four previously raised findings are addressed with no new defects found.

Files Reviewed (95 files)
  • apps/web/src/lib/auth/email-signin-eligibility.ts
  • apps/web/src/lib/auth/email-signin-eligibility.test.ts
  • apps/web/src/app/api/auth/magic-link/route.ts
  • apps/web/src/app/api/auth/magic-link/route.test.ts
  • apps/web/src/app/api/auth/native/otp/route.ts
  • apps/web/src/app/api/auth/native/otp/route.test.ts
  • apps/mobile/src/lib/auth/auth-error-messages.ts
  • apps/mobile/src/i18n/locales/*.json (87 catalogs)
  • ENVIRONMENT.md

Previous review (commit 63bb4e6)

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/lib/auth/email-signin-eligibility.ts 42 Per-caller bucket keyed on client-controlled x-forwarded-for
apps/web/src/lib/auth/email-signin-eligibility.ts 193 Shared global bucket spent by requests that never send mail

SUGGESTION

File Line Issue
apps/web/src/lib/auth/email-signin-eligibility.ts 78 Missing-rule fail-closed path has no Sentry alert
apps/web/src/lib/auth/email-signin-eligibility.ts 199 Mobile has no SIGNUP-UNAVAILABLE mapping
Files Reviewed (5 files)
  • ENVIRONMENT.md - 0 issues
  • apps/web/src/app/api/auth/magic-link/route.test.ts - 0 issues
  • apps/web/src/app/api/auth/native/otp/route.test.ts - 0 issues
  • apps/web/src/lib/auth/email-signin-eligibility.test.ts - 0 issues
  • apps/web/src/lib/auth/email-signin-eligibility.ts - 4 issues

Fix these issues in Kilo Cloud


Reviewed by deepseek-flash-latest · Input: 93.1K · Output: 23.2K · Cached: 1.2M

Review guidance: REVIEW.md from base branch main

…ge forwarded header

The shared total bucket is now counted only after an address passed
eligibility, in the two routes that send the code, so refusals that
never send cannot spend it. The per-caller key reads the forwarded
header the edge provides, matching the gateway OAuth client limiter.
A rule missing from the firewall configuration is reported to Sentry
through the same throttled helper used by the other firewall wrappers.
Mobile maps the new unavailable code in every locale catalog, and the
suites cover the send-time counting, the caller key, and the reporting.

@iscekic iscekic left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

bot: Fixed. The per-caller key now reads x-vercel-forwarded-for, the header Vercel overwrites at the edge, with the same fallback chain as the gateway OAuth client limiter (x-real-ip, then unknown). A test pins the first-hop derivation.

@iscekic

iscekic commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Fixed. The per-caller key now reads x-vercel-forwarded-for, the header Vercel overwrites at the edge, with the same fallback chain as the gateway OAuth client limiter (x-real-ip, then unknown). A test pins the first-hop derivation.

@iscekic iscekic added the merge:human Substantive functional risk requires human review. label Oct 19, 2026
@iscekic
iscekic enabled auto-merge October 19, 2026 17:45
@iscekic
iscekic disabled auto-merge October 20, 2026 19:26
@iscekic iscekic added merge:blocked merge:blocked:insufficient-e2e Verification does not cover the change and removed merge:blocked labels Oct 20, 2026
Resolve the eligibility result union, the shared eligibility test file, and the Khmer catalog entry so both sides' changes stay.
The jest-worker resolution and the optional wasm runtime entries were absent from the lockfile, so a frozen install could not build the workspace graph. Adding them keeps the resolved versions unchanged.
…fying objects

The helper stringified the fetch mock's url and body, which lint rejects because both can be objects. It now resolves the url from each accepted shape and reads the body only when it is a string.
The terminal-race test stringified a BodyInit, which lint rejects. It reads the body when it is a string, so the assertion still fails if the request carries another body shape.
Take main's ai-gateway test-helper fixes and its lockfile repair.
@iscekic iscekic removed merge:human Substantive functional risk requires human review. merge:blocked:insufficient-e2e Verification does not cover the change labels Oct 21, 2026
…end budget

Adds a route case for an address the signup switch refuses: the shared total allowance is enforced before the code is created, answering 429 with no code stored or sent.
@iscekic

iscekic commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

bot: End-to-end evidence on head e6d1e25, exercised on this VM against the local Postgres and the repository's local email capture.

  • POST /api/auth/native/otp with a test address answered 200 {"success":true,"challengeId":…}.
  • The same request stored one magic_link_tokens row with purpose sign_in_code and a ten-minute expiry.
  • The message body was written to the local email capture directory with a matching timestamp.

The per-rule counters and their keys are pinned by the unit suites. The firewall SDK reports every rule as available outside production, so the 429 and 503 branches are not observable on this VM. No screenshot: the change has no visual surface.

@iscekic iscekic added the merge:blocked:insufficient-e2e Verification does not cover the change label Oct 22, 2026
@iscekic iscekic added merge:human Substantive functional risk requires human review. and removed merge:blocked:insufficient-e2e Verification does not cover the change labels Oct 22, 2026

@iscekic iscekic left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Request changes: please narrow this PR to the per-caller limit only. Drop the global send cap: one caller could use up the budget and block email sign-in for every user. When a firewall rule is missing, keep the current fail-open behavior with a throttled Sentry report, not a 503. Remove the new unavailable error copy if nothing uses it after this change.

Sign-in code eligibility applies the existing per-address firewall rule and
a new caller-keyed rule before a code is issued. The caller key comes from
the edge-set forwarded header, then the real-ip header, then a fixed key.
An exhausted count returns the existing 429 SIGNUP-RATE-LIMITED shape.

A rule that is not present in the project's firewall configuration is
skipped and reported to Sentry at most once a minute.

The module doc and ENVIRONMENT.md name the rule ids for the firewall
configuration review. Tests cover both keys, the header fallbacks, the 429
mapping, the not-configured path, and the ordering before the domain rules.
@iscekic iscekic removed the merge:human Substantive functional risk requires human review. label Oct 23, 2026
@iscekic

iscekic commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Narrowed as requested.

  • The total-send rule and its two route calls are gone; the change now adds only the caller-keyed rule beside the existing per-address rule.
  • A rule that is not in the firewall configuration is skipped and reported to Sentry at most once a minute, so the 503 path is gone.
  • The added catalog key and the mobile error mapping are removed.
  • ENVIRONMENT.md names the two rule ids for the firewall configuration review.

email-signin-eligibility.test.ts covers both keys, the header fallbacks, the 429 mapping, the not-configured path with one report per interval, and ordering before the domain rules. The route and timing suites are back to their main form, except the magic-link key assertions, which now select calls by rule id because each request makes two firewall calls.

Re-verified on this VM: production-mode run of the route against local Postgres with a local HTTPS edge serving the rate-limit API. An available count returns 200 with a code row and one dispatch; an exhausted count returns 429 SIGNUP-RATE-LIMITED with no code row and no dispatch.

@iscekic iscekic added the merge:human Substantive functional risk requires human review. label Oct 23, 2026
@iscekic iscekic changed the title feat(auth): count sign-in code email sends per caller and globally feat(auth): count sign-in code email sends per caller Oct 24, 2026
@iscekic iscekic added the needs:prod-action Prod prerequisite (env var, secret, firewall rule, config) needed before merge label Oct 24, 2026
@iscekic

iscekic commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Prod prerequisite before merge: create the Vercel Firewall rate-limit rule sign-in-code-email-ip with a chosen threshold, and confirm magic-link-email exists. Without the new rule the per-caller limit is skipped and each instance reports the missing rule to Sentry once a minute.

@eshurakov eshurakov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shallow review: no critical issues found.

@iscekic iscekic removed the needs:prod-action Prod prerequisite (env var, secret, firewall rule, config) needed before merge label Oct 27, 2026
@iscekic
iscekic added this pull request to the merge queue Oct 27, 2026
@iscekic

iscekic commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

bot: Production prerequisite complete. The sign-in-code-email-ip Vercel Firewall rule is active, and the existing magic-link-email rule is present. A synthetic-key check confirmed the new rule enforces its configured limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drain Work item from the findings drain janitor:trust Janitor finding: trust boundary hardening merge:human Substantive functional risk requires human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants