Conversation
The scheduled load test runs tests/k6/execution-load-test.js, which has its own self-contained h() header builder rather than importing from tests/k6/helpers/http.js. The prior bypass-header wiring only landed in the helpers module, so all VUs in execution mode still got signup 400 + 65 follow-on wf-create 401s after KEEP-574 (load-test captcha bypass) merged. Mirror the same pattern: read LOAD_TEST_CAPTCHA_BYPASS_TOKEN from __ENV, attach X-Load-Test-Captcha-Bypass to h() when set. The header is only sent when the GH Actions secret is populated (staging), so prod is unchanged. The HTTP ramp mode already works because tests/k6/load-test.js routes through scenarios/user-journey.js -> helpers/auth.js -> helpers/http.js.
…bypass fix(tests/k6): send captcha bypass header from execution-load-test
K6 now signs in as one of 50 pre-seeded users (k6-loadtest-vu1..50) instead of dynamically signing up + verifying email each run. Removes the captcha-bypass wrapper and bypass-header wiring landed in #1502 and #1503; they no longer have a code path that exercises them. Server-side: - proxy.ts: skip the mandatory-MFA gate when X-Load-Test-Mfa-Bypass matches LOAD_TEST_BYPASS_TOKEN (timing-safe). No env var in prod -> no bypass surface. - app/api/admin/test/cleanup/route.staging.ts: add a workflow-only cleanup pass for the seeded users (user/org/membership rows preserved); tighten the destructive pattern from k6-% to k6-vu% so it no longer matches seeded users. - scripts/seed-load-test-users.ts: idempotent seeder, run once against staging via kubectl exec. Reverted from #1502 / #1503: - lib/auth.ts captcha-plugin wrapper - tests/k6/helpers/http.js + execution-load-test.js bypass header - tests/unit/signup-defenses.test.ts bypass tests Env var name changed from LOAD_TEST_CAPTCHA_BYPASS_TOKEN to LOAD_TEST_BYPASS_TOKEN since it only powers the MFA bypass now. The SSM path stays at load-test-captcha-bypass-token so we don't have to re-issue the secret. KEEP-574
Verified locally that twoFactorEnabled=true on the user row causes better-auth's twoFactor plugin to intercept signin with a 2FA challenge cookie instead of issuing a session. K6 has no way to complete TOTP, so no usable session is created. With twoFactorEnabled=false, signin issues a real session, and the proxy.ts MFA bypass header (X-Load-Test-Mfa-Bypass) handles the mfa_enrollment_required gate on subsequent requests. Verified locally end-to-end against pnpm dev + the seeded users: - signin: 200 with real session token - /api/workflows/create with bypass header: 200 - /api/workflows/create without bypass header: 403 mfa_enrollment_required - /api/workflows/create with wrong bypass: 403 mfa_enrollment_required - /api/admin/test/cleanup: removes workflows for seeded users, preserves user/org/membership rows
refactor(load-test): switch k6 to pre-seeded users
Without a subscription row, getOrgPlan() defaults the seeded org to "free", and enforceWorkflowFeatures returns 402 upgrade_required on every /api/workflows/create attempt that uses an HTTP Request action (which the k6 test does). Inserting a plan=pro row in the same transaction matches what staging needs and avoids manual SQL on every re-seed. Verified locally and against staging: - seed -> all 4 row types + subscription - staging k6 dispatch with pro subs: 1625 workflows created, 1781 manual triggers ok, http_req_failed=7.25%, end-to-end green
Web3 write steps always execute in isolated K8s Job runner pods, which only receive the variables listed in RUNNER_SYSTEM_ENV_VARS. The sponsorship path needs three vars that were never forwarded: - NEXT_PUBLIC_GAS_SPONSORSHIP_ENABLED: isGasSponsorshipEnabled() read it as undefined in the runner, so the sponsorship guard was always false and every write fell back to direct EOA signing. - TURNKEY_ORGANIZATION_ID: getTurnkeyClientForOrg() throws when it is unset, so enabling the flag alone would still fail in the runner. - NEXT_PUBLIC_BILLING_ENABLED: without it the runner treats billing as off, so the per-org gas-credit cap is not enforced on scheduled runs. Add the three vars to the runner forward list and set them on the executor deployment (staging enabled, prod mirrors the app: disabled).
fix(scripts/seed): give seeded orgs a pro subscription
… check getTurnkeyClientForOrg() throws when TURNKEY_ORGANIZATION_ID is unset, so a runner pod missing it silently falls back to direct signing. Add it to assertTurnkeyEnvForActiveWallets so a missing org id fails fast at executor boot (warns in ephemeral PR envs) instead of degrading at runtime.
…p-flag fix(executor): forward gas sponsorship envs to workflow runner pods
Staging is validated (sponsorship works, gas-credit cap guardrail enforced). Flip NEXT_PUBLIC_GAS_SPONSORSHIP_ENABLED to true on both the prod app and prod executor so scheduled web3 writes are sponsored in prod too. Staging already true on both.
…prod chore(deploy): enable gas sponsorship in prod
OleksandrUA
approved these changes
Jun 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.