mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-04 21:04:37 +08:00
fcbb2492c4
1506 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
29cea48beb
|
Remote dev envs (#1435) | ||
|
|
d68631ea4f | Update GitHub URL | ||
|
|
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 -->
[](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 -->
|
||
|
|
049c557a06 | --config-file is now a file, not a folder | ||
|
|
9102b3db75
|
[Feat] Hexclave AI integration: skill, MCP SKILL.md route, docs (#1434)
## Summary - Adds a `hexclave` SKILL.md pointer skill that fetches the live skill body on every invocation - Adds an `/SKILL.md` route on the MCP app that renders the full skill (CLI usage + docs sidebar generated from `docs.json`) - Expands `docs-mintlify/guides/getting-started/ai-integration.mdx` with three install paths (CLI, Skill, MCP) and per-agent config snippets - Updates `packages/stack-shared/src/helpers/init-prompt.ts` to install both the MCP server and skill file, with per-project vs global scope detection ## Test plan - [ ] `pnpm typecheck` - [ ] `pnpm lint` - [ ] Hit the MCP app's `/SKILL.md` endpoint locally and verify it returns valid markdown with the full docs sidebar - [ ] Render the updated `ai-integration.mdx` in Mintlify preview and confirm tabs/cards render <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Rewrote the AI integration guide with complete, user-facing instructions for connecting Stack Auth to coding agents; removed the separate MCP setup page and updated site navigation. * Added the canonical Stack Auth skill content and guidance that clients should fetch the latest skill at runtime. * **New Features** * MCP now serves the canonical Stack Auth skill dynamically and provides interactive skill responses. * Init prompts now include full MCP + skill install workflows and scope guidance. * Added a health-check endpoint. * **Chores** * Added scaffold and configs for a new skills app (build, dev, lint, and type settings). <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1434?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5cb9240bc3
|
refactor(dashboard): unify AI chat surfaces on assistant-ui Thread (#1427)
## Summary - Replace the bespoke `ai-chat-shared` chat UI (used by ask-ai, the stack companion widget, vibe coding chat, and the create-dashboard preview) with the shared `assistant-ui` `Thread` component. - Extract streaming request/format helpers into a new `components/assistant-ui/chat-stream.ts` module so each surface only owns its `ChatModelAdapter`. - Add a reusable `ToolFallback` for tool-call rendering and delete the now-unused `ai-chat-shared.tsx` (-1386 / +747 lines net). Stacked on top of `refactor/data-grid-and-dashboard-surfaces`. Base: `refactor/data-grid-and-dashboard-surfaces` → Head: `refactor/assistant-ui-chat-surfaces` · 18 files changed > Red outlines on the **after** shots mark the unified `assistant-ui` `Thread` surface in each location. ## Screenshots ### Analytics → Tables — AI Query dialog | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-after-dark.png" width="480" /> | ### Stack Companion — chat widget | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-after-dark.png" width="480" /> | ### Ask-AI command palette (⌘K → Ask AI) | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-after-dark.png" width="480" /> | ### Email editor — embedded chat panel | | Before | After | |---|---|---| | **Light** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-before-light.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-after-light.png" width="480" /> | | **Dark** | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-before-dark.png" width="480" /> | <img src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-after-dark.png" width="480" /> | ## Notes for reviewers The four surfaces above all previously shared `components/commands/ai-chat-shared.tsx` (516 lines, deleted). After this PR they each own a thin `ChatModelAdapter` and render through `components/assistant-ui/thread.tsx` + the new `chat-stream.ts` helpers. Visual differences between **before** and **after** are intentional — the `assistant-ui` `Thread` brings its own message bubbles, scroll-to-bottom behaviour, composer, and `ToolFallback` rendering. The email editor's chat panel is the surface where the behaviour change is most visible (tool-call rendering now consistent with the rest of the app). Heaviest changes (lines): - `components/stack-companion/ai-chat-widget.tsx` (571) - `components/commands/ai-chat-shared.tsx` (516, deleted) - `analytics/tables/ai-query-dialog.tsx` (429) - `components/vibe-coding/chat-adapters.ts` (400) - `components/assistant-ui/chat-stream.ts` (284, new) - `components/commands/ask-ai.tsx` (274) - `components/assistant-ui/thread.tsx` (115) - `components/assistant-ui/tool-fallback.tsx` (113) ## Test plan - [ ] `pnpm lint` - [ ] `pnpm typecheck` - [ ] Manually exercise each affected surface: command-center Ask AI, stack-companion widget, vibe-coding chat, analytics tables AI query, create-dashboard preview, email editor chat. - [ ] Verify tool-call chips render consistently across all four surfaces (uses the new `ToolFallback`). - [ ] Verify streaming + cancel works on each adapter (`chat-stream.ts` is shared). |
||
|
|
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 | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### User detail — new session-replays card + weekly metrics | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Session replays — moved out of `/analytics` | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Project permissions — new pagination | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Other migrated surfaces | Page | Light | Dark | | --- | --- | --- | | Project picker |  |  | | Overview / setup |  |  | | Teams list |  |  | | Team permissions |  |  | | API keys |  |  | ### Scroll behaviour — new data-grid on the users list | Light | Dark | | --- | --- | |  |  | ## 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 --> |
||
|
|
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> |
||
|
|
15faf709f3
|
stack-cli: explicit --cloud-project-id / --config-file across exec, config, project (#1422)
## Summary Reworks the `stack` CLI surface so the cloud-vs-local choice is **explicit at every invocation**, removing the global `--project-id` / `STACK_PROJECT_ID` env var and the local-default `exec` behavior introduced earlier in this branch. ### `stack exec` - Removes `--cloud`, `STACK_EXEC_DEFAULT_TARGET`, and the implicit local default. The CLI now requires **exactly one** of: - `--cloud-project-id <id>` — run against the Stack Auth cloud API - `--config-file <path>` — run against the local emulator project mapped to that absolute config-file path - The `--config-file` branch resolves the project id by calling the existing `GET /api/latest/internal/local-emulator/project` endpoint and matching `absolute_file_path` client-side. No new backend endpoint introduced. ### `stack config pull` / `stack config push` - Both now take `--cloud-project-id <id>` per-command instead of the global flag / `STACK_PROJECT_ID` env. - `config pull --config-file` is **optional**: when omitted, the CLI uses `./stack.config.ts` from the current directory. If neither flag nor cwd file is present, it exits with a clear hint to pass `--config-file` or `cd` into a directory containing `stack.config.ts`. ### `stack project list` - Default (no flags) lists both **cloud and local emulator** projects. Each entry carries a `target: "cloud" | "dev"` field (text format: `<id>\t<displayName>\t[<target>]`). - `--cloud` / `--dev` filter to a single source (mutually exclusive — passing both errors). - On the default code path, an unreachable local emulator emits a single stderr warning (`warning: skipping dev projects — local emulator not reachable …`) and the command still succeeds with cloud results. With `--dev` explicit, the unreachable case hard-errors. ### `stack project create` - Now requires `--cloud` to make the cloud-vs-local choice explicit. There is no local alternative today; the flag exists to surface the decision so a future local-project create doesn't silently change behavior. ### Backend - Bumps the `LIMIT` on `GET /api/latest/internal/local-emulator/project` from 20 → 100 so `project list --dev` doesn't silently truncate. ### Refactors (from earlier in this branch, unchanged here) - Local-emulator paths/ports/PCK polling live in `packages/stack-cli/src/lib/emulator-paths.ts`. - Shared local-emulator admin credentials live in `packages/stack-shared/src/local-emulator.ts`. - `resolveAuth` / `resolveLocalEmulatorAuth` take an explicit `projectId: string` (no more `Flags` parameter). - New `packages/stack-cli/src/lib/local-emulator-client.ts` encapsulates the GET-and-match flow used by both `exec --config-file` and `project list --dev`. ## Breaking changes **Scripts that relied on any of the following must be updated:** | Removed | Replacement | | --- | --- | | Global `--project-id <id>` flag | Per-command `--cloud-project-id <id>` | | `STACK_PROJECT_ID` env var | Per-command `--cloud-project-id <id>` | | `stack exec --cloud` | `stack exec --cloud-project-id <id>` | | `STACK_EXEC_DEFAULT_TARGET=cloud\|local` | `--cloud-project-id <id>` or `--config-file <path>` | | `stack exec` defaulting to local emulator | Explicit `--config-file <path>` required | | `stack project create` without a flag | `stack project create --cloud …` required | ## Test plan - [x] `pnpm lint` (stack-cli, backend, e2e) — clean - [x] `pnpm --filter @stackframe/stack-cli typecheck` — clean - [x] `pnpm --filter @stackframe/stack-cli exec vitest run` — **72/72 passing** (new unit tests: `parseExecTarget`, `resolveConfigFilePathForPull`, `resolveProjectListSources`, `formatProjectList`) - [x] `pnpm test run apps/e2e/tests/general/cli.test.ts` — **73 passing, 4 skipped, 0 failing**. New e2e cases cover: - `exec` with neither flag → errors with "Specify a target" - `exec` with both flags → errors with "not both" - `exec --config-file` with missing file / missing PCK / unreachable API - `exec --config-file` happy path against a real local-emulator backend (gated on `NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR=true`) - `config pull` cwd fallback to `./stack.config.ts` - `config pull` with no `--config-file` and no cwd `stack.config.ts` → errors with `Pass --config-file …` - `project list --cloud --dev` together → errors - `project list` default with unreachable emulator → cloud results + single stderr warning - `project create` without `--cloud` → errors - All previously-`--cloud` exec cases ported to `--cloud-project-id` - [x] Manual smoke: `stack exec --help`, `stack project list --cloud --dev`, `stack project create` all emit the expected friendly errors / help text. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * CLI `exec`, `config`, and `project` commands now require explicit targeting via `--cloud-project-id` (cloud) or `--config-file` (local emulator). * `project list` now supports `--cloud` and `--dev` flags to display projects from both sources with target indicators. * Enhanced environment variable validation for emulator service ports with proper fallback handling. * **Bug Fixes** * `project list` now gracefully handles unreachable emulator with warning fallback instead of failure. * **Tests** * Expanded test coverage for project targeting, config file resolution, and emulator connectivity scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
748d708d53
|
[Docs Mintlify] - Updates and new additions (#1401)
## Summary Refreshes the docs around Stack Auth setup, CLI workflows, local development, the local emulator, known SDK errors, self-hosting, and the public showcase. This also wires the new docs into Mintlify navigation and normalizes `sharp` dependency resolution for docs/image tooling. Base: `dev` -> Head: `docs-mintlify/updates` Scope: 17 files, +1154 / -435 ## What's New - Adds a dedicated **Stack CLI** guide covering install, auth, init modes, project commands, config pull/push, `stack exec`, and emulator commands. - Adds a full **Local Emulator** guide for QEMU requirements, ports, default credentials, config-file backed projects, image pulls, state, and troubleshooting. - Reworks **Local Development** around two supported workflows: cloud-backed local dev and emulator-backed local dev, including app env vars, local config files, CI usage, and common failure modes. - Rewrites **Self-host** around the supported `stackauth/server` Docker deployment path, including Postgres, ClickHouse, cron scheduling, seeded admin access, reverse proxy setup, SDK env vars, email, webhooks, S3 storage, upgrades, and common issues. - Adds a **Known Errors** reference for public SDK-exposed known errors, runtime `errorCode` values, and REST API handling. - Clarifies **CLI App Authentication** so users can distinguish authenticating their own CLI app from using the official `stack` command. - Updates the JWT guide to remove the missing inline viewer reference and recommend an external JWT viewer. - Adds showcase cards for Browser Use and Overworld with supporting images and styles. - Pins `sharp` to `0.34.5` through pnpm overrides and lockfile cleanup. ## Review Notes - The self-host guide was audited against the current Docker entrypoint, server env templates, seed script, ClickHouse migration behavior, cron endpoints, and SDK API URL env resolution. - The Docker image starts the backend and dashboard, but not production schedulers, so the new cron section is called out explicitly. - Managed Domain email setup is documented as operator-managed because it depends on server-side Resend/DNSimple credentials; self-hosters are directed toward Custom SMTP or their own Resend API key. - `self-host-old.mdx` is kept as a legacy reference file and is not added to navigation. - `emulator run` documentation now matches CLI behavior: it stops the emulator only when it started that emulator instance. ## Test Plan - [x] Reviewed all files changed by `origin/dev...HEAD`. - [x] Ran `git diff --check origin/dev...HEAD`. - [x] Checked IDE diagnostics for the changed docs/CLI files. - [ ] Preview Mintlify docs locally and click through new navigation entries. - [ ] Verify showcase cards and images in light and dark themes. - [ ] Smoke-test the copied self-host commands in a non-production Docker environment. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added comprehensive Stack CLI, Local Emulator, Known Errors, and Local Development guides * Restructured Self-Hosting guide for production deployments and expanded authentication docs * Updated site navigation to include new guide pages * **New Features** * Added visual showcase section with responsive cards and hover/zoom interactions (and supporting styles) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
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> |
||
|
|
e0c1cc5376
|
Fix null-unsafe payments config validation for partial overrides (#1363)
## Summary - Make the `branchPaymentsSchema` custom validator tolerant of partial override objects - Avoid crashing when `payments.products` or `payments.productLines` are absent during validation - Add regression tests for partial configs plus the existing missing-line and customer-type mismatch cases ## Testing - Added Vitest coverage for partial payments configs and validation failures - Lint passed for the touched schema files - Typecheck passed for `packages/stack-shared` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved validation robustness with stricter type-safety checks for payment-related data configurations. * Enhanced error messages for clearer feedback on validation failures. * **Tests** * Added comprehensive test coverage for edge cases including missing configurations and type mismatches. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e880df121d
|
Dev tool redesign (#1409) | ||
|
|
80a26ca15d | chore: update package versions | ||
|
|
68ae6d1f1c
|
[codex] Add TanStack Start SDK integration (#1399)
## Summary - Adds the generated `@stackframe/tanstack-start` workspace package registration. - Adds TanStack Start platform macros/dependencies to the SDK template and generator. - Adds TanStack Start cookie/token-store support plus the handler SSR guard needed by Start. ## Scope This intentionally excludes Dashboard V2 routes, hooks, components, app shell logic, and dashboard API type additions. Those stay in the existing dashboard PR/branch. ## Validation - `pnpm install --lockfile-only --ignore-scripts` - `pnpm install --ignore-scripts` - `pnpm -C packages/template lint src/components-page/stack-handler-client.tsx src/lib/cookie.ts src/lib/stack-app/apps/implementations/client-app-impl.ts` Package typecheck was attempted with `pnpm -C packages/template typecheck`, but the clean worktree lacks generated package declaration outputs for workspace dependencies such as `@stackframe/stack-shared` and `@stackframe/stack-ui`. Per repo instructions, package builds/codegen are not run by agents. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * TanStack Start integration: published SDK package, example demo app, dashboard onboarding flow, framework-aware CTAs/docs, and a TanStack-specific provider for client-only auth routes. * Improved client/server auth: safer runtime guards and consistent cookie/token-store behavior across SSR and client. * **Documentation** * New Integrations guide and expanded getting-started/setup docs with TanStack Start examples and env/key guidance. * **Chores** * Template, build, tooling, and demo config updates to support the new platform. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
acc646cb0b
|
stack-cli: cloud/local init flow, auto-create on empty projects, post-setup next-steps (#1383)
### Summary Reworks `stack init` UX, adds Sentry error reporting to the CLI, polishes the emulator start flow, and overhauls the local-emulator dashboard's "Open config file" dialog. #### `stack init` flow - **New top-level flow.** Drops the old "link existing vs. create new local" fork. `init` now asks *where* to create the project — "Stack Auth Cloud" or "Local". Adds a new `create-cloud` mode that logs the user in, creates a cloud project, mints keys, and writes `.env` — no round-trip through the dashboard. - **Conditional emulator-install warning.** The "Local" choice label only shows "(requires local emulator installation, ~1.3gb storage required)" when the QEMU image isn't already on disk; otherwise it shows "(emulator already installed)". Driven by a new `isEmulatorImageInstalled()` helper in `commands/emulator.ts`. - **Auto-create on zero-projects.** When the link-from-cloud path hits an empty project list, the CLI now prompts *"You don't have any Stack Auth projects yet. Would you like to create one?"* and, on yes, runs the same flow as `stack project create`. Skips the pointless "select a project" prompt when we just created one. - **MCP-server notice.** Before invoking the coding agent, the CLI announces that it's also registering the Stack Auth MCP server (`mcp.stack-auth.com`) so the agent can answer Stack-specific questions going forward. - **Local-emulator env header.** When `writeProjectKeysToEnv` runs in `local` mode it writes a 3-line comment header above the keys explaining they're emulator-only and only valid while the emulator is running. - **"What's next" footer.** After setup finishes, prints a short orientation block: where the sign-up/sign-in routes live (`/handler/sign-up`, `/handler/sign-in`), how to start the local emulator (for `create` mode), a dashboard deep link for cloud projects (respects `STACK_DASHBOARD_URL`), and a docs link. #### Sentry error reporting (`lib/sentry.ts`, `index.ts`, `tsdown.config.ts`) - New `lib/sentry.ts` initializes `@sentry/node` with PII scrubbing (Stack key prefixes, JWTs, home-dir paths, sensitive field names like `token`/`secret`/`password`/`dsn`). - DSN is baked at build time via a tsdown `define` sentinel (`__STACK_CLI_SENTRY_DSN__`) — no DSN in source, no runtime env-var dependency for installed users. CI sets `STACK_CLI_SENTRY_DSN_BUILD` before `pnpm build`. - Disabled when `NODE_ENV=development` or `CI`. No user opt-out. - Wired into `main()`'s catch (only for unexpected errors — `CliError`/`AuthError` still print and exit cleanly) plus `uncaughtException` and `unhandledRejection` handlers via a `handleFatal` helper. #### `stack emulator start` welcome - After a fresh start (not when reusing a running VM, not when `--config-file` keeps stdout JSON-only), prints a short "Emulator is up" block with service URLs (dashboard / backend / inbucket) and common commands (`status`, `stop`, `reset`, `run`). #### Local-emulator dashboard "Open config file" dialog The dialog at `http://localhost:26700` (when no project is loaded) used to be a single text input asking for an absolute path, with no explanation of where that path comes from. **Backend** (`apps/backend/src/app/api/latest/internal/local-emulator/project/route.tsx`): - POST is now tolerant of directory paths or paths that don't end in `.ts`/`.js`/`.mjs` — it appends `stack.config.ts` and creates the file if missing (`writeConfigToFile` mkdir's parents). Lets users paste a project folder instead of hunting for the config file. - New GET endpoint returns up to 20 most-recent `LocalEmulatorProject` rows joined with their display names, sorted by `updatedAt` desc. Same `isLocalEmulatorEnabled()` + client-auth gating as POST. **Dashboard** (`apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx`): - Title changed to "Open your Stack Auth project". Description now explicitly ties the file to `stack init`: *"Point the local dashboard at the `stack.config.ts` in your project. If you just ran `stack init`, it was created at the root of that project."* - Added: *"Don't have one yet? Paste your project folder path instead and we'll create stack.config.ts for you."* - Recent-projects list (clickable rows that prefill the input) fetched from the new GET endpoint when the dialog opens. - OS-specific copy-path tip below the input (macOS ⌥-Copy as Pathname, Windows Shift+RC Copy as path, Linux `realpath`). - "Open project" button is disabled when the input is empty. - All error paths (empty input, non-absolute path, server errors, exceptions) surface via destructive toasts instead of throwing. Why no native file picker: browsers do not expose absolute filesystem paths from `<input type="file">`, drag-and-drop, or the File System Access API. The backend requires an absolute path, so a Finder-style picker isn't possible from a web page. The recent list + OS tips are the workaround. ### Goal The previous `init` flow dead-ended new users: if you had no project you got an error telling you to go create one in the dashboard and come back. The happy path also forced a choice between "link existing" and "create local emulator" — not the question most users are trying to answer. The emulator dashboard's open-project dialog had similar friction: an unexplained path field with no recall of previously-opened projects. And the CLI silently swallowed unexpected errors with no telemetry. This branch makes the first-run path work end-to-end from the terminal, gives the emulator dashboard a usable open-project surface, and turns CLI crashes into actionable bug reports. ### How to review - Start with `packages/stack-cli/src/commands/init.ts` — the whole user-facing flow lives in `runInit`. Mode dispatch at the top, `handleCreateCloud` is the new cloud branch, `printNextSteps` is the footer, the MCP notice prints right before `runClaudeAgent`. - `packages/stack-cli/src/lib/sentry.ts` is small and self-contained; the sentinel-replacement contract is in `tsdown.config.ts`'s `define` block. Confirm `dist/index.js` contains zero `__STACK_CLI_SENTRY_DSN__` occurrences after a build with the env var unset, and the actual DSN host after a build with it set. - `packages/stack-cli/src/commands/emulator.ts` — `printEmulatorWelcome()` is the welcome block; `isEmulatorImageInstalled()` is the new exported helper used by `init.ts`. - `apps/backend/src/app/api/latest/internal/local-emulator/project/route.tsx` — the directory-tolerance branch is in the POST handler around the `looksLikeConfigFile` check; the GET handler is appended at the bottom. - `apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/projects/page-client.tsx` — dialog markup, recent-list fetch effect, `pathCopyTip` memo, and the toast-based error handling in `handleOpenConfigFile`. - Non-interactive (CI) paths stay strict: empty-project list still errors with a pointer to `stack project create --display-name`. No surprise project creation in CI. - No tests. The CLI has no harness for the interactive flow; verification is manual. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Recent local emulator projects listed in the config dialog for quick selection. * New CLI create-cloud mode and --display-name flag; interactive cloud project creation and clearer next steps. * Emulator start shows a welcome banner with service URLs when a new instance starts. * **Improvements** * Config dialog UX, validation, error-toasting, and platform-aware copy refined; “Open project” disabled for empty/invalid paths. * CLI: centralized interactive project creation and improved fatal error handling. * **Chores** * Sentry added and initialized for CLI error reporting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Bilal Godil <bg2002@gmail.com> |
||
|
|
6eaf49237f
|
Add fix command registration and update agent UI label handling (#1387)
Adds a fix command to the stack cli <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a CLI "fix" command to submit Stack Auth errors (flag, stdin, or interactive), confirm before applying changes, show a customizable progress label, and produce a final markdown report with Error, Files changed, and Solution. * Added a CLI "doctor" command to analyze projects (framework override, output directory, JSON output), run framework-specific checks, validate env and config, and exit non-zero on failures. * **Tests** * Added comprehensive end-to-end tests for the doctor command. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
647883c7ac
|
Move MCP server into a standalone apps/mcp app (#1405)
## Summary Splits the Stack Auth MCP server out of `apps/backend` and into a dedicated Next.js app at `apps/mcp/`, served on port `:42` (suffixed via `NEXT_PUBLIC_STACK_PORT_PREFIX`) and exposed in production at `https://mcp.stack-auth.com/mcp`. The backend no longer carries the MCP transport route; clients now point at the new host. Base: `dev` → Head: `chore/move-mcp-to-a-sep-app` Scope: 34 files, +1425 / −353 ## What changed - **New app** `apps/mcp/` — standalone Next.js + `@vercel/mcp-adapter`, with: - `src/app/api/internal/[transport]/route.ts` — MCP transport handler (moved from backend) - `src/app/mcp/route.ts`, `src/app/route.ts` — public landing + setup page - `src/app/health/route.ts` — health check - `src/mcp-handler.ts`, `src/setup-page.ts`, `src/analytics.ts` - **Backend** drops `apps/backend/src/app/api/internal/[transport]/route.ts` (−105) — MCP code is gone from the backend image. - **Dashboard** install hint updated to point at `https://mcp.stack-auth.com/mcp` (was `/`). - **Dev launchpad** gets an MCP tile so the new service shows up alongside the rest of the local stack. - **CI** workflows (`db-migration-backwards-compatibility`, `e2e-api-tests*`) start the MCP service in the background before running tests. - **Docs** (`docs-mintlify`, `docs/`) and `init-stack` / `init-prompt` updated to reference the new URL. - **E2E** `apps/e2e/tests/backend/endpoints/api/v1/internal/mcp.test.ts` reworked to hit the new host; `helpers.ts` and env files gain an MCP base-URL var. ## Visuals ### New `apps/mcp` setup page (`https://mcp.stack-auth.com/`) The standalone app's root now serves a self-contained MCP setup guide with per-client instructions (Cursor, VS Code, Codex, Claude Code, Claude Desktop, Windsurf, ChatGPT, Gemini CLI):  ### Dev launchpad now lists the MCP service New tile at port suffix `:42`, importance 2, alongside Backend / Dashboard / Demo app:  ## Notes for reviewers - The MCP transport endpoint moved path: it was mounted under `/api/internal/[transport]` in the backend; in the new app it's at the same path but on the dedicated host. The public-facing URL is `https://mcp.stack-auth.com/mcp`. - `apps/mcp` ships its own PostHog analytics client (`src/analytics.ts`) so the backend doesn't have to proxy events for it anymore. - Port allocation: `${PORT_PREFIX}42` (default `8142` in dev). Picked to fit the existing dev-launchpad importance-2 row. - No DB migrations. ## Test plan - [x] `apps/mcp` builds and `pnpm dev` serves on `:8142` - [x] Dev launchpad renders the new MCP tile (screenshot above) - [x] MCP setup page renders client tabs (screenshot above) - [x] E2E `mcp.test.ts` updated to hit the new host - [ ] CI green on `e2e-api-tests*` and `db-migration-backwards-compatibility` workflows (they were touched to start the MCP service) - [ ] `init-stack` / `mcp.ts` install flow lands users on the new URL <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Standalone MCP app added with a public /mcp endpoint and health check. * MCP appears in the dev-launchpad apps list. * **Documentation** * MCP endpoint updated to https://mcp.stack-auth.com/mcp in all setup guides and installer snippets. * Setup page enhanced with detailed client install tabs and instructions. * **Chores** * MCP service integrated into CI/e2e workflows and local env configs. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7acbd8d56d | Improved StackAssertionError error logging | ||
|
|
d69773c9df | Retry OAuth refreshes | ||
|
|
5ccd8dfd38 | Update GitHub URL | ||
|
|
602cf4be82 | Fix CI/CD | ||
|
|
bd8c4489ed | Fix dev server on clean repo | ||
|
|
765b0f4e29
|
New setup (#1413) | ||
|
|
440c18c894 | chore: update package versions | ||
|
|
2e41fde9c2 | _useSession now refreshes tokens more aggressively | ||
|
|
775a3be8cc
|
small component ui fix (#1414)
<!-- 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 * **Refactor** * Enhanced CLI authentication confirmation tracking to improve session persistence and state management during sign-in flows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b0812c8808
|
feat(analytics): gzip event batch body to bypass adblockers (#1407)
## Summary
The `POST /api/latest/analytics/events/batch` endpoint was being dropped
by content-blocking browser extensions (adblockers) because the JSON
request body literally contains the substring `$click`. Many filter
lists pattern-match on tokens like that and silently kill the request —
analytics events from anyone with an adblocker enabled never reached our
backend.
This PR encodes the request body so keyword-matching filters can't see
those tokens, while keeping the URL path unchanged (only the body was
being matched here) and keeping older SDK clients working.
## Approach
- **Client**: gzip the JSON payload via the browser-native
`CompressionStream("gzip")` API and POST it as
`application/octet-stream`. Falls back to plain JSON if
`CompressionStream` isn't available (very old browsers / non-browser
runtimes).
- **Server**: a yup `.transform()` on the body schema detects an
`ArrayBuffer`/`Uint8Array` input, gunzips it, and `JSON.parse`s before
normal schema validation runs. The existing JSON path is untouched, so
requests from older SDK versions in the wild continue to work without
changes — and all existing schema-error snapshot tests still pass
verbatim.
- **Safety**: hard caps on compressed (1 MB) and decompressed (8 MB)
sizes guard against zip-bomb shaped abuse. `node:zlib`'s
`maxOutputLength` enforces the latter at the C++ layer.
Bonus: gzip also gives a meaningful bandwidth win — click/page-view
events compress very well — and keepalive bodies (which have a 64 KB cap
in browsers) get more headroom.
## Files
- `apps/backend/src/app/api/latest/analytics/events/batch/route.tsx` —
body schema gains `.transform()` that gunzips binary inputs; size limits
added; everything else unchanged.
- `packages/stack-shared/src/interface/client-interface.ts` —
`sendAnalyticsEventBatch` now routes through a new module-level
`encodeAnalyticsBody` helper that gzips and switches Content-Type. Same
outer signature; encoding is internal.
- `apps/e2e/tests/backend/backend-helpers.ts` — `niceBackendFetch` gains
optional `rawBody`/`rawContentType` params so tests can send non-JSON
payloads. Existing JSON callers unaffected.
-
`apps/e2e/tests/backend/endpoints/api/v1/analytics-events-batch.test.ts`
— adds two tests:
- happy path: gzipped binary body returns `inserted: 1`
- sad path: garbage bytes return 400
## Out of scope (intentional)
- **URL path renaming**: not all adblockers match on `/analytics/`, but
some do. We're shipping the body fix first and will revisit if requests
still get blocked after deployment.
- **Encryption**: gzip is enough to defeat keyword filters. Encryption
adds key-management cost with no real adversary.
- **SDK regen**: only `client-interface.ts` (in `stack-shared`) was
touched; `event-tracker.ts` (the caller) is unchanged because it already
passes a JSON string. No `pnpm -w run generate-sdks` needed.
## Test plan
- [x] `pnpm typecheck` — green
- [x] `pnpm lint` — green
- [ ] Manually verify in dev: enable adblocker, click around with
analytics enabled, confirm batch requests now go through
- [ ] Spot-check ClickHouse `analytics_internal.events` shows the
expected rows
- [ ] Run the new e2e tests (`pnpm test run
apps/e2e/tests/backend/endpoints/api/v1/analytics-events-batch.test.ts`)
and confirm both new cases plus all preexisting snapshots pass
- [ ] Confirm the JSON back-compat path still works by hitting the route
with the existing JSON-body curl/test payloads
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Analytics batch uploads now accept gzipped binary payloads; clients
can send compressed bytes and the server will detect and decompress.
* Client sender can gzip event batches (falls back to JSON) and uses
keepalive to choose JSON vs compressed bytes.
* **Bug Fixes**
* Malformed, non-gzip, or overly-large compressed payloads now return a
clear 400 response.
* **Tests**
* Added E2E and unit tests plus test-helper support for raw/gzipped
request bodies and encoding behaviors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
185bddec9e
|
[Dashboard] Redefine the user page with tabs and updated UI (#1351)
<!-- 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** * Tabbed user profile with Activity (30-day analytics, KPIs, daily chart, top lists, recent events), Payments (transactions, subscriptions, product/item balances) and an activity heatmap sidebar. * New internal user-activity API and admin-facing activity hook; admin API client can fetch per-user activity. * **UI/UX Improvements** * Unified menus, cards and tables; inline editable user details with accept/revert; metadata editor validates JSON; country-code input has draft editing; tabs support optional icons. * **API** * Transactions endpoint and admin transaction queries now support optional customer-scoped filtering. * **Tests** * End-to-end coverage for the user-activity endpoint. <!-- end of auto-generated comment: release notes by coderabbit.ai --> <img width="1326" height="752" alt="image" src="https://github.com/user-attachments/assets/97c04dca-db59-4357-98b1-8eae5a7a3673" /> <img width="1142" height="251" alt="image" src="https://github.com/user-attachments/assets/e1aa44fc-0d7e-436d-90a5-c7cb15155e24" /> <img width="1170" height="1125" alt="image" src="https://github.com/user-attachments/assets/bf6659fd-a9b5-4ae6-a13d-dab9956ad650" /> |
||
|
|
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> |
||
|
|
c69a27017b
|
fix team invitation email check + verification code TOCTOU (#1365)
## Summary Two authorization fixes in the backend. Both are pre-existing in `dev` and were found during a security audit of `apps/backend/src`. ### 1. Team invitation accept — email not validated [`team-invitations/accept/verification-code-handler.tsx`](https://github.com/stack-auth/stack-auth/blob/dev/apps/backend/src/app/api/latest/team-invitations/accept/verification-code-handler.tsx) destructured the invited email as `{}` and only used `data.team_id` + the accepting `user`. Any signed-in user in the tenancy who possessed the 45-char code could join the team as themselves — the invitation was not actually bound to the email it was addressed to. **Attack scenarios that work without this fix** - Forwarded invitation email (shared inbox, assistant inbox, auto-forward rules). - Screenshot of the invitation link pasted into Slack / Notion. - Insider with server-access reading the email outbox (`GET /api/latest/emails/outbox` returns rendered `html` + `variables.teamInvitationLink`). - Stale invite still sitting in spam after the invitee forwarded it elsewhere. **Fix.** The accept handler now requires that the accepting user owns the invited email as a *verified* contact channel on their account. Matches the invariant already used by the "list invitations for me" endpoint ([`team-invitations/crud.tsx:41-66`](https://github.com/stack-auth/stack-auth/blob/dev/apps/backend/src/app/api/latest/team-invitations/crud.tsx#L41-L66)). Rejections return a new `TEAM_INVITATION_EMAIL_MISMATCH` (403) error. ### 2. Verification-code handler TOCTOU [`route-handlers/verification-code-handler.tsx`](https://github.com/stack-auth/stack-auth/blob/dev/apps/backend/src/route-handlers/verification-code-handler.tsx) had a classic read-then-write TOCTOU: ```ts const verificationCode = await prisma.verificationCode.findUnique(...); if (verificationCode.usedAt) throw new KnownErrors.VerificationCodeAlreadyUsed(); // ... validation ... await prisma.verificationCode.update({ data: { usedAt: new Date() } }); // unconditional return await options.handler(...); ``` Five concurrent requests with the same code all pass the `if (usedAt)` gate, all mark the code used, all run the post-handler. For OTP sign-in the handler calls `createAuthTokens` which writes a fresh `projectUserRefreshToken` row per call — so **one OTP → N refresh tokens**. `auth/sessions/current` only revokes by `id: refreshTokenId` and there is no bulk-revoke for passwordless users (only password change in [`users/crud.tsx:1210`](https://github.com/stack-auth/stack-auth/blob/dev/apps/backend/src/app/api/latest/users/crud.tsx#L1210) does `deleteMany`). A phished OTP therefore becomes a session-persistence primitive. **Fix.** Replace the unconditional `update` with a conditional `updateMany({ where: { …, usedAt: null } })` executed before `options.handler`; if `count === 0` the race was already lost and we throw `VERIFICATION_CODE_ALREADY_USED` (409). This also benefits MFA sign-in and passkey sign-in, which share the same handler. ## Changes | File | Change | |---|---| | `team-invitations/accept/verification-code-handler.tsx` | Require verified contact channel matching `method.email` | | `route-handlers/verification-code-handler.tsx` | Atomic `updateMany` claim gated on `usedAt: null` | | `stack-shared/src/known-errors.tsx` | New `TeamInvitationEmailMismatch` (403) | | `e2e/.../team-invitations.test.ts` | Two new tests (mismatch + happy path) | | `e2e/.../auth/otp/sign-in.test.ts` | One new test: 5 parallel redemptions of one OTP → 1× 200 + 4× 409 | ## Test plan - [x] `pnpm test run apps/e2e/tests/backend/endpoints/api/v1/team-invitations.test.ts` — 27/27 pass - [x] `pnpm test run apps/e2e/tests/backend/endpoints/api/v1/auth/otp/sign-in.test.ts` — 12/12 (+ 4 pre-existing `it.todo`) - [x] `pnpm test run apps/e2e/tests/backend/endpoints/api/v1/auth/password` — 33/33 (+ 7 pre-existing todos) - [x] `pnpm test run apps/e2e/tests/backend/endpoints/api/v1/contact-channels` — 24/24 - [x] `pnpm test run apps/e2e/tests/backend/endpoints/api/v1/auth/passkey apps/e2e/tests/backend/endpoints/api/v1/auth/mfa` — 16/16 - [x] `pnpm --filter @stackframe/backend typecheck` — clean - [x] `pnpm --filter @stackframe/backend lint` + `pnpm --filter @stackframe/stack-shared lint` — clean ## Notes - The broader "plaintext credentials in DB + Sentry logs every header" finding from the same audit is **not** in this PR — a scrubber for `Sentry.setContext` request headers + unit tests is prepared on a local stash and will go out as a separate PR. - The team-invitation fix does not require any config change; fresh signups via the OTP / password flows that set `primary_email_verified: true` during creation already land the user with a verified channel matching the invited email, so the happy path is unaffected. ### Follow-up review (Codex) Addressed in follow-up commit `954cddb`: - **Finding 1 (High)**: mismatched invite acceptance was consuming the invitation before rejecting. Moved the email-ownership check into the pre-claim `options.validate` hook so a wrong-email attempt leaves `usedAt` untouched and the real recipient can still redeem. New test asserts this end-to-end. - **Finding 3 (Medium)**: invitation stored `body.email` raw but contact channels are stored via `normalizeEmail`, so case-varied invites (e.g. `Alice@Example.com`) wouldn't match a `alice@example.com` channel. `send-code` now normalizes on storage and `accept` normalizes on compare for back-compat with already-issued invites. New test covers the mixed-case path. - **Finding 2 (partial)**: added `expiresAt > now` to the atomic claim predicate for the boundary case where a code expires between the read and the claim. The reviewer's broader point about the `attemptCount` rate-limit check being non-atomic with its own increment **pre-dates this PR** (it reads the in-memory `verificationCode.attemptCount` from line 150, not a fresh read) and exists independently of the `usedAt` TOCTOU I'm fixing here. Tracking that as a separate follow-up so this PR stays scoped to the two originally-flagged issues. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Invite acceptance now requires the invitee’s verified, normalized (case‑insensitive) email; mismatches return HTTP 403 (TEAM_INVITATION_EMAIL_MISMATCH). * Client APIs now surface the new email-mismatch error alongside verification errors. * **Bug Fixes** * OTP verification codes are now guarded against parallel double‑redeem so only one request succeeds. * **Tests** * Added E2E tests for invitation email validation, non‑consuming rejection, case‑insensitive matching, and OTP concurrency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
0ab2654051 | chore: update package versions | ||
|
|
e2dc5f5ee0
|
[codex] fix OAuth redirect contract (#1393)
## Summary - Route browser OAuth redirects through the configured `redirectMethod` instead of hardcoded `window.location` calls. - Keep OAuth redirect APIs pending after navigation starts, including custom redirect methods. - Add `cliAuthConfirm` handler URL metadata and custom-page prompt coverage. - Update SDK spec text for browser OAuth callback and `returnTo` behavior. ## Root Cause OAuth helpers previously combined URL construction with direct browser navigation. That bypassed configured redirect methods and made it too easy for public redirect APIs to resolve after navigation started. ## Impact Browser SDK consumers get consistent redirect behavior across built-in and custom navigation methods. `returnTo` is handled as the post-callback destination while the OAuth callback URL remains fixed to the configured handler route. ## Validation - `pnpm test run packages/template/src/lib/auth.test.ts` - `pnpm test run apps/e2e/tests/js/oauth.test.ts` - `pnpm -C packages/template lint` - `pnpm -C apps/e2e lint` - `pnpm -C packages/template typecheck` - `pnpm -C apps/e2e typecheck` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added CLI authorization confirmation page/flow for terminal-based auth. * Added optional returnTo parameter for OAuth to control post-auth redirects. * Exposed configurable redirect behavior so apps follow the chosen redirect method. * **Bug Fixes** * OAuth callback now uses app navigation/queued redirects and shows a fallback link instead of forcing location.assign. * **Tests** * Added unit and e2e tests covering OAuth URL generation, scope handling, and CLI auth confirmation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5e5cfdec4f
|
[Dashboard][Backend][SDK] - Adds sharable session replay ids. (#1294)
# Shareable Session Replay Links Adds the ability to share individual session replays via unique, direct URLs. https://www.loom.com/share/1e3298a19b114fc38af4bc43dcd5ec48 ## What changed - New admin endpoint — GET /api/v1/internal/session-replays/:id - Fetches a single session replay by ID with user metadata (display name, primary email) and chunk/event counts - Returns 404 if the replay doesn't exist - Admin-only access, consistent with the existing list endpoint ## New standalone replay page — /projects/:projectId/analytics/replays/:replayId - Thin server page wrapper that passes the replay ID to the existing PageClient - PageClient detects standalone mode via initialReplayId prop and fetches replay metadata directly instead of loading the full session list - Sidebar is hidden; the replay viewer takes the full width - "Back to all replays" link shown under the page title ## Copy link button - Moved from per-session sidebar items to the replay viewer header (next to the settings gear) - Copies a direct URL to the currently selected replay ## SDK plumbing - AdminGetSessionReplayResponse type in stack-shared - getSessionReplay() on StackAdminInterface, StackAdminApp interface, and _StackAdminAppImplIncomplete ## Tests - Happy path: fetch single replay by ID with inline snapshot - 404 for nonexistent replay ID - 401 for non-admin access (client and server) ## Test plan - [ ] Open /analytics/replays, select a replay, click the link icon in the header — verify URL is copied to clipboard - [ ] Paste that URL in a new tab — verify the standalone replay page loads and plays the correct replay - [ ] Verify "Back to all replays" link navigates back to the list page - [ ] Verify the original /analytics/replays list page still works as before (selecting, filtering, pagination) - [ ] Run pnpm test run session-replays <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Backend: internal endpoint to fetch a single session replay with user info, millisecond timestamps, and chunk/event counts. * Admin SDK/App: added response type and admin method to retrieve a single session replay; admin app maps response into the app model. * Dashboard: standalone session-replay page, UI adjustments for standalone mode, and a “copy replay link” button. * **Tests** * Added end-to-end tests for retrieval, not-found, and access-control scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9d1eee8ab8
|
Add useCliAuthConfirmation hook and customizable cliAuthConfirm URL target (#1388)
## Summary - Extract CLI auth confirmation into a `useCliAuthConfirmation()` hook (status / error / isLoading / authorize / retry) so custom pages don't have to reimplement the protocol; `CliAuthConfirmation` now consumes the hook. - Make `cliAuthConfirm` a first-class handler URL target — resolved via `resolveHandlerUrls`, customizable per project, and used by `promptCliLogin` through a new `buildCliAuthConfirmUrl()` helper. - Move `StackContext` to its own module so the hook can be unit-tested with a test double without tripping the client-version sentinel; register `cliAuthConfirm` in custom-page prompts and the dev-tool components tab; export the hook + types from `@stackframe/stack`. ## Test plan - [ ] `pnpm typecheck` - [ ] `pnpm lint` - [ ] `pnpm --filter @stackframe/stack test cli-auth-confirm url-targets` - [ ] Manually verify default `/handler/cli-auth-confirm` flow + a project with a custom `cliAuthConfirm` URL <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Adds a CLI authentication confirmation page with clear states (invalid, authorizing, redirecting, success, error), retry action, and flows for signed-in and anonymous users. * CLI login URL generation now derives from the configured handler target and app base, improving reliability. * CLI confirmation page exposed in the components/dev UI for previewing. * **Tests** * End-to-end and unit tests covering confirmation behaviors and URL generation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b3d0ab66cc
|
fix(stack-shared): make process.env access browser-safe (#1391)
## Summary - Bare `process.env.X` accesses in `stack-shared` throw `ReferenceError: process is not defined` when the package is bundled into a browser app without a `process` shim (e.g. a plain Vite app). The most reachable offenders are in `StackAssertionError`'s constructor and `schema-fields.ts`'s Neon Basic-auth validator, both of which can run on the client during normal sign-in flows with `@stackframe/react`. - Extracted a zero-dependency `getProcessEnv` helper at `packages/stack-shared/src/utils/process-env.tsx` and routed the bare references through it. Returns `undefined` when `process` is not defined; otherwise behaves like a normal `process.env[name]` read, so Next.js/webpack inlining is unchanged on the server. - Touched: `schema-fields.ts:884` (`STACK_INTEGRATION_CLIENTS_CONFIG`), `utils/errors.tsx:81` (`NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR`), `utils/promises.tsx` (`NODE_ENV` in `runAsynchronouslyWithAlert`), `utils/esbuild.tsx:16` (`NODE_ENV`, also reordered the `typeof process` guard so the env access is unreachable in browsers). ## Why a separate helper module `utils/env.tsx` already exists but its `getEnvVariable` explicitly throws in the browser, so it can't be reused here. The new module has zero imports so it can be safely consumed from low-level utilities like `errors.tsx` without creating a cycle (env.tsx ↔ errors.tsx). ## Test plan - [x] `pnpm lint` passes - [x] `pnpm typecheck` passes - [ ] Reproduced the original failure in a Vite + `@stackframe/react` app: sign-in flow logged `ReferenceError: process is not defined` from `StackAssertionError`, plus `clientSecret must not be empty` cascading from the same path - [ ] Verify the same flow in a Vite app no longer throws once `@stackframe/react` is rebuilt against this `stack-shared` change - [ ] Confirm Next.js consumer behavior is unchanged (env vars still inlined at build time for `NEXT_PUBLIC_*`) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Improved environment variable handling across shared utilities for enhanced browser compatibility and safety. Introduced a new utility for dynamic, browser-safe environment variable access that prevents errors in non-Node.js environments. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
65d87a4836
|
Dashboard: DataGrid refactor + layout (stacked on overview-revamp) (#1338)
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 `overview-revamp` (now rebased against `dev`). Introduces a first-class `DataGrid` component in `@stackframe/dashboard-ui-components`, migrates every dashboard table off the legacy `DesignDataTable` / hand-rolled `<Table>` pattern to it, and ships a matching dashboard design guide. Since the last writeup the `DataGrid` runtime has been substantially rewritten: the virtualizer now supports `rowHeight="auto"` with `estimatedRowHeight`, every column can opt into `cellOverflow: "wrap"`, the toolbar + header stick under a configurable `stickyTop`, and the seeded dummy data has been fleshed out so the migrated surfaces render with realistic density. The AI-analytics prompt was also extended with full schema docs for the auth / team / email / payments tables so natural-language queries produce better SQL. **Base:** `dev` → **Head:** `ui-fixes-minor` **Scope:** 39 files, ~+6.5k / -2.4k ## Screenshots Captured against the seeded Demo Project on the local dashboard (`admin@example.com` via mock GitHub OAuth). Viewport: **1920×1200** (standard) and **2560×1440** (widescreen). Assets hosted in [this gist](https://gist.github.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9). ### Overview — revamped metrics + line chart | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Users — DataGrid with seeded rows | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Transactions — new DataGridToolbar + sticky chrome | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Teams | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Email Outbox | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Payments — Customers | Light | Dark | | --- | --- | |  |  | Widescreen: | Light | Dark | | --- | --- | |  |  | ### Sticky behaviour — scrolled views Grids scrolled down ~600px. The page header is still pinned, and the `DataGrid` toolbar + column header row stay put under it (backdrop-blur + `stickyTop` offset) while the virtualized body rows scroll past. Compare the scrolled view against the top-of-page view above. | Page | Light | Dark | | --- | --- | --- | | Users |  |  | | Teams |  |  | | Transactions |  |  | | Payments Customers |  |  | | Email Outbox |  |  | | Analytics Tables |  |  | ### Other migrated surfaces | Page | Light | Dark | | --- | --- | --- | | Analytics Tables |  |  | | Emails |  |  | | Email Sent |  |  | | Domains |  |  | | Webhooks |  |  | | External DB Sync |  |  | ## What's new ### `DataGrid` in `@stackframe/dashboard-ui-components` A new, fully-typed, fully-controlled grid component under `packages/dashboard-ui-components/src/components/data-grid/`. Single source of truth for tabular UI across the dashboard. Package files: - `data-grid.tsx` — main grid renderer (virtualized rows, sticky toolbar + header) - `data-grid-toolbar.tsx` — built-in toolbar (search, columns, density, export) - `data-grid-sizing.ts` — column width / flex / min-width resolution - `state.ts` — state helpers (`createDefaultDataGridState`, sort / select / paginate utilities, `exportToCsv`, date formatters) - `strings.ts` — i18n string table + `resolveDataGridStrings` - `types.ts` — public types (`DataGridColumnDef`, `DataGridProps`, `DataGridState`, `DataGridDataSource`, etc.) - `use-data-source.ts` — `useDataSource` hook with `client` / `server` / `infinite` modes - `index.ts` — package entrypoint Features: - Controlled state (`state` + `onChange`) covering sorting, pagination, column visibility, column widths, column pinning, selection, date-display mode, and quick search. - Column definitions with `string` / `number` / `date` / `dateTime` / `boolean` / `singleSelect` / `custom` types, custom `renderCell`, custom sort comparators, per-column `parseValue` / `dateFormat`, pinning, align, flex / min / max width. - **Cell overflow control** — new `cellOverflow: "truncate" | "wrap"` per column. `"wrap"` + `rowHeight="auto"` lets rows grow to fit multi-line content. - **Dynamic row heights** — `rowHeight` now accepts `"auto"` with an `estimatedRowHeight` hint for the virtualizer, eliminating scroll-position jank while rows are still being measured. - **Sticky chrome with `stickyTop`** — the toolbar and header stick under a caller-provided offset (matching the page header height) with a proper blur backdrop. See the _Sticky behaviour — scrolled views_ section above for the visual. - Client-side sort + quick-search + pagination via `useDataSource` — consumer never pre-sorts / paginates. - Server-side and async-generator data sources for streaming / cursor pagination. - Paginated and infinite-scroll UI modes. - CSV export + clipboard copy. - Row single / multi selection with shift-range anchor. - Row + cell click / double-click callbacks. - Pluggable toolbar / footer / empty / loading states and i18n strings. ### Dashboard design guide New `apps/dashboard/DESIGN-GUIDE.md`: prescriptive, AI-readable source of truth for dashboard UI. Documents when to use each `design-components` primitive, the `DataGrid` canonical pattern, color / typography / spacing / motion rules, route-specific guidance, and the migration priority. Now also documents the new `cellOverflow` and dynamic-`rowHeight` patterns, and marks `DesignDataTable` as deprecated in favor of `DataGrid` + `useDataSource` + `createDefaultDataGridState`. ### Overview page revamp `apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx` — line chart rewritten on top of the shared `AnalyticsChart` / `DonutChartDisplay` primitives, feeding the revamped Overview. ### Data-table migrations Every shared table under `apps/dashboard/src/components/data-table/` has been rewritten on top of `DataGrid`: - `api-key-table.tsx` - `payment-product-table.tsx` - `permission-table.tsx` - `team-member-search-table.tsx` - `team-member-table.tsx` - `team-search-table.tsx` - `team-table.tsx` - `transaction-table.tsx` — now also wires in `DataGridToolbar` with search / column visibility - `user-search-picker.tsx` - `user-table.tsx` — extracted `USER_TABLE_COLUMNS` for readability / reuse ### Page adoption Page-level tables migrated to `DataGrid` (or the new `useDataSource` + `createDefaultDataGridState` pattern): - `(overview)/line-chart.tsx` - `analytics/tables/query-data-grid.tsx` (now with sticky header) - `domains/page-client.tsx` - `email-drafts/[draftId]/page-client.tsx` - `email-outbox/page-client.tsx` (with `DataGridToolbar`) - `email-sent/page-client.tsx`, `grouped-email-table.tsx`, `sent-emails-view.tsx` - `emails/page-client.tsx` - `external-db-sync/page-client.tsx` - `payments/layout.tsx`, `payments/customers/page-client.tsx`, `payments/products/[productId]/page-client.tsx` - `users/[userId]/page-client.tsx` - `webhooks/page-client.tsx`, `webhooks/[endpointId]/page-client.tsx` - `design-language/page-client.tsx`, `design-language/realistic-demo/page-client.tsx` - `playground/page-client.tsx` ### Backend & supporting changes - `apps/backend/src/lib/ai/prompts.ts` — extends the AI-analytics prompt with detailed schema docs for `contact_channels`, `teams`, `team_member_profiles`, `team_permissions`, `team_invitations`, `email_outboxes`, `project_permissions`, `notification_preferences`, `refresh_tokens`, and `connected_accounts`, so natural-language queries have richer context to compile against. - `apps/backend/src/lib/seed-dummy-data.ts` — additional OAuth providers on seed users, improving dummy-data coverage for the migrated tables (visible on the Users grid). - `apps/dashboard/src/app/globals.css` — adds `--data-grid-sticky-top` token used to derive the grid's sticky offset under the page header. - `packages/template/src/dev-tool/dev-tool-core.ts` — persist the "closed" state when the user closes the dev-tool panel so it doesn't reopen on next load. ## Notes for reviewers - Rebased onto latest `dev`; conflict in `api-key-table.tsx` resolved by keeping the `DataGrid` implementation (consistent with the other migrated tables). - `DesignDataTable` is still in the codebase but marked deprecated in the design guide — new code must use `DataGrid`. - `DataGrid` is fully controlled: consumers must pass state + onChange, must feed `rows` from `useDataSource` (never raw arrays), and must define columns outside the component or via `useMemo`. The guide's §4.12 spells this out. - `rowHeight="auto"` is opt-in; the default fixed-height virtualization path is unchanged and remains the fast path for dense, single-line grids (users, transactions, etc.). - Screenshots are JPEG this round — the local capture tooling's PNG path was producing blank frames, so the new set is `.jpg` end-to-end. Same viewports, same seeded project. ## Test plan - [ ] `pnpm lint` passes - [ ] `pnpm typecheck` passes - [ ] Load the dashboard and verify every migrated surface renders, sorts, searches, paginates, and handles row-click navigation: - [ ] Overview (line chart + donut metrics) - [ ] Users list + user detail (teams, sessions, permissions, API keys) - [ ] Teams list + team detail (members, permissions) - [ ] Domains - [ ] Emails, email-sent, email-outbox, email-drafts - [ ] Webhooks list + endpoint detail - [ ] Payments customers, product detail, transactions (new toolbar) - [ ] External DB sync - [ ] Analytics query table (sticky header) - [ ] Verify infinite-scroll surfaces (domains, etc.) load additional rows on scroll - [ ] Verify sticky header stays below the page header in light and dark themes - [ ] Verify CSV export produces correct output on a representative table - [ ] Verify column resize, visibility toggle, and sort work across themes - [ ] Verify `cellOverflow: "wrap"` rows grow to fit when `rowHeight="auto"` and clip when `rowHeight` is numeric - [ ] Spot-check AI analytics queries against the new schema context (contact_channels, teams, email_outboxes, …) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Unified table components across dashboard with improved infinite pagination and quick search. * **Improvements** * Enhanced table performance with sticky headers and better row height handling. * Improved sorting, filtering, and data loading with consistent state management. * Better visual consistency across all data grids and table layouts. * **UI/Styling** * Refined table styling for better text truncation and content wrapping. * Optimized layout spacing and alignment across dashboard tables. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Developing-Gamer <maxcodes11110@gmail.com> Co-authored-by: Armaan Jain <84474476+Developing-Gamer@users.noreply.github.com> Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com> |
||
|
|
5423d774a2
|
feat(stack-cli): auto-install emulator deps on pull (#1384)
## Summary - `stack emulator pull` now preflights VM dependencies (QEMU binaries, socat/curl/nc/lsof/openssl/zstd, and aarch64 UEFI firmware on arm64) before downloading. - Missing deps are listed, then installed with user confirmation via `brew` on macOS (bootstrapping Homebrew itself if absent) or `sudo apt-get` on Linux. - Skipped when `--skip-snapshot` is passed, since that path never boots the VM. - `gh` / `GITHUB_TOKEN` are intentionally excluded from the auto-install set. ## Test plan - [ ] `node packages/stack-cli/dist/index.js emulator pull` on a machine with all deps present → no prompt, proceeds as before. - [ ] Unlink a dep (e.g. `brew unlink zstd`) and rerun → missing dep listed, decline prompt → exits with a clear error; accept prompt → brew install runs and pull continues. - [ ] `emulator pull --skip-snapshot` still bypasses the dep check. - [ ] Linux path: missing binaries trigger `sudo apt-get update && sudo apt-get install -y …`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Emulator pull now detects missing host dependencies and, on macOS/Linux with an interactive terminal and supported package tools, shows a proposed install plan, prompts for confirmation, and can auto-install required packages (including optional ARM64 firmware). Homebrew will be bootstrapped if absent. * Use --skip-snapshot to bypass the interactive dependency check and installation. * **Behavior** * In non-interactive or unsupported environments, the tool falls back to the prior preflight behavior instead of attempting installation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3b8667d5f8
|
cli add back init options (#1379)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a "create-cloud" mode to the CLI init flow. * New interactive project creation flow that can prompt for display name and select/create a team-backed project. * **Behavior Changes** * Init now resolves mode from flags, config, or interactive prompts; prompts to choose linking vs creating when inputs are missing. * Non-interactive runs now error when required inputs are absent; cloud linking offers auto-create in interactive mode. * **Refactor** * Centralized auth, project-creation, and env key writing for clearer, safer linking and creation flows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: aadesh18 <110230993+aadesh18@users.noreply.github.com> |
||
|
|
04d57d91ed
|
fix(emulator): move mock OAuth off 8114 to avoid pnpm dev conflict (#1385)
## Summary
- The emulator's mock OAuth server bound to `${PORT_PREFIX}14` (8114)
inside the VM and the host forwarded the same port, colliding with `pnpm
dev`'s mock-oauth-server on 8114.
- Moves the emulator's mock OAuth to `EMULATOR_MOCK_OAUTH_PORT` (default
`26704`, joining the existing `267xx` host port block) and binds the
VM-internal mock to the same port. Same port on both sides keeps the
OIDC issuer URL (`http://localhost:26704`) resolvable identically from
the browser and from the backend inside the VM.
- Plumbed via `runtime-config.iso` as
`STACK_EMULATOR_MOCK_OAUTH_HOST_PORT`, read by cloud-init into
`STACK_OAUTH_MOCK_URL` + new `STACK_OAUTH_MOCK_PORT`;
`mock-oauth-server` now prefers `STACK_OAUTH_MOCK_PORT` so `pnpm dev`
(which doesn't set it) stays on 8114.
## Files
- `docker/local-emulator/qemu/run-emulator.sh` — new
`EMULATOR_MOCK_OAUTH_PORT`, hostfwd/ensure_ports_free/runtime.env
updates
- `docker/local-emulator/qemu/cloud-init/emulator/user-data` — reads the
host port, sets `STACK_OAUTH_MOCK_URL` + `STACK_OAUTH_MOCK_PORT`
- `apps/mock-oauth-server/src/index.ts` — honors `STACK_OAUTH_MOCK_PORT`
- `packages/stack-cli/src/commands/emulator.ts` — default + runtime.env
entry
## Test plan
- [ ] `pnpm emulator:build` succeeds and new snapshot boots
- [ ] `stack emulator start` with `pnpm dev` running on 8114 — no port
collision
- [ ] OAuth sign-in via mock provider completes end-to-end in the
emulator
- [ ] `pnpm dev` mock OAuth unchanged (still 8114)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Release Notes
* **New Features**
* The mock OAuth server port is now configurable in the local emulator
with a sensible default, allowing custom port assignments via
environment variable.
* **Improvements**
* Updated port forwarding and environment variable handling to ensure
consistent mock OAuth endpoint configuration across host and guest
systems in the emulator.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
d1751a7634
|
fix(init-prompt): require StackProvider for all frameworks (#1374)
## Summary - The init prompt marked the `StackProvider` step as *React only* and placed it after the `StackHandler` step. Following it on a Next.js project produced a layout with no provider, so `StackHandler` crashed at runtime with `useStackApp must be used within a StackProvider`. - Make the provider step unconditional and move it ahead of the handler step so the dependency order matches the instruction order. Also quote the exact error message so the model won't skip it. ## Test plan - [ ] Run `npx @stackframe/stack-cli init` (or the web flow) against a fresh Next.js app and confirm `/handler/[...stack]` renders without the `useStackApp` error. - [ ] Re-run against a Vite/React app to confirm the reordered instructions still produce a working setup. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added explicit global MCP config file path guidance for several coding agents. * Documented required provider configuration across supported frameworks. * Clarified where to place provider wrappers in root app layouts (including Next.js app/layout). * Reordered setup steps to surface the required "Wrap your app in a Stack provider" step and updated step numbering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
cbd945e3a6
|
[codex] Fix Neon malformed Basic auth validation (#1381)
## What changed This fixes Sentry issue [STACK-BACKEND-1A3](https://stackframe-pw.sentry.io/issues/7436639623/?project=4507442898272256&query=is%3Aunresolved&referrer=issue-stream&seerDrawer=true). A request with this malformed header: ```http Authorization: Basic ``` used to crash the Neon auth validator with a `StackAssertionError`, which turned a bad client request into a 500. The fix makes `neonAuthorizationHeaderSchema` only validate Neon client credentials after the Basic auth header successfully decodes. If decoding fails, the Neon-specific validator returns `true` and lets `basicAuthorizationHeaderSchema` produce the intended 400 schema error: `Authorization header must be in the format "Basic <base64>"`. ## Reviewer walkthrough There are two checks chained together: 1. `basicAuthorizationHeaderSchema` checks that the header is structurally valid Basic auth. 2. `neonAuthorizationHeaderSchema` checks that the decoded `client_id:client_secret` matches a configured Neon client. Yup may still run the second check after the first one has failed, because route validation collects errors with `abortEarly: false`. The old code assumed the first check had already passed and called `throwErr(...)` when decoding returned `null`. This PR changes that path to return `true`, because the format error is already owned by the first check. ## Tests - `pnpm -C packages/stack-shared exec vitest run --maxWorkers=1 --minWorkers=1 src/schema-fields.ts` - `pnpm -C apps/e2e exec vitest run --maxWorkers=1 --minWorkers=1 tests/backend/endpoints/api/v1/integrations/neon/projects/transfer.test.ts -t "malformed"` - `pnpm -C packages/stack-shared lint` - `pnpm -C packages/stack-shared typecheck` - `pnpm -C apps/e2e lint` - `pnpm -C apps/e2e typecheck` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Enhanced authorization header validation in API endpoints with improved error handling, ensuring malformed credentials return clear, specific validation error messages. * **Tests** * Added comprehensive end-to-end test coverage for API request validation, including edge cases for authorization headers. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
37e70ca1ae | rotate stack cli publishable client key | ||
|
|
3ea8052d35 | chore: update package versions | ||
|
|
37ee5ec320
|
Fast-start local emulator via RAM snapshot + live secret rotation (#1340)
## Summary
`stack emulator start` now resumes a fully-warm VM snapshot instead of
cold-booting, bringing startup from 30–120s down to ~5–8s with
per-install secret rotation, or ~2.5s with rotation opt-out. The
snapshot is captured **locally on first `stack emulator pull`**, not
shipped from CI — QEMU migration state isn't portable across
accelerators (KVM/HVF/TCG) or `-cpu max` feature sets, so a CI-captured
snapshot couldn't resume reliably on arbitrary user hardware.
Also bundles a pile of CLI QoL fixes (progress bars, PR/run artifact
pulls, PR-build download, native-TS ISO writer replacing
`hdiutil`/`mkisofs`/`genisoimage` host dep, unit tests).
| Scenario | Before | After |
|---|---|---|
| Cold boot (no snapshot) | 30–120s | same, works as fallback |
| `stack emulator pull` (one-time, includes local snapshot capture) |
~30s download | ~30s download + ~1–3 min cold-boot capture |
| Snapshot resume, normal start | — | **~5–8s** |
| Snapshot resume, `EMULATOR_NO_ROTATION=1` | — | **~2.5s** |
Backend (`/health?db=1`) and dashboard (`/handler/sign-in`) return 200
on all paths. Two successive snapshot resumes produce different rotated
PCK/SSK/SAK/CRON_SECRET values per install.
## How it works
**Build (CI)** — `docker/local-emulator/qemu/build-image.sh`:
1. Cloud-init provisioning runs to completion (migrations, seed,
slim-image) producing `stack-emulator-<arch>.qcow2`.
2. Image is built with a topology compatible with later snapshot capture
(pinned SMP=4, phantom seed/bundle ISOs, STACKCFG runtime ISO mounted at
build time, qemu-guest-agent running, placeholder hex secrets baked in
under `STACK_EMULATOR_BUILD_SNAPSHOT=1`).
3. CI publishes **only the qcow2** — no `.savevm.zst` ships.
**Pull (user's machine)** —
`packages/stack-cli/src/commands/emulator.ts` + `run-emulator.sh
capture`:
1. `stack emulator pull` downloads the qcow2 with a progress bar (or
from a PR / workflow run via `--pr` / `--run`).
2. CLI invokes `run-emulator.sh capture`: cold-boots the qcow2 with a
matching device layout (phantom ISOs, fsdev, pcie-root-port, virtfs
detached — migration-incompatible), waits for backend+dashboard health,
then drives QMP: `stop` → set `mapped-ram` + `multifd` caps → `migrate
file:state.raw` → poll `query-migrate` → `quit`. Raw mapped-ram file is
zstd-compressed to `stack-emulator-<arch>.savevm.zst` in the images dir.
3. `--skip-snapshot` opts out (first `start` will then cold-boot).
**Runtime** — `run-emulator.sh start`:
1. Launch QEMU with `-incoming defer` when a `.savevm.zst` is present;
decompress on first use, keep the `.raw` cached for subsequent starts.
2. QMP: same `mapped-ram` + `multifd` caps → `migrate-incoming
file:<.raw>` → poll for `paused` → `cont`.
3. Generate fresh per-install secrets on the host; pipe them
base64-encoded through QGA `guest-exec input-data` →
`trigger-fast-rotate` in the guest → `docker exec -e … rotate-secrets`.
4. `rotate-secrets` in the container: validate keys (hex-only), targeted
`sed` on the placeholder PCK across built JS, `UPDATE ApiKeySet`,
`supervisorctl restart stack-app cron-jobs` (with
`stopasgroup`/`killasgroup` so the Node children actually die and
release their ports).
5. Poll backend+dashboard health; if anything fails, clean up and fall
back to cold boot transparently.
**Security model**: placeholder hex values are baked into the snapshot
(`00…ff` PCK, `00…ee` SSK, `00…dd` SAK, `00…cc` CRON_SECRET). They are
non-secret by construction. Real per-install secrets are generated at
each `emulator start` and never leave the host.
## CLI changes (`packages/stack-cli`)
- **`src/lib/iso.ts`** (new): native TypeScript ISO 9660 + Joliet
writer, replacing the host-side `hdiutil`/`mkisofs`/`genisoimage`
dependency for generating the STACKCFG runtime config disk. Unit tests
in `src/lib/iso.test.ts`.
- **`src/commands/emulator.ts`**:
- `pull`: streamed downloads with progress bar + ETA; `--pr <number>`
and `--run <id>` to pull from a PR build's CI artifacts (uses
`extract-zip` for the nested zip); `--skip-snapshot` to opt out of the
one-time local capture.
- `start` (existing, extended): auto-pulls AND auto-captures when no
image exists, so first-ever `start` is self-bootstrapping; emits
`STACK_EMULATOR_CLI_WROTE_ISO=1` so the shell helper skips its own ISO
regen (avoids the genisoimage host dep).
- `capture` (new, invoked by `pull` and the auto-pull path of `start`):
drives the local snapshot capture via `run-emulator.sh`.
- `status`, `stop`, `reset`, `list-releases`: preflight +
path-resolution tightening (`STACK_EMULATOR_HOME` → images/run dirs).
- Unit tests in `src/commands/emulator.test.ts`.
- **`EMULATOR_NO_ROTATION=1`** env var skips the post-resume rotation
(intended for tests/CI where the placeholder secrets are fine — comes
with a loud warning).
## CI (`.github/workflows/qemu-emulator-build.yaml`)
- Builds **QEMU 10.2.2 from source** (cached), because
`mapped-ram`/`multifd` migration capabilities aren't available in the
distro's QEMU. Enables KVM on ubicloud runners so amd64 boots at
hardware speed.
- amd64 + arm64 both build on the same amd64 matrix
(`ubicloud-standard-8`); arm64 runs under cross-arch TCG (provisioning
only — boot/verify smoke test is amd64-only).
- Verification now runs through the CLI: `emulator start` → `emulator
status` → `emulator stop` against the freshly-built qcow2 (via
`STACK_EMULATOR_HOME` pointing at the workspace, so the CLI doesn't
silently auto-pull a prior release).
- Packages **only** the qcow2. No `.savevm.zst` upload / publish.
- Release notes updated.
## Key files
**Shell / guest:**
- `docker/local-emulator/qemu/build-image.sh` — snapshot-compatible
device topology + STACKCFG runtime ISO at build time
- `docker/local-emulator/qemu/run-emulator.sh` — `start`, `capture`,
`stop`, `reset`, `status`; `-incoming defer`, `.raw` cache, QGA-driven
rotation, cold-boot fallback
- `docker/local-emulator/qemu/common.sh` (new) — shared `qmp_session` +
`capture_vm_state` (factored out so build-image.sh and run-emulator.sh
share the capture path)
- `docker/local-emulator/qemu/cloud-init/emulator/user-data` —
placeholder secrets in snapshot mode, `wait-for-stack-ready`,
`trigger-fast-rotate`, qemu-guest-agent enabled
- `docker/local-emulator/rotate-secrets.sh` (new) — in-container
rotation (sed + UPDATE + supervisorctl)
- `docker/local-emulator/supervisord.conf` — `stopasgroup`/`killasgroup`
on `stack-app` and `cron-jobs`
- `docker/local-emulator/entrypoint.sh` — only mint CRON_SECRET if unset
(placeholder supplied in snapshot mode via --env-file)
- `docker/local-emulator/Dockerfile` — ships `rotate-secrets` to
`/usr/local/bin`
- `docker/server/entrypoint.sh` — source
`/run/stack-auth/rotated-secrets.env`; skip full-tree sentinel scan on
warm restarts via marker
**CLI:**
- `packages/stack-cli/src/lib/iso.ts` (new) + `iso.test.ts` (new)
- `packages/stack-cli/src/commands/emulator.ts` + `emulator.test.ts`
(new)
- `packages/stack-cli/vitest.config.ts` (new)
**CI:**
- `.github/workflows/qemu-emulator-build.yaml`
## Test plan
- [x] `docker/local-emulator/qemu/build-image.sh {amd64,arm64}` produces
`stack-emulator-<arch>.qcow2` with snapshot-compatible topology
- [x] `stack emulator pull` downloads qcow2 with progress, then captures
locally (~1–3 min) and writes `stack-emulator-<arch>.savevm.zst` in the
images dir
- [x] `stack emulator pull --skip-snapshot` stops after download
- [x] `stack emulator pull --pr <n>` / `--run <id>` pull from PR /
workflow run artifacts
- [x] `stack emulator start` on a fresh dir auto-pulls **and**
auto-captures, then starts; subsequent starts fast-resume in ~5–8s;
backend + dashboard return 200
- [x] `EMULATOR_NO_ROTATION=1 stack emulator start` completes in ~2.5s;
backend + dashboard return 200 with warning printed
- [x] Two consecutive `emulator start` invocations produce different PCK
values in the internal `ApiKeySet` row
- [x] `stack emulator status` / `stop` / `reset` resolve paths from
`STACK_EMULATOR_HOME`
- [x] Verified end-to-end on arm64 macOS under HVF (capture ~50s,
fast-resume ~6.5s)
- [x] `pnpm lint` and `pnpm typecheck` pass; stack-cli unit tests (iso +
emulator) pass
- [ ] CI green on this PR (qemu-emulator-build matrix, smoke test)
- [ ] `gh release download emulator-<branch>-latest` contains only
`stack-emulator-<arch>.qcow2` once this PR merges and publish runs
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Snapshot fast-start/resume with optional warm-snapshot assets, runtime
ISO generation, and a cached QEMU build to speed emulator setup.
* CLI: streamed artifact downloads with progress, improved release/asset
handling, stronger preflight checks, and start/status/stop emulator
commands.
* Automated secret rotation and ability to apply rotated secrets at
container startup; supervisor control socket enabled.
* **Bug Fixes**
* More robust start/stop/resume flows with automatic fallback to cold
boot and improved process-group shutdown behavior.
* **Tests**
* New tests for CLI utilities and ISO image generation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
0621ad2032
|
ai proxy fix (#1343)
<!-- 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 * **Refactor** * Request sanitization now includes an extra proxy-specific preprocessing step for safer AI proxying. * **New Features** * Initialization prompts centralized into a shared helper, with a web-specific prompt variant. * Authenticated requests can optionally route via a provided external API key to access alternate models. * **Chores** * Added and exposed a preprocessing hook with a default no-op implementation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
91fbf63f7f | chore: update package versions | ||
|
|
f4ca6cb4c7 | More tracing for replication-related functions | ||
|
|
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> |
||
|
|
94dd22c1c5
|
Overview revamp (#1238) |