feat(billing): add Stripe Test Mode Credits recharge - #344
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Heads-up from #348 — this branch's
CI is green because Please raise |
…-design # Conflicts: # dashboard/backend/tests/test_frontend_fast_boot.py # dashboard/frontend/app.html
|
Thanks for catching this. I merged the latest The branch now ships:
Local verification after the merge:
The fix is in |
Security review fixes for Open-Finance-Lab#341. The admin routes themselves held up — one correct `require_admin` gate, a transactional PATCH, a constant-time compare, and both twins serializing the one-shot promotion. What did not hold up was the plane around them: the entitlement was built as a control and wired where it could not bind, and bootstrap's own budgets could be turned on the operator. - Close the two cap bypasses. `resolve_owner_cap_context` returned None the instant `owner_user_id` was falsy, and agent creation needs no login at all (`_require_owner_context` accepts a self-chosen X-Session-Id), so signing in was strictly worse than staying anonymous: one shared account budget versus the per-agent cap times however many agents you registered. Unclaimed agents are now billed to the browser session that created them, resolved inside the store (`list_owner_scope_agent_ids`, both twins) because `_public_agent` withholds `owner_browser_session` on purpose — `_owner_context` accepts that value AS an ownership credential. - Cap the legacy `/api/v1/backtest/*` surface. It authenticates nothing and writes no `protocol_runs` row, so all three protocol caps were blind to it: a bare POST spawned a thread and pinned a bar window, unbounded, beside a quota system holding every authenticated agent to its limit. Per-session and global budgets, counted and inserted under one lock. Opt-in, so the protocol path (already capped three ways) is not charged twice. - Raise the default quota to 5 and make it env-overridable. Nothing seeds `user_entitlements` at signup and nothing backfills, so `1` was not a default but every existing account's live limit on deploy day — a silent demotion from 5x(agents), with no remedy short of an admin. - Bootstrap: check the global budget AFTER the compare. Checked first, 20 wrong guesses a window — re-spent every window, from any account, and signup is open — refused the correct secret indefinitely, and the window it blocked is exactly the fresh-deploy window the route exists for. Require >= 32 chars (refused as if unset), and answer 403 for unset/weak/wrong alike so a caller cannot learn whether a deployment is bootstrappable. - Lock FixedWindowRateLimiter. The module had no `threading` import at all, and the buckets are mutated on every call — `_pruned` popleft()s from inside `check`, not only `record`. `allow()` is now atomic. - Reject control characters in emails at the validator. strip() only touched the ends, so an interior newline validated, was stored, and forged lines in the admin console's role-change confirm() — a native dialog with no markup to escape. One fix at the source beats one guard per renderer. - Get the store off the event loop in signup/login. This PR added a sync `get_entitlements()` to `_auth_json`, called from both async handlers, which the threadpool guard cannot see (it pins plain-`def` routes, and those two are exempt for already awaiting). Bundled with `create_session` into one `asyncio.to_thread` hop, plus the guard that can see it. Also: router-level `require_admin` so a route added later is gated by default, a 403 case for all four routes enumerated from the router, an audit line on every role/quota change (ids only — no email reaches a print sink), quota floor 0 so a quota can actually suspend, `credits` labelled not-enforced in the console it is editable from, and the first Postgres tests for `promote_first_admin` — prod's copy of the one-shot predicate had none. Cache busters to 100/100: Open-Finance-Lab#342 also ships styles.css?v=97 and Open-Finance-Lab#344 ships 99/98, so 98/97 would have collided with no git conflict (Open-Finance-Lab#347/Open-Finance-Lab#348). Backend suite: 2797 passed, 76 skipped. SDK suite: 171 passed, 2 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QCiPGs3ZYVjyFYVLa6E7bZ
Summary
Safety and scope
Verification
2805 passed, 72 skippedin the full backend suite before the finalmainsync174 passed, 6 skippedin the focused Credits/API/security/integration suite after syncing the latestmain171 passed, 2 skippedin the packaging SDK suite after syncing the latestmain+10.00ledger entrycheckout.session.completedwebhook with HTTP 200-4.00ledger entryrefund.createdandrefund.updatedwith HTTP 2006.00 Creditsand the order wasPartially Refunded