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
## Summary
Rewrites the **Email Server** section of the project email settings page
and the managed-domain setup flow. Replaces the dropdown +
conditional-fields layout with a visual four-card picker, a clearer
unsaved-state model, a stepper dialog for managed-domain onboarding, and
a consistent tracked-domains list. Also fixes two data-correctness bugs
in the managed-domain backend.
## Walkthrough (2×, dead-frames trimmed)

## Before
The saved state was a minimal dropdown, but choosing Custom SMTP /
Resend revealed a long conditional form with a hidden gear toggle for
server config, no clear "what is saved" signal, and a separate dialog
pattern for managed domains.
| Saved (Managed) | Custom SMTP selected |
|---|---|
|

|

|
## After — Provider cards
Four visual cards (Stack Shared, Managed Domain, Resend, Custom SMTP)
with updated copy. The saved provider shows a green **Current** pill;
the card the user is previewing shows an amber dashed **Draft** pill. An
amber unsaved-changes banner appears between the picker and the form
when state diverges from saved, so it is unambiguous that a click is not
yet committed.
| Saved state | Previewing a different provider |
|---|---|
|

|

|
Copy changes:
- **Stack Shared** — "Only default emails — no custom templates, themes,
or sender identity." (was: "Shared (noreply@stackframe.co)")
- **Managed Domain** — "Bring your own domain. You add DNS records; we
handle signing & delivery." (was: "Managed (via managed domain setup)")
- **Resend** uses the official Resend brand mark (light/dark variants in
`apps/dashboard/public/assets/`)
## After — Managed domain list + stepper dialog
Selecting **Managed Domain** immediately shows the tracked-domain list
with an **Add domain** button. Each row reflects real status (Active /
Verified / Waiting for DNS / Verifying / Failed). Exactly one domain can
be **Active** — the one matching the saved email config; every other
verified/applied domain shows a **Use this domain** button so switching
is always possible.
Adding a domain opens a 3-stage dialog with a horizontal stepper (Verify
is right-aligned for the final step). Stage 2 replaces the old bare
NS-list with a proper **Type / Name / Content** DNS records table with
per-row copy buttons.
| Tracked domains list | DNS records table |
|---|---|
|

|

|
## Bug fixes
- **Backend: applying a managed domain did not demote previously-applied
ones.** Multiple rows could end up with status `APPLIED` even though
only one could be in the saved config. New helper
`demoteOtherAppliedManagedEmailDomains({ tenancyId, keepId })` runs
inside `applyManagedEmailProvider` to demote all other applied rows in
the tenancy back to `VERIFIED` before marking the new one.
- **Frontend: "Use this domain" only appeared for `status ===
verified`.** A domain that had been applied then replaced could never be
re-applied from the UI. Button now appears for any `verified` or
`applied` row that is not currently in use; the **Active** label is
derived from config match instead of DB status.
- **Dev mock onboarding now mirrors production timing.**
`shouldUseMockManagedEmailOnboarding()` used to insert domains as
`verified` synchronously. Now the domain is created as
`pending_verification`, and a fire-and-forget `runAsynchronously(() =>
wait(1000))` updates it to `verified` — mirroring the real Resend
webhook flow so the UI states (pending → verifying → verified) are
exercised in local dev.
## Test plan
- [ ] Cards: clicking each card shows `Draft` pill + amber banner;
Discard restores; Save commits and flips `Current` to the new card
- [ ] Managed: Add domain → stage 1 input → stage 2 DNS table + copy →
Check verification flips to stage 3 → Use this domain sets it Active and
demotes the previously-active domain in the list
- [ ] Managed: clicking **Use this domain** on a non-active verified row
makes it Active and the previously-active row back to Verified
- [ ] Shared / Resend / SMTP: existing save + test-email flows still
work (logic preserved verbatim)
- [ ] `pnpm typecheck` (dashboard + backend) and `pnpm lint` pass
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Redesigned email domain setup flow with multi-step verification dialog
* Added copy-to-clipboard for DNS records
* Enhanced provider selection interface with improved visual
presentation
* Onboarding now shows initial "pending verification" state and
completes verification asynchronously
* **Bug Fixes**
* Ensures only one managed domain becomes active when applying a domain
* Improved error handling for email configuration saves
* **Tests**
* Updated end-to-end tests to reflect async verification timing
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
The sign-up rules tester dialog was dense and hard to parse: a
two-column layout crammed 8 input fields against 4 stacked result panels
(Outcome, Triggered rules, Evaluation trace, Normalized context), and
used technical jargon ("Turnstile override", "Normalized context",
"Evaluation trace") without much hierarchy. This PR reworks it around
the user's actual question — *"will this sign-up be allowed?"* — and
moves the entrypoint somewhere more discoverable.
## What changed
### 1. Dialog UI — essentials-first layout
- Only **Email** and **Sign-up method** are shown upfront.
- Everything else (OAuth provider, Country, Bot / free-trial-abuse
scores, Turnstile) is hidden behind a single **Advanced options**
collapsible panel. The label previews what's inside, so users know when
they need to expand it.
- Results are outcome-first: a large green/red hero card with a check/X
icon and a plain-English decision ("Sign-up would be allowed"). Matched
rules and resolved context are tucked into `<details>` sections below.
- Removed the "Fill out the form above…" placeholder — it added clutter
without adding info.
### 2. Loading → result transition
- The outcome card now mounts **immediately** when Run test is clicked.
While the request is in flight it shows a neutral gray card with a
spinning `CircleNotchIcon` and "Running test…".
- When the result arrives, the card's border/background transitions over
500ms to green or red, the spinner fades out, and the check/X fades in.
Matched rules and resolved context slide down underneath via a
`grid-rows-[0fr→1fr]` animation.
### 3. Entry-point moved to the page header
- "Open tester" now sits **next to Add rule** in the header (secondary
variant, same size).
- Removed the dedicated "Test rules" card at the bottom of the page — it
was using real estate for something a button can do.
### 4. Code cleanup
- Dropped three exploratory variants (wizard, inspector, the original
complex card) that were temporarily in the file during design
exploration.
- Extracted `useTestRulesState()` to encapsulate state + API call, so
the card is purely presentational.
## Why
The tester is an admin-only debugging tool, so it lives or dies by how
fast someone can glance at it and answer *"would this sign-up go
through?"*. The old dialog asked readers to visually parse two columns
and seven fields just to find the outcome. The new layout answers that
question in the first card.
## Walkthrough

21s demo (2x speed): page → open tester → type email → Run test →
loading spinner transitions into the green decision card.
[Download
MP4](https://gist.githubusercontent.com/BilalG1/67639d1590ac172880dc705a027560d3/raw/tester-flow.mp4)
· [Gist with all
media](https://gist.github.com/BilalG1/67639d1590ac172880dc705a027560d3)
## Before / After
### Original tester

### New header layout
"Open tester" next to "Add rule"; no more bottom card.

### New tester dialog — initial
Just Email + Sign-up method. Advanced options collapsed.

### New tester dialog — mid-run (loading)
Outcome card mounts with a spinner while the request is in-flight.

### New tester dialog — result
Outcome hero transitions to green; matched rules + resolved context
collapsibles underneath.

## Test plan
- [x] `pnpm typecheck` (dashboard) passes
- [x] `pnpm lint` (dashboard) passes
- [x] Manually exercised the tester against a configured rule
(`emailDomain.endsWith("tempmail.com")`) with Advanced options both open
and closed
- [x] Verified the loading → green/red transition under artificial
latency (1.2s)
- [x] Verified the "Open tester" button sits next to "Add rule" and the
bottom card is gone
## Scope notes
- No backend, schema, or API changes. Only touches
`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx`.
- The existing analytics / trigger-history / rule-editor code is
untouched.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Advanced testing options now available in a collapsible panel
* Enhanced test results visualization with detailed rule evaluation
display
* **UI/UX Improvements**
* Test trigger button relocated to main action area
* Larger, repositioned "Run test" button
* Reorganized results display with collapsible sections for rules and
context details
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Bilal Godil <bilal@stack-auth.com>
## Summary
The **Block new purchases** toggle on the Payments → Settings page was
visually out of place: it rendered as a bare `SettingSwitch` outside the
`max-w-3xl` settings column, while every neighboring setting (Stripe
Connection, Test Mode, Payment Methods, Platform-Managed Methods) was a
full-width `Card`.
This PR wraps it in a `Card` that matches the existing `TestModeToggle`
pattern so it inherits the same width constraint, border, padding,
title/description structure, and state-colored icon badge.
**File changed:**
[`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/payments/settings/page-client.tsx`](https://github.com/stack-auth/stack-auth/blob/fix/payments-block-new-purchases-card/apps/dashboard/src/app/(main)/(protected)/projects/%5BprojectId%5D/payments/settings/page-client.tsx)
## What was wrong
Two concrete mismatches with the rest of the page:
1. **Wrong container.** The `SettingSwitch` was a direct child of
`<PageLayout>` rather than the `<div className="space-y-6 max-w-3xl">`
column that wraps the other settings — so it stretched to the full page
width instead of the 3xl column and broke the vertical rhythm (no
consistent `space-y-6` gap from the card above).
2. **Wrong style primitive.** It used the bare `SettingSwitch` row
component instead of a `Card` +
`CardHeader`/`CardTitle`/`CardDescription`/`CardContent` structure — so
there was no border, no heading hierarchy, and no state-colored icon
badge, which every other setting on the page has.
## Fix
- Moved the block inside the `space-y-6 max-w-3xl` column so it's
constrained and spaced like its siblings.
- Replaced the `SettingSwitch` with a `Card` mirroring `TestModeToggle`:
- `CardHeader` with `CardTitle` (\"Block New Purchases\") and
`CardDescription` (\"Stops new checkouts while keeping existing
subscriptions active.\").
- `CardContent` with an icon badge (`ProhibitIcon`) that turns red when
blocking is active, plus a short \"Block new purchases\" label and the
`Switch`.
- Copy is intentionally minimal: one title, one sentence of description,
one label next to the switch. No two-state narration.
## Visual comparison
### Pixel diff (changed pixels tinted red over the after image)
4.7% of pixels changed, all concentrated in the bottom of the settings
column — everything else is pixel-identical, confirming the fix is
scoped.

### Cropped before/after toggle (zoomed to the changed region)
Full-viewport comparisons are noisy when the delta is a single component
at the bottom. This one is cropped to the changed bbox so the card fix
is the whole frame — 1s before, 1s after, looped.

### Wipe reveal (before on the left, after swept in from the left)
A vertical red sweeps across the full page, revealing the after state
over the before state. Useful for spotting any unintended drift
elsewhere on the page (there is none).

## Test plan
- [ ] Open `/projects/<id>/payments/settings` in the dashboard.
- [ ] Verify \"Block New Purchases\" renders as a `Card` with the same
width as Stripe Connection / Test Mode / Payment Methods.
- [ ] Toggle the switch on — icon badge turns red, config write fires
(`payments.blockNewPurchases = true`, `pushable: true`).
- [ ] Toggle off — icon returns to muted gray, config write fires with
`false`.
- [ ] Reload the page and confirm the persisted state matches the
toggle.
- [ ] `pnpm lint` and `pnpm typecheck` pass.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Improvements**
* Redesigned the "Block New Purchases" toggle in payment settings with a
new card-based interface and visual prohibit indicator for improved
clarity and user experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Fixes two dashboard UI bugs surfaced while auditing the project area for
large user-visible issues:
1. **Webhook detail page completely broken** — the page shows a blank
screen because the SvixProvider token was being set to the string
`"[object Object]"`.
2. **Editing a trusted domain with an `http://` base URL silently
upgrades it to `https://`** — saving the edit dialog without changing
anything changes the protocol, breaking callbacks to the original host.
Both are corrected with minimal, targeted changes in the dashboard app.
No API, schema, or shared package changes are required.
---
## Bug 1 — Webhook detail page crashes because `svixToken + ''` yields
`"[object Object]"`
### Where
`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/[endpointId]/page-client.tsx`
### Root cause
`stackAdminApp.useSvixToken()` returns an object of shape `{ token:
string, url: string | null }` (see
`packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts`).
The page was doing:
```ts
const svixToken = stackAdminApp.useSvixToken();
const [updateCounter, setUpdateCounter] = useState(0);
// This is a hack to make sure svix hooks update when content changes
const svixTokenUpdated = useMemo(() => {
return svixToken + '';
}, [svixToken, updateCounter]);
// …
<SvixProvider token={svixTokenUpdated} …>
```
`svixToken + ''` coerces the object to the string `"[object Object]"`,
which is then passed to `<SvixProvider>` as the auth token. Every nested
Svix hook (`useEndpoint`, `useEndpointSecret`,
`useEndpointMessageAttempts`) authenticates with that bogus token, gets
a `401 {"code":"authentication_failed","detail":"Invalid token"}` from
Svix, and `getSvixResult`
(`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/webhooks/utils.tsx`)
throws, crashing the page.
Additional notes while in there:
- `setUpdateCounter` was declared but never called anywhere, so the
surrounding `useMemo`/`useState` was dead weight as well as broken.
Removing it removes the dead code too.
- The neighbouring list page (`webhooks/page-client.tsx`) already uses
the correct shape (`svixToken.token`, `svixToken.url`), which is why the
list page rendered correctly while the detail page didn't.
### Fix
Pass `svixToken.token` directly to `<SvixProvider>` and drop the unused
counter/memo.
```ts
export default function PageClient(props: { endpointId: string }) {
const stackAdminApp = useAdminApp();
const svixToken = stackAdminApp.useSvixToken();
return (
<AppEnabledGuard appId="webhooks">
<SvixProvider
token={svixToken.token}
appId={stackAdminApp.projectId}
options={{ serverUrl: getPublicEnvVar('NEXT_PUBLIC_STACK_SVIX_SERVER_URL') }}
>
<PageInner endpointId={props.endpointId} />
</SvixProvider>
</AppEnabledGuard>
);
}
```
### Reproduction (before fix)
1. Enable the Webhooks app on a project.
2. Create an endpoint with any URL.
3. Open the row's action menu and click **View Details**.
4. The page renders blank (Svix hooks throw 401 Invalid token; the error
boundary unmounts the detail tree). URL, Description, Verification
Secret, and Events History never appear.
### Before / After
| Before | After |
| --- | --- |
| 
| 
|
---
## Bug 2 — Editing an `http://` trusted domain silently upgrades it to
`https://`
### Where
`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/domains/page-client.tsx`
### Root cause
In `EditDialog`, the form's `defaultValues` always set `insecureHttp:
false`, regardless of the protocol of the domain being edited:
```ts
defaultValues={{
addWww: props.type === 'create',
domain: props.type === 'update' ? props.defaultDomain.replace(/^https?:\/\//, "") : undefined,
handlerPath: props.type === 'update' ? props.defaultHandlerPath : "/handler",
insecureHttp: false, // ← ignores the existing protocol
}}
```
The `domain` field strips `http(s)://` for display but the protocol
itself is only tracked through the `insecureHttp` switch, which lives
inside the collapsed-by-default **Advanced** accordion. On submit:
```ts
const protocol = values.insecureHttp ? 'http://' : 'https://';
const baseUrl = protocol + values.domain;
```
So an `http://myapp.test` entry reopens with `insecureHttp: false`, the
Advanced section stays collapsed, the user sees nothing wrong, and
hitting **Save** (even with zero visible changes) writes
`https://myapp.test` back to config. Existing redirects from SSO / email
verification flows that depend on the original `http://` host stop
working.
### Fix
Derive `insecureHttp` from the existing `defaultDomain` when editing:
```ts
insecureHttp: props.type === 'update' ? props.defaultDomain.startsWith('http://') : false,
```
This makes the switch in the Advanced panel pre-check itself correctly
and the submit path emits the preserved protocol.
### Reproduction (before fix)
1. Go to **Project Settings → Trusted Domains**.
2. Add a new domain, expand **Advanced**, toggle **Use HTTP instead of
HTTPS** on, enter `myapp.test`, click **Create**. The list now shows
`http://myapp.test`.
3. Click the row's **⋯ → Edit**, then **Save** without changing
anything.
4. Observe the list now shows `https://myapp.test`.
### Before / After
**Domain list after an edit+save:**
| Before (http silently became https) | After (http preserved) |
| --- | --- |
| 
| 
|
In the "before" screenshot, `http://myapp.test` was edited with no
changes and silently became `https://myapp.test`.
`http://www.myapp.test` (not edited) stayed `http://`, confirming the
bug is triggered only through the edit-save path.
**Edit dialog (Advanced expanded):**
| Before (HTTP switch always off) | After (reflects stored protocol) |
| --- | --- |
| 
| 
|
The "after" dialog also shows the protocol prefix label flip from
`https://` to `http://` next to the input — a second visual cue that the
user is editing an HTTP domain.
---
## Scope / out of scope
In scope here:
- The two fixes above, plus a small amount of dead-code cleanup adjacent
to the first fix (the unused `updateCounter` / `useMemo` hack).
Intentionally **not** included (tracked separately from the same audit —
see internal notes):
- Cursor pagination cache wipe across Users/Teams/Transactions tables
(`data-table/common/cursor-pagination.tsx`)
- Email Outbox "Scheduled At" input being reset on every keystroke and
rendered in the wrong timezone (`email-outbox/page-client.tsx`)
- Latent empty-group handling in the sign-up rule builder (validator +
CEL emitter), which is real in code but not currently reachable through
the editor UI
These are broader and deserve their own PRs.
## Test plan
- [ ] **Bug 1 (webhook detail):** Enable Webhooks on a project, create
an endpoint, open **View Details**. Confirm URL, Description,
Verification Secret, and Events History render (no 401s in the console,
no blank page). Confirm the Copy button on the verification secret still
copies the key.
- [ ] **Bug 2 (domain edit preserves http):** Add an `http://` trusted
domain. Edit it and save with no changes — list should still show
`http://`. Edit again, flip the Advanced switch to HTTPS, save — list
should show `https://`. Repeat with the inverse direction (start https,
flip to http).
- [ ] **Regression sweep:** Webhooks list page, create/delete endpoint,
copy signing secret; Trusted Domains add/delete; auth-methods callbacks
against an `http://localhost` domain continue to work.
- [ ] `pnpm typecheck` passes locally. (`pnpm lint` was also run against
the dashboard app and is clean.)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Domain editing now correctly initializes and preserves the protocol
type (HTTP or HTTPS) based on the existing domain configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Six UI issues found across the email-* dashboard pages, ranked by
impact, fixed here:
1. **email-sent layout** — the email log table and domain reputation
card were forced side-by-side at all widths. A fixed-width sidebar plus
a flex-1 table meant that on tablet the table got crushed, and on mobile
the row overflowed horizontally. Fix: stack vertically below `lg`, and
let the reputation card span full width on narrow viewports.
2. **Domain status enum leaks to the UI** — `<span>Status:
{domain.status}</span>` rendered raw values like `pending_dns` /
`pending_verification`. Added a `MANAGED_DOMAIN_STATUS_LABELS` map and
route through it before rendering.
3. **email-themes dialog grid cramped on mobile** — the Change Theme
dialog hardcoded `grid-cols-2`, so at 375px each theme card had ~150px
and the preview images were illegible. Changed to `grid-cols-1
sm:grid-cols-2`.
4. **Template name row overflow** — long template names pushed the Edit
Template button off the right edge of the card because the flex row had
no `min-w-0` / `truncate`. Fixed both, and made the action column
`shrink-0`.
5. **Boosted-capacity label was color-only** — during an active boost
the label used a red strikethrough for the base value and a blue number
for the boosted value with no non-color cue. Added an explicit `→` arrow
between the two numbers, `title` tooltips on each, and a visible
\"(boosted)\" marker after `/h max`.
6. **Draft progress bar overflowed at mobile width** — the 4-step
progress bar used fixed 80px connectors, giving a minimum width of
~400px that clipped off both ends at 375px. Changed connectors to `w-8
sm:w-20` (32px on mobile, 80px otherwise) so all four steps and their
labels fit below 640px.
## Before / after
Each GIF below loops \"before\" (1s) → \"after\" (1s) with a red pill in
the top-right indicating which frame is which. Full-size stills (before
+ after + extra viewports) are listed under **All screenshots** at the
bottom.
### 1. email-sent — two-column layout collapses on narrow viewports
Mobile (375px):

Tablet (900px):

### 2. email-settings — managed-domain status label

### 3. email-themes — Change Theme dialog on mobile

### 4. email-templates — long name overflow

### 5. email-sent — boosted capacity label

### 7. email-drafts — draft progress bar on mobile

## Test plan
- [x] \`pnpm --filter @stackframe/dashboard lint\` — clean
- [x] \`pnpm --filter @stackframe/dashboard typecheck\` — clean
- [x] Manual verification in a browser at 375px / 900px / 1440px, light
+ dark mode, for each fixed page
- [ ] Reviewer sanity check of the remaining email-* pages
(email-outbox, email-viewer) for similar responsive regressions
## Notes
- The initial review flagged a \"white-on-white capacity boost timer\" —
on closer look the label sits on a deliberately dark `bg-zinc-900/0.82`
overlay inside the boost card, so it reads fine in light and dark mode.
Not fixing; that part of the review was a false positive.
- The initial review also flagged a missing empty state on
email-templates. Because Stack seeds built-in templates, the empty
branch is unreachable in practice — skipping that fix to avoid dead
code.
## All screenshots
Gist with all the individual before/after PNGs and the GIFs themselves:
https://gist.github.com/BilalG1/edb04740a19c3f2d048da6e602209d45
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Added human-readable status labels for managed domains in domain
settings
* **Improvements**
* Enhanced responsive layouts across dashboard pages for improved mobile
experience
* Improved email capacity display with visual indicators and tooltips
for boost status
* Refined template and theme selection layouts with better text handling
and spacing
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Fixes five independent UI bugs in the dashboard. Each is a narrow,
localized fix — no changes to shared table / card primitives.
### 1. Auth methods preview didn't update until save
Toggling Email/password, Magic link, or Passkey updated the switch UI
but the right-hand sign-in preview kept rendering the pre-save config
until "Save changes" was clicked. The preview was reading
`project.config` instead of the local pending state.
**Fix:** pass the computed local state (`passwordEnabled`, `otpEnabled`,
`passkeyEnabled`) into `AuthPage`'s `mockProject.config` so the preview
reflects toggles immediately.
| Before | After |
|---|---|
|

|

|
---
### 2. Email-drafts "New Draft" dropdown items stacked on two rows
Icon rendered above text in the dropdown because the icon was a child of
a non-flex inner wrapper inside `DropdownMenuItem` and phosphor icons
default to `display: block`.
**Fix:** use `DropdownMenuItem`'s built-in `icon` prop (which
absolute-positions the icon) instead of passing it as a child.
| Before | After |
|---|---|
|

|

|
---
### 3. Project-keys status filter: clicking options did nothing visible
`DesignDataTable` renders the toolbar outside the card when
`glassmorphic && !insideDesignCard`. The table instance was captured
once via `onTableReady`; filter clicks updated the table's internal
state (rows actually filtered to "No results") but the toolbar's parent
never re-rendered, so checkboxes, chip count, and button label stayed
frozen.
**Fix:** wrap `InternalApiKeyTable` in `DesignCard` so
`useInsideDesignCard()` returns true, `needsOwnCard` becomes false, and
the toolbar renders inside the `DataTable` where it re-renders normally.
No changes to the shared `DesignDataTable` component.
| Before | After |
|---|---|
|

|

|
---
### 4. Analytics "Tables" page only listed Events
`AVAILABLE_TABLES` was hardcoded to a single entry.
**Fix:** registered all 12 ClickHouse views that exist in the `default`
schema (events, users, contact_channels, teams, team_member_profiles,
team_permissions, team_invitations, email_outboxes, project_permissions,
notification_preferences, refresh_tokens, connected_accounts) with
sensible default sort columns. Widened `TableId` to `string`.
| Before | After |
|---|---|
|

|

|
---
### 5. Price input `$` prefix overlapped the number on prod
The Input composed `h-9 px-3 ... pl-7`. In production's CSS bundle order
`.px-3` declared after `.pl-7`, so `padding-left` resolved to 12px —
same as the prefix's `left-3` position — making `$` overlap the first
digit. The emulator's bundle happened to order them the other way, which
is why it only reproduced in prod. Verified with a devtools injection
that mimics the prod CSS ordering.
**Fix:** change `pl-7` → `!pl-7` in `repeating-input.tsx` so the prefix
padding wins regardless of CSS order.
| Before (prod CSS ordering) | After (same ordering) |
|---|---|
|

|

|
---
## Test plan
- [x] `pnpm --filter @stackframe/dashboard typecheck`
- [x] `pnpm --filter @stackframe/dashboard lint`
- [x] Manual verification of each issue against the local dev dashboard
at localhost:8101
- [ ] Reviewer: confirm no visual regressions on other `DesignDataTable`
usages (api-key-table is the only one wrapped here)
- [ ] Reviewer: confirm analytics queries on added tables work with the
signed-in user's permissions
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* Added 12 new analytics tables to the dashboard for enhanced data
visibility and tracking.
* **Bug Fixes**
* Fixed input styling issue with prefix alignment.
* **Style**
* Improved visual presentation of data tables with enhanced card
styling.
* Refined dropdown menu icon display for better UI consistency.
* Enhanced authentication preview settings to reflect current
configuration state.
<!-- 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
## Release Notes
* **New Features**
* Added Stripe, OAuth, and Freestyle mock services to the local emulator
* Introduced `emulator run` CLI command to execute applications with
emulator credentials automatically injected
* Enhanced credential management for local development
* **Improvements**
* Improved ARM64 QEMU emulation with cross-architecture support
* Better error detection and logging during emulator provisioning
* Added example middleware configuration with authentication support
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Updated package versions for '@supabase/*' libraries to 2.99.2 and
'@supabase/ssr' to 0.9.0.
- Added new devDependencies for 'rimraf' and 'framer-motion' in the
pnpm-lock file.
- Modified Next.js configuration to conditionally omit 'X-Frame-Options'
in development mode for better integration with Stack Auth dev tools.
- Refactored component exports in the template package to include
tracking for dev tools.
- Introduced new dev tool components and context for improved logging
and state management.
- Added styles for the dev tool indicator and panel, ensuring a
consistent dark theme.
- Implemented fetch interception to log API calls and user
authentication events in the dev tool.
<!--
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
## Release Notes
* **New Features**
* Added comprehensive Developer Tools interface with tabs for Overview,
Components, AI Chat, Console, Dashboard, and Support.
* Integrated AI Chat assistant within Developer Tools for enhanced
debugging.
* Added component version tracking and update notifications.
* Implemented API request logging and event monitoring.
* Enhanced feedback system with support for bug reports and feature
requests.
* **Bug Fixes**
* Fixed Content Security Policy headers for local development
environments.
* **Dependencies**
* Added AI SDK integration packages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
This PR fixes the bug where analytics tool returns a lot of rows, which
results in huge token count. We do it by checking the number of
characters in the tool call, and if it is more than 50000 characters, we
send an error message rather than the rows and ask the ai to make more
focused queries.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* AI assistant shows friendlier, categorized error messages and captures
unexpected errors for diagnosis.
* UI now displays classifier-derived, user-friendly AI error text.
* **Bug Fixes & Improvements**
* Enforced a hard size budget for SQL query results and gracefully
handles oversized responses.
* Centralized safer database error messaging to avoid leaking internal
details.
* Strengthened AI guidance to prefer narrower queries, safer column
selection, and pairing GROUP BY with ORDER BY + LIMIT.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR puts the ask ai functionality into the ai stack companion, along
with persistent history.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* "Ask AI" chat sidebar with streaming assistant responses, progressive
word-by-word reveal, auto-scroll, Enter-to-send and Arrow-key
navigation, "Thinking…" and error indicators
* Chat UI primitives: inline/code blocks, smart links, copy-to-clipboard
for code/URLs, and expandable tool-result cards with copyable outputs
* **Bug Fixes**
* Prevented button/menu clicks inside list items from bubbling to parent
row handlers
* **Refactor**
* Chat rendering, streaming, parsing, and UI helpers consolidated into a
shared module and integrated into the sidebar widget
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Konsti 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**
* External DB sync now covers teams, team members, permissions,
invitations, email outbox, session replays, refresh tokens, and
connected accounts.
* New sequence ID fields and automatic change-flagging added to many
record types to enable incremental sync.
* **Improvements**
* Added concurrent indexes, faster/parallelized sync pipelines,
verification tooling, and richer observability.
* Dashboard sequencer stats expanded and end-to-end sync tests
significantly extended.
<!-- 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
commit 5d43722575b826a8ed8dbb6b828f48eae4bca02c
Author: mantrakp04 <mantrakp@gmail.com>
Date: Wed Mar 18 12:27:01 2026 -0700
Add QEMU emulator snapshot functionality and reset command
- Introduced a new `emulator-qemu:reset` command in package.json to
clear snapshots and force a fresh boot of the emulator.
- Enhanced the `run-emulator.sh` script to support saving and restoring
snapshots, significantly reducing restart time from ~62s to ~4s.
- Implemented logic to check for existing snapshots and restore them
during startup, improving the emulator's efficiency.
- Updated documentation in CLAUDE-KNOWLEDGE.md to explain the new
snapshot restore process and its benefits.
These changes enhance the QEMU emulator's performance and usability for
developers, providing a more efficient workflow during development.
commit 3877445bdd83cb8690da18c8520bf260d2795172
Author: mantrakp04 <mantrakp@gmail.com>
Date: Wed Mar 18 11:55:18 2026 -0700
Enhance QEMU emulator performance and configuration management
- Added optimizations to the QEMU emulator's app container startup
process, reducing startup time from ~92s to ~62s by using qcow2 backing
files and setting the working directory to /app.
- Updated the build-image.sh script to conditionally wait for background
processes, improving robustness.
- Modified the run-emulator.sh script to create the disk image using
qcow2 format instead of copying, enhancing efficiency.
- Adjusted the cloud-init user-data to set STACK_RUNTIME_WORK_DIR to
/app, streamlining file operations during container initialization.
- Improved the entrypoint script to avoid unnecessary file copying when
the working directory is set to /app.
These changes significantly enhance the performance and usability of the
QEMU emulator for developers.
commit e0b86d3f1d5c08e46d0d343bc632e2a8c5777845
Author: mantrakp04 <mantrakp@gmail.com>
Date: Wed Mar 18 11:07:55 2026 -0700
Refactor local emulator configuration management and enhance Docker
setup
- Removed redundant comments and improved code clarity in the local
emulator's route handling.
- Streamlined the Dockerfile and docker-compose.yaml for better
readability and maintenance.
- Updated entrypoint and initialization scripts to enhance service
startup processes.
- Introduced a new common script for QEMU emulator to centralize
architecture detection and firmware handling.
- Enhanced error handling in the host file bridge for improved
robustness.
- Removed obsolete country code utilities to clean up the codebase.
These changes significantly improve the local emulator's configuration
management and overall setup experience for developers.
commit 4fb0f93c6cc4f749a14acf0228c261e180875609
Author: mantrakp04 <mantrakp@gmail.com>
Date: Wed Mar 18 10:24:53 2026 -0700
Implement local emulator file bridge for enhanced configuration
management
- Introduced a new host file bridge to facilitate reading and writing
configuration files between the local emulator and the host system.
- Refactored the local-emulator module to utilize the file bridge for
file operations, improving error handling and response validation.
- Added tests to ensure the file bridge functionality works as expected,
including handling of non-existent files and writing configurations.
- Updated the run-emulator script to start the file bridge
automatically, ensuring seamless integration during emulator startup.
- Enhanced documentation to reflect the new file bridge capabilities and
usage instructions.
These changes significantly improve the local emulator's ability to
manage configuration files, enhancing the development experience.
commit 3d18a7ce5bbf00a62a40a3f48f27856e79ecc62f
Author: mantrakp04 <mantrakp@gmail.com>
Date: Tue Mar 17 22:36:46 2026 -0700
Refactor QEMU local emulator setup and enhance app bundle handling
- Introduced a new script for packaging Docker images into a compressed
app bundle, improving the emulator's deployment process.
- Updated build-image.sh to create a runtime configuration ISO, ensuring
better management of environment settings.
- Enhanced cloud-init user-data scripts for both dev-server and deps
guests, streamlining service setup and configuration.
- Improved the run-emulator.sh script to facilitate better handling of
runtime configurations and dependencies.
- Adjusted the .gitignore to include .DS_Store and removed obsolete
entries, cleaning up the repository.
These changes significantly enhance the local emulator's functionality
and reliability for developers.
commit 8a35fb1ce79898d73e2259e256c11b6fd9b0a584
Author: mantrakp04 <mantrakp@gmail.com>
Date: Tue Mar 17 21:52:24 2026 -0700
Enhance local emulator functionality and configuration
- Updated package.json to improve the start-emulator command, providing
clearer dashboard and backend URLs.
- Added a new wait-until-emulator-is-ready command to ensure the
emulator is fully operational before proceeding.
- Refactored the local-emulator project route to streamline file
existence checks and default config creation.
- Enhanced user guidance in the dashboard for local Stack config file
handling.
- Updated tests to reflect changes in config file handling, ensuring
non-existent files are created with default settings.
- Improved Docker configurations for the local emulator, including new
environment variables and service dependencies.
These changes significantly enhance the local development experience and
emulator reliability.
commit 3910ed4bc40bbb37340c1c316c24c2826ba372bd
Author: mantrakp04 <mantrakp@gmail.com>
Date: Tue Mar 17 19:59:36 2026 -0700
Remove unused stash-0.patch file to clean up the repository.
commit 74146d974458037a7a9590120a524629a1a6a162
Author: mantrakp04 <mantrakp@gmail.com>
Date: Tue Mar 17 19:58:46 2026 -0700
Enhance QEMU local emulator with app bundle support and runtime
configuration
- Introduced a new script to package the backend and dashboard assets
into a standalone app bundle for the QEMU emulator.
- Updated the build-image.sh script to create an ISO containing the app
bundle, ensuring the guest image includes the full runtime.
- Modified cloud-init user-data to handle the new app bundle and runtime
configuration, improving the setup process for local development.
- Enhanced the run-emulator.sh script to prepare and mount the runtime
configuration ISO, facilitating better environment management for the
emulator.
- Updated the user-data to include necessary environment variables for
the stack application, ensuring seamless integration during startup.
These changes significantly improve the local emulator's functionality
and ease of use for developers.
commit 9e865a1cf524398bc58f00e0836278775c4ae936
Author: mantrakp04 <mantrakp@gmail.com>
Date: Tue Mar 17 16:50:45 2026 -0700
Enhance local emulator setup with new services and configurations
- Added Docker support for a local emulator, integrating PostgreSQL,
Redis, Inbucket, Svix, ClickHouse, MinIO, and QStash.
- Introduced new scripts for managing the emulator lifecycle, including
build and run commands.
- Implemented cloud-init provisioning for automatic service setup on
first boot.
- Updated package.json with new commands for emulator management and
added dotenv-cli for environment variable management.
- Added tests for OAuth authorization flow to return JSON responses.
- Included configuration files for ClickHouse and user management.
This commit significantly improves the local development experience by
providing a comprehensive emulator environment.
<!--
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
## Release Notes
* **New Features**
* Introduced a local QEMU-based emulator for development with bundled
services (PostgreSQL, Redis, ClickHouse, MinIO, Inbucket, Svix, QStash).
* Added CLI commands to manage the emulator (start, stop, reset, status,
pull images).
* Added emulator status dashboard to monitor service health.
* Introduced new configuration system via `stack.config.ts`.
* **Tests**
* Added configuration read/write tests for the emulator.
* Added emulator CLI validation tests.
* **Documentation**
* Added emulator setup and usage guide.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Updated `validateVerifyResponse` to capture errors for invalid or
unexpected responses.
- Improved handling of malformed responses in `checkEmailWithEmailable`,
ensuring a consistent return structure.
- Refactored `getDerivedSignUpCountryCode` to log errors for non-ISO
country codes.
- Simplified country code determination logic in
`createOrUpgradeAnonymousUserWithRules`.
<!--
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**
* Replaced country code dropdown selection with a direct text input
field for simpler data entry.
* Updated country code validation to accept any 2-letter code format,
improving flexibility.
* **Bug Fixes**
* Refined country code normalization logic across sign-up rules and user
profile pages for consistency.
* **Documentation**
* Clarified country code field messaging from "ISO code" to "2-letter
country code" terminology for better user guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Other minor redirect URL changes:
- app.urls.* is now deprecated
- redirectToSignOut now sets and preserves after_auth_return_to
- OAuth sign-in after_auth_return_to now carries callback-return context
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **High Risk**
> High risk because it changes OAuth authorization/token issuance,
redirect URL validation, and introduces a new cross-domain handoff
endpoint plus a DB migration linking authorization codes to refresh
tokens, which can affect login/session security and reliability.
>
> **Overview**
> Adds **hosted URL targets** for SDK `urls` resolution (new `{ type:
"hosted" }`/`{ type: "handler-component" }`/`{ type: "custom" }`
options), including env-driven hosted handler domain/template support
and fallback routing for unknown `/handler/*` paths.
>
> Implements a **cross-domain OAuth PKCE handoff**: a new
`/auth/oauth/cross-domain/authorize` endpoint issues one-time
authorization-code redirects bound to the caller’s session refresh
token; authorization codes now persist `grantedRefreshTokenId` and token
issuance reuses/validates ownership of that refresh token. Redirect
planning for `redirectTo*` (and OAuth callback handling) is refactored
into `redirect-page-urls.ts` to preserve `after_auth_return_to` and
cross-domain handoff params.
>
> Tightens redirect safety (e.g., `after_callback_redirect_url` is
validated/whitelisted), centralizes SDK env var reads via `envVars` with
lint enforcement, hardens `EventTracker` startup for partial DOM test
environments, and adds unit/E2E coverage plus a demo page for manual
cross-domain verification.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
9197d4f32b. 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**
* Cross-domain OAuth PKCE handoff flow (client + server) for hosted
sign-in.
* Hosted handler URL templating with local development domain suffix
support.
* Demo UI page to exercise hosted cross-domain sign-in/out and OAuth
flows.
* Authorization codes now preserve an associated refresh-token id to
support cross-domain exchanges.
* **Bug Fixes**
* Stricter redirect-URL validation and stronger refresh-token ownership
checks.
* More robust event-tracker startup guards in partial DOM environments.
* **Tests**
* New E2E and unit tests covering cross-domain authorize, callback
validation, and handoff flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- replace the dashboard feedback form's Web3Forms submission with an
authenticated internal backend endpoint
- send support and feature-request notifications through Stack Auth's
native internal email pipeline
- share internal project auth headers in the dashboard and add backend
E2E coverage for support feedback
## Testing
- pnpm typecheck
- pnpm lint -- "src/components/feedback-form.tsx"
"src/components/stack-companion/feature-request-board.tsx"
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Internal feedback submission endpoint with automated internal email
notifications
* New internal email builder and sending utility; recipient list
configurable via env
* **Enhancements**
* Feedback form requires sign-in, disables submit when unauthenticated,
and tightens validation
* Centralized header helper for authenticated internal requests
* Feature request board gates actions for signed-out users and improves
upvote/submit reliability
* Runtime retrieval/validation of the feature-tracking API key and
streamlined user handling
* **Tests**
* End-to-end tests covering internal feedback flows, validation, and
email delivery
<!-- end of auto-generated comment: release notes by coderabbit.ai -->