Commit Graph

233 Commits

Author SHA1 Message Date
BilalG1
609579abab
feat(hexclave): PR 3 — native @hexclave/* source rename + delete dual-publish wiring (#1482)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
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
DB migration compat / No migration changes (skipped) (push) Has been cancelled
2026-05-29 15:21:59 -07:00
BilalG1
57ff5d3ce9
feat(hexclave): PR 2 — visible rebrand (Hexclave brand goes public) (#1481)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
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
DB migration compat / No migration changes (skipped) (push) Has been cancelled
## Summary

**Stacked on [#1475](https://github.com/hexclave/stack-auth/pull/1475)**
(`cl/hexclave-pr1`, the invisible compatibility layer). Diff vs that
base = the actual PR 2 code.

This is **PR 2 of the Stack Auth → Hexclave rebrand: the visible flip**.
Old wire identifiers (cookies, request/response headers, Bearer prefix,
JWT issuers, MCP tool name) keep working indefinitely via PR 1's
dual-accept. This PR flips every user-visible surface — package names
taught in docs, SDK class names in code examples, dashboard setup
snippets, page titles, error messages, email content, CLI binary,
default base URLs, GitHub repo slug, contributor guidance — to the
Hexclave brand.

See [`RENAME-TO-HEXCLAVE.md`](./RENAME-TO-HEXCLAVE.md) → *"PR 2: Rebrand
to Hexclave (visible)"* for the full per-work-area spec.

## What's implemented (per the plan's PR 2 scope)

- **SDK base URLs** flipped: `defaultBaseUrl` and
`defaultAnalyticsBaseUrl` in
[common.ts](packages/template/src/lib/stack-app/apps/implementations/common.ts:127)
→ `https://api.hexclave.com` / `https://r.hexclave.com`. PR 1's
[`getHardcodedFallbackUrls`](packages/stack-shared/src/utils/urls.tsx:199)
table now keys on the Hexclave domain.

- **Domain inventory sweep** (16 subdomains from the plan): every
`api/app/docs/discord/demo/mcp/skill/feedback/test/preview/r/api2/api.staging/idp-jwk-audience/built-with.stack-auth.com`
reference in production code, docs-mintlify, examples, READMEs, and
contributor guidance flipped to `*.hexclave.com`. Carve-outs: PR 1's
intentional JWT issuer dual-accept table in
[tokens.tsx](apps/backend/src/lib/tokens.tsx), the legacy `./docs/`
folder, the `unified-docs-widget` allowlist (deliberately accepts both
during DNS transition), and `url-targets.ts` hosted-component default
(baked into existing customer deploys).

- **`@deprecated` JSDoc** on every `Stack*` public export
([packages/template/src/lib/stack-app/index.ts](packages/template/src/lib/stack-app/index.ts)
+ [packages/template/src/index.ts](packages/template/src/index.ts)) —
`StackClientApp`, `StackServerApp`, `StackAdminApp` + every
constructor/options/JSON type, `StackHandler`, `StackProvider`,
`StackTheme`, `useStackApp`, `defineStackConfig`, `StackConfig`.
Hexclave\* aliases are now canonical.

- **Runtime `console.warn`**
([packages/template/src/internal/deprecation-warning.ts](packages/template/src/internal/deprecation-warning.ts))
— once-per-process when the SDK is loaded from a `@stackframe/*`
artifact. Detection uses the existing
`STACK_COMPILE_TIME_CLIENT_PACKAGE_VERSION_SENTINEL` (rewritten at build
time to e.g. `js @stackframe/stack@2.8.92` or `js
@hexclave/next@1.0.0`); `@hexclave/*` mirror artifacts short-circuit the
warning.

- **Tier 3 data migration**: new idempotent SQL migration
[`20260523000000_rename_internal_project_to_hexclave`](apps/backend/prisma/migrations/20260523000000_rename_internal_project_to_hexclave/migration.sql)
— updates the internal Project `displayName` 'Stack Dashboard' →
'Hexclave Dashboard' and `description` only if both still hold the
pre-rebrand defaults. Operator-renamed projects untouched, missing row
no-ops, re-runs are no-ops. [`seed.ts`](apps/backend/prisma/seed.ts:87)
default flipped. `getSharedEmailConfig("Stack Auth")` → `("Hexclave")`.

- **Tier 4 brand strings** (mechanical sweep, ~340 files):
- Page + OpenAPI titles (Hexclave API / Dashboard / REST API / Webhooks
API / Documentation). OpenAPI `info.description` documents
`X-Hexclave-*` headers as canonical with compat note on `X-Stack-*`.
- `HexclaveAssertionError` message text
([errors.tsx:71](packages/stack-shared/src/utils/errors.tsx:71)) — "an
error in Stack." → "an error in Hexclave."
- Known-error message templates
([known-errors.tsx](packages/stack-shared/src/known-errors.tsx)) flipped
to lead with `x-hexclave-*` + the new `docs.hexclave.com` URL; legacy
`x-stack-*` mentioned as compat aliases. **25 e2e test files updated in
lockstep**.
- Email content: failed-emails-digest body, sendTestEmail recipient (now
`sent-with-hexclave.com`), test-email-recipient default.
  - `CHANGELOG.md` title → "Hexclave Changelog".
- `AGENTS.md` env var convention: new vars prefix `HEXCLAVE_` /
`NEXT_PUBLIC_HEXCLAVE_` for Category A/B; legacy `STACK_*` explicitly
noted as accepted via PR 1's dual-read.

- **CLI / init wizard**:
- Every dashboard setup snippet, init-stack template, and docs-mintlify
page teaches `npx @hexclave/cli@latest init` (was
`@stackframe/stack-cli`).
[setup-page.tsx](apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/setup-page.tsx)
+
[link-existing-onboarding](apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/link-existing-onboarding.tsx).
- [init-stack](packages/init-stack/src/index.ts:634)
`STACK_*_INSTALL_PACKAGE_NAME_OVERRIDE` defaults flipped to
`@hexclave/*`.
- Generated `stack/client.ts` / `stack/server.ts` import from
`@hexclave/next` and reference `HexclaveClientApp` /
`HexclaveServerApp`.
- Internal `StackAuthKeys` dashboard component renamed to
`HexclaveKeys`.

- **docs-mintlify rewrite** (legacy `./docs/` intentionally untouched
per scoping decision):
- **78 MDX files swept**.
`@stackframe/{react,stack,js,tanstack-start,...}` →
`@hexclave/{react,stack,js,...}` in install snippets and code blocks;
`Stack*` SDK class names → `Hexclave*` in all code examples; 'Stack
Auth' brand phrase → 'Hexclave'.
- `openapi/{server,admin,client,webhooks}.json` titles → 'Hexclave REST
API' / 'Hexclave Webhooks API'.

- **Generators flipped before regeneration**:
-
[`packages/stack-shared/src/helpers/init-prompt.ts`](packages/stack-shared/src/helpers/init-prompt.ts),
[`/ai/prompts.ts`](packages/stack-shared/src/ai/prompts.ts),
[`apps/backend/src/lib/ai/prompts.ts`](apps/backend/src/lib/ai/prompts.ts),
[`apps/backend/src/lib/ai/tools/create-email-{template,draft}.ts`](apps/backend/src/lib/ai/tools/create-email-template.ts),
[`apps/skills/src/app/route.ts`](apps/skills/src/app/route.ts) (taught
MCP tool → `ask_hexclave` with compat note; CLI binary teach →
`hexclave`),
[`docs-mintlify/snippets/home-prompt-island.jsx`](docs-mintlify/snippets/home-prompt-island.jsx),
[`packages/template/README.md`](packages/template/README.md) +
integrations/convex/component/README.md.
  - `generate-sdks` propagated changes to `packages/{react,stack,js}`.

- **OpenAPI dual-documentation**:
[`apps/backend/src/app/api/latest/route.ts`](apps/backend/src/app/api/latest/route.ts)
now lists `X-Hexclave-*` headers as primary documented schemas with
`X-Stack-*` duplicates marked `.optional()` (both accepted at runtime by
PR 1's normalize-at-proxy shim).

- **`@stackframe/emails` virtual module**: dual-aliased to
`@hexclave/emails` at the bundler boundary
([email-rendering.tsx:89](apps/backend/src/lib/email-rendering.tsx:89)).
Stored email templates continue to import from either name; new
AI-generated templates and the system prompt teach `@hexclave/emails`.

- **Tier 2 mirror-publish wiring** (new this PR, lays the groundwork for
`@hexclave/*` first publish):
-
[`scripts/rewrite-packages-to-hexclave.ts`](scripts/rewrite-packages-to-hexclave.ts)
— rewrites 9 publishable `@stackframe/*` → `@hexclave/*` `package.json`
files (reads `HEXCLAVE_VERSION` env or `--version=` flag), pins
cross-deps to the shared `@hexclave` version, registers `hexclave` bin
alongside `stack` for `@hexclave/cli`.
-
[`.github/workflows/npm-publish.yaml`](.github/workflows/npm-publish.yaml)
appended with rewrite-then-republish step. `pnpm publish` skips
already-on-npm versions so reruns are safe.

- **Sender email domain**: `noreply@stackframe.co` →
`noreply@sent-with-hexclave.com` (the dedicated transactional-sender
domain split per the plan, to isolate bulk deliverability from
`hexclave.com` reputation); `security@` / `team@stack-auth.com` inbound
mailboxes → `@hexclave.com`.

- **Self-host docs**: docker network / container names in the bash
examples flipped from `stack-auth` to `hexclave` (`hexclave-postgres`,
`hexclave-clickhouse`, `hexclave.env`). The docker image tag
`stackauth/server:latest` stays per the plan's locked decision.

- **GitHub repo slug**: `hexclave/stack-auth` → `hexclave/hexclave` in
every `package.json` `repository` field, README link, CHANGELOG
raw-asset URL.

## Carve-outs (deliberately untouched)

-
**[`apps/backend/src/lib/tokens.tsx`](apps/backend/src/lib/tokens.tsx)**
JWT issuer dual-accept table — PR 1 intentional infrastructure, kept
indefinitely.
- **Legacy `./docs/` folder** — per scoping decision (only
`docs-mintlify/` rewritten).
- **`unified-docs-widget` hostname allowlist** — accepts both
`.hexclave.com` (canonical) and `.stack-auth.com` (transition window)
for DNS rollout.
- **`url-targets.ts`** hosted-domain default
`.built-with-stack-auth.com` — wire identifier baked into existing
customer deploys; indefinite read-fallback.
- **Binary visual assets** (logos, favicons, OG images, README
screenshots) — out of scope for this PR. Need design work; tracked
separately.

## Verification

- **`pnpm typecheck`** on
`packages/{template,stack-shared,react,stack,js}` + `apps/dashboard`:
**all green**. The remaining backend / e-commerce-demo typecheck errors
are pre-existing (Prisma codegen output +
`./generated/api-versions.json` not present in fresh worktrees without
`pnpm run codegen-prisma` + a live DB) and unrelated to this diff.
- **`pnpm lint`** on the same 6 packages: all green.
- **Final grep** for residual `Stack Auth` / `stack-auth.com` /
`@stackframe/stack-cli@latest` references: zero outside the intentional
carve-outs above.
- **25 e2e test files updated in lockstep** with the known-error message
changes (asserted strings flipped to match the new x-hexclave-* +
compat-note messages).

## Deploy blockers (ops sequencing before this rebrand goes live)

This PR is code-complete, but the rebrand's visible surfaces (SDK
default URLs, dashboard links, npm READMEs, REST error messages, runtime
deprecation warning) all point at `*.hexclave.com` / `@hexclave/*`
resources that don't exist yet. None of these are fixable from a PR —
they're ops/registrar/npm work that has to be sequenced before merging
this to a release tag.

Suggested ordering, hardest blockers first:

### Tier 1 — required before customer-facing deploy (everything below
this line *will visibly break customers on day 1* if skipped)

1. **DNS + TLS for `api.hexclave.com` + `api1./api2.hexclave.com`** →
must point at the same backend that serves `api.stack-auth.com` (or a
backend that mirrors PR 1's dual-accept). The SDK's new `defaultBaseUrl`
is `https://api.hexclave.com`; every customer that relied on the old
default and upgrades to a post-PR2 SDK build sends API requests here.
Until this resolves, every default-config customer's API call NXDOMAINs.
2. **DNS for `app.hexclave.com`** → the dashboard. Referenced in the
SDK's default-error messages ("Please create a project on the Hexclave
dashboard at https://app.hexclave.com"), the init-stack flow's
`wizard-congrats` redirect, and the OAuth dashboard handoff.
3. **DNS for `docs.hexclave.com`** + Mintlify deploy → the SDK runtime
deprecation warning (`https://docs.hexclave.com/migration`), every
README, every "Learn more" link in the dashboard, and every REST API
error body (`/api/overview#authentication`) points here. The MDX is in
this PR; the docs build target needs DNS.
4. **DNS for `mcp.hexclave.com`** → the MCP server endpoint that every
taught agent integration (`claude mcp add ...`, `cursor`, `codex`,
`vscode`) registers. Until this resolves, every `npx
@hexclave/cli@latest init` MCP-registration step fails.
5. **Reserve the `@hexclave` npm scope + set repo variable
`HEXCLAVE_VERSION`** → the mirror-publish step in
`.github/workflows/npm-publish.yaml` is gated on this variable. Without
it, the entire taught onboarding command `npx @hexclave/cli@latest init`
404s from the npm registry, *and* every README that says "install
`@hexclave/next`" leads to install failure. Pick the initial version
intentionally (`1.0.0` or aligned to `@stackframe/stack`); don't accept
a silent default.

### Tier 2 — required before announcing the rebrand publicly (lookalike
or low-traffic surfaces, but visibly broken)

6. **DNS for `r.hexclave.com`** → the analytics beacon
`defaultAnalyticsBaseUrl`. Silent failure if missing (analytics drops),
but should land alongside Tier 1.
7. **Register `sent-with-hexclave.com` + full email auth (SPF / DKIM /
DMARC)** → the new default sender domain for shared-sender transactional
emails. Without it the dashboard "send test email" path emits bounces,
and shared-sender flows (`getSharedEmailConfig("Hexclave")`) deliver to
spam at best.
8. **MX + SPF / DMARC for `hexclave.com`** → `team@hexclave.com` and
`security@hexclave.com` mailboxes. The security disclosure mailbox is
referenced in [`.github/SECURITY.md`](.github/SECURITY.md);
`team@hexclave.com` is the actual recipient of internal feedback emails
sent at runtime by
[`apps/backend/src/lib/internal-feedback-emails.tsx`](apps/backend/src/lib/internal-feedback-emails.tsx).
Today, every runtime feedback email bounces.
9. **DNS for `skill.hexclave.com`** → the canonical AI-agent skill fetch
URL (the agent bootstrap pivot). Without it, the entire "agent downloads
`SKILL.md` from a known URL" flow taught in
[`packages/stack-shared/src/helpers/init-prompt.ts`](packages/stack-shared/src/helpers/init-prompt.ts)
fails.
10. **Create `github.com/hexclave/hexclave` as a public repo** (even as
a redirect to `hexclave/stack-auth`) **OR** rewrite every `package.json`
`"repository"` field + dashboard footer "view on GitHub" link to point
at `hexclave/stack-auth` (which already exists). Currently every npm
package page's "Repository" link is dead, and the dashboard's GitHub
button + dev-tool repo link are dead.

### Tier 3 — broken but low-visibility / low-traffic

11. **DNS for `discord.hexclave.com`** → Discord invite redirect, used
in every README's chip and the dashboard footer.
12. **DNS for `demo.hexclave.com`** → " Demo" badge in every npm
package README. Broken-image badge on the package page.
13. **DNS + TLS for `built-with-hexclave.com`** → optional
hosted-handler domain (the default reverted to
`.built-with-stack-auth.com` in this PR's carve-outs, so this only
matters for projects that manually flip).

## Other follow-ups (not deploy-blocking)

- **E2E snapshot regen across the full suite** for the dual-emitted
`x-hexclave-*` response headers (PR 1 follow-up; `vitest -u` in CI
absorbs).
- **Binary visual assets** — logos, favicons, OG images, README
screenshots; need design pass.
- **Backend OpenAPI fumadocs regen** in CI flow — the JSON files in
`docs-mintlify/openapi/` are committed but regen runs in CI. Verify the
workflow that does this still works against the post-PR2 source.
- **Backend typecheck infra debt** — needs `codegen-prisma` +
`codegen-route-info` to clear; pre-existing, unaffected by this PR.

## Test plan

- [ ] CI runs full e2e suite (with `vitest -u` to absorb residual
snapshot deltas, then committed back).
- [ ] Spot-check: new `@hexclave/cli init` (once published) generates
`hexclave.config.ts` and works against a fresh project.
- [ ] Spot-check: existing customer with `@stackframe/stack` import sees
the once-per-process `console.warn` recommending `@hexclave/next` on SDK
init.
- [ ] Manual: dashboard setup page renders the `npx @hexclave/cli@latest
init` snippet and the `x-hexclave-publishable-client-key` API header in
the curl example.
- [ ] Manual: a fresh `pnpm run prisma migrate` against a clean DB sets
the internal project displayName to 'Hexclave Dashboard'.

---------

Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
2026-05-26 19:18:20 -07:00
BilalG1
a4ae7edecd
fix(ci): repair two pre-existing test failures on dev (#1488)
Both failures are pre-existing on `dev` (confirmed by checking the most
recent dev run
[26434368271](https://github.com/hexclave/stack-auth/actions/runs/26434368271)
— same two annotations, same line numbers). Neither is caused by an open
PR.

## Failure 1 — \`apps/backend/src/lib/redirect-urls.test.tsx:75\`

\`\`\`
AssertionError: expected false to be true
\`\`\`

The \`withHostedHandlerEnv\` helper set/cleared only the
\`STACK_*\`-prefixed env vars. CI's
[e2e-custom-base-port-api-tests.yaml:21](.github/workflows/e2e-custom-base-port-api-tests.yaml#L21)
sets only the \`HEXCLAVE_*\`-prefixed sibling
(\`NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX=67\`), and the dual-read shim in
[packages/stack-shared/src/utils/env.tsx#L53-L55](packages/stack-shared/src/utils/env.tsx#L53-L55)
prefers \`HEXCLAVE_*\` over \`STACK_*\`:

\`\`\`ts
const hexclaveName = getHexclaveEnvVarName(name);
let value = (hexclaveName ? process.env[hexclaveName] : undefined) ??
process.env[name];
\`\`\`

So \`getEnvVariable(\"NEXT_PUBLIC_STACK_PORT_PREFIX\", \"81\")\`
returned \`\"67\"\` instead of the test's \`\"92\"\`, the template
resolved to port \`6709\` instead of \`9209\`, and the assertion at line
75 failed.

**Fix:** mirror every \`STACK_*\` key managed by the helper to its
\`HEXCLAVE_*\` sibling. The dual-read then resolves to the
test-controlled value regardless of which key it checks first.

## Failure 2 —
\`apps/backend/prisma/migrations/20260526060000_nullable_oauth_access_token_expires_at/tests/nullable-expires-at.ts:58\`

\`\`\`
PostgresError: null value in column \"updatedAt\" of relation
\"OAuthAccessToken\" violates not-null constraint
\`\`\`

The migration test's raw INSERT omits \`\"updatedAt\"\`. The Prisma
model declares \`updatedAt DateTime @updatedAt\` with no
\`@default(now())\`, so the DB column is \`NOT NULL\` with no default —
Prisma populates it at the ORM layer on insert, but this test bypasses
Prisma via \`postgres.js\`.

**Fix:** add the \`\"updatedAt\"\` column to the INSERT, set to
\`NOW()\`, with a comment noting why raw SQL must set it explicitly.

## Verification

- **Failure 1, before fix:** ran \`NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX=67
pnpm test run apps/backend/src/lib/redirect-urls.test.tsx\` locally →
reproduces the exact line-75 assertion failure from CI.
- **Failure 1, after fix:** same command → 33/33 pass.
- **Failure 2:** local reproduction requires the migration-test postgres
harness; the fix is one column matching how every other raw SQL insert
in this repo handles \`@updatedAt\` fields. CI on this branch will
confirm.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes two failing tests on dev CI by aligning env var handling in
redirect URL tests and by setting the missing updatedAt in a migration
test. Restores green CI with no runtime changes.

- **Bug Fixes**
- Redirect URL tests: `withHostedHandlerEnv` now mirrors `STACK_*`
values to their `HEXCLAVE_*` siblings and restores both, so
`getEnvVariable` reads the test-controlled values even when CI sets only
`HEXCLAVE_*` (e.g. `NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX`).
- Migration test: the raw insert into `OAuthAccessToken` now sets
`"updatedAt" = NOW()` since `Prisma`’s `@updatedAt` isn’t applied when
using `postgres.js` and the column is NOT NULL.

<sup>Written for commit 75c8e4343e.
Summary will update on new commits. <a
href="https://cubic.dev/pr/hexclave/stack-auth/pull/1488?utm_source=github">Review
in cubic</a></sup>

<!-- End of auto-generated description by cubic. -->
2026-05-26 12:59:44 -07:00
Konstantin Wohlwend
fae8d2dfab Longer refresh token expiries for OAuth providers that don't return one 2026-05-25 22:35:22 -07:00
Armaan Jain
e42ec65c88
Payments app design fixes (#1375)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

## Summary

This PR brings the Payments dashboard surfaces in line with the shared
design system: product creation, product-line / included-item dialogs,
auth-method toggles, payments empty states, and related layout polish.
Dialogs migrate from raw shadcn `Dialog` to `DesignDialog` with
consistent headers, footers, inputs, and selector dropdowns.

**Base:** `dev` → **Head:** `Payments-app-design-fixes`  
**Scope:** 31 files, ~+1.4k / −1.3k lines  
**Captured on:** local dev server (`internal` project), signed in as
`admin@example.com`

## Screenshots

Captured from `http://localhost:8101` (viewport: **1920×1200** standard,
**2560×1440** widescreen). Assets hosted in [this
gist](https://gist.github.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf).

> Red outlines on the **after** shots mark the new or changed UI
introduced by this PR.

### Create Product — payments form redesign

| | Before | After |
| --- | --- | --- |
| Light |
![payments-products-new-before-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-light.png)
|
![payments-products-new-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-light.png)
|
| Dark |
![payments-products-new-before-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-dark.png)
|
![payments-products-new-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-dark.png)
|

Widescreen:

| | Before | After |
| --- | --- | --- |
| Light |
![payments-products-new-before-light-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-light-wide.png)
|
![payments-products-new-after-light-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-light-wide.png)
|
| Dark |
![payments-products-new-before-dark-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-dark-wide.png)
|
![payments-products-new-after-dark-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-dark-wide.png)
|

### Product Lines onboarding — vertical centering fix

| | Before | After |
| --- | --- | --- |
| Light |
![payments-product-lines-before-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-before-light.png)
|
![payments-product-lines-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-after-light.png)
|
| Dark |
![payments-product-lines-before-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-before-dark.png)
|
![payments-product-lines-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-after-dark.png)
|

### Create Product Line dialog — `DesignDialog` migration

| | Before | After |
| --- | --- | --- |
| Light | *(legacy shadcn dialog on `dev` — open via Product Line →
Create new)* |
![dialog-create-product-line-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-create-product-line-after-light.png)
|
| Dark | |
![dialog-create-product-line-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-create-product-line-after-dark.png)
|

### Auth Methods — toggle row accessibility

| | Before | After |
| --- | --- | --- |
| Light |
![auth-methods-before-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-before-light.png)
|
![auth-methods-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-after-light.png)
|
| Dark |
![auth-methods-before-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-before-dark.png)
|
![auth-methods-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-after-dark.png)
|

### Other migrated surfaces (after only)

| Page | Light | Dark |
| --- | --- | --- |
| Payments settings |
![payments-settings-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-settings-after-light.png)
|
![payments-settings-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-settings-after-dark.png)
|
| Sign-up rules |
![sign-up-rules-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-after-light.png)
|
![sign-up-rules-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-after-dark.png)
|
| Projects list (Create Project button) |
![projects-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/projects-after-light.png)
|
![projects-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/projects-after-dark.png)
|
| Playground / DesignDialog |
![playground-dialog-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/playground-dialog-after-light.png)
|
![playground-dialog-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/playground-dialog-after-dark.png)
|
| Included Item dialog |
![dialog-included-item-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-included-item-after-light.png)
|
![dialog-included-item-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-included-item-after-dark.png)
|

### Scroll behaviour — Sign-up Rules

| | Light | Dark |
| --- | --- | --- |
| Scroll |
![sign-up-rules-scroll-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-scroll-light.gif)
|
![sign-up-rules-scroll-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-scroll-dark.gif)
|

## What's new

- **`DesignDialog`** extended with `customHeader`, `noBodyPadding`, and
section `className` hooks; Playground updated to showcase them.
- **Payments dialogs** (`CreateProductLineDialog`, `IncludedItemDialog`,
price edit, item dialog) migrated to design-system components.
- **Create Product** page uses `DesignButton`, `DesignInput`,
`DesignSelectorDropdown`, and refreshed header actions.
- **Auth Methods** toggle rows use semantic `<Label htmlFor>` instead of
click-capture divs.
- **Payments layout** empty-state card centers correctly; product-lines
onboarding slideshow vertically centers.
- **Backend** seed invariant for Growth product price; removed unused
import in product switch route.

## Notes for reviewers

- Dialog migrations preserve validation + async error handling
(`runAsynchronouslyWithAlert` where applicable).
- Included-item dialog uses a sentinel value for “Create new item” to
avoid colliding with real item IDs.
- `packages/stack` / `packages/js` are untouched; template +
dashboard-ui-components carry SDK-facing dialog changes.

## Test plan

- [x] Visual capture on `internal` project (`admin@example.com`) —
light/dark, standard + widescreen
- [ ] Create product flow: customer type → product line dropdown →
create line dialog
- [ ] Add included item dialog from create/edit product
- [ ] Auth Methods toggles (label click + switch)
- [ ] Payments product-lines onboarding slideshow at varied viewport
heights
- [ ] `pnpm typecheck` / `pnpm lint` / targeted E2E if API surface
changed

---------

Co-authored-by: nams1570 <amanganapathy@gmail.com>
Co-authored-by: mantrakp04 <mantrakp@gmail.com>
Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
2026-05-21 14:48:56 -07:00
Konstantin Wohlwend
0848a1aaed Add schema to migration that was missing it 2026-05-19 16:14:28 -07:00
Konsti Wohlwend
29cea48beb
Remote dev envs (#1435) 2026-05-19 15:54:18 -07:00
BilalG1
d0202eeef9
payments: rework refund flow to three-knob API (#1429)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
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
DB migration compat / No migration changes (skipped) (push) Has been cancelled
## Summary
- Replaces per-entry refund schema with a flat `{ amount_usd,
revoke_product, end_subscription? }` shape; refund state is now derived
from bulldozer ledger rows (`refund:<sourceTxnId>:<uuid>`) instead of
the legacy `refundedAt` column, enabling multiple partial refunds up to
the remaining cap.
- Adds `invoice_id` for refunding any subscription invoice (start or
renewal), Stripe idempotency keys derived from `(tenancyId, sourceTxnId,
amount, prior_refunded)` so retries dedupe but intentional partials
don't collide, and a legacy backstop that rejects pre-rework
`refundedAt` purchases.
- Dashboard refund dialog rebuilt around the three toggles (revoke→end
coupling cascades into the UI); refund rows surface in the listing as
`type: "refund"` with `adjusted_by` linkage handling both new and legacy
formats.

## Implements
[STA2-52 — Build in refund logic for
payments](https://linear.app/stack-auth/issue/STA2-52/build-in-refund-logic-for-payments)

## Documented limitations (planned follow-up work)
These are called out in code comments and intentionally deferred to a
follow-up PR:
- **Cap-check race under concurrent refunds.** Bulldozer's embedded
`BEGIN/COMMIT` prevents an outer Prisma tx from scoping the writes, so
two concurrent refunds can both pass the cap check. Needs a
bulldozer-aware mutex or pending-refund-intent pattern. In practice
refunds are admin-only and rare, so the race window is small.
- **Stripe + DB non-atomicity on the DB-success → response-loss path.**
The Stripe idempotency key is keyed on `(tenancyId, sourceTxnId, amount,
priorRefunded)`, so a retry after Stripe-success → DB-fail self-heals
(Stripe dedupes; the next attempt writes the bulldozer row). The hole is
the reverse direction: if the bulldozer row commits but the response is
lost, a retry sees a higher `priorRefunded` and generates a fresh key —
Stripe would issue a second real refund. No out-of-band reconciliation
today.
- **Dashboard can't reach the `invoice_id` path.** Refund actions are
only enabled on `purchase` rows and the submit call never passes
`invoice_id`, so admins refunding a renewal must use the API directly.
Follow-up: enable the action on `subscription-renewal` rows and thread
`invoice_id` through.

## Architectural note
`active-subscription-end` and `item-quantity-expire` entries are **not**
emitted on the refund row itself. They're produced by the derived
sub-end transaction (`transactions.ts:158-228`) once Prisma
`subscription.endedAt` is updated, keeping the `expiresWhen` /
`when-repeated` semantics in one place. This is the main structural
divergence from the ticket's literal entry recipe.

## Review follow-ups addressed in this PR

**First-pass review:**
- **KnownError back-compat preserved**: `SubscriptionAlreadyRefunded` /
`OneTimePurchaseAlreadyRefunded` are once again thrown by the
legacy-`refundedAt` backstop, and `TestModePurchaseNonRefundable` is
thrown when an admin sends `amount_usd > 0` against a test-mode
purchase. Callers catching by error code keep working through the
rework.
- **Idempotency-key comment corrected**: now accurately describes the
`(tenancyId, sourceTxnId, amount, priorRefunded)` key and its
self-healing behaviour on the Stripe-success → DB-fail retry path (see
Documented limitations above for the remaining hole).
- **Renewal-invoice e2e coverage added**: new test sets up a live-mode
subscription via Stripe webhooks (`subscription_create` +
`subscription_cycle` invoices), refunds the renewal invoice via
`invoice_id`, and asserts the resulting `refund_transaction_id` starts
with `refund:sub-renewal:` and is linked back via `adjusted_by` on the
*renewal* row (not the start row). Plus negative cases:
cross-subscription `invoice_id` → 404, `invoice_id` on a one-time
purchase → SchemaError.

**Second-pass review:**
- **Idempotent sub-cancel error-code string fix**: the Stripe code for
re-cancelling an already-canceled sub is
`subscription_already_canceled`, not `subscription_canceled` — the
previous catch would have re-thrown.
- **End-only sub refund replay rejected**: when `amount=0, revoke=false,
end=true` and the sub is already `cancelAtPeriodEnd` or `endedAt`, throw
SchemaError. Otherwise `readPriorRefundSummary` doesn't see end-only
events and the call would be a forever-no-op accumulating empty refund
rows.
- **`revoke_product=true` with renewal `invoice_id` rejected**: the
product grant lives on the sub-start txn, not on renewal txns — a
renewal-scoped revocation would write a back-reference to a non-existent
entry. Forces admin to revoke against the start invoice (or the default
no-`invoice_id` call).
- **Refund row `id` matches the linkage**: the listing route now returns
the full refund txnId as `id` for `type: "refund"` rows so it matches
`adjusted_by.transaction_id` — the dashboard can join source rows to
their refund rows.
- **+2 e2e tests** for the above (end-only replay rejection,
revoke+renewal rejection).

**Third-pass review:**
- **Dashboard refund dialog seeds state on open**: previously the reset
block lived in `ActionDialog`'s `onOpenChange`, which doesn't fire on
the open transition for a controlled dialog. As a result the dialog
opened with the initial `useState` defaults (`amountUsd = '0'`), and an
admin submitting unchanged on a paid purchase would revoke/end at $0
instead of refunding the charged amount. The seed now runs in the menu
`onClick` before `setIsDialogOpen(true)`.
- **`SUBSCRIPTION_START_PRODUCT_GRANT_ENTRY_INDEX` corrected from 1 →
0**: the constant is persisted as `adjustedEntryIndex` on
product-revocation entries and copied through verbatim by
`mapLedgerEntry`. That mapper drops the hidden
`active-subscription-start` entry, so the public-API layout puts the
product grant at index 0. The prior value of `1` pointed at the
money-transfer entry (or out of range on test-mode subs) through the
public listing.
- **`amountTotal` cap gated behind a USD pre-flight**:
`SubscriptionInvoice` doesn't persist invoice currency, and the previous
code took `invoice.amountTotal` as USD cents directly. Now
`getTotalUsdStripeUnits` (which throws on non-USD pricing) is always
called first; `amountTotal` is only preferred as the actual cap after
that pre-flight succeeds.

## Test plan
- [x] `pnpm typecheck` — 28/28 pass
- [x] `pnpm lint` — 28/28 pass
- [x] `pnpm test run
apps/e2e/tests/backend/endpoints/api/v1/internal/transactions-refund.test.ts`
— **19/19 pass** (was 14/14 on the original PR; +3 for `invoice_id`
path: renewal refund happy path, unrelated `invoice_id` rejection,
`invoice_id` on OTP rejection; +2 for second-pass: end-only replay
rejection, revoke+renewal rejection)
- [x] curl smoke against
`/api/latest/internal/payments/transactions/refund` — unknown purchase →
404, no-op → 400, negative → 400, sub-revoke-without-end → 400
- [x] **Dashboard UI end-to-end re-run pending** — the original
agent-browser pass ran before the third-pass dialog-seed fix, so any
"money + revoke" submissions may have actually sent `amount_usd = "0"`.
Re-test before un-drafting: open the refund dialog from the menu,
confirm the amount field pre-fills with the charged amount, exercise
validation (negative / exceeds-cap / no-op), and submit both an
end-subscription-only sub refund and a money+revoke OTP refund; verify
bulldozer rows and Prisma `cancelAtPeriodEnd` updates.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Ledger-driven refund flow with stable refund IDs, invoice-aware
refunds, OTP/product-revocation support, tri-state end_action (now /
at-period-end / none), and API responses that include
refund_transaction_id.

* **Bug Fixes / Improvements**
* Deterministic Stripe idempotency, stronger replay protection,
refundable-amount caps, test-mode constraints, and transactions listing
updated to surface refunds.

* **Tests**
* Expanded unit and E2E coverage for new request shape, invoice paths,
money-unit conversion, and edge cases.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1429)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 19:29:21 -07:00
Mantra
c808e23b7d
Data-grid overhaul + session-replays / team-payments dashboard surfaces (#1424)
## Summary

Refactors the dashboard data-grid into a smaller, URL-state-aware
primitive and lands several new dashboard surfaces around it: per-user
session replays, team-level analytics and payments, and pagination for
permission definitions. Also moves session replays out from under
`/analytics` to a top-level surface and adds a
`project_user.last_active_at` index that the new weekly-active metrics
depend on.

**Base:** `dev` → **Head:** `refactor/data-grid-and-dashboard-surfaces`
**Scope:** 91 files, +5,644 / −1,858. Assets in [this
gist](https://gist.github.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7).

## Screenshots

Captured from a local dev server (dashboard at `:8101`, dummy project
seeded with 26 users). Standard viewport **1920×1200**, widescreen
**2560×1440**.

### Users list — data-grid overhaul in context

| Light | Dark |
| --- | --- |
|
![users-list-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-light.png)
|
![users-list-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![users-list-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-light-wide.png)
|
![users-list-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-dark-wide.png)
|

### User detail — new session-replays card + weekly metrics

| Light | Dark |
| --- | --- |
|
![user-detail-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-light.png)
|
![user-detail-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![user-detail-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-light-wide.png)
|
![user-detail-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-dark-wide.png)
|

### Session replays — moved out of `/analytics`

| Light | Dark |
| --- | --- |
|
![session-replays-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-light.png)
|
![session-replays-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![session-replays-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-light-wide.png)
|
![session-replays-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-dark-wide.png)
|

### Project permissions — new pagination

| Light | Dark |
| --- | --- |
|
![project-permissions-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-light.png)
|
![project-permissions-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![project-permissions-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-light-wide.png)
|
![project-permissions-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-dark-wide.png)
|

### Other migrated surfaces

| Page | Light | Dark |
| --- | --- | --- |
| Project picker |
![projects-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/projects-light.png)
|
![projects-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/projects-dark.png)
|
| Overview / setup |
![overview-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/overview-light.png)
|
![overview-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/overview-dark.png)
|
| Teams list |
![teams-list-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/teams-list-light.png)
|
![teams-list-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/teams-list-dark.png)
|
| Team permissions |
![team-permissions-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/team-permissions-light.png)
|
![team-permissions-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/team-permissions-dark.png)
|
| API keys |
![api-keys-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/api-keys-light.png)
|
![api-keys-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/api-keys-dark.png)
|

### Scroll behaviour — new data-grid on the users list

| Light | Dark |
| --- | --- |
|
![users-list-scroll-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-scroll-light.gif)
|
![users-list-scroll-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-scroll-dark.gif)
|

## What's new

- **`packages/dashboard-ui-components/src/components/data-grid`** —
rewritten. Trimmed `data-grid.tsx` from ~1.7k LOC, split sizing logic
into `data-grid-sizing.ts`, added `use-url-state.ts` for URL-synced
state, and added `data-grid.test.tsx`.
- **Session replays** moved from `…/analytics/replays` to
`…/session-replays` (top-level surface). New `user-session-replays.tsx`
card on the user detail page; new internal `route.tsx` to feed it.
- **Teams** detail page gains `team-analytics.tsx` and
`team-payments.tsx`.
- **Permissions** — new shared `permission-definitions-pagination.ts`
consumed by both project and team permission CRUD routes.
- **Backend** — Prisma migration `add_project_user_last_active_at_idx` +
a `lastActiveAt` index that backs the new weekly-active metrics.
- **Polish** — `editable-input`, `inline-save-discard`, `settings.tsx`,
walkthrough steps, and several data-table components touched in line
with the data-grid rewrite.

## Notes for reviewers

- The data-grid rewrite changes the *shape* of state (now URL-synced),
not just internals. Consumers in
`apps/dashboard/src/components/data-table/*` were updated to match —
please scan those for any missed knobs.
- The `analytics/replays` → `session-replays` rename is git-tracked as
renames; diffs should be small in those files.
- New SDK surface in
`packages/template/src/lib/stack-app/session-replays/index.ts` and
additions in `admin-app-impl.ts` / `server-app-impl.ts` mean OpenAPI
specs (`docs-mintlify/openapi/{admin,client}.json`) regenerate; the diff
is mostly mechanical.

## Test plan

- [ ] `pnpm typecheck` clean
- [ ] `pnpm lint` clean
- [ ] Data-grid unit tests pass (`packages/dashboard-ui-components`)
- [ ] Manual: users list — column resize, sort, filter, paginate; URL
state reflects each change and survives reload
- [ ] Manual: user detail — session-replays card lists replays;
weekly-metrics card renders without `lastActiveAt` index migration
applied (i.e. on a fresh DB) and after applying it
- [ ] Manual: project + team permissions — pagination cursor advances
and stays consistent under search
- [ ] Manual: session-replays top-level page loads; old
`/analytics/replays/...` URL path is no longer expected to be linked
anywhere


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Session Replays app (embedded mode, search, sorting, share links)
  * Tabbed Team pages with Team Analytics and Team Payments dashboards
* Server-backed cursor pagination, debounced search, and infinite-scroll
for teams/users/permissions

* **UX**
* Permission and member tables refresh after edits; permission creation
triggers table refresh
  * Users list supports sorting by last-active

* **Performance**
  * Index added to speed ProjectUser last-active queries

* **Documentation**
  * API/SDK docs updated for pagination and new query params
* Contributor guidance: explicit git-safety rules added (no destructive
git ops without consent)

* **Tests**
  * Added e2e tests for pagination and filtering on list endpoints
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 14:16:47 -07:00
Aman Ganapathy
a9623d976a
[Refactor] [Fix] Remove default prod creation (#1350)
With the new bulldozer rework we dont support default products anymore.
Users are encouraged to currently manually handle granting products to
their end users.

We block api requests and new product creations that attempt to set no
price, and we remove any options to set include-by-default. We also
migrate users' existing product snapshots in `Subscriptions`,
`OneTimePurchases`, and `ProductVersions` to have no price set if it's
an include-by-default product. This will make it so that next time a
user goes onto their products page, they will be informed that the
pricing is invalid and it is no longer delivered by default.

Note, however, that these products will still be providing items and the
like to the users who have them.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Migrated legacy product snapshots so missing included-items no longer
break readers.
* Removed deprecated "include-by-default" pricing sentinel; pricing now
requires explicit price entries and write validation rejects the old
sentinel.

* **Chores**
* Simplified dashboard pricing flows: create/edit/save now use explicit
prices and surface an alert when a formerly implicit free plan needs an
explicit $0 price.
* Config overrides and stored data are auto-normalized to explicit price
objects.

* **Tests**
* Updated and added tests covering migration, validation, and switching
behavior for explicit prices.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: mantrakp04 <mantrakp@gmail.com>
Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
2026-05-15 10:38:33 -07:00
Madison
2cf0f6f981
[Apps] Adding support app alpha and dogfooding (#1368)
<!--

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**
* Support app: inbox UI to create, view, reply, and manage conversations
(status, priority, assignee, tags, internal notes).
* Dashboard pages: Conversations and Support Settings; feedback can
create managed conversations.
* Public/internal APIs for listing, creating, updating, and fetching
conversation details; client-side helpers.

* **SLA**
* Configurable first/next response targets, urgency classification, and
timing logic.

* **Data**
* New conversation persistence (conversations, entry points, messages)
and migration tests; preserves conversations on user/team deletion and
anonymizes sender data.

* **Tests**
  * Unit, migration, and end-to-end tests added.

* **Documentation**
  * Updated docs describing conversation model and workflow rules.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 11:36:11 -05:00
Mantra
e50358710a
fix(tests): use sql.json in onboarding migration test and refresh metrics snapshot (#1420)
## Summary

Two small test-maintenance fixes that came up while running the suite:

- **Onboarding migration test**
(`apps/backend/prisma/migrations/20260420000000_add_project_onboarding_state/tests/default-and-updates.ts`):
switch the JSON insert from `\${JSON.stringify(onboardingState)}::jsonb`
to `\${sql.json(onboardingState)}`. This matches the pattern used by
every other migration test in the repo (see
`20260214000000_fix_trusted_domains_config/tests/*`) and lets the
`postgres` driver handle serialization and parameter binding
consistently rather than relying on a manual `::jsonb` cast.
- **Internal metrics snapshot**
(`apps/e2e/tests/backend/endpoints/api/v1/__snapshots__/internal-metrics.test.ts.snap`):
update `active_users_by_country.AQ` to list `mailbox-2` before
`mailbox-1`. The `should return metrics data with users` test signs in
`mailbox-1` (mailboxes[0]) into AQ first, then later signs `mailbox-2`
(mailboxes[1]) into AQ, so sorted by `last_active_at_millis desc`
`mailbox-2` should come first. The snapshot now matches that ordering.

No production code is touched — both changes are limited to test
fixtures.

## Test plan

- [ ] `pnpm -C apps/backend test run` (migration tests)
- [ ] `pnpm -C apps/e2e test run internal-metrics` (snapshot test)
- [ ] `pnpm lint`
- [ ] `pnpm typecheck`


Made with [Cursor](https://cursor.com)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* No user-facing behavior changed; test flows made more robust and less
flaky (migration validation, metrics ingestion polling, CLI expiry
checks, failed-emails digest expectations).
* **API / Documentation**
* CLI auth default expiration reduced from 2 hours to 2 minutes (updated
OpenAPI defaults and related test expectations).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 10:06:29 -07:00
Konstantin Wohlwend
7f35ae7d54 Fix migration tests
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
Mirror main branch to main-mirror-for-wdb / lint_and_build (push) Has been cancelled
Publish npm packages / publish (push) Has been cancelled
Publish Swift SDK to prerelease repo / publish (push) Has been cancelled
Sync Main to Dev / sync-commits (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
2026-05-06 15:18:48 -07:00
Konsti Wohlwend
765b0f4e29
New setup (#1413) 2026-05-06 12:03:06 -07:00
BilalG1
8901a93b55
Rename STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY to STACK_INTERNAL_PROJECT_SECRET_SERVER_KEY (#1415)
## Summary
- Renames the env var `STACK_SEED_INTERNAL_PROJECT_SECRET_SERVER_KEY` to
`STACK_INTERNAL_PROJECT_SECRET_SERVER_KEY` everywhere it is used (20
occurrences across 8 files), covering backend env files, the Prisma seed
script, runtime config, and the docker entrypoint/local-emulator
scripts.
- Mirrors the prior publishable-client-key rename in #1411.

## Test plan
- [x] `pnpm lint`
- [x] `pnpm typecheck`
- [ ] Verify local emulator still boots with the renamed variable
- [ ] Verify any deploy/CI configs that set the old name are updated
alongside this change

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated internal environment variable naming for API key management
and server configuration consistency across backend systems, Docker
deployment, and local development setup.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-06 10:35:08 -07:00
BilalG1
24245ae54e
Rename STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY to STACK_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY (#1411)
## Summary
- Renames the env var
`STACK_SEED_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY` to
`STACK_INTERNAL_PROJECT_PUBLISHABLE_CLIENT_KEY` everywhere it is used
(24 occurrences across 9 files), covering backend env files, the Prisma
seed script, runtime config, and the docker entrypoint/local-emulator
scripts.

## Test plan
- [x] `pnpm lint`
- [x] `pnpm typecheck`
- [ ] Verify local emulator still boots with the renamed variable
- [ ] Verify any deploy/CI configs that set the old name are updated
alongside this change

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated internal environment variable naming for consistency across
backend configuration files and deployment scripts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-06 09:10:45 -07:00
Aman Ganapathy
c01c052ac9
[Refactor][Feat] Implement Plan Limits for Hard-and-Soft Item Caps (#1215)
### Suggested Review Areas
Please see `plans.ts` and `seed.ts` to verify whether the item caps are
where they should be. Outside of that, each commit should be atomic so
stepping through the commits should give you an idea of how I
implemented each limit.

### Discussion
Something to discuss: when a user cancels team/growth we regrant free
fine, but any extra-seats they had just keeps billing. So they end up
paying ~$29/mo per extra-seat on top of free's 1 seat, which is strictly
worse than just staying on team. This surfaced while manually testing
this PR, we only enforce the add-on base requirement at purchase time,
nothing cascades on cancel. Should we cascade cancel add ons?

### Context
Now that we have a stable suite of products for stack-auth, we want to
limit the items under each product a customer has access to based on
their plan. So for example, a free plan user has a certain amount of
emails they can send out each month, and so on. We try to implement
limits in this PR.

### Summary of Changes
Implemented hard limits for dashboard admins, analytics per-query
timeouts, sent email monthly capacity, events, and session replays.
Implemented a soft cap for auth users (where if there's a signup beyond
the limit, we log it to sentry so we can manually choose to email that
user/team).

For auth users, we do not block new user sign ups once plan limit has
been hit. We also don't degrade or impact the customer experience. It
logs to sentry and it is up to us to take manual action to email the
user to upgrade the plan. Also, implementation wise, we count all the
users across all the projects for this team and compare it to their plan
item limit, rather than debiting items like we do for other approaches.
As a soft cap, this should be fine plus this is a better source of
truth.

For email capacity, we operate a monthly limit of emails. Once this is
hit, no more emails can be sent until the next month/ a plan upgrade.
These emails will be treated as a send error, so they can be manually
resent once the capacity is reset. With respect to the `email-queue`
state engine, they go from `SENDING`->`SERVER_ERROR`, hooking into the
existing state engine flow, with an external error that shows it's
because of the rate limit. This is cleaner than inventing a new state
that is identical for all intents and purposes to `SERVER_ERROR`. We
check in processSingleEmail since that maps to the sending state.

For analytics query timeouts, the backend route accepts a timeout
parameter with the request. The way we implement the timeout for each
query is by taking the `min(request_timeout,plan_timeout)` and using
that. This determines how long a query can run for.

For analytics events, there are server-side events (like refresh token
refreshes or sign up rule triggers) and client side events (like page
views or clicks). When these events occur, they are written to the
events table in clickhouse. We choose to implement a hard cap for the
total events, not just server side or client side. Once the cap is hit,
we stop storing the events and display a banner on the analytics page. A
different banner renders when we are at >=80% of total plan capacity.

For session replays, we stop creating new session replays when the limit
is hit. Old replays can still have chunks appended to them. The source
of truth here is the session replay table- a new replay corresponds to a
new row in the table. We have similar banners as to the events.

Dashboard admins should be 4 for both team and unlimited.

#### Implementation Caveats

For debiting items across these limits, we now use `tryDecreaseQuantity`
at the beginning. This means we debit first if possible before
conducting the action (like writing events to clickhouse). In practice,
this means that if clickhouse fails, then the user is debited for
something that doesn't happen. However trying to build a refund
workaround would be very clunky, and also, clickhouse is reliable. For
debits that are very small in the order of things (say, 200 items on a
100k plan), it doesn't mean much.

For emails, we don't debit items if it's a retry. This prevents the user
for being charged multiple times for effectively one email.


### UI Changes
The only UI changes in this PR are having certain banners render in
analytics when a customer is approaching/ is at their monthly limit of
session replays or events.


### Out of Scope for this PR
We do not have metered pricing yet, so events/session replays/ email use
beyond the limits cannot be charged yet. This is why for this
implementation, we rely on hard and soft caps.
We do not implement payment per-transaction pricing yet. That is
deferred to a followup PR.
The UI for the onboarding call will be set up as part of the overall
onboarding flow which doesn't exist yet, so it has been deferred.
Since the UI for the dashboard home page and project/account settings is
currently being reworked, finding a better spot for plan upgrades is not
handled in this PR.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Session replays added as a monthly included entitlement; onboarding
calls added to Team/Growth plans. Dashboard banners warn about
analytics-event and session-replay limits. Projects page adds extra-seat
flow and improved invitation error handling.

* **Behavior Changes**
* Monthly renewal semantics for emails-per-month and analytics-events;
analytics query timeouts now respect plan limits and are clamped. Email
sends, analytics events, and new session creation are blocked when
quotas are exhausted. Growth plan seats set to 4.

* **Tests**
* E2E and unit tests added to verify quota enforcement and free-plan
regranting.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
2026-05-04 18:25:13 -07:00
Konstantin Wohlwend
1594ed94d5 Speed up seed script by a lot 2026-04-18 17:29:21 -07:00
Aman Ganapathy
847d14df70
[Fix]: Assortment of Bugs with Timefold Table and Payments (#1348) 2026-04-18 14:17:24 -07:00
Aman Ganapathy
1de8a17183
Payments bulldozer txn rework (#1315)
### Object of this PR
This PR is NOT a monolithic series of fixes for the payments suite + a
complete rework. Its aims were
a) introducing and robustly testing the bulldozer db system 
b) reworking the payments underlying architecture to use bulldozer for
correctness and scalability
c) Achieving parity with the old payments system excepting a few changes
like ensuring correctness of the ledger algo
There may still be some work to do with handling refunds, decoupling the
concepts of purchases from that of products, and some other things.

### Ledger Algorithm
This has been tuned and fixed. Item removals i.e negative item quantity
changes will apply to the soonest expiring item grant i.e positive item
quantity change. This is what is best for the user. Item grants can also
expire, and when they expire we obviate whatever is left of their
original capacity (meaning after all the removals that were applied to
it). Our ledger algo is applied via Bulldozer, so automatic
re-computation is handled when a new grant/ removal is inserted in the
middle of the existing ones.

### Things we got rid of 
* No more automatic support for default products. You can use $0 plan
provisions to accomplish the same effect but it's manual
* Negative item quantity changes (i.e item removals) no longer can have
expiries



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Enhanced payment processing pipeline with improved data consistency
and state management.
  * Advanced refund handling with comprehensive transaction tracking.
* Better tracking and management of customer item quantities and owned
products.
* Improved subscription lifecycle management including period-end
handling.

* **Bug Fixes**
  * Fixed payment data integrity verification.
  * Improved handling of edge cases in refund scenarios.

* **Chores**
  * Updated cSpell configuration with additional words.
  * Expanded developer documentation for linting workflows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
Co-authored-by: Aadesh Kheria <kheriaaadesh@gmail.com>
Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
2026-04-17 22:11:21 +00:00
Armaan Jain
654c97c56e
Onboarding redo (#1308) 2026-04-15 09:35:48 -07:00
BilalG1
88d3317b22
local emulator security and features fixes (#1247)
<!--

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 -->
2026-04-14 15:36:24 -07:00
Mantra
7f8e3df852
feat: add anonRefreshToken to CLI auth flow and enhance session management (#1303)
- Extended `CliAuthAttempt` with `anonRefreshToken` and a migration.
- CLI `POST /auth/cli` accepts optional `anon_refresh_token` (must be an
anonymous user's refresh token for the current project).
- `POST /auth/cli/complete` supports `mode` `check` (anonymous vs none),
`claim-anon-session` (issue tokens for the linked anonymous session),
and `complete` (bind the browser session's refresh token to the
attempt). Completing clears `anonRefreshToken` on the row. We do **not**
merge anonymous account data into the signed-in user (that behavior was
removed as a security risk; the anonymous user remains unchanged).
- Template CLI confirmation page, stack-cli optional
`STACK_CLI_ANON_REFRESH_TOKEN`, SDK/spec updates, and e2e coverage.

<!--

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**
* CLI login supports attaching anonymous sessions and a multi-mode
confirm/claim/check flow; CLI tools now surface login codes and remove
anon token after use.
  * Added interactive CLI auth demo page and a CLI simulator script.
* Client libraries: prompt flow accepts an optional anon token and a
promptLink(url, loginCode) callback.

* **Tests**
* Expanded end-to-end coverage for anonymous CLI sessions,
claim/complete/poll flows, upgrades, and error cases.

* **Documentation**
* Updated prompt CLI docs/spec to describe new options and callback
signature.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-14 02:09:35 +00:00
aadesh18
8aa80ceb2c
AI in Stack Companion (#1297)
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>
2026-04-13 18:40:32 +00:00
Mantra
328fd0252f
rework weights for same name signups (#1298)
- **update submodule**
- **Enhance sign-up risk assessment by adding sameEmailCount and
sameEmailLimit to recent stats request. Update loadRecentSignUpStats
function to include email normalization checks. Adjust tests to reflect
new return structure.**

<!--

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**
* Risk scoring now tracks and reports counts of recent signups that
share a normalized email (with configurable limit), exposing this as
part of signup-risk statistics.

* **Performance**
* Added a database index and migration to speed up recent-signup
queries, improving risk assessment responsiveness.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-12 23:30:55 +00:00
BilalG1
4f99c469fe
stack auth preview mode (#1307)
<!--

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**
* Preview mode: sandboxed experience with mock projects, placeholder
data, and disabled external integrations (payments, webhooks, email
rendering, session replays).
* One-click preview project creation and automatic preview sign-in for
quick access.

* **New Features — Walkthrough**
* Interactive guided walkthroughs with spotlight, animated cursor,
step-driven navigation, and targeted element hooks.

* **Style**
* UI/UX adjustments for preview: theme behavior, conditional
banners/alerts, informational alerts, and walkthrough attributes added
across pages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-08 16:57:42 -07:00
BilalG1
8857dbaa48
clickhouse new syncs and verify-data (#1304)
<!--

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 -->
2026-04-08 14:43:22 -07:00
Mantra
ce49eae155
emu with a q stuff (#1266)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
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
DB migration compat / No migration changes (skipped) (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 -->
2026-04-04 00:33:52 +00:00
Konsti Wohlwend
5bfe1a79ce
New { type: "hosted" } for page URLs (#1261)
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 -->
2026-03-27 14:48:01 -07:00
Konstantin Wohlwend
c062ae62d2 Make migration backwards-compatible 2026-03-25 16:57:13 -07:00
Mantra
d2ed9f8244
move current timestamp assignment above the not null check (#1286) 2026-03-24 10:59:05 -07:00
Mantra
736c1a19b1
make signed up at default to now (#1284) 2026-03-23 15:02:43 -07:00
Mantra
d22593d535
private files n sm build shit (#1276)
- Introduced a fallback mechanism for the private sign-up risk engine,
allowing for zero-score assessments when the primary engine is
unavailable.
- Updated Next.js configuration to support dynamic resolution of the
private risk engine, including aliasing for both Turbopack and Webpack.
- Added a new fallback implementation in
`private-sign-up-risk-engine-fallback.ts` to ensure consistent behavior
during builds.
- Adjusted `risk-scores.tsx` to utilize the new compiled engine,
improving error handling and logging for risk assessment failures.

This update improves the robustness of the sign-up risk scoring system
and enhances the development experience by streamlining engine
resolution.

<!--

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

* **Improvements**
* Sign-up risk engine is initialized and validated at startup for more
predictable performance.
* If the risk engine is unavailable or invalid, the system immediately
returns safe zero-risk scores to avoid runtime failures.
* **Tests**
* End-to-end tests updated to match the new engine initialization and
detection behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
2026-03-23 12:31:36 -07:00
Mantra
e59a70783e
Turnstile integration for fraud protection (#1239)
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>
2026-03-20 21:26:45 +00:00
Armaan Jain
b5d5e5cea6
Add onboarding status to Project model and implement related database… (#1246) 2026-03-13 12:00:40 -07:00
Aman Ganapathy
485fa9d623
[Refactor][Feat][Fix] Rework Email Section With New Sent Page, Better Drafts Page, and Settings Page (#1221)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
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
DB migration compat / No migration changes (skipped) (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>
2026-03-11 12:01:36 -07:00
BilalG1
66adb4e50f
Local emulator base (#1233)
<!--

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**
* Provision local-emulator projects from a local config file and return
emulator credentials via a new internal endpoint.
* Dashboard: "Open config file" flow to open local projects and refresh
owned projects.

* **Changes**
* Branch config can prefer/read/write local files for emulator projects.
* Environment config updates/resets are blocked for local-emulator
projects.
* Dashboard UI shows read-only notices and disables project creation in
emulator mode.
* Added DB mapping and a standard env flag to identify local-emulator
projects.

* **Tests**
  * New E2E tests covering provisioning and config restrictions.

* **Chores**
* Removed legacy emulator docs and compose; added CI workflow for
local-emulator E2E runs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
2026-03-10 15:15:06 -07:00
BilalG1
b701fdfb0a
Managed email provider (#1222)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
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
DB migration compat / No migration changes (skipped) (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 -->
2026-03-09 20:23:11 -07:00
Aman Ganapathy
e9886bc45a
[Fix] [Refactor] Implement Base Settings for Stack-Auth Plans and Move Metadata from Stripe Webhook Event to Table (#1214)
### 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.
2026-02-23 22:09:27 -08:00
Konsti Wohlwend
8052a2be62
"Require publishable client key" toggle (#1158)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
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
DB migration compat / No migration changes (skipped) (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 -->
2026-02-19 10:23:16 -08:00
Konstantin Wohlwend
08c3447477 Migration tests 2026-02-17 15:58:06 -08:00
BilalG1
fa27c80319
rename tabId to sessionReplaySegmentId (#1206)
<!--

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 -->
2026-02-17 11:00:07 -08:00
BilalG1
c7ef526bb4
session replays (#1187)
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 -->
2026-02-16 14:15:17 -08:00
Aman Ganapathy
fa360aba9c
[Refactor] Change Retry Logic in Email Sending (#1191)
### 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.
2026-02-16 21:07:16 +00:00
Konstantin Wohlwend
b255c5edda Increase trusted domain migration batch size 2026-02-16 12:22:17 -08:00
Konstantin Wohlwend
c7e8cde2ec Rename migration 2026-02-16 12:15:02 -08:00
Konsti Wohlwend
d319285403
Queries view (#1145) 2026-02-16 11:39:21 -08:00
Bilal Godil
4c6a89f774 qstash dedup key 2026-02-13 14:47:27 -08:00
Konstantin Wohlwend
d914d7f3ec Split DB migration into two files
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migrations are backwards-compatible / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E API Tests with external source of truth / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
Mirror main branch to main-mirror-for-wdb / lint_and_build (push) Has been cancelled
Publish npm packages / publish (push) Has been cancelled
Sync Main to Dev / sync-commits (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
DB migrations are backwards-compatible / No migration changes (skipped) (push) Has been cancelled
2026-02-09 11:25:50 -08:00
Konstantin Wohlwend
f2f44086d8 Merge existing DB sync migrations 2026-02-09 11:20:17 -08:00