Skip to content

release: to prod#1500

Merged
zdumitru merged 11 commits into
prodfrom
staging
Jun 10, 2026
Merged

release: to prod#1500
zdumitru merged 11 commits into
prodfrom
staging

Conversation

@zdumitru

Copy link
Copy Markdown
Collaborator

No description provided.

joelorzet added 6 commits June 9, 2026 14:03
Replace the per-IP MFA gate with a country-based one. A new IP inside a
country the user has already signed in from is trusted silently; only a
new country defers the session to the /verify-ip dual-factor step-up.

A new device (identified by a signed kh_device_id cookie) signing in from
a trusted country is allowed through but the account owner is emailed a
warning. The account's first device is not emailed.

Transition is seamless: user_trusted_ips stays as a legacy bridge. On a
country miss, a session whose IP is already trusted passes and the country
is backfilled into user_trusted_countries, so users signed in before this
shipped are never bounced to /verify-ip. The proxy adopts such browsers
with a device cookie on the next document navigation, silently.

IP normalization (/24, /64) is now used only for the /verify-ip replay
match; the full IP is persisted, shown in the active-sessions panel,
carried in the pending cookie, and sent in emails.

Adds user_trusted_countries and user_trusted_devices tables (migration
0105) plus unit tests for the country gate, device cookie, device trust,
and new-device notification.
Address code review on the country/device trust change:

- resolveCfCountry now requires the CF-set cf-connecting-ip before
  honoring cf-ipcountry, so a forged country header on a direct-to-origin
  request cannot pin a trusted country (mirrors resolveLoginLocation).
- Replace the `ip ?? ""` fallbacks in strict-signin and the proxy gate
  with explicit IP narrowing: when an untrusted country has no resolvable
  IP (impossible behind CF) we pass rather than issue a replay-unbindable
  verify cookie.
- Add proxy-layer tests for the country gate: trusted/no_country pass,
  untrusted page redirect to /verify-ip, untrusted API 403, the no-IP
  pass, document-nav device adoption, and the non-document fan-out guard.
- Add a regression test for the forged-cf-ipcountry case.
Let orgs subscribe to any combination of daily/weekly/monthly digests
instead of a single cadence, and rework the digest email layout.

- Migrate digest settings from a single cadence column to a cadences set
  plus a per-cadence last_sent map so each schedule sends independently
- Add a monthly cadence that fires on the 1st (UTC) and reports the
  previous calendar month, aligned to the billing cycle
- Cron evaluates each subscribed cadence per org and only advances that
  cadence's last-sent on a successful send
- Settings UI: cadence dropdown becomes daily/weekly/monthly checkboxes
- Email: explicit UTC period range, distinct-workflow count, success and
  failure as count plus percentage, grouped Succeeded/Failed sections,
  inline truncated error with hover tooltip, and a social-icon footer
  delivered as inline (CID) attachments
feat: trust sign-ins by country and device instead of raw IP
…ti-cadence

feat: multi-cadence execution digest with redesigned email
@zdumitru zdumitru requested review from a team, OleksandrUA, eskp, joelorzet and suisuss and removed request for a team June 10, 2026 08:07
… the deploy

The docs-site Helm values set the image tag as a bare scalar (tag:
${IMAGE_TAG}). The deploy workflow substitutes the raw git short SHA via
sed. When a short SHA is all decimal digits (no a-f), the bare YAML scalar
is parsed as a number and re-serialised in scientific notation (e.g.
6801028 -> 6.801028e+06), producing an invalid Docker tag and an
InvalidImageName pod failure.

Quote the tag in the prod, staging and techops-prod values files so the
substituted value is always a string scalar, matching the already-quoted
deploy/local template. Other services are immune because they prefix the
tag with letters (app-, event-, executor-, etc.).
…e-tag

fix(ci): quote docs-site image tag so numeric short SHAs do not break the deploy
The scheduled k6 load test signs up VUs against
/api/auth/sign-up/email which is gated by the Turnstile captcha
plugin and cannot solve a real captcha challenge from a headless
runner.

Wrap the plugin's onRequest to honor an X-Load-Test-Captcha-Bypass
header whose value matches LOAD_TEST_CAPTCHA_BYPASS_TOKEN
(timing-safe compared via node:crypto). When the env var is unset
the wrapper is a no-op and no bypass path exists, so prod is
unchanged.

- lib/auth.ts: withLoadTestBypass wrapper around the captcha plugin
- deploy/keeperhub/staging/values.yaml: mount the env var from SSM
- tests/k6/helpers/http.js + ramp-until-breach.sh: send the bypass
  header on every signup when the k6 env var is set
- .github/workflows/load-test.yml: pass the staging-env GH Actions
  secret to k6 for both execution and http-ramp modes
- tests/unit/signup-defenses.test.ts: 5 new tests covering matched,
  unmatched same-length, wrong-length, missing-header, and env-unset
  cases
- .env.example: document the new env var

Pair PR in techops-services/infrastructure provisions the SSM
parameter.
…pass

feat(auth): add load-test bypass header for signup captcha
@zdumitru zdumitru merged commit 8ae2ff9 into prod Jun 10, 2026
34 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants