Enhances sign-up process with Turnstile integration for fraud
protection. Builds on top of fraud-protection-temp-emails.
Made with [Cursor](https://cursor.com)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Cloudflare Turnstile bot-protection across signup/sign-in flows
(including SDK JSON mode).
* Email deliverability checks via Emailable.
* Sign-up risk scoring with persisted risk metrics and country code
tracking.
* UI: country-code selector, risk-score editing in user details, users
list refresh button, and Turnstile signup demo pages.
* **Bug Fixes**
* Use actual sign-up timestamp for reporting/metrics.
* **Documentation**
* Expanded knowledge base on Turnstile, risk scoring, and env
configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
Co-authored-by: BilalG1 <bg2002@gmail.com>
Co-authored-by: Armaan Jain <84474476+Developing-Gamer@users.noreply.github.com>
Co-authored-by: nams1570 <amanganapathy@gmail.com>
This PR implements unified AI endpoint and custom dashboards.
**Unified AI Endpoint**
We now use a single endpoint throughout the codebase that makes the call
to openrouter. Specifically, email drafts, email templates, email
themes, wysiwyg, cmd centre ai search and docs ai, all use this unified
ai endpoint. All the tools are defined in the backend, all the prompts
exist in the backend.
How to review this PR for unified ai endpoint:
This PR will be easier to review if we look at the different folders
that were affected.
under packages - We added streaming functionality, and made renaming
changes
under docs - there are three files that have changed
package.json - we updated the package (we were previously using a very
old version of the package)
route.ts - we changed the call from a direct call to openrouter to the
unified ai endpoint
ai-chat.tsx - because of updating the package, we had to make changes to
adapt to the latest versions of the package
under backend
route.ts - the main unified ai endpoint. this endpoint uses various
support files
forward.ts - this is the forward to production functionality
models.ts - consists of the models, and the rules for selecting those
models
prompts.ts - consists of the base prompt + specific system prompts
depending upon the usage
schema.ts
every single file under ai/tools folder - which as the name suggests,
consists of the implementations of the different tools that can be
provided to the llm
route-handlers - added support for streaming to SmartRoute and response
under dashboard
ai-search/route.ts - refactored the file to use unified ai endpoint
chat-adapters.ts - refactored the file to use unified ai endpoint and
created extra checks for the ai generated code
**Custom Dashboards**
We let the user write their query in english. We then use AI to create
dashboards that are interactive, live and savable. This PR includes a
new package called dashboard-ui-components. This package has components
that are used in the dashboard and more importantly, these components
are being imported from esm in the ai generated code for custom
dashboards. We also change the bar at the top for the products pages.
How to review this PR:
Review the new package (package/dashboard-ui-components), the setup and
the files inside it.
Review the schema changes in stack-shared/src
Review the changes in dashboard. The following changes have been made
Updated the design-components folder since we moved the dashboard
components to the new package
Updated imports for these components accordingly
Updated the title bar of the product pages
Created the files for custom dashboards under the dashboards folder and
components under commands/create-dashboard
Created a script under dashboard/scripts that generates the file with
type definitions that would go to the llm
Review the backend
Started using unified ai endpoint
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added custom dashboards feature allowing users to create and manage
personalized dashboards with AI assistance.
* Integrated AI-assisted dashboard code generation with visual preview
and editing capabilities.
* Introduced new AI query endpoints supporting stream and generate modes
with configurable model quality/speed settings.
* **Improvements**
* Reorganized UI components into a dedicated component library package
for better code reuse.
* Enhanced chat architecture with improved message handling and tool
integration.
* Updated AI provider integration with improved configuration
management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
Co-authored-by: Bilal Godil <bg2002@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
### Context
We didn't have an easy place for a user to see their domain statistics
and track their sent emails, either overall or by draft. Additionally,
there was scope creep with the sidebar, where we were supporting more
pages. Our emails landing page was also rather confusing, especially
toggling/ working with different email server types. So, we decide to
add a "sent" page, to track email logs and email statistics, as well as
let users temporarily override their sending limits if need be.
Additionally, a user may want to see a particular email in more detail:
what stage is it in? How did it proceed through time? How can I pause
the sending of this email or change the scheduled time or edit the code?
We allow for that to happen.
### Summary of Changes
#### New Pages
1. **Sent Page:** A Domain Reputation card lets you track how many of
your sent emails were bounced or marked as spam as well as how much
capacity you have left. We also provide a temporary override, where you
can use up to 4 times your capacity for a limited period of time.
Additionally, we provide an email log that lets you see the recently
sent emails. You can also toggle this view from a "list all emails" to
"group by template/draft" which shows stats for each template/draft id
(i.e a bar showing how many emails were sent, are pending, were marked
as spam, were bounced etc, and the total number of emails sent with that
template or draft). Clicking on an email in the list all view takes you
to the "email-viewer" endpoint for that email (see below). Clicking on a
template/draft in the group by view takes you to a page where you can
see the statistics for that template/draft in more detail (the "send"
stage view for that template/draft, as referenced below).
2. **Settings Page:** This is a new page we created because the old
"emails" landing page wasn't doing its job. This page is to track all
the email settings. Currently, we put in 2 sections. A "theme settings"
card where users can see their active theme and click on a button to be
navigated to the themes page. This is necessary as we remove themes from
the sidebar. The other section is a card for email server and domain
configuration - you can change your server type and adjust the settings
or send a test email. It's cleaner and less noisy.
3. **Drafts Page**: There are a lot of changes here. On the landing
page, we actually separate out the drafts into "active drafts" and
"draft history" because drafts are meant to be fire-and-forget, not
reusable. We also add the functionality to create a draft from a
template. This was tricky to manage because templates rely on template
variables which sent to the backend along with the code and injected
during render time. We deal with this by having AI rewrite the template
source code to remove any references to template variables and to make
the draft standalone. The drafts page has been separated into a
stepper-controlled multi stage process:
draft->recipients->schedule->sent. Sent is a read only view that shows
you the statistics of the emails sent using that draft, as mentioned
earlier. You can also see the sent view of a historical draft. You can
also bulk pause/cancel any unsent emails from the sent view of the
drafts.
4. **Sidebar Updates**: The email sidebar now doesn't show "themes" or
"emails" (the old landing page), but it does show "settings" and "sent",
and the default landing page for emails is "sent".
5. **Email Viewer**: When you click on an individual email, you get
navigated here. This has a timeline showing the progress of the email on
the right, and some optional info for the user that's toggleable on the
right bottom, while having either a preview of the email if it's sent or
a way to edit it. You can also change the scheduledAt date of an email
if it hasn't already been sent.
#### Bug Fixes
1. **Search in `TeamMemberSearchTable`**: This was broken. Every time
you tried to enter or remove a character, it would trigger skeleton
loading that overlapped the search bar too, preventing you from
adding/removing more. This was caused because the `useUser` hook
eventually ended up calling a `use` hook, which throws a promise that
triggers a suspense. This, coupled with the fact that the implementation
of `TeamMemberSearchTable` involved a prop-drilling/ dependency
inversion approach to passing down its toolbar to a base table
component, meant the suspense would cover the toolbar too and couldn't
be scoped to just the table. A refactor has gotten rid of the need for
those base components while fixing tables in `payments/customers`,
`teams/team_id`, and `payments/transactions` on top of the existing use
in email drafts recipients stage. We also dedupped some code.
2. **Stale draft fetches on draft landing page**: `useEmailDrafts` uses
an asyncCache to cache the fetched drafts. It is used on the drafts
landing page to render the drafts. When a draft is sent, its `sentAt` is
marked versus when it is still active, it is marked as null. The cache
was stale and so navigating to the landing page after firing off a draft
would errorneously represent that draft as still active and indeed, even
allow you to edit it and fire it again. This violated the principle of
drafts being fire and forget. This has been dealt with by adding
functionality to refresh the draft cache upon firing off a draft.
#### Other Changes
1. We bumped up the base time for the exponential send attempt retry
backoff in `email-queue-step` to 20 seconds. The previous base was two
seconds, and this effectively just made it wait until the next iteration
of the `email-queue-step` cron job or at most an iteration that wasn't
too far away. When an outage with our provider happens, it may take a
while for it to be resolved, so a longer backoff is justified
2. We transitioned the themes page and the templates page to using the
new components, though deeper UI refactors for them were out of scope
for this ticket.
3. We implement a "temporarily increase capacity" button, that bumps up
the throughput/ capacity limit fourfold for a user for a given period of
time. It works like this:
> Clicking the button sets a boost expiredat time.
> When this time is set and still valid, the capacity rate is multiplied
by 4.
> When the button is clicked, trigger a loading spinner until the route
finishes processing.
> When the timer runs out, we reset the button back to its original
state.
> We dont need to wrap the onclick with runAsyncWithAlert because the
component does that already.
4. We add a new default theme: a colorful theme with a lavender base.
This was mainly done so we could have three times in a theme showcase in
the settings page.
### UI Demos
**Sent Page Demo:**
https://github.com/user-attachments/assets/19294a90-bb65-4f00-9a97-111f6c08287f
**Drafts Page Demo**
https://github.com/user-attachments/assets/847609ef-d699-470c-a699-297bb9e17f04
**Settings Page Demo**
https://github.com/user-attachments/assets/190a3829-036a-4f57-89c0-a873bef5a7ce
**Email Viewer Page Demo**
https://github.com/user-attachments/assets/3bc50159-4acb-4865-a4dd-830c84ee4235
---------
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a new "Hosted Components" app with its own app shell, routing,
auth-aware UI, a handler route, and a welcome page showing the signed-in
user.
* **Chores**
* Added dev tooling and configuration for the new app (build, lint,
typecheck, Vite/TS, package manifest) and updated dev env API URL.
* **Tests**
* Excluded the new app from the test workspace.
* **Bug Fixes**
* Suppressed noisy console errors for a specific internal sentinel and
clarified related error messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Managed email domain onboarding: setup, DNS provisioning,
verification, status checks, and apply flow (Resend-backed).
* **UI**
* Project email settings: managed-provider setup dialog, managed sender
fields, status display, and test-send mapping.
* **Integrations**
* DNS provider automation and Resend webhook handling for domain status
updates; scoped keys for sending.
* **API**
* Admin endpoints / client APIs to setup, check, list, and apply managed
email domains.
* **Tests**
* End-to-end tests covering the full onboarding flow.
* **Chores**
* Added environment variables and config schema support for Resend and
DNS integrations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
### Context
We're looking at implementing plan pricing. While doing so, we
encountered a problem with Stripe.
**Problem:** when we run a stripe operation (purchase), the product info
is encoded as part of the stripe metadata request. Stripe encodes
metadata as key-value pairs, and the [value has a limit of 500
chars](https://docs.stripe.com/metadata#data). We do this because once
we run the stripe operation, stripe fires a webhook event which is
caught by our stripe webhook handler syncStripeSubscriptions. This gets
the stripe metadata info from the event and then updates our db in
prisma.
### Summary of Changes
We add a `ProductVersion` table and only pass the `productVersionId` via
stripe metadata instead of the whole product json. This
`productVersionId` is created by hashing the `productJson`. Since the
same product may be ordered differently without being intrinsically
different, we add a helper function for ensuring a canonical order to
the json. We also pass tenancy id and product id to the table.
Since there are existing subscriptions which used to pass the
productJson via metadata, we ensure backwards compatibility.
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **High Risk**
> Touches authentication and OAuth token/authorize flows and changes how
client requests are validated, so regressions could cause widespread
login/client-access failures. Also includes a data migration that alters
effective security posture for existing projects.
>
> **Overview**
> Adds a **project-level toggle**
(`project.requirePublishableClientKey`) to control whether client
requests/OAuth flows must include a publishable client key, including a
DB migration that backfills existing projects to require it.
>
> Backend auth now treats the publishable client key as *optional when
allowed*, introducing a public sentinel (`__stack_public_client__`) and
returning a new specific error
(`PUBLISHABLE_CLIENT_KEY_REQUIRED_FOR_PROJECT`) across smart request
auth + OAuth `authorize`/`callback`/`token` endpoints.
>
> Dashboard and SDKs update key generation/display and request
construction to handle missing publishable keys, expose an advanced
toggle on the Project Keys page, and extend internal config overrides to
support a new `project` level; E2E/tests and schema fuzzing are expanded
accordingly, and CI adds a forward-compat migration check job when
back-compat fails.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5d06c08613. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Project-level config to require publishable client keys; migration
applied to existing projects.
* **Improvements**
* Auth flows now support optional publishable client keys with explicit
validation and a sentinel for keyless OAuth.
* Dashboard/UI and SDKs handle publishable keys as optional and
conditionally show/generate them.
* Admin/client APIs extended to manage project-level overrides.
* **Bug Fixes**
* Key validation behavior aligned with project config.
* **Tests**
* Expanded E2E and unit tests covering optional/required publishable-key
scenarios.
* **Documentation**
* Spec and knowledge docs updated to describe the sentinel and config
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Subscription IDs are now included in product listings and UI data for
clearer subscription tracking.
* Cancellation can be performed by subscription ID as well as by
product; client and template APIs support passing a subscription
identifier.
* **Tests**
* End-to-end tests added/updated to cover canceling subscriptions via
subscription ID and updated listing snapshots.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Browser-side event tracker with batching, navigation & click capture
and background/keepalive delivery
* Server endpoint to accept batched analytics events and associate them
with session replay segments
* Client APIs to send analytics batches and integrate with session
replay
* **Bug Fixes / UX**
* Pausing replay now uses the UI-facing playback time for more accurate
pause positions
* Replay endpoint now returns a clear analytics-disabled error
(ANALYTICS_NOT_ENABLED) when analytics is off
* **Tests**
* End-to-end tests covering batch ingestion, validation, and replay
timing behavior
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added new session replay analytics columns to ClickHouse for enhanced
tracking and reporting
* **Refactor**
* Renamed session recording segment identifier across APIs and data
models from `tab_id` to `session_replay_segment_id`
* Updated internal data structures and type definitions to align with
new naming convention
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
https://www.loom.com/share/3b7c9288149e4f878693281778c9d7e0
## Todos (future PRs)
- Fix pre-login recording
- Better session search (filters, cmd-k, etc)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Analytics → Replays: session recording & multi-tab replay with
timeline, speed, seek, and playback settings; dashboard UI for listing
and viewing replays.
* **Admin APIs**
* Admin endpoints to list recordings, list chunks, fetch chunk events,
and retrieve all events (paginated).
* **Client**
* Client-side rrweb recording with batching, deduplication, upload API
and a send-batch client method.
* **Configuration**
* New STACK_S3_PRIVATE_BUCKET for private session storage.
* **Tests**
* Extensive unit and end-to-end tests for replay logic, streams,
playback, and APIs.
* **Chores**
* Removed an E2E API test GitHub Actions workflow.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### Context
Some of our users' emails were getting stuck in sending. The long delays
in processing the retries caused a vercel function timeout.
### Summary of Changes
We refactor the low level email sending functions to remove the retry
logic there. We kick it up to the email queue step. Additionally, we
flag emails to be retried when they encounter issues but leave it for a
future iteration to actually perform the retry. We perform an
exponential backoff with a random component to decide when they have to
be retried. We also make some small adjustments to the queuing function
to not queue skipped emails.
When an email fails to send during the sending function, we check to see
if it is a retryable error or not. Some errors are transient and trying
again may succeed while others indicate deeper issues. If it is
retryable, and the max number of retry attempts hasn't been reached, we
set `nextSendRetryAt` to a time determined by an exponential backoff
calculation function. When the queuing function looks for emails to
queue, it doesn't just pick up the `SCHEDULED`. emails whose
`scheduledAt` time <= `NOW()`, but also those emails whose
`nextSendRetryAt` time <= `NOW()`. What this means in practice is that
one iteration of the `email-queue-step` will mark emails as retryable
while another iteration will perform the retry. This should be cleaner
and prevent long delays in the `email-queue-step` process due to
retries. This also makes it easier to scale up the number of retries if
need be.
DB migrations are backwards-compatible / Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migrations are backwards-compatible / Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Real-time AI search with project-scoped analytics and dynamic query
execution; streaming AI responses replace the placeholder flow.
* External DB sync adds ClickHouse support: users sync, sync metadata
tracking, tenancy-aware status, and per-mapping throttling.
* AI assistant UI shows expandable tool-invocation results and streams
via the real AI pipeline.
* **Chores**
* Dashboard dependencies and workspace exclusions updated; development
OpenAI env var added; editor config flag toggled.
* **Tests**
* E2E coverage extended to validate ClickHouse user sync and analytics
queries.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: aadesh18 <110230993+aadesh18@users.noreply.github.com>
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
<img width="1920" height="969" alt="Screenshot 2026-02-04 at 9 47 16 AM"
src="https://github.com/user-attachments/assets/d7d0cd04-0051-4fc4-b857-e6f87ee97a59"
/>
**This PR revolves around the following components**
1. Sequencer - sequences the updates in the internal db
2. Poller - polls for the latest updates to sync with the external db
3. Outgoing Request Handler - essentially a trigger that can make http
requests based on a change in the internal db
4. Sync Engine - syncs with the latest changes from the internal db to
the external db
**What has been done**
- Added a global sequence id for ProjectUser, ContactChannel and
DeletedRow.
- Added the deletedRow table to keep track of the rows that were deleted
across ProjectUser and ContactChannel.
- Added the OutgoingRequest table to keep track of the outgoing requests
- Added function for the sequencer to call to sequence updates
- Added a sequencer that sequences all the changes in the internal db
every 50 ms
- Added a poller that polls for the latest changes in the internal db
every 50 ms, and adds to a queue
- Added a Vercel cron that calls sequencer and poller every minute
- Added a queue that fulfills the outgoing requests by making http calls
(for external db sync, it calls the sync engine endpoint)
- Added a sync engine that uses the defined sql mapping query in the
user's schema to pull in the changes for the user, and sync them with
the external db
- Added tests to test out each functionality
**How to review this PR:**
1. Review the migrations (sequence id, deletedRow, triggers, backlog
sync) (all files created under the migrations folder)
2. Review sequencer
3. Review poller
4. Review the changes in schema
5. Review sync-engine (the function, and it's helper file)
6. Review the schema changes, and query mappings
7. Review the tests (basic, advanced and race, along with the helper
file)
8. Review the changes made in Dockerfile to support local testing using
the postgres docker
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Introduces a cron-driven external DB sync pipeline with global
sequencing, internal poller and webhook sync engine, new DB
tables/functions, config schema/mappings, and comprehensive e2e tests.
>
> - **Database (Prisma/Migrations)**:
> - Add global sequence (`global_seq_id`) and
`sequenceId`/`shouldUpdateSequenceId` to `ProjectUser`,
`ContactChannel`, `DeletedRow` with partial indexes.
> - Create `DeletedRow` (capture deletes) and `OutgoingRequest` (queue)
tables; add unique/indexes.
> - Add triggers/functions: `log_deleted_row`,
`reset_sequence_id_on_update`, `backfill_null_sequence_ids`,
`enqueue_tenant_sync`.
> - **Backend/API**:
> - New internal routes: `GET
/api/latest/internal/external-db-sync/sequencer`, `GET /poller`, `POST
/sync-engine` (Upstash-verified) for sync orchestration.
> - Add cron wiring: `vercel.json` schedules and local
`scripts/run-cron-jobs.ts`; start in dev via `dev` script.
> - Tweak route handler (remove noisy logging) without behavior change.
> - **Sync Engine**:
> - Implement `src/lib/external-db-sync.ts` to read tenant mappings and
upsert to external Postgres (schema bootstrap, param checks,
sequencing).
> - Add default mappings `DEFAULT_DB_SYNC_MAPPINGS` and config schema
`dbSync.externalDatabases` in shared config.
> - **Testing/Infra**:
> - Add extensive e2e tests (basics, advanced, race conditions) for
sequencing, idempotency, deletes, pagination, multi-mapping, and
permissions.
> - Docker compose: add `external-db-test` Postgres for tests; e2e deps
for `pg` types.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
3f2a8efcfb. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* External PostgreSQL sync: automatic, batched replication with
mappings, resume/idempotency, and on-demand enqueueing.
* **Admin UI**
* Real-time External DB Sync dashboard and status API showing
per-mapping backlog, sequencer/poller/sync-engine telemetry, and fusebox
controls.
* **Tests**
* Large e2e suite: basic, advanced, race, high-volume tests and test
utilities for external DB sync.
* **Chores**
* DB migrations, CI/workflow updates, background cron runner and
local/dev test support.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
Co-authored-by: Bilal Godil <bg2002@gmail.com>
DB migrations are backwards-compatible / Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
### Context
Recently, a user raised [this
issue](https://github.com/stack-auth/stack-auth/issues/1144), which
indicated that `tokenOverrides` were not being respected/used in the
`getUser()` function. If we trace the flow through this function, we see
`this._getSession -> this._getOrCreateTokenStore -> _createCookieHelper
-> createCookieHelper -> createNextCookieHelper -> await rscHeaders()`.
What this means is that even when a `requestLike tokenOverride` was
passed, we would not end up using it because the `createCookieHelper`
call occurs before the extant override checking logic in
`getOrCreateTokenStore`, and the `createCookieHelper` didn't check the
override but only the default `tokenStoreInit`. This caused the error to
propagate up.
### Summary of Changes
We check the `tokenStoreOverride` in the `createCookieHelper` function
now, preventing this issue from happening. We also add extra test
coverage to verify that overrides are respected, and don't overwrite the
default token store.
### Out of Scope Discussion
The original issue was raised with a `bun` runtime running `next.js`
code. There seems to be some incompatibility between `bun 1.3.8` and
`nextjs 15+`, not just with our backend but with fetching and working
with responses from any `nextjs` server.
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **High Risk**
> Touches core sign-up/auth flows and user restriction semantics
(including new DB constraints) and introduces dynamic rule
evaluation/logging; misconfiguration or CEL/parser bugs could block
sign-ups or incorrectly restrict users.
>
> **Overview**
> Introduces **CEL-based sign-up rules** (config-driven) that are
evaluated during password/OTP/OAuth sign-ups and anonymous upgrades;
matching rules can reject sign-ups or mark users as admin-restricted,
and triggers are logged for analytics.
>
> Extends `ProjectUser` with `restrictedByAdmin` plus public/private
restriction details, updates restriction computation/filtering, and
exposes these fields via user CRUD (including validation + DB constraint
enforcing consistency when unrestricted).
>
> Adds a new dashboard **Sign-up Rules** page with a visual condition
builder (CEL <-> visual tree), drag-reorder by priority, per-rule 48h
sparkline analytics via a new hidden internal endpoint, and adds
user-page UI to view/edit manual restrictions. Also refactors ClickHouse
client initialization to require env vars (removing
`isClickhouseConfigured` checks) and adjusts CI container startup wait
time.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
2141e689e8c1b72303b805e9234f996010d0880. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Sign-up Rules: visual rule builder, in-project CRUD with drag-reorder,
per-rule analytics, backend evaluation, and admin UI.
* Admin user restrictions: dashboard controls, banners/status,
public/private admin details surfaced in user views.
* **APIs & Schema**
* Config and user schemas extended; new SignUpRejected error and sign-up
rule types added.
* **Tests**
* Extensive unit and E2E coverage for rules, parser, evaluator,
analytics, and restricted-user flows.
* **Docs**
* Editorial guidance added to AGENTS.md.
* **Chores**
* DB statement timeout, updated clean script, minor dependency
additions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a dedicated endpoint to retrieve query timing statistics using
query identifiers.
* Query responses now include a unique query_id for tracking and
reference.
* **Improvements**
* Query timeout validation now enforces a maximum limit of 2 minutes.
* Query response structure updated to support separate timing
information retrieval.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
DB migrations are backwards-compatible / Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
https://www.loom.com/share/bb7abfde507f40d386ee856f5ffbd506
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* USD-based refund system enabling partial and full refunds with
explicit USD amounts
* Per-entry refund selection with granular quantity controls in refund
dialogs
* **Bug Fixes**
* Stronger refund validation and error handling to prevent invalid or
out-of-bounds refunds
* **Tests**
* Expanded end-to-end coverage for refund edge cases and scenarios
* **Style**
* Improved refund dialog UI with contextual alerts and better controls
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
DB migrations are backwards-compatible / Test migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
### Summary of Changes
Previously, on the Swift SDK, the `signInWithOAuth` function wasn't
working. In this PR, we fix it by having the `getOAuthUrl` function to
actually redirect correctly. Note that to do so, we updated the
`validRedirectUrl` check on the backend to accept app native redirects
(from our new trusted url scheme). Another thing to note is that we
added functionality to the `TokenStore` abstraction to conditionally
refresh the access token that the user is trying to fetch if it is
expired/close to expiring if possible. `getOAuthUrl` will attempt to get
a valid access token, and thus will rely on our algorithm documented in
`utilities.md`.
The specs serve as the source of truth.
We go further and implement Apple Native sign in. To do so, we have it
hit a new route on the backend and verify the `jwtToken` retrieved by
the sdk against an Apple-provided set of `jwks`. We use jose to do so,
in line with the rest of the codebase.
We take this opportunity to refactor the oauth provider route owing to
the amount of duplicated logic. Additionally, to enable the apple sign
in, users will have to update the Apple authentication method modal on
the dashboard and add accepted bundle ids. These are identifiers for
projects, and we will check the `JWT` on the backend to make sure the
audience is set to an accepted bundleId.
We also update the Apple modal to be more informative.
### Using the new Features
To use the Apple native sign in, users will have to 1) sign up with an
apple developer account, 2) set up their bundleids for their projects by
connecting them to the apple developer account, 3) update the Stack-Auth
Authentication Methods dashboard apple modal with the relevant fields.
Then, trying to sign in with apple with our Swift SDK will use the apple
native sign in.
### UI Changes
Renamed the fields in the apple modal. Added a new field for bundle ids.
See below.
https://github.com/user-attachments/assets/0e760c0e-3198-4818-ac7f-4900d7a125bb
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Security**
* Added client-side access checks on payments endpoints and expanded
customer-type handling (including a new "custom" type).
* **SDK / Client**
* Client interface methods now accept explicit request types
(client/server/admin) to route requests appropriately.
* **Server**
* New server-side product listing to support server requests and
caching.
* **Tests**
* E2E tests updated to use a fast sign-up flow and pass authentication
tokens for authorized requests.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->