create-nextblock 0.17.0 → 0.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ NextBlock CMS is an AI-Native, Open-Core Content Management System purpose-built
10
10
 
11
11
  The system's central value proposition — "Speed. Scalability. AI-Readiness (coming soon)." — is encoded directly in its architectural decisions: a Next.js 16 App Router application (`apps/nextblock`) backed by Supabase, leveraging React Server Components, edge caching, and an image-optimization pipeline targeting a default 100/100 Lighthouse Performance score. Users bootstrap new projects in under thirty seconds via the `npm create nextblock@latest` CLI, which produces a standalone, production-ready Next.js application.
12
12
 
13
- A live sandbox instance at `https://cms.nextblock.dev/` (accessible with demo credentials `demo@nextblock.dev` / `password`) resets daily via a cron-triggered endpoint (`/api/cron/reset-sandbox`) to provide evaluators with a clean-state environment.
13
+ A live sandbox instance at `https://cms.nextblock.dev/` (accessible with demo credentials `demo@nextblock.dev` / `password`) resets every 15 minutes via a cron-triggered endpoint (`/api/cron/reset-sandbox`) to provide evaluators with a clean-state environment.
14
14
 
15
15
  ### 1.1.2 Core Business Problem
16
16
 
@@ -27,7 +27,7 @@ The core problem statement — "stop cloning heavy repos; start with our CLI and
27
27
 
28
28
  ### 1.1.3 Key Stakeholders and Users
29
29
 
30
- The system recognizes three database-enforced user roles (defined as the `user_role` enum `ADMIN`, `WRITER`, `USER` in migration `00000000000000_setup_foundation_and_enums.sql`) and three additional implicit stakeholder classes observable from the scaffolding and documentation artifacts.
30
+ The system recognizes three database-enforced user roles (defined as the `user_role` enum `ADMIN`, `WRITER`, `USER` in migration `02001_baseline_schema.sql`) and three additional implicit stakeholder classes observable from the scaffolding and documentation artifacts.
31
31
 
32
32
  | Stakeholder | Role / Audience | Primary Entry Point |
33
33
  |:--|:--|:--|
@@ -247,7 +247,7 @@ The following architectural invariants are enforced at workspace level and must
247
247
  | Media Optimization | Blur placeholders generated for uploaded images | `sharp` + `plaiceholder` pipeline |
248
248
  | Developer Adoption | CLI install success rate | `apps/create-nextblock/bin/create-nextblock.js` |
249
249
  | Commerce Conversion | Checkout success by provider (Stripe / Freemius) | `app/api/checkout/route.ts` + webhooks |
250
- | Scheduled Job Health | Daily success of reset-sandbox and sync-currencies | `vercel.json` cron configuration |
250
+ | Scheduled Job Health | Success of reset-sandbox (every 15 min) and sync-currencies (daily) | `vercel.json` cron configuration |
251
251
  | Bundle Discipline | Removed console calls in production | `compiler.removeConsole` in `next.config.js` |
252
252
 
253
253
  ---
@@ -267,7 +267,7 @@ The following architectural invariants are enforced at workspace level and must
267
267
 
268
268
  **Editor Capabilities** — The `@nextblock-cms/editor` library exports `Editor`, `NotionEditor`, `EditorToolbar`, `EditorBubbleMenu`, `EditorFloatingMenu`, `EnhancedFloatingMenu`, `SlashCommandList`, `DragHandle`, `HtmlContent`, and `editorExtensions`. Feature set includes Tiptap StarterKit rich text, syntax-highlighted code blocks, tables, task lists, slash commands, drag handles, image handling, character counting, typography, mathematics, emoji, mentions, inline alert and call-to-action widgets, and custom HTML-preserving extensions for `div`, `style`, `script`, `svg`, `span`, and catch-all attribute preservation. A media-picker bridge is exposed via `setOpenImagePicker()`.
269
269
 
270
- **Translation & Localization** — The set of served locales is the active rows of the `languages` table (managed at `/cms/settings/languages`; the proxy reads them with a 60-second in-memory cache and only falls back to the hardcoded `FALLBACK_LOCALES` `en`/`fr` when the DB is unreadable), backed by `languages` and `translations` tables from migration `00000000000001_setup_cms_core.sql`. First-visit language detection is admin-configurable (see F-007): browser `Accept-Language`, IP-country via host geo headers, combined, or always-default — implemented in `apps/nextblock/lib/i18n/detection.ts` and stored in `site_settings.language_detection_settings`. Content revision history is stored as snapshot + JSON Patch diff (enum `revision_type: snapshot, diff`) per migration `00000000000002_setup_content_tables.sql`.
270
+ **Translation & Localization** — The set of served locales is the active rows of the `languages` table (managed at `/cms/settings/languages`; the proxy reads them with a 60-second in-memory cache and only falls back to the hardcoded `FALLBACK_LOCALES` `en`/`fr` when the DB is unreadable), backed by `languages` and `translations` tables from migration `02001_baseline_schema.sql`. First-visit language detection is admin-configurable (see F-007): browser `Accept-Language`, IP-country via host geo headers, combined, or always-default — implemented in `apps/nextblock/lib/i18n/detection.ts` and stored in `site_settings.language_detection_settings`. Content revision history is stored as snapshot + JSON Patch diff (enum `revision_type: snapshot, diff`) per migration `02001_baseline_schema.sql`.
271
271
 
272
272
  **Page Lifecycle** — Pages move through `draft`, `published`, and `archived` statuses (enum `page_status`).
273
273
 
@@ -343,7 +343,7 @@ Profile completion is enforced via a redirect: users without a `full_name` value
343
343
 
344
344
  #### 1.3.2.3 Geographic and Market Coverage
345
345
 
346
- Shipping resolution operates at country and state granularity. Multi-currency pricing is supported with a configurable default currency seeded in migration `00000000000008_seed_platform_defaults.sql`. The database schema includes a `shipping_zone_locations.postal_code` column for future finer-grained geographic resolution, although the current runtime resolver does not yet consume it.
346
+ Shipping resolution operates at country and state granularity. Multi-currency pricing is supported with a configurable default currency seeded in migration `02001_baseline_schema.sql`. The database schema includes a `shipping_zone_locations.postal_code` column for future finer-grained geographic resolution, although the current runtime resolver does not yet consume it.
347
347
 
348
348
  #### 1.3.2.4 Data Domains Included
349
349
 
@@ -403,7 +403,7 @@ The system does not natively integrate with: alternative payment processors beyo
403
403
  - `tsconfig.base.json` — Shared TypeScript strict-mode configuration and `@nextblock-cms/*` path aliases
404
404
  - `components.json` — shadcn/ui configuration (Slate base, CSS variables, RSC)
405
405
  - `tailwind.config.js` — Root Tailwind CSS theme tokens and dark-mode configuration
406
- - `vercel.json` — Cron schedule definitions (reset-sandbox at 03:00 UTC, sync-currencies at 18:00 UTC)
406
+ - `vercel.json` — Cron schedule definitions (reset-sandbox every 15 minutes, sync-currencies at 18:00 UTC)
407
407
  - `.env.example` — Environment variable reference template
408
408
 
409
409
  ### 1.4.2 Documentation Hub (`docs/`)
@@ -446,11 +446,14 @@ The system does not natively integrate with: alternative payment processors beyo
446
446
 
447
447
  ### 1.4.5 Database Migrations
448
448
 
449
- - `libs/db/src/supabase/migrations/00000000000000_setup_foundation_and_enums.sql` Enums: `user_role`, `page_status`, `menu_location`, `revision_type`
450
- - `libs/db/src/supabase/migrations/00000000000001_setup_cms_core.sql` — Languages, translations, media, profiles, settings
451
- - `libs/db/src/supabase/migrations/00000000000002_setup_content_tables.sql` — Pages, posts, blocks, navigation, revisions
452
- - `libs/db/src/supabase/migrations/00000000000008_seed_platform_defaults.sql` — Default settings (footer copyright, payment providers, inventory, invoice settings)
453
- - Migrations 00000000000003 through 00000000000010 Commerce catalog, fulfillment, and platform seeds
449
+ One squash generation at a time (`GGNNN_name.sql`; see §6.2.3.1 and docs/04):
450
+
451
+ - `libs/db/src/supabase/migrations/02000_catchup_gen1.sql` — Version-aware replay of the retired generation-1 forward migrations
452
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Enums (`user_role`, `page_status`, `menu_location`, `revision_type`, …), functions, every table and sequence
453
+ - `libs/db/src/supabase/migrations/02002_baseline_constraints_and_indexes.sql`Constraints and indexes
454
+ - `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — RLS, policies, triggers, grants
455
+ - `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Default settings (footer copyright, payment providers, inventory, invoice settings), languages, translations, demo content
456
+ - `02005` onward — forward migrations appended since the generation-2 squash
454
457
 
455
458
  ### 1.4.6 Agent Skill Documents
456
459
 
@@ -623,7 +626,7 @@ Served locales are the active rows of the `languages` table, managed at `/cms/se
623
626
 
624
627
  Locale propagation uses the `NEXT_USER_LOCALE` cookie and the `X-User-Locale` request header, both set by the request proxy. When `rememberVisitorChoice` is `true` (default) the cookie persists for one year (`maxAge: 31_536_000` seconds); when `false` it is a session cookie, so detection re-runs each new browser session (the client `LanguageProvider` mirrors the same expiry for manual switcher choices). A valid cookie always beats detection. The client-side provider chain `LanguageProvider → TranslationsProvider` in `apps/nextblock/app/providers.tsx` bridges server-resolved locale into React context.
625
628
 
626
- **Business Value:** Unlocks multi-market deployments without requiring adopters to integrate a separate i18n library. **User Benefits:** Language switching persists across sessions; same content IDs preserve relationships across translations. **Technical Context:** Implemented at schema level (migration `00000000000001_setup_cms_core.sql`), proxy level, and provider level.
629
+ **Business Value:** Unlocks multi-market deployments without requiring adopters to integrate a separate i18n library. **User Benefits:** Language switching persists across sessions; same content IDs preserve relationships across translations. **Technical Context:** Implemented at schema level (migration `02001_baseline_schema.sql`), proxy level, and provider level.
627
630
 
628
631
  **Dependencies**
629
632
 
@@ -648,7 +651,7 @@ Locale propagation uses the `NEXT_USER_LOCALE` cookie and the `X-User-Locale` re
648
651
 
649
652
  **Description**
650
653
 
651
- Revisions are implemented in the `page_revisions` and `post_revisions` tables defined in migration `00000000000002_setup_content_tables.sql`, using a hybrid snapshot/diff model. The `revision_type` enum (`snapshot`, `diff`) distinguishes between full snapshots and JSON Patch diffs (generated via `fast-json-patch`). A UNIQUE constraint on `(page_id, version)` ensures monotonically increasing snapshot versions. The CMS surface resides under `apps/nextblock/app/cms/revisions/`.
654
+ Revisions are implemented in the `page_revisions` and `post_revisions` tables defined in migration `02001_baseline_schema.sql`, using a hybrid snapshot/diff model. The `revision_type` enum (`snapshot`, `diff`) distinguishes between full snapshots and JSON Patch diffs (generated via `fast-json-patch`). A UNIQUE constraint on `(page_id, version)` ensures monotonically increasing snapshot versions. The CMS surface resides under `apps/nextblock/app/cms/revisions/`.
652
655
 
653
656
  **Business Value:** Provides audit trail and rollback capability for authored content. **User Benefits:** Authors can restore prior states; accidental deletions are recoverable. **Technical Context:** Diff generation reduces storage overhead for frequently-updated content.
654
657
 
@@ -675,7 +678,7 @@ Revisions are implemented in the `page_revisions` and `post_revisions` tables de
675
678
 
676
679
  **Description**
677
680
 
678
- Three menu locations are supported: `HEADER`, `FOOTER`, and `SIDEBAR`, encoded by the `menu_location` enum. The `navigation_items` table (migration `00000000000002_setup_content_tables.sql`) supports hierarchical menus through a `parent_id` self-reference, explicit `order` for sibling sorting, translation group affiliation, and optional page references. Administrative CRUD operations reside at `apps/nextblock/app/cms/navigation/`.
681
+ Three menu locations are supported: `HEADER`, `FOOTER`, and `SIDEBAR`, encoded by the `menu_location` enum. The `navigation_items` table (migration `02001_baseline_schema.sql`) supports hierarchical menus through a `parent_id` self-reference, explicit `order` for sibling sorting, translation group affiliation, and optional page references. Administrative CRUD operations reside at `apps/nextblock/app/cms/navigation/`.
679
682
 
680
683
  **Dependencies**
681
684
 
@@ -729,7 +732,7 @@ Authentication is layered over Supabase Auth using `@supabase/ssr` and `@supabas
729
732
 
730
733
  **Description**
731
734
 
732
- RBAC is enforced through a three-valued `user_role` enum (`ADMIN`, `WRITER`, `USER`) defined in migration `00000000000000_setup_foundation_and_enums.sql`. Route-level enforcement is implemented in `apps/nextblock/proxy.ts` (lines 12–17): `/cms` requires `WRITER` or `ADMIN`; `/cms/admin`, `/cms/users`, and `/cms/settings` require `ADMIN` exclusively. A foundational authorization rule encoded in the `on_auth_user_created` trigger elevates the first registered user to `ADMIN` and assigns all subsequent users the `USER` role, guaranteeing each deployment has exactly one guaranteed administrator at bootstrap. Users without a `full_name` value are redirected to `/profile` before being permitted to access other authenticated surfaces. Database-layer enforcement is provided via Row-Level Security with helper functions `get_current_user_role()` and `is_admin()` (both `SECURITY DEFINER`).
735
+ RBAC is enforced through a three-valued `user_role` enum (`ADMIN`, `WRITER`, `USER`) defined in migration `02001_baseline_schema.sql`. Route-level enforcement is implemented in `apps/nextblock/proxy.ts` (lines 12–17): `/cms` requires `WRITER` or `ADMIN`; `/cms/admin`, `/cms/users`, and `/cms/settings` require `ADMIN` exclusively. A foundational authorization rule encoded in the `on_auth_user_created` trigger elevates the first registered user to `ADMIN` and assigns all subsequent users the `USER` role, guaranteeing each deployment has exactly one guaranteed administrator at bootstrap. Users without a `full_name` value are redirected to `/profile` before being permitted to access other authenticated surfaces. Database-layer enforcement is provided via Row-Level Security with helper functions `get_current_user_role()` and `is_admin()` (both `SECURITY DEFINER`).
733
736
 
734
737
  **Dependencies**
735
738
 
@@ -833,7 +836,7 @@ All features in this section are gated by F-022 (Package Activation) via `verify
833
836
 
834
837
  **Description**
835
838
 
836
- Products are modeled in migration `00000000000003_setup_catalog_and_licensing.sql` with fields including `sku`, `slug`, `title`, `type` (`physical` | `digital`), `payment_provider` (`stripe` | `freemius`), `price` as an integer in the smallest currency unit, a multi-currency `prices` JSONB column, `stock`, `status` (`draft` | `active` | `archived`), descriptions, Freemius-specific fields, UPC, and an `is_taxable` flag. A CHECK constraint named `products_type_provider_consistency_check` enforces that `physical` products use `stripe` and `digital` products use `freemius`. Related tables `product_media`, `product_attributes`, `product_attribute_terms`, `product_variants`, and `variant_attribute_mapping` model assets and variations. CMS surfaces at `apps/nextblock/app/cms/products/` provide list, create, edit, media, attribute, and variation management flows.
839
+ Products are modeled in migration `02001_baseline_schema.sql` with fields including `sku`, `slug`, `title`, `type` (`physical` | `digital`), `payment_provider` (`stripe` | `freemius`), `price` as an integer in the smallest currency unit, a multi-currency `prices` JSONB column, `stock`, `status` (`draft` | `active` | `archived`), descriptions, Freemius-specific fields, UPC, and an `is_taxable` flag. A CHECK constraint named `products_type_provider_consistency_check` enforces that `physical` products use `stripe` and `digital` products use `freemius`. Related tables `product_media`, `product_attributes`, `product_attribute_terms`, `product_variants`, and `variant_attribute_mapping` model assets and variations. CMS surfaces at `apps/nextblock/app/cms/products/` provide list, create, edit, media, attribute, and variation management flows.
837
840
 
838
841
  **Dependencies**
839
842
 
@@ -858,7 +861,7 @@ Products are modeled in migration `00000000000003_setup_catalog_and_licensing.sq
858
861
 
859
862
  **Description**
860
863
 
861
- The `inventory_items` table (migration `00000000000003_setup_catalog_and_licensing.sql`) uses SKU as key and enforces `quantity >= 0` via CHECK constraint. Tracking is globally controlled by the `trackQuantities` setting in `ecommerce_inventory_settings`. Inventory deduction during order finalization follows a resilient pattern: the runtime first calls the `apply_order_inventory_deduction()` Postgres RPC, and if that path is unavailable, falls back to direct SQL via the `POSTGRES_URL` or `DATABASE_URL` connection, as coordinated by `libs/ecommerce/src/lib/shared-inventory.ts` and `libs/ecommerce/src/lib/order-inventory.ts`.
864
+ The `inventory_items` table (migration `02001_baseline_schema.sql`) uses SKU as key and enforces `quantity >= 0` via CHECK constraint. Tracking is globally controlled by the `trackQuantities` setting in `ecommerce_inventory_settings`. Inventory deduction during order finalization follows a resilient pattern: the runtime first calls the `apply_order_inventory_deduction()` Postgres RPC, and if that path is unavailable, falls back to direct SQL via the `POSTGRES_URL` or `DATABASE_URL` connection, as coordinated by `libs/ecommerce/src/lib/shared-inventory.ts` and `libs/ecommerce/src/lib/order-inventory.ts`.
862
865
 
863
866
  **Dependencies**
864
867
 
@@ -958,7 +961,7 @@ Freemius integration lives in `libs/ecommerce/src/lib/providers/` and supports d
958
961
 
959
962
  **Description**
960
963
 
961
- The `currencies` table (migration `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql`) stores `exchange_rate` as a `numeric(20,10)`, a single-row `is_default` constraint, `rounding_mode` (`none`, `nearest`, `up`, `down`, `charm`), `rounding_increment`, `rounding_charm_amount`, and flags `auto_update_exchange_rate` and `auto_sync_product_prices`. The default currency must satisfy `exchange_rate = 1`, `auto_update_exchange_rate = false`, and `auto_sync_product_prices = false`. FX rates are pulled from `https://api.frankfurter.dev` (overridable via `FX_API_BASE_URL`). Operations `syncStoreCurrencyRates()` and `rebaseStoreCurrencyExchangeRates()` are exposed by `libs/ecommerce/src/lib/currency-sync.ts`. A daily cron invokes `/api/cron/sync-currencies` at 18:00 UTC (`vercel.json`). Seed migration `00000000000008_seed_platform_defaults.sql` provides USD as the default.
964
+ The `currencies` table (migration `02001_baseline_schema.sql`) stores `exchange_rate` as a `numeric(20,10)`, a single-row `is_default` constraint, `rounding_mode` (`none`, `nearest`, `up`, `down`, `charm`), `rounding_increment`, `rounding_charm_amount`, and flags `auto_update_exchange_rate` and `auto_sync_product_prices`. The default currency must satisfy `exchange_rate = 1`, `auto_update_exchange_rate = false`, and `auto_sync_product_prices = false`. FX rates are pulled from `https://api.frankfurter.dev` (overridable via `FX_API_BASE_URL`). Operations `syncStoreCurrencyRates()` and `rebaseStoreCurrencyExchangeRates()` are exposed by `libs/ecommerce/src/lib/currency-sync.ts`. A daily cron invokes `/api/cron/sync-currencies` at 18:00 UTC (`vercel.json`). Seed migration `02001_baseline_schema.sql` provides USD as the default.
962
965
 
963
966
  **Dependencies**
964
967
 
@@ -1008,7 +1011,7 @@ Three shipping tables model geographic and rate configurations: `shipping_zones`
1008
1011
 
1009
1012
  **Description**
1010
1013
 
1011
- The `tax_rates` table (migration `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql`) supports two modes: `manual` (keyed on country and state, supporting stacked rates such as GST + PST) and `automatic` (delegated to Stripe Tax via tax codes on line items). Mode selection is controlled by the `enableTaxes` and `taxCalculationMode` properties in `ecommerce_inventory_settings`. Schema constraints require `tax_rate` between 0 and 100 and enforce a uniqueness constraint on `(country_code, state_code, lower(tax_name))`. Implementation lives in `libs/ecommerce/src/lib/tax-calculation.ts` and `libs/ecommerce/src/lib/order-tax-details.ts`.
1014
+ The `tax_rates` table (migration `02001_baseline_schema.sql`) supports two modes: `manual` (keyed on country and state, supporting stacked rates such as GST + PST) and `automatic` (delegated to Stripe Tax via tax codes on line items). Mode selection is controlled by the `enableTaxes` and `taxCalculationMode` properties in `ecommerce_inventory_settings`. Schema constraints require `tax_rate` between 0 and 100 and enforce a uniqueness constraint on `(country_code, state_code, lower(tax_name))`. Implementation lives in `libs/ecommerce/src/lib/tax-calculation.ts` and `libs/ecommerce/src/lib/order-tax-details.ts`.
1012
1015
 
1013
1016
  **Dependencies**
1014
1017
 
@@ -1033,7 +1036,7 @@ The `tax_rates` table (migration `00000000000004_setup_fulfillment_shipping_taxe
1033
1036
 
1034
1037
  **Description**
1035
1038
 
1036
- Orders transition through five statuses — `pending`, `paid`, `shipped`, `cancelled`, `refunded` — and store totals (`total`, `subtotal`, `shipping_total`, `tax_total`), a JSONB `tax_details` breakdown, `exchange_rate_at_purchase` as `numeric(20,10)`, `invoice_number`, `paid_at`, and `inventory_deducted_at`. Stable invoice numbering is produced by the `order_invoice_number_seq` sequence. The `invoice_settings` site-setting (seeded by `00000000000008_seed_platform_defaults.sql`) stores business name, email, address, and tax registrations. UI components `InvoiceDocument` and `InvoiceViewerShell` in `libs/ecommerce/src/lib/` render invoices. Customer order history is surfaced via `libs/ecommerce/src/lib/customer-orders.ts`, with admin management at `apps/nextblock/app/cms/orders/` and customer self-service at `apps/nextblock/app/profile/orders/`.
1039
+ Orders transition through five statuses — `pending`, `paid`, `shipped`, `cancelled`, `refunded` — and store totals (`total`, `subtotal`, `shipping_total`, `tax_total`), a JSONB `tax_details` breakdown, `exchange_rate_at_purchase` as `numeric(20,10)`, `invoice_number`, `paid_at`, and `inventory_deducted_at`. Stable invoice numbering is produced by the `order_invoice_number_seq` sequence. The `invoice_settings` site-setting (seeded by `02001_baseline_schema.sql`) stores business name, email, address, and tax registrations. UI components `InvoiceDocument` and `InvoiceViewerShell` in `libs/ecommerce/src/lib/` render invoices. Customer order history is surfaced via `libs/ecommerce/src/lib/customer-orders.ts`, with admin management at `apps/nextblock/app/cms/orders/` and customer self-service at `apps/nextblock/app/profile/orders/`.
1037
1040
 
1038
1041
  **Dependencies**
1039
1042
 
@@ -1058,7 +1061,7 @@ Orders transition through five statuses — `pending`, `paid`, `shipped`, `cance
1058
1061
 
1059
1062
  **Description**
1060
1063
 
1061
- The `package_activations` table (migration `00000000000003_setup_catalog_and_licensing.sql`) contains `license_key`, `instance_name`, `package_id`, `status` (defaulting to `active`), `meta`, `last_validated_at`, and a UNIQUE constraint on `(license_key, package_id)`. The helper `verifyPackageOnline(packageId, customClient?)` in `libs/db/src/lib/package-validation.ts` returns a boolean based on `status === 'active'` and uses `unstable_cache` with a 60-second revalidation window. This function is invoked from four surfaces: the CMS commerce navigation visibility check, the checkout API gate at `apps/nextblock/app/api/checkout/route.ts` line 36, premium route wrappers injected during scaffold activation, and the CLI's module activation flows.
1064
+ The `package_activations` table (migration `02001_baseline_schema.sql`) contains `license_key`, `instance_name`, `package_id`, `status` (defaulting to `active`), `meta`, `last_validated_at`, and a UNIQUE constraint on `(license_key, package_id)`. The helper `verifyPackageOnline(packageId, customClient?)` in `libs/db/src/lib/package-validation.ts` returns a boolean based on `status === 'active'` and uses `unstable_cache` with a 60-second revalidation window. This function is invoked from four surfaces: the CMS commerce navigation visibility check, the checkout API gate at `apps/nextblock/app/api/checkout/route.ts` line 36, premium route wrappers injected during scaffold activation, and the CLI's module activation flows.
1062
1065
 
1063
1066
  **Dependencies**
1064
1067
 
@@ -1160,7 +1163,7 @@ The SDK library at `libs/sdk` provides a typed contract for external block autho
1160
1163
 
1161
1164
  **Description**
1162
1165
 
1163
- Two cron jobs are declared in `vercel.json`. The `/api/cron/reset-sandbox` endpoint runs daily at 03:00 UTC (`0 3 * * *`); it clears R2 storage, executes the generated SQL bootstrap (`SANDBOX_RESET_SQL`), normalizes media records, ensures required assets, seeds commerce and content data, and synchronizes Freemius products (product ID `24851`). The `/api/cron/sync-currencies` endpoint runs daily at 18:00 UTC (`0 18 * * *`) and invokes `syncStoreCurrencyRates()` from `@nextblock-cms/ecommerce/server`. Both endpoints require an `Authorization: Bearer ${CRON_SECRET}` header.
1166
+ Two cron jobs are declared in `vercel.json`. The `/api/cron/reset-sandbox` endpoint runs every 15 minutes (`*/15 * * * *`); it clears R2 storage, executes the generated SQL bootstrap (`SANDBOX_RESET_SQL`), normalizes media records, ensures required assets, seeds commerce and content data, and synchronizes Freemius products (product ID `24851`). The `/api/cron/sync-currencies` endpoint runs daily at 18:00 UTC (`0 18 * * *`) and invokes `syncStoreCurrencyRates()` from `@nextblock-cms/ecommerce/server`. Both endpoints require an `Authorization: Bearer ${CRON_SECRET}` header.
1164
1167
 
1165
1168
  **Dependencies**
1166
1169
 
@@ -1185,13 +1188,13 @@ Two cron jobs are declared in `vercel.json`. The `/api/cron/reset-sandbox` endpo
1185
1188
 
1186
1189
  **Description**
1187
1190
 
1188
- Sandbox mode is toggled via the `NEXT_PUBLIC_IS_SANDBOX` environment variable. When enabled, the application renders the `SandboxBanner` and `SandboxCredentialsAlert` components (with demo credentials `demo@nextblock.dev`/`password`), and relaxes Freemius webhook signature verification to tolerate signature mismatches. A live deployment at `https://cms.nextblock.dev/` demonstrates the feature and resets daily via F-025.
1191
+ Sandbox mode is toggled via the `NEXT_PUBLIC_IS_SANDBOX` environment variable. When enabled, the application renders the `SandboxBanner` and `SandboxCredentialsAlert` components (with demo credentials `demo@nextblock.dev`/`password`), and relaxes Freemius webhook signature verification to tolerate signature mismatches. A live deployment at `https://cms.nextblock.dev/` demonstrates the feature and resets every 15 minutes via F-025.
1189
1192
 
1190
1193
  **Dependencies**
1191
1194
 
1192
1195
  | Dependency Type | Details |
1193
1196
  |:--|:--|
1194
- | Prerequisite Features | F-025 (for daily reset) |
1197
+ | Prerequisite Features | F-025 (for the 15-minute reset) |
1195
1198
  | System Dependencies | None |
1196
1199
  | External Dependencies | None |
1197
1200
  | Integration Requirements | `NEXT_PUBLIC_IS_SANDBOX` flag |
@@ -1422,7 +1425,7 @@ This subsection provides the detailed, testable requirements that operationalize
1422
1425
  | F-023-RQ-002 | `activate ecommerce` MUST install the alias `@nextblock-cms/ecommerce@npm:@nextblock-cms/ecom@latest` and inject route wrappers with `verifyPackageOnline()` | Must-Have | High |
1423
1426
  | F-024-RQ-001 | SDK MUST expose `BlockContentSchema`, `BlockData`, `BlockProps`, `BlockEditorProps`, `BlockConfig`, `LucideIcon` | Must-Have | Low |
1424
1427
  | F-024-RQ-002 | External block authoring MUST follow the contract shape documented in `docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md` | Must-Have | Medium |
1425
- | F-025-RQ-001 | `/api/cron/reset-sandbox` MUST run daily at 03:00 UTC with `Bearer CRON_SECRET` authorization | Must-Have | Medium |
1428
+ | F-025-RQ-001 | `/api/cron/reset-sandbox` MUST run every 15 minutes with `Bearer CRON_SECRET` authorization | Must-Have | Medium |
1426
1429
  | F-025-RQ-002 | `/api/cron/sync-currencies` MUST run daily at 18:00 UTC with `Bearer CRON_SECRET` authorization | Must-Have | Low |
1427
1430
  | F-026-RQ-001 | Sandbox banner and credential alert MUST render when `NEXT_PUBLIC_IS_SANDBOX === 'true'` | Should-Have | Low |
1428
1431
  | F-027-RQ-001 | `/api/revalidate` MUST validate `REVALIDATE_SECRET_TOKEN` before calling `revalidatePath` | Must-Have | Medium |
@@ -1679,7 +1682,7 @@ The following matrix links features to the sections of the technical specificati
1679
1682
  | Package alignment | `@nextblock-cms/ecom` package name vs. `@nextblock-cms/ecommerce` alias (per §1.3.3.1) requires coordination when republished | F-013–F-022 |
1680
1683
  | Locale expansion | Locales are DB-driven: add an active `languages` row (plus translations/content); `FALLBACK_LOCALES` in `proxy.ts` is only a DB-unreachable safety net | F-007 |
1681
1684
  | Block registry updates | New block types must satisfy F-024 contract and register in `blockRegistry.ts` | F-004, F-024 |
1682
- | RLS policy review | Migration `00000000000006_setup_rls_and_grants.sql` should be audited on new table introduction | All DB-backed features |
1685
+ | RLS policy review | Migration `02001_baseline_schema.sql` should be audited on new table introduction | All DB-backed features |
1683
1686
 
1684
1687
  ### 2.4.6 Assumptions and Constraints Summary
1685
1688
 
@@ -1700,7 +1703,7 @@ The following matrix links features to the sections of the technical specificati
1700
1703
  - `README.md` — Product value proposition, competitive positioning, Lighthouse and CLI claims
1701
1704
  - `package.json` — Workspace dependency versions (Next.js, React, TypeScript, Nx, Tailwind, Tiptap)
1702
1705
  - `nx.json` — Nx plugin and release configuration
1703
- - `vercel.json` — Cron schedule definitions (reset-sandbox 03:00 UTC; sync-currencies 18:00 UTC)
1706
+ - `vercel.json` — Cron schedule definitions (reset-sandbox every 15 minutes; sync-currencies 18:00 UTC)
1704
1707
  - `.env.example` — Environment variable reference template
1705
1708
  - `eslint.config.mjs` — `@nx/enforce-module-boundaries` scope-tag rules (F-028)
1706
1709
  - `apps/nextblock/next.config.js` — Image format/device-size config; CSP; `transpilePackages`
@@ -1726,14 +1729,14 @@ The following matrix links features to the sections of the technical specificati
1726
1729
  - `apps/create-nextblock/bin/create-nextblock.js` — CLI `create` and `activate` commands (F-023)
1727
1730
  - `libs/db/src/lib/package-validation.ts` — `verifyPackageOnline()` with 60 s cache (F-022)
1728
1731
  - `libs/db/src/lib/media-actions.ts` — Role-gated media recording (F-006)
1729
- - `libs/db/src/supabase/migrations/00000000000000_setup_foundation_and_enums.sql` — `user_role`, `page_status`, `menu_location`, `revision_type` enums
1730
- - `libs/db/src/supabase/migrations/00000000000001_setup_cms_core.sql` — languages, translations, media, profiles (F-002, F-006, F-007)
1731
- - `libs/db/src/supabase/migrations/00000000000002_setup_content_tables.sql` — pages, posts, blocks, navigation, revisions (F-004, F-008, F-009)
1732
- - `libs/db/src/supabase/migrations/00000000000003_setup_catalog_and_licensing.sql` — products, variants, attributes, inventory, package_activations (F-013, F-014, F-022)
1733
- - `libs/db/src/supabase/migrations/00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` — orders, shipping zones, tax rates, currencies (F-018–F-021)
1734
- - `libs/db/src/supabase/migrations/00000000000005_setup_functions_and_triggers.sql` — `handle_new_user()`, `on_auth_user_created` (F-002, F-003)
1735
- - `libs/db/src/supabase/migrations/00000000000006_setup_rls_and_grants.sql` — RLS policies and helper functions
1736
- - `libs/db/src/supabase/migrations/00000000000008_seed_platform_defaults.sql` — Default settings, USD currency, `en`/`fr` languages
1732
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `user_role`, `page_status`, `menu_location`, `revision_type` enums
1733
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — languages, translations, media, profiles (F-002, F-006, F-007)
1734
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — pages, posts, blocks, navigation, revisions (F-004, F-008, F-009)
1735
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — products, variants, attributes, inventory, package_activations (F-013, F-014, F-022)
1736
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — orders, shipping zones, tax rates, currencies (F-018–F-021)
1737
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `handle_new_user()`, `on_auth_user_created` (F-002, F-003)
1738
+ - `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — RLS policies and helper functions
1739
+ - `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Default settings, USD currency, `en`/`fr` languages
1737
1740
  - `libs/db/src/supabase/templates/` — Six Supabase Auth email templates (F-002)
1738
1741
  - `libs/editor/README.md` — Public editor surface exports (F-005)
1739
1742
  - `libs/editor/ADVANCED_FEATURES.md` — Enhanced floating menu, placeholder behaviors (F-005)
@@ -2203,7 +2206,7 @@ The `vercel.json` file declares two scheduled jobs:
2203
2206
 
2204
2207
  | Path | Schedule (UTC) | Purpose |
2205
2208
  |:--|:--|:--|
2206
- | `/api/cron/reset-sandbox` | `0 3 * * *` (03:00 daily) | Resets sandbox R2 + data for the demo deployment (F-025, F-026) |
2209
+ | `/api/cron/reset-sandbox` | `*/15 * * * *` (every 15 minutes) | Resets sandbox R2 + data for the demo deployment (F-025, F-026) |
2207
2210
  | `/api/cron/sync-currencies` | `0 18 * * *` (18:00 daily) | FX rate synchronization (F-018, F-025) |
2208
2211
 
2209
2212
  Both endpoints enforce `Authorization: Bearer ${CRON_SECRET}` per the security mitigation in Section 2.4.4.
@@ -2222,14 +2225,14 @@ Supabase PostgreSQL is the authoritative data store for all structured data in t
2222
2225
 
2223
2226
  | Migration File | Purpose |
2224
2227
  |:--|:--|
2225
- | `00000000000000_setup_foundation_and_enums.sql` | Enums: `user_role`, `page_status`, `menu_location`, `revision_type` |
2226
- | `00000000000001_setup_cms_core.sql` | `languages`, `translations`, `media`, `profiles`, `settings`, `logos` |
2227
- | `00000000000002_setup_content_tables.sql` | `pages`, `posts`, `blocks`, `navigation_items`, `page_revisions`, `post_revisions` |
2228
- | `00000000000003_setup_catalog_and_licensing.sql` | `products`, `product_variants`, `inventory_items`, `package_activations` |
2229
- | `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` | `currencies`, `tax_rates`, shipping tables |
2228
+ | `02001_baseline_schema.sql` | Enums: `user_role`, `page_status`, `menu_location`, `revision_type` |
2229
+ | `02001_baseline_schema.sql` | `languages`, `translations`, `media`, `profiles`, `settings`, `logos` |
2230
+ | `02001_baseline_schema.sql` | `pages`, `posts`, `blocks`, `navigation_items`, `page_revisions`, `post_revisions` |
2231
+ | `02001_baseline_schema.sql` | `products`, `product_variants`, `inventory_items`, `package_activations` |
2232
+ | `02001_baseline_schema.sql` | `currencies`, `tax_rates`, shipping tables |
2230
2233
  | `00000000000005` through `00000000000007` | Commerce extensions |
2231
- | `00000000000006_setup_rls_and_grants.sql` | Row-Level Security policies |
2232
- | `00000000000008_seed_platform_defaults.sql` | Default settings, USD currency, English language |
2234
+ | `02001_baseline_schema.sql` | Row-Level Security policies |
2235
+ | `02001_baseline_schema.sql` | Default settings, USD currency, English language |
2233
2236
  | `00000000000009` — `00000000000010` | Additional platform seeds |
2234
2237
  | `00000000000011` through `00000000000016` | Cortex AI settings, coupons, audit, drafts, and page feature images |
2235
2238
  | `00000000000017` through `00000000000024` | Product blocks, bot-protection settings, product categories + translations, hero→section migration, Cortex AI guide seed, custom block definitions, and cart sessions |
@@ -2521,7 +2524,7 @@ The premium `libs/ecommerce` is guarded at runtime by the `verifyPackageOnline('
2521
2524
  - `package.json` — Root workspace package metadata, all dependency versions, and npm scripts
2522
2525
  - `nx.json` — Nx workspace configuration, registered plugins, generators, and targets
2523
2526
  - `tsconfig.base.json` — Shared TypeScript strict-mode configuration and monorepo path aliases
2524
- - `vercel.json` — Cron schedules for `reset-sandbox` (03:00 UTC) and `sync-currencies` (18:00 UTC)
2527
+ - `vercel.json` — Cron schedules for `reset-sandbox` (every 15 minutes) and `sync-currencies` (18:00 UTC)
2525
2528
  - `tailwind.config.js` — Root Tailwind CSS theme tokens, dark-mode configuration, content globs
2526
2529
  - `postcss.config.js` — PostCSS pipeline with `@tailwindcss/postcss` and `autoprefixer`
2527
2530
  - `eslint.config.mjs` — Flat ESLint configuration with `@nx/enforce-module-boundaries` rules
@@ -3432,7 +3435,7 @@ flowchart LR
3432
3435
 
3433
3436
  ### 4.5.1 Sandbox Reset Cron
3434
3437
 
3435
- The sandbox reset cron at `/api/cron/reset-sandbox/` executes daily at 03:00 UTC and performs a comprehensive environment rebuild: R2 storage deletion and re-seeding, database bootstrap via generated `SANDBOX_RESET_SQL`, and seeding of commerce products, localized content, and navigation entries. The endpoint requires `NEXT_PUBLIC_IS_SANDBOX === 'true'` and rejects execution in production environments.
3438
+ The sandbox reset cron at `/api/cron/reset-sandbox/` executes every 15 minutes and performs a comprehensive environment rebuild: R2 storage deletion and re-seeding, database bootstrap via generated `SANDBOX_RESET_SQL`, and seeding of commerce products, localized content, and navigation entries. The endpoint requires `NEXT_PUBLIC_IS_SANDBOX === 'true'` and rejects execution in production environments.
3436
3439
 
3437
3440
  ```mermaid
3438
3441
  flowchart TB
@@ -3923,7 +3926,7 @@ The following table consolidates all documented timing constraints and SLAs acro
3923
3926
  | Sync-Currencies maxDuration | 30 seconds | `api/cron/sync-currencies` |
3924
3927
  | Reset-Sandbox maxDuration | 60 seconds | `api/cron/reset-sandbox` |
3925
3928
  | Currency Sync Schedule | 18:00 UTC daily (`0 18 * * *`) | `vercel.json` |
3926
- | Sandbox Reset Schedule | 03:00 UTC daily (`0 3 * * *`) | `vercel.json` |
3929
+ | Sandbox Reset Schedule | every 15 minutes (`*/15 * * * *`) | `vercel.json` |
3927
3930
  | Max Source Image Width | 2560 pixels | `api/process-image` |
3928
3931
  | Image Derivative Widths | 1920, 1280, 768, 384, 128 | `api/process-image` |
3929
3932
  | Lighthouse Performance Target | 100/100 | `README.md` |
@@ -3953,9 +3956,9 @@ flowchart LR
3953
3956
  HstsHeader[HSTS Header<br/>2 years max-age]
3954
3957
  end
3955
3958
 
3956
- subgraph Scheduled[" Scheduled Daily "]
3959
+ subgraph Scheduled[" Scheduled (Vercel Cron) "]
3957
3960
  CurrencyDay[18:00 UTC<br/>Sync Currencies]
3958
- ResetDay[03:00 UTC<br/>Sandbox Reset]
3961
+ ResetDay[Every 15 min<br/>Sandbox Reset]
3959
3962
  end
3960
3963
  ```
3961
3964
 
@@ -4002,14 +4005,14 @@ flowchart LR
4002
4005
 
4003
4006
  #### Database Migrations Referenced
4004
4007
 
4005
- - `00000000000000_setup_foundation_and_enums.sql` - `user_role` enum
4006
- - `00000000000001_setup_cms_core.sql` - Translations schema
4007
- - `00000000000002_setup_content_tables.sql` - Pages, posts, revisions, navigation
4008
- - `00000000000003_setup_catalog_and_licensing.sql` - Products, inventory, package_activations
4009
- - `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` - Orders, tax, shipping, currencies
4010
- - `00000000000005_*.sql` - `on_auth_user_created` trigger (first-user ADMIN rule)
4011
- - `00000000000006_*.sql` - RLS policies and helper functions
4012
- - `00000000000008_seed_platform_defaults.sql` - Default currency, languages, invoice settings
4008
+ - `02001_baseline_schema.sql` - `user_role` enum
4009
+ - `02001_baseline_schema.sql` - Translations schema
4010
+ - `02001_baseline_schema.sql` - Pages, posts, revisions, navigation
4011
+ - `02001_baseline_schema.sql` - Products, inventory, package_activations
4012
+ - `02001_baseline_schema.sql` - Orders, tax, shipping, currencies
4013
+ - `02003_baseline_security_and_grants.sql` - `on_auth_user_created` trigger (first-user ADMIN rule), RLS policies
4014
+ - `02001_baseline_schema.sql` - helper functions
4015
+ - `02001_baseline_schema.sql` - Default currency, languages, invoice settings
4013
4016
 
4014
4017
  #### Folders Explored
4015
4018
 
@@ -4194,7 +4197,7 @@ The integration landscape comprises eight external domains declared in `libs/env
4194
4197
  | Freemius | HTTPS + `x-freemius-signature` (HMAC SHA-256) | Sandbox bypass when `NEXT_PUBLIC_IS_SANDBOX===true` |
4195
4198
  | Frankfurter FX | HTTPS (JSON) | Cron runs 18:00 UTC daily; `maxDuration: 30s` |
4196
4199
  | SMTP | SMTP + TLS | Best-effort from server actions |
4197
- | Vercel Cron | HTTPS + Bearer `CRON_SECRET` | 03:00 UTC (reset-sandbox, 60s); 18:00 UTC (sync-currencies, 30s) |
4200
+ | Vercel Cron | HTTPS + Bearer `CRON_SECRET` | every 15 min (reset-sandbox, 60s); 18:00 UTC (sync-currencies, 30s) |
4198
4201
  | Google Tag Manager | HTTPS (JS) | GTM id from `privacy_settings` (site_settings); allowlisted in CSP |
4199
4202
 
4200
4203
  ## 5.2 COMPONENT DETAILS
@@ -4638,7 +4641,7 @@ Observability instrumentation includes @vercel/speed-insights for performance me
4638
4641
  | Prefetch Accuracy | Correct `X-Prefetch-Priority` per page-type | `proxy.ts` |
4639
4642
  | Media Optimization | Blur placeholders on uploads | sharp + plaiceholder pipeline |
4640
4643
  | Commerce Conversion | Checkout success by provider | `app/api/checkout/route.ts` + webhooks |
4641
- | Scheduled Job Health | Daily success of sandbox reset + currency sync | `vercel.json` cron configuration |
4644
+ | Scheduled Job Health | Success of sandbox reset (every 15 min) + currency sync (daily) | `vercel.json` cron configuration |
4642
4645
  | Bundle Discipline | Removed console calls in production | `compiler.removeConsole` in `next.config.js` |
4643
4646
 
4644
4647
  ### 5.4.2 Logging and Tracing Strategy
@@ -4761,7 +4764,7 @@ Measurable performance objectives include 100/100 Lighthouse performance, a CLI
4761
4764
 
4762
4765
  #### 5.4.6.1 Sandbox Reset
4763
4766
 
4764
- The `/api/cron/reset-sandbox` endpoint, scheduled daily at 03:00 UTC, reconstructs the demo environment end-to-end: it clears and repopulates Cloudflare R2 media, runs a generated SQL bootstrap (`sandboxResetSql.ts`) against Supabase, normalizes legacy media records, ensures required media assets exist, and seeds commerce/content data. The endpoint only executes when in sandbox mode and after verifying the Bearer `CRON_SECRET`.
4767
+ The `/api/cron/reset-sandbox` endpoint, scheduled every 15 minutes, reconstructs the demo environment end-to-end: it clears and repopulates Cloudflare R2 media, runs a generated SQL bootstrap (`sandboxResetSql.ts`) against Supabase, normalizes legacy media records, ensures required media assets exist, and seeds commerce/content data. The endpoint only executes when in sandbox mode and after verifying the Bearer `CRON_SECRET`.
4765
4768
 
4766
4769
  #### 5.4.6.2 Currency Synchronization
4767
4770
 
@@ -4779,8 +4782,8 @@ Supabase provides point-in-time recovery at the platform level; the eleven canon
4779
4782
 
4780
4783
  ```mermaid
4781
4784
  flowchart LR
4782
- subgraph Scheduled["Daily Schedules (Vercel Cron)"]
4783
- Reset[03:00 UTC<br/>/api/cron/reset-sandbox<br/>maxDuration 60s]
4785
+ subgraph Scheduled["Schedules (Vercel Cron)"]
4786
+ Reset[Every 15 min<br/>/api/cron/reset-sandbox<br/>maxDuration 60s]
4784
4787
  Sync[18:00 UTC<br/>/api/cron/sync-currencies<br/>maxDuration 30s]
4785
4788
  end
4786
4789
 
@@ -5443,7 +5446,7 @@ Two scheduled recovery flows are declared in `vercel.json` and serve both routin
5443
5446
  ```mermaid
5444
5447
  flowchart LR
5445
5448
  subgraph Schedules["vercel.json crons[]"]
5446
- Reset03[03:00 UTC daily<br/>/api/cron/reset-sandbox<br/>maxDuration 60s]
5449
+ Reset03[Every 15 min<br/>/api/cron/reset-sandbox<br/>maxDuration 60s]
5447
5450
  Sync18[18:00 UTC daily<br/>/api/cron/sync-currencies<br/>maxDuration 30s]
5448
5451
  end
5449
5452
 
@@ -5480,7 +5483,7 @@ flowchart LR
5480
5483
  C4 --> C5
5481
5484
  ```
5482
5485
 
5483
- The sandbox reset (`apps/nextblock/app/api/cron/reset-sandbox/route.ts`) only executes when in sandbox mode, providing a self-healing loop for the public demo environment. It serves as a **nightly reconstruction rehearsal** that validates the schema migrations and seed scripts.
5486
+ The sandbox reset (`apps/nextblock/app/api/cron/reset-sandbox/route.ts`) only executes when in sandbox mode, providing a self-healing loop for the public demo environment. It serves as a **continuous reconstruction rehearsal** that validates the schema migrations and seed scripts.
5484
5487
 
5485
5488
  #### 6.1.6.3 Content Recovery via Revisions
5486
5489
 
@@ -5533,7 +5536,7 @@ Failover behavior is **implicit via Vercel's platform** and **explicit via dual-
5533
5536
  - The workspace uses **library decomposition** (eight components in an Nx monorepo) with boundaries enforced at compile time by ESLint `@nx/enforce-module-boundaries`.
5534
5537
  - **Scalability is platform-delegated**: Vercel provides automatic horizontal scaling of stateless serverless functions. Application-level tuning is confined to cache TTLs, `maxDuration`, and caching layers.
5535
5538
  - **Resilience is expressed at the integration surface** via a five-pattern classification (strict failure, dual-path, and graceful degrade), exemplified by the RPC + SQL fallback inventory deduction.
5536
- - **Disaster recovery** rests on Supabase platform PITR, 11 canonical migrations, `page_revisions`/`post_revisions` hybrid snapshot/diff content history, and a nightly sandbox-reset cron that doubles as a reconstruction rehearsal.
5539
+ - **Disaster recovery** rests on Supabase platform PITR, 11 canonical migrations, `page_revisions`/`post_revisions` hybrid snapshot/diff content history, and a 15-minute sandbox-reset cron that doubles as a reconstruction rehearsal.
5537
5540
 
5538
5541
  #### 6.1.7.2 Cross-References
5539
5542
 
@@ -5565,7 +5568,7 @@ Failover behavior is **implicit via Vercel's platform** and **explicit via dual-
5565
5568
  - `apps/nextblock/app/api/checkout/route.ts` — Checkout orchestration with `resolveProviderFromItem` chain and license-gate integration
5566
5569
  - `apps/nextblock/app/api/webhooks/stripe/route.ts` — Stripe signature verification + event dispatch
5567
5570
  - `apps/nextblock/app/api/webhooks/freemius/route.ts` — Freemius HMAC-SHA-256 verification + sandbox bypass
5568
- - `apps/nextblock/app/api/cron/reset-sandbox/route.ts` — Daily sandbox reset flow; R2 clearing, SQL bootstrap, media normalization, seeding
5571
+ - `apps/nextblock/app/api/cron/reset-sandbox/route.ts` — 15-minute sandbox reset flow; R2 clearing, SQL bootstrap, media normalization, seeding
5569
5572
  - `apps/nextblock/app/api/cron/sync-currencies/route.ts` — Daily Frankfurter FX sync; Bearer `CRON_SECRET` auth
5570
5573
  - `apps/nextblock/app/api/revalidate/route.ts` — On-demand ISR invalidation from Supabase webhooks
5571
5574
  - `apps/nextblock/app/api/upload/presigned-url/route.ts` — R2 presigned URL generation (300s TTL, 10 MB cap)
@@ -5580,7 +5583,7 @@ Failover behavior is **implicit via Vercel's platform** and **explicit via dual-
5580
5583
  - `libs/db/src/lib/supabase/middleware.ts` — Supabase session synchronization helper
5581
5584
  - `libs/db/project.json` — Nx project definition with `scope:public` tag
5582
5585
  - `libs/environment.d.ts` — NodeJS.ProcessEnv augmentation declaring all external-service env vars
5583
- - `vercel.json` — Two cron schedule declarations (03:00 UTC reset-sandbox; 18:00 UTC sync-currencies)
5586
+ - `vercel.json` — Two cron schedule declarations (every-15-minutes reset-sandbox; 18:00 UTC sync-currencies)
5584
5587
  - `nx.json` — Nx workspace orchestration and target defaults
5585
5588
  - `eslint.config.mjs` — `@nx/enforce-module-boundaries` rules for scope:public/scope:premium
5586
5589
  - `tsconfig.base.json` — Path aliases for all `@nextblock-cms/*` packages
@@ -5643,7 +5646,7 @@ The design leans on the Supabase managed platform for replication, connection po
5643
5646
 
5644
5647
  #### 6.2.2.1 Enumerated Types
5645
5648
 
5646
- All domain-specific enumerations are declared in migration `00000000000000_setup_foundation_and_enums.sql` using idempotent `DO $$` guards so re-application is safe. The migration also grants `USAGE` on the `public` schema to the Supabase-managed roles `postgres`, `anon`, `authenticated`, and `service_role`.
5649
+ All domain-specific enumerations are declared in migration `02001_baseline_schema.sql` using idempotent `DO $$` guards so re-application is safe. The migration also grants `USAGE` on the `public` schema to the Supabase-managed roles `postgres`, `anon`, `authenticated`, and `service_role`.
5647
5650
 
5648
5651
  | Enum | Values | Usage |
5649
5652
  |------|--------|-------|
@@ -5654,7 +5657,7 @@ All domain-specific enumerations are declared in migration `00000000000000_setup
5654
5657
 
5655
5658
  #### 6.2.2.2 Identity and Core CMS Entities
5656
5659
 
5657
- Declared in migration `00000000000001_setup_cms_core.sql`, these tables bootstrap the authentication mirror, languages, media registry, translations, logos, and the global key/value settings store.
5660
+ Declared in migration `02001_baseline_schema.sql`, these tables bootstrap the authentication mirror, languages, media registry, translations, logos, and the global key/value settings store.
5658
5661
 
5659
5662
  | Table | Primary Key | Key Columns / Constraints |
5660
5663
  |-------|-------------|----------------------------|
@@ -5673,7 +5676,7 @@ Declared in migration `00000000000001_setup_cms_core.sql`, these tables bootstra
5673
5676
 
5674
5677
  #### 6.2.2.3 Authoring Content Entities
5675
5678
 
5676
- Declared in migration `00000000000002_setup_content_tables.sql`, these tables form the CMS authoring domain. The relationships are summarized in the ER diagram below.
5679
+ Declared in migration `02001_baseline_schema.sql`, these tables form the CMS authoring domain. The relationships are summarized in the ER diagram below.
5677
5680
 
5678
5681
  ```mermaid
5679
5682
  erDiagram
@@ -5760,7 +5763,7 @@ erDiagram
5760
5763
 
5761
5764
  #### 6.2.2.4 Commerce Entities
5762
5765
 
5763
- Declared across migrations `00000000000003_setup_catalog_and_licensing.sql` and `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql`, commerce spans catalog, licensing, orders, shipping, taxation, and currency infrastructure.
5766
+ Declared across migration `02001_baseline_schema.sql`, commerce spans catalog, licensing, orders, shipping, taxation, and currency infrastructure.
5764
5767
 
5765
5768
  ```mermaid
5766
5769
  erDiagram
@@ -5896,7 +5899,7 @@ Both `products` and `product_variants` store prices in two redundant shapes —
5896
5899
 
5897
5900
  #### 6.2.2.5 Indexing Strategy
5898
5901
 
5899
- Migration `00000000000007_setup_indexes.sql` adds 38 secondary indexes. The strategy groups into four categories:
5902
+ Migration `02001_baseline_schema.sql` adds 38 secondary indexes. The strategy groups into four categories:
5900
5903
 
5901
5904
  | Category | Representative Indexes | Optimization Target |
5902
5905
  |----------|------------------------|---------------------|
@@ -5957,7 +5960,7 @@ flowchart TB
5957
5960
  | Manual restore | `psql` with duplicate-error collapser | `apps/nextblock/scripts/restore.js` |
5958
5961
  | Schema reconstruction | Replay 11 migrations in order | `npm run db:reset` via `supabase db reset --workdir libs/db/src` |
5959
5962
  | Content rollback | Hybrid snapshot/diff revisions | `page_revisions`, `post_revisions` (F-008) |
5960
- | Sandbox rehearsal | Daily 03:00 UTC cron, 60s maxDuration | `/api/cron/reset-sandbox` (§4.5) |
5963
+ | Sandbox rehearsal | Every 15 minutes, 60s maxDuration | `/api/cron/reset-sandbox` (§4.5) |
5961
5964
 
5962
5965
  The `backup.js` script reads `POSTGRES_URL` or `DATABASE_URL` via `dotenv`, parses the connection URL, supports a `--name` CLI flag (or interactive prompt) for friendly backup names, creates timestamped directories under `apps/nextblock/backups/`, and spawns `pg_dump` with `PGPASSWORD` and `PGSSLMODE` passed via environment variables.
5963
5966
 
@@ -5967,29 +5970,17 @@ The `backup.js` script reads `POSTGRES_URL` or `DATABASE_URL` via `dotenv`, pars
5967
5970
 
5968
5971
  #### 6.2.3.1 Migration Procedures
5969
5972
 
5970
- Migrations live in `libs/db/src/supabase/migrations/` and are managed via the Supabase CLI. Each file is a single SQL script prefixed by a lexicographically ordered timestamp. The **eleven consolidated migrations** represent merged historical migrations; internal comment headers preserve the original logical ordering within each file.
5973
+ Migrations live in `libs/db/src/supabase/migrations/` and are managed via the Supabase CLI plus the repo's own appliers (the `/setup` wizard, the build hook, `npm run update`, the Docker migration runner). The folder holds exactly one **squash generation**; files are named `GGNNN_name.sql` (GG = generation, NNN = contiguous sequence) and applied in lexical order. The current generation is 2 (built 2026-09-10); its first five slots are fixed and everything from `02005` upward is an ordinary forward migration.
5971
5974
 
5972
5975
  | File | Purpose | Key Outputs |
5973
5976
  |------|---------|-------------|
5974
- | `00000000000000_setup_foundation_and_enums.sql` | Foundation grants and enums | `user_role`, `page_status`, `menu_location`, `revision_type` |
5975
- | `00000000000001_setup_cms_core.sql` | Identity + content primitives | `profiles`, `user_addresses`, `languages`, `media`, `translations`, `logos`, `site_settings` |
5976
- | `00000000000002_setup_content_tables.sql` | Authoring domain | `pages`, `posts`, `blocks`, `navigation_items`, `page_revisions`, `post_revisions` |
5977
- | `00000000000003_setup_catalog_and_licensing.sql` | Commerce catalog and license registry | `products`, `product_variants`, `product_attributes`, `product_attribute_terms`, `product_media`, `variant_attribute_mapping`, `inventory_items`, `package_activations`, `freemius_plans`, `freemius_pricing` |
5978
- | `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` | Order lifecycle + shipping/tax + currencies | `orders`, `order_items`, `order_invoice_number_seq`, `shipping_zones`, `shipping_zone_locations`, `shipping_zone_methods`, `tax_rates`, `currencies` + 12 functions + 7 triggers |
5979
- | `00000000000005_setup_functions_and_triggers.sql` | Business logic layer | Auth helpers, first-user bootstrap, timestamp triggers, invoice sequence, inventory RPC |
5980
- | `00000000000006_setup_rls_and_grants.sql` | Security policies | Enables RLS on all tables; declares SELECT/INSERT/UPDATE/DELETE policies |
5981
- | `00000000000007_setup_indexes.sql` | Performance indexes | 38 indexes across all domains |
5982
- | `00000000000008_seed_platform_defaults.sql` | Baseline platform state | English/French languages, USD as default currency, site_settings defaults |
5983
- | `00000000000009_seed_translations.sql` | Internationalization content | Hundreds of en/es/fr i18n rows |
5984
- | `00000000000010_seed_content_scaffold.sql` | Starter content | Default logos, home/articles pages, featured posts, navigation |
5985
- | `00000000000011_setup_cortex_ai_settings.sql` | AI settings | Cortex AI configuration |
5986
- | `00000000000012_setup_commerce_coupons.sql` | Commerce coupons | Coupon tables and constraints |
5987
- | `00000000000013_setup_cortex_ai_db_mutation_audit.sql` | AI audit | Cortex AI database mutation audit support |
5988
- | `00000000000014_setup_content_drafts.sql` | Content drafts | Visual-editing draft support |
5989
- | `00000000000015_setup_product_drafts.sql` | Product drafts | Product draft workflow support |
5990
- | `00000000000016_add_feature_image_to_pages.sql` | CMS page media | Optional page feature image relationship |
5991
-
5992
- Per Section 2.4.5, the **numbered migration files in `libs/db/src/supabase/migrations/` must remain applied in order**; out-of-sequence application will violate referential integrity. For live/shared databases, new changes must be appended as new non-destructive migrations. Do not rewrite, recycle, squash, reorder, or delete migrations that may already be recorded in production.
5977
+ | `02000_catchup_gen1.sql` | Version-aware replay of the retired generation-1 forward migrations | Converges databases that sat behind generation 1; skipped on empty databases and on databases already at generation ≥ 2; sets `site_settings.migration_baseline_generation` |
5978
+ | `02001_baseline_schema.sql` | Schema | All enums, functions, tables, sequences and defaults (`IF NOT EXISTS` / `CREATE OR REPLACE`); the `auth.users` `handle_new_user` trigger |
5979
+ | `02002_baseline_constraints_and_indexes.sql` | Integrity + performance | Primary/unique/check/foreign-key constraints (catalog-guarded) and every index |
5980
+ | `02003_baseline_security_and_grants.sql` | Security | RLS enablement on every table, all policies, business/timestamp triggers, grants |
5981
+ | `02004_baseline_seed.sql` | Seed | Canonical demo content (languages, currencies, site settings, translations, media, pages/posts/blocks, navigation, shipping defaults, themes) runs only on an empty database |
5982
+
5983
+ Per Section 2.4.5, the **numbered migration files in `libs/db/src/supabase/migrations/` must remain applied in order**; out-of-sequence application will violate referential integrity. For live/shared databases, new changes must be appended as new non-destructive migrations. Do not rewrite, recycle, reorder, or delete migration files outside the documented squash runbook (`docs/04-DATABASE-AND-AUTH.md` "Squashing migrations"); a squash always ships a catch-up so existing databases converge.
5993
5984
 
5994
5985
  ##### 6.2.3.1.1 Migration Command Surface
5995
5986
 
@@ -6000,7 +5991,7 @@ The root `package.json` exposes the following migration-related scripts:
6000
5991
  | `db:migrate:check` | `node tools/scripts/push-db-migrations.js --check` | Dry-run pending remote migrations |
6001
5992
  | `db:migrate` / `db:push` | `node tools/scripts/push-db-migrations.js --confirm` | Apply pending migration files only; no reset, sandbox seed, function deploy, or config push |
6002
5993
  | `db:migrate:repair-history:check` | `node tools/scripts/repair-db-migration-history.js --check` | Preview baseline migration-history repair |
6003
- | `db:migrate:repair-history` | `node tools/scripts/repair-db-migration-history.js --confirm` | Mark existing baseline migrations as applied without running their SQL |
5994
+ | `db:migrate:repair-history` | `node tools/scripts/repair-db-migration-history.js --confirm` | Mark existing baseline migrations as applied without running their SQL; with `-- --reconcile-squash`, record a migration squash (revert retired versions, mark the new generation applied) |
6004
5995
  | `db:migrate:fresh` | `node tools/scripts/push-db-migrations.js --confirm --allow-baseline-replay` | Apply the full baseline only to a brand-new empty database |
6005
5996
  | `db:reset` | `supabase db reset --workdir libs/db/src` | Full local reset and replay |
6006
5997
  | `db:link` | `dotenv + supabase-link` via `tools/scripts/supabase-link.js` | Link local workspace to remote project |
@@ -6191,11 +6182,11 @@ All three paths **fail closed**: missing env vars, query errors, or non-`active`
6191
6182
 
6192
6183
  #### 6.2.4.2 Backup and Fault-Tolerance Policies
6193
6184
 
6194
- Detailed in §6.2.2.8. In summary: Supabase-managed PITR and managed backups (primary DR); `pg_dump` / `psql` scripts (operator-initiated); 11 migrations as schema-of-truth (rebuild); hybrid snapshot/diff revisions (content rollback). The Sandbox Reset Cron at 03:00 UTC daily (60s `maxDuration`) doubles as a reconstruction rehearsal, as documented in §4.5.
6185
+ Detailed in §6.2.2.8. In summary: Supabase-managed PITR and managed backups (primary DR); `pg_dump` / `psql` scripts (operator-initiated); 11 migrations as schema-of-truth (rebuild); hybrid snapshot/diff revisions (content rollback). The Sandbox Reset Cron every 15 minutes (60s `maxDuration`) doubles as a reconstruction rehearsal, as documented in §4.5.
6195
6186
 
6196
6187
  #### 6.2.4.3 Privacy Controls via Row-Level Security
6197
6188
 
6198
- RLS is **enabled on every table** in migration `00000000000006_setup_rls_and_grants.sql`. The policy matrix distributes into four access tiers.
6189
+ RLS is **enabled on every table** in migration `02001_baseline_schema.sql`. The policy matrix distributes into four access tiers.
6199
6190
 
6200
6191
  | Access Tier | Representative Scope | Tables |
6201
6192
  |-------------|----------------------|--------|
@@ -6327,7 +6318,7 @@ Application-layer details:
6327
6318
 
6328
6319
  | Batch Workload | Mechanism | Frequency |
6329
6320
  |----------------|-----------|-----------|
6330
- | Sandbox reset (cron) | Service-role client replays seed migrations | Daily 03:00 UTC, 60s `maxDuration` |
6321
+ | Sandbox reset (cron) | Service-role client replays seed migrations | Every 15 minutes, 60s `maxDuration` |
6331
6322
  | Currency exchange-rate sync (cron) | Service-role client updates `currencies.exchange_rate` from Frankfurter API | Daily 18:00 UTC, 30s `maxDuration` |
6332
6323
  | Bulk variant upsert | `upsert_product_with_variants(jsonb)` PL/pgSQL function — admin-only via `is_admin()` | Ad-hoc, CMS-triggered |
6333
6324
  | Migration seeds | Direct SQL in migrations `00000000000008` through `00000000000010` with `ON CONFLICT` merge logic preserving existing values | At deploy time |
@@ -6479,17 +6470,17 @@ The local Supabase stack is configured through `libs/db/src/supabase/config.toml
6479
6470
 
6480
6471
  #### 6.2.9.1 Files Examined
6481
6472
 
6482
- - `libs/db/src/supabase/migrations/00000000000000_setup_foundation_and_enums.sql` — Enums and schema grants
6483
- - `libs/db/src/supabase/migrations/00000000000001_setup_cms_core.sql` — Core CMS tables (profiles, media, languages, translations)
6484
- - `libs/db/src/supabase/migrations/00000000000002_setup_content_tables.sql` — Pages, posts, blocks, revisions, navigation
6485
- - `libs/db/src/supabase/migrations/00000000000003_setup_catalog_and_licensing.sql` — Commerce catalog and license registry
6486
- - `libs/db/src/supabase/migrations/00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` — Orders, shipping, tax, currencies
6487
- - `libs/db/src/supabase/migrations/00000000000005_setup_functions_and_triggers.sql` — Business logic functions and triggers
6488
- - `libs/db/src/supabase/migrations/00000000000006_setup_rls_and_grants.sql` — Row-Level Security policy matrix
6489
- - `libs/db/src/supabase/migrations/00000000000007_setup_indexes.sql` — 38 secondary indexes
6490
- - `libs/db/src/supabase/migrations/00000000000008_seed_platform_defaults.sql` — Baseline platform seed
6491
- - `libs/db/src/supabase/migrations/00000000000009_seed_translations.sql` — Internationalization seed
6492
- - `libs/db/src/supabase/migrations/00000000000010_seed_content_scaffold.sql` — Starter content seed
6473
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Enums and schema grants
6474
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Core CMS tables (profiles, media, languages, translations)
6475
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Pages, posts, blocks, revisions, navigation
6476
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Commerce catalog and license registry
6477
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Orders, shipping, tax, currencies
6478
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Business logic functions and triggers
6479
+ - `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — Row-Level Security policy matrix
6480
+ - `libs/db/src/supabase/migrations/02002_baseline_constraints_and_indexes.sql` — 38 secondary indexes
6481
+ - `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Baseline platform seed
6482
+ - `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Internationalization seed
6483
+ - `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Starter content seed
6493
6484
  - `libs/db/src/supabase/config.toml` — Supabase local stack configuration
6494
6485
  - `libs/db/src/lib/supabase/client.ts` — Browser client factory
6495
6486
  - `libs/db/src/lib/supabase/server.ts` — Server / SSR / service-role client factories
@@ -6829,7 +6820,7 @@ All "events" in this system are either **HTTP webhooks delivered by external pro
6829
6820
  | Stripe | `checkout.session.completed` (others ignored) | `libs/ecommerce/src/lib/stripe/webhooks.ts` |
6830
6821
  | Freemius | `install.upgraded`, `license.activated` (others ignored) | `apps/nextblock/app/api/webhooks/freemius/route.ts` |
6831
6822
  | Supabase DB Triggers | Row INSERT/UPDATE/DELETE on `pages` / `posts` | `apps/nextblock/app/api/revalidate/route.ts` |
6832
- | Vercel Cron Ticks | Scheduled GET at `0 3 * * *` / `0 18 * * *` | `/api/cron/reset-sandbox`, `/api/cron/sync-currencies` |
6823
+ | Vercel Cron Ticks | Scheduled GET at `*/15 * * * *` / `0 18 * * *` | `/api/cron/reset-sandbox`, `/api/cron/sync-currencies` |
6833
6824
 
6834
6825
  ##### 6.3.3.1.2 Stripe Event Dispatcher Discipline
6835
6826
 
@@ -6862,12 +6853,12 @@ The revalidation flow functions as a degenerate "stream" with batch size 1: ever
6862
6853
 
6863
6854
  #### 6.3.3.4 Batch Processing Flows
6864
6855
 
6865
- Two daily Vercel cron jobs constitute the entire batch processing surface, declared explicitly in `vercel.json`:
6856
+ Two Vercel cron jobs constitute the entire batch processing surface, declared explicitly in `vercel.json`:
6866
6857
 
6867
6858
  ```mermaid
6868
6859
  flowchart LR
6869
6860
  subgraph CronDecl["vercel.json crons[]"]
6870
- A[path: /api/cron/reset-sandbox<br/>schedule: 0 3 * * *]
6861
+ A[path: /api/cron/reset-sandbox<br/>schedule: */15 * * * *]
6871
6862
  B[path: /api/cron/sync-currencies<br/>schedule: 0 18 * * *]
6872
6863
  end
6873
6864
 
@@ -6916,7 +6907,7 @@ flowchart LR
6916
6907
  SA6 --> SA7
6917
6908
  ```
6918
6909
 
6919
- ##### 6.3.3.4.1 Sandbox Reset Cron (03:00 UTC, maxDuration 60s)
6910
+ ##### 6.3.3.4.1 Sandbox Reset Cron (every 15 minutes, maxDuration 60s)
6920
6911
 
6921
6912
  Purpose: Reconstruct the public demo environment from scratch so that visitor-driven modifications do not pollute the demo. The handler executes eleven ordered steps including R2 bucket clearing via `ListObjectsV2Command` + `DeleteObjectsCommand`, seed asset re-upload via `PutObjectCommand`, SQL bootstrap via the `postgres` driver, media record normalization, Freemius product synchronization, and commerce catalog seeding. The `finally { db.end() }` block guarantees Postgres connection teardown even on error.
6922
6913
 
@@ -7370,7 +7361,7 @@ Per Section 5.1.4.2, each external integration carries explicit SLA-like propert
7370
7361
  | Freemius | HTTPS + `x-freemius-signature` HMAC-SHA-256 | Sandbox bypass when `NEXT_PUBLIC_IS_SANDBOX===true` |
7371
7362
  | Frankfurter | HTTPS (JSON) | Cron daily 18:00 UTC; `maxDuration: 30s` |
7372
7363
  | SMTP | SMTP + TLS | Best-effort from server actions |
7373
- | Vercel Cron | HTTPS + Bearer CRON_SECRET | 03:00 UTC (reset-sandbox 60s); 18:00 UTC (sync-currencies 30s) |
7364
+ | Vercel Cron | HTTPS + Bearer CRON_SECRET | every 15 min (reset-sandbox 60s); 18:00 UTC (sync-currencies 30s) |
7374
7365
  | Google Tag Manager | HTTPS (JS) | GTM id from `privacy_settings` (site_settings); allowlisted in CSP |
7375
7366
 
7376
7367
  #### 6.3.6.2 Known Integration Limitations
@@ -7403,7 +7394,7 @@ The `shipping_zone_locations.postal_code` column exists in the schema but the cu
7403
7394
  - **Four canonical patterns** (outbound HTTPS, signed webhook, token webhook, scheduled cron) cover every integration.
7404
7395
  - **Seven authentication mechanisms** coexist, each tailored to its upstream provider's requirements.
7405
7396
  - **Three-layer authorization** (cookie session → path-prefix guard → RLS + license gate) provides defense in depth.
7406
- - **Batch processing** is limited to two daily Vercel crons (sandbox reset at 03:00 UTC, currency sync at 18:00 UTC).
7397
+ - **Batch processing** is limited to two Vercel crons (sandbox reset every 15 minutes, currency sync daily at 18:00 UTC).
7407
7398
  - **Error handling** is classified into strict-fail, dual-path, and best-effort categories with the RPC + SQL fallback for inventory deduction being the most sophisticated resilience primitive.
7408
7399
  - **Known limitations** include incomplete Freemius reconciliation, a package alias mismatch, FX schema coupling, and an unused postal-code column.
7409
7400
 
@@ -7428,7 +7419,7 @@ The `shipping_zone_locations.postal_code` column exists in the schema but the cu
7428
7419
 
7429
7420
  #### Files Examined
7430
7421
 
7431
- - `vercel.json` — Cron schedule declarations (`reset-sandbox` at 03:00 UTC, `sync-currencies` at 18:00 UTC)
7422
+ - `vercel.json` — Cron schedule declarations (`reset-sandbox` every 15 minutes, `sync-currencies` at 18:00 UTC)
7432
7423
  - `libs/environment.d.ts` — NodeJS.ProcessEnv augmentation declaring all external-integration environment variables
7433
7424
  - `.env.example` — Authoritative environment variable template
7434
7425
  - `apps/nextblock/proxy.ts` — 272-line request proxy consolidating session sync, RBAC, locale, security headers, CSP, and page-type classification
@@ -7507,7 +7498,7 @@ Security controls are layered so that the failure of any single layer does not r
7507
7498
  |-------|----------------------|------------------|
7508
7499
  | Edge | Request proxy — session refresh, path-prefix RBAC, security headers, CSP | `apps/nextblock/proxy.ts` |
7509
7500
  | Application | Client-layout role guard, server-action `verifyAdmin()`, role-gated page components | `app/cms/CmsClientLayout.tsx`, `app/cms/users/actions.ts` |
7510
- | Data | Row-Level Security policies, SECURITY DEFINER helpers, service-role bypass | `libs/db/src/supabase/migrations/00000000000006_setup_rls_and_grants.sql` |
7501
+ | Data | Row-Level Security policies, SECURITY DEFINER helpers, service-role bypass | `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` |
7511
7502
  | Integration | Webhook signature verification, Bearer-secret cron, HMAC-verified callbacks | `/api/webhooks/*`, `/api/cron/*`, `/api/revalidate/route.ts` |
7512
7503
 
7513
7504
  #### 6.4.1.3 Security Architecture Topology
@@ -7617,7 +7608,7 @@ The system manages three distinct token classes, each with differentiated lifecy
7617
7608
  | OAuth Authorization Code | Short-lived code exchange | URL query parameter, consumed immediately at `/auth/callback` |
7618
7609
  | Supabase Service Role Key | Server-only RLS bypass | Vercel environment variable (`SUPABASE_SERVICE_ROLE_KEY`) |
7619
7610
 
7620
- A **per-request nonce** is generated via `crypto.randomUUID()` in `proxy.ts` and used for the nonce-based CSP (see §6.4.4.5). JWT claims are read inside Postgres by the `get_my_claim(claim text)` SECURITY DEFINER function via `current_setting('request.jwt.claims', true)::jsonb`, which is defined in `libs/db/src/supabase/migrations/00000000000005_setup_functions_and_triggers.sql`.
7611
+ A **per-request nonce** is generated via `crypto.randomUUID()` in `proxy.ts` and used for the nonce-based CSP (see §6.4.4.5). JWT claims are read inside Postgres by the `get_my_claim(claim text)` SECURITY DEFINER function via `current_setting('request.jwt.claims', true)::jsonb`, which is defined in `libs/db/src/supabase/migrations/02001_baseline_schema.sql`.
7621
7612
 
7622
7613
  The **service role key** requires two independent compile-time and runtime guards to prevent leakage into the browser bundle:
7623
7614
 
@@ -7715,7 +7706,7 @@ The `resolvePostAuthRedirect` helper enforces four rules as a gate against open-
7715
7706
 
7716
7707
  #### 6.4.3.1 Role-Based Access Control Model
7717
7708
 
7718
- Authorization is driven by the `user_role` enum declared in migration `00000000000000_setup_foundation_and_enums.sql`, which admits three values plus an implicit fourth (Supabase's `service_role`) for server-only privilege elevation:
7709
+ Authorization is driven by the `user_role` enum declared in migration `02001_baseline_schema.sql`, which admits three values plus an implicit fourth (Supabase's `service_role`) for server-only privilege elevation:
7719
7710
 
7720
7711
  | Role | Assignment Mechanism | Zone Capabilities |
7721
7712
  |------|---------------------|-------------------|
@@ -7728,7 +7719,7 @@ Role assignment at bootstrap is governed by the `handle_new_user()` SECURITY DEF
7728
7719
 
7729
7720
  #### 6.4.3.2 Permission Management and SECURITY DEFINER Helpers
7730
7721
 
7731
- Role checks executed inside SQL contexts use SECURITY DEFINER helper functions declared in `libs/db/src/supabase/migrations/00000000000005_setup_functions_and_triggers.sql`. SECURITY DEFINER elevates the function's execution privileges to the function owner, bypassing caller-side RLS for the function body while `SET search_path = ''` prevents search-path hijacking attacks.
7722
+ Role checks executed inside SQL contexts use SECURITY DEFINER helper functions declared in `libs/db/src/supabase/migrations/02001_baseline_schema.sql`. SECURITY DEFINER elevates the function's execution privileges to the function owner, bypassing caller-side RLS for the function body while `SET search_path = ''` prevents search-path hijacking attacks.
7732
7723
 
7733
7724
  | Function | Purpose | Security Annotation |
7734
7725
  |----------|---------|---------------------|
@@ -7741,7 +7732,7 @@ These helpers form the primitive vocabulary of all RLS policies: a user's effect
7741
7732
 
7742
7733
  #### 6.4.3.3 Resource Authorization Matrix
7743
7734
 
7744
- RLS is enabled on every table in migration `00000000000006_setup_rls_and_grants.sql` (872 lines). The matrix distributes into four access tiers:
7735
+ RLS is enabled on every table in migration `02001_baseline_schema.sql` (872 lines). The matrix distributes into four access tiers:
7745
7736
 
7746
7737
  | Access Tier | Typical Policy Predicate | Representative Tables |
7747
7738
  |-------------|--------------------------|------------------------|
@@ -8127,7 +8118,7 @@ The security control matrix below enumerates the principal threats, their mitiga
8127
8118
 
8128
8119
  | Threat | Mitigation | Migration or File |
8129
8120
  |--------|-----------|-------------------|
8130
- | Public data exposure through writes | RLS writes restricted; public read only for intended tables and statuses | `migrations/00000000000006_setup_rls_and_grants.sql` |
8121
+ | Public data exposure through writes | RLS writes restricted; public read only for intended tables and statuses | `migrations/02003_baseline_security_and_grants.sql` |
8131
8122
  | Draft content leakage | Anon policy `status='published'` on pages; `status='published' AND published_at<=now()` on posts | `migration 00000000000006` |
8132
8123
  | Path traversal in uploads | `sanitizeFolder()` collapses `../`, strips leading slashes, removes illegal chars | `/api/upload/presigned-url/route.ts` |
8133
8124
  | Search-path hijacking in SECURITY DEFINER | `SET search_path = ''` on auth helper functions | `migration 00000000000005` |
@@ -8196,7 +8187,7 @@ For details beyond the scope of this section, consult:
8196
8187
 
8197
8188
  - `apps/nextblock/proxy.ts` — 272-line edge proxy (session sync, RBAC, locale, CSP, headers, page-type signaling)
8198
8189
  - `apps/nextblock/next.config.js` — 91-line image security config + `compiler.removeConsole`
8199
- - `apps/nextblock/vercel.json` — Cron schedule declarations (03:00 reset-sandbox, 18:00 sync-currencies)
8190
+ - `apps/nextblock/vercel.json` — Cron schedule declarations (every-15-minutes reset-sandbox, 18:00 sync-currencies)
8200
8191
 
8201
8192
  #### Authentication and Authorization
8202
8193
 
@@ -8226,9 +8217,9 @@ For details beyond the scope of this section, consult:
8226
8217
 
8227
8218
  #### Database Security Surface
8228
8219
 
8229
- - `libs/db/src/supabase/migrations/00000000000000_setup_foundation_and_enums.sql` — `user_role` enum declaration
8230
- - `libs/db/src/supabase/migrations/00000000000005_setup_functions_and_triggers.sql` — `handle_new_user`, `get_current_user_role`, `is_admin`, `get_my_claim` SECURITY DEFINER functions
8231
- - `libs/db/src/supabase/migrations/00000000000006_setup_rls_and_grants.sql` — 872-line RLS policy matrix
8220
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `user_role` enum declaration
8221
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `handle_new_user`, `get_current_user_role`, `is_admin`, `get_my_claim` SECURITY DEFINER functions
8222
+ - `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — 872-line RLS policy matrix
8232
8223
  - `libs/db/src/supabase/config.toml` — 341-line Supabase Auth + MFA + rate-limit configuration
8233
8224
  - `libs/db/src/supabase/templates/` — Six transactional email templates
8234
8225
 
@@ -8546,7 +8537,7 @@ No dedicated `/api/health` or `/api/readiness` endpoint exists in the repository
8546
8537
 
8547
8538
  ##### 6.5.3.1.2 Scheduled-Execution Health Signal
8548
8539
 
8549
- The two Vercel cron schedules in `vercel.json` serve a dual purpose: they perform their business function (sandbox reset and currency sync) while simultaneously producing a daily heartbeat that is visible on the Vercel dashboard. A cron invocation that returns HTTP 500 is therefore the closest the system comes to an automated unhealthy signal, because it is automatically surfaced in Vercel's cron execution history.
8540
+ The two Vercel cron schedules in `vercel.json` serve a dual purpose: they perform their business function (sandbox reset and currency sync) while simultaneously producing a recurring heartbeat that is visible on the Vercel dashboard. A cron invocation that returns HTTP 500 is therefore the closest the system comes to an automated unhealthy signal, because it is automatically surfaced in Vercel's cron execution history.
8550
8541
 
8551
8542
  #### 6.5.3.2 Performance Metrics
8552
8543
 
@@ -8608,7 +8599,7 @@ SLAs are encoded directly in configuration files (`vercel.json`, `next.config.js
8608
8599
  | Sync-currencies `maxDuration` | 30 seconds | `vercel.json` + route |
8609
8600
  | Reset-sandbox `maxDuration` | 60 seconds | `vercel.json` + route |
8610
8601
  | Currency sync schedule | `0 18 * * *` (18:00 UTC daily) | `vercel.json` |
8611
- | Sandbox reset schedule | `0 3 * * *` (03:00 UTC daily) | `vercel.json` |
8602
+ | Sandbox reset schedule | `*/15 * * * *` (every 15 minutes) | `vercel.json` |
8612
8603
  | Max source image width | 2560 pixels | `/api/process-image` |
8613
8604
  | Lighthouse performance target | 100/100 | `README.md` |
8614
8605
  | CLI scaffold target | ≤ 30 seconds | `README.md` |
@@ -8651,9 +8642,9 @@ flowchart LR
8651
8642
  HstsHeader[HSTS Header<br/>2 years max-age]
8652
8643
  end
8653
8644
 
8654
- subgraph Scheduled[" Scheduled Daily "]
8645
+ subgraph Scheduled[" Scheduled (Vercel Cron) "]
8655
8646
  CurrencyDay[18:00 UTC<br/>Sync Currencies]
8656
- ResetDay[03:00 UTC<br/>Sandbox Reset]
8647
+ ResetDay[Every 15 min<br/>Sandbox Reset]
8657
8648
  end
8658
8649
  ```
8659
8650
 
@@ -8764,7 +8755,7 @@ Operators have four primary recovery mechanisms, all described in detail in Sect
8764
8755
  1. **Content rollback via revisions.** `page_revisions` and `post_revisions` tables store hybrid snapshot/diff records (JSON Patch via `fast-json-patch`) keyed by `UNIQUE (page_id, version)`. Any published state can be restored without a database restore.
8765
8756
  2. **Supabase Point-in-Time Recovery.** Platform-level PITR is the authoritative recovery path for catastrophic data loss.
8766
8757
  3. **Schema reconstruction from migrations.** The eleven canonical SQL files in `libs/db/src/supabase/migrations/` (`000_foundation_and_enums` through `010_seed_content_scaffold`) allow deterministic schema rebuild on a fresh Supabase project.
8767
- 4. **Nightly sandbox reset as reconstruction rehearsal.** The 03:00 UTC `/api/cron/reset-sandbox` job exercises the full R2-clear → SQL-bootstrap → media-normalize → seed pipeline daily, serving as continuous validation that the recovery procedure still works.
8758
+ 4. **15-minute sandbox reset as reconstruction rehearsal.** The 15-minute `/api/cron/reset-sandbox` job exercises the full R2-clear → SQL-bootstrap → media-normalize → seed pipeline every 15 minutes, serving as continuous validation that the recovery procedure still works.
8768
8759
 
8769
8760
  #### 6.5.4.5 Post-mortem Processes
8770
8761
 
@@ -8856,7 +8847,7 @@ The following observability gaps are acknowledged and documented for honest stak
8856
8847
  - **Alert routing is manual.** The primary alert path is the `FeedbackModal` → `submitFeedback` → SMTP → `feedback@nextblock.dev` flow; platform alerts are configured in Vercel/Supabase consoles.
8857
8848
  - **No distributed tracing, no alert manager, no runbooks, no post-mortems, no persistent audit log table.** These absences are deliberate for the system's scale and are documented as explicit gaps.
8858
8849
  - **SLAs are codified in config, not monitored actively.** The canonical SLA table (Section 4.12) lists all timing constraints; crossings surface as HTTP errors or terminated function invocations in the Vercel log stream.
8859
- - **Disaster recovery rests on Supabase PITR, canonical migrations, content revisions, and the nightly sandbox-reset reconstruction rehearsal** (detailed in Section 6.1.6 and Section 5.4.6).
8850
+ - **Disaster recovery rests on Supabase PITR, canonical migrations, content revisions, and the 15-minute sandbox-reset reconstruction rehearsal** (detailed in Section 6.1.6 and Section 5.4.6).
8860
8851
 
8861
8852
  #### 6.5.7.2 Cross-References to Related Sections
8862
8853
 
@@ -8899,7 +8890,7 @@ The following observability gaps are acknowledged and documented for honest stak
8899
8890
  - `libs/ecommerce/src/lib/stripe/webhooks.ts` — Stripe webhook handler with `[Stripe Webhook Error]` prefix; `console.error` on missing `STRIPE_WEBHOOK_SECRET` and on `constructEvent` failure
8900
8891
  - `libs/db/src/lib/package-validation.ts` — License gate with `console.error` and 60-second `unstable_cache` tagged `'package-activation'`
8901
8892
  - `libs/environment.d.ts` — `NodeJS.ProcessEnv` augmentation declaring external-service env vars (Supabase, R2/S3, SMTP, Freemius, OpenRouter/Cortex AI). GTM is no longer env-configured — it lives in `privacy_settings`.
8902
- - `vercel.json` — Two cron schedule declarations: `0 3 * * *` reset-sandbox (60s) and `0 18 * * *` sync-currencies (30s)
8893
+ - `vercel.json` — Two cron schedule declarations: `*/15 * * * *` reset-sandbox (60s) and `0 18 * * *` sync-currencies (30s)
8903
8894
  - `package.json` (root) — Dependency declarations including `@vercel/speed-insights` and `@next/third-parties`
8904
8895
  - `apps/nextblock/package.json` — Template-level dependency declarations including `@vercel/analytics` (declared but not imported)
8905
8896
  - `docs/05-DEVELOPER-GUIDE.md` — Operational handbook, deployment notes, sandbox operations
@@ -9102,7 +9093,7 @@ Because no integration test suite exists, there is no dedicated test environment
9102
9093
  |:--|:--|
9103
9094
  | Local Supabase via `libs/db/src/supabase/config.toml` | Developer database for hand-driven verification |
9104
9095
  | Vercel Preview Deployments | Per-branch verification prior to production |
9105
- | Sandbox production environment | End-to-end smoke testing via `NEXT_PUBLIC_IS_SANDBOX=true` flag with nightly reset cron |
9096
+ | Sandbox production environment | End-to-end smoke testing via `NEXT_PUBLIC_IS_SANDBOX=true` flag with a 15-minute reset cron |
9106
9097
 
9107
9098
  ---
9108
9099
 
@@ -9112,7 +9103,7 @@ Because no integration test suite exists, there is no dedicated test environment
9112
9103
 
9113
9104
  No E2E test framework (Playwright, Cypress, WebdriverIO, Puppeteer) is installed. This is explicitly documented as the accepted tradeoff in ADR §5.3.1: *"Vitest-only — Limited end-to-end coverage."* The absence is compensated by:
9114
9105
 
9115
- 1. **Sandbox reset cron as reconstruction rehearsal.** The `/api/cron/reset-sandbox` endpoint (§6.1.6.2) runs nightly at 03:00 UTC, clearing R2, running `SANDBOX_RESET_SQL`, normalizing legacy media, re-seeding commerce and content. A successful reset validates that the full happy-path data pipeline — migrations, storage, seeding — functions end-to-end.
9106
+ 1. **Sandbox reset cron as reconstruction rehearsal.** The `/api/cron/reset-sandbox` endpoint (§6.1.6.2) runs every 15 minutes, clearing R2, running `SANDBOX_RESET_SQL`, normalizing legacy media, re-seeding commerce and content. A successful reset validates that the full happy-path data pipeline — migrations, storage, seeding — functions end-to-end.
9116
9107
  2. **Lighthouse performance score targeting 100/100.** Per §5.3.4 and §5.4.5, the 100/100 Lighthouse target provides an observational E2E signal on a per-deployment basis.
9117
9108
  3. **Production monitoring via Vercel Speed Insights + structured `console.warn`/`console.error`.** Per §6.4.3.5, production diagnostics survive `compiler.removeConsole` stripping and provide post-release observability.
9118
9109
 
@@ -9545,7 +9536,7 @@ The accepted tradeoff in §5.3.1 ("Vitest-only — Limited end-to-end coverage")
9545
9536
  - `apps/create-nextblock/package.json` — npm-init stub `"test": "echo \"Error: no test specified\" && exit 1"`
9546
9537
  - `apps/nextblock/project.json` — No test target declared
9547
9538
  - `tools/scripts/release-lib.js` — Library release pipeline; runs `npx nx run ${nxProject}:build --skip-nx-cache --with-deps` with no test invocation
9548
- - `vercel.json` — Cron declarations (03:00 UTC reset-sandbox; 18:00 UTC sync-currencies); no test hooks
9539
+ - `vercel.json` — Cron declarations (every-15-minutes reset-sandbox; 18:00 UTC sync-currencies); no test hooks
9549
9540
  - `docs/05-DEVELOPER-GUIDE.md` — Developer command reference; no test commands documented
9550
9541
  - `.agent/skills/nx-operations/SKILL.md` — Front matter mentions tests but body documents only Building, Linting, and Development
9551
9542
 
@@ -10564,7 +10555,7 @@ The repository supports three deployment environments, with promotion driven by
10564
10555
  | Preview / Staging | Push to non-deployment branch or PR | Vercel Preview Deployment (unique URL per commit) |
10565
10556
  | Production | Push to Vercel-integrated deployment branch (default base: `master`) | Vercel Production Deployment (`NEXT_PUBLIC_URL`) |
10566
10557
 
10567
- A special **Sandbox production environment** is gated by `NEXT_PUBLIC_IS_SANDBOX=true` and is reset nightly at 03:00 UTC via the `/api/cron/reset-sandbox` endpoint. Sandbox serves as a public demonstration site and as a continuous reconstruction rehearsal for the disaster recovery pipeline (see §8.2.2.5).
10558
+ A special **Sandbox production environment** is gated by `NEXT_PUBLIC_IS_SANDBOX=true` and is reset every 15 minutes via the `/api/cron/reset-sandbox` endpoint. Sandbox serves as a public demonstration site and as a continuous reconstruction rehearsal for the disaster recovery pipeline (see §8.2.2.5).
10568
10559
 
10569
10560
  #### 8.2.2.4 Environment Promotion Flow Diagram
10570
10561
 
@@ -10575,7 +10566,7 @@ flowchart LR
10575
10566
  Preview[Vercel Preview Deployment<br/>Per-commit URL<br/>Shared Supabase preview]
10576
10567
  Merge[Merge to master]
10577
10568
  Prod[Vercel Production Deployment<br/>NEXT_PUBLIC_URL<br/>Production Supabase]
10578
- Sandbox[Sandbox Production<br/>NEXT_PUBLIC_IS_SANDBOX=true<br/>Nightly reset 03:00 UTC]
10569
+ Sandbox[Sandbox Production<br/>NEXT_PUBLIC_IS_SANDBOX=true<br/>Reset every 15 min]
10579
10570
  ResetCycle[R2 clear +<br/>SANDBOX_RESET_SQL +<br/>media normalize +<br/>seed content]
10580
10571
 
10581
10572
  Dev --> PR
@@ -10585,7 +10576,7 @@ flowchart LR
10585
10576
  Prod -.parallel branch.-> Sandbox
10586
10577
 
10587
10578
  Sandbox -->|cron/reset-sandbox| ResetCycle
10588
- ResetCycle -.nightly.-> Sandbox
10579
+ ResetCycle -.every 15 min.-> Sandbox
10589
10580
 
10590
10581
  style Dev fill:#e0f2fe,stroke:#0284c7
10591
10582
  style Preview fill:#fef3c7,stroke:#d97706
@@ -10602,7 +10593,7 @@ Disaster recovery relies on **four compounding recovery mechanisms**, none of wh
10602
10593
  | Supabase Point-in-Time Recovery (PITR) | Catastrophic data loss | Platform-managed by Supabase |
10603
10594
  | Content revisions | Accidental edits / content rollback | `page_revisions` + `post_revisions` tables with JSON Patch diffs (via `fast-json-patch`), `UNIQUE(page_id, version)` |
10604
10595
  | Schema reconstruction from migrations | Cold-start rebuild | 11 canonical SQL files in `libs/db/src/supabase/migrations/` applied in order |
10605
- | Nightly sandbox reset (reconstruction rehearsal) | Continuous validation that recovery pipeline works | `/api/cron/reset-sandbox` at 03:00 UTC |
10596
+ | 15-minute sandbox reset (reconstruction rehearsal) | Continuous validation that recovery pipeline works | `/api/cron/reset-sandbox` every 15 minutes |
10606
10597
 
10607
10598
  Backup responsibility matrix:
10608
10599
 
@@ -10787,7 +10778,7 @@ Security is enforced across four layers, with compliance-relevant controls conce
10787
10778
  |:--|:--|:--|
10788
10779
  | Edge | Nonce-based CSP, HSTS, X-Frame-Options SAMEORIGIN, X-Content-Type-Options nosniff, Referrer-Policy origin-when-cross-origin, Permissions-Policy, COOP same-origin | `proxy.ts` |
10789
10780
  | Application | RBAC via `cmsRoutePermissions` (`/cms` ADMIN/WRITER; `/cms/admin`, `/cms/users`, `/cms/settings` ADMIN only); `verifyAdmin()` in server actions | `proxy.ts`, server actions |
10790
- | Data | RLS policies + SECURITY DEFINER helpers (`get_my_claim`, `get_current_user_role`, `is_admin`, `handle_new_user`) | Migration `00000000000006_setup_rls_and_grants.sql` (872 lines) |
10781
+ | Data | RLS policies + SECURITY DEFINER helpers (`get_my_claim`, `get_current_user_role`, `is_admin`, `handle_new_user`) | Migration `02001_baseline_schema.sql` (872 lines) |
10791
10782
  | Integration | Stripe `constructEvent` signature verification; Freemius HMAC-SHA-256; `CRON_SECRET` Bearer auth; `REVALIDATE_SECRET_TOKEN` via `x-revalidate-secret` header | Webhook + cron route handlers |
10792
10783
 
10793
10784
  **Service role key hardening**: `SUPABASE_SERVICE_ROLE_KEY` is guarded at runtime via `typeof window !== 'undefined'` checks and build-time via `import 'server-only'` directives to prevent browser exposure.
@@ -11047,7 +11038,7 @@ Scheduled jobs deploy automatically with the application via `vercel.json`:
11047
11038
  ```json
11048
11039
  {
11049
11040
  "crons": [
11050
- {"path": "/api/cron/reset-sandbox", "schedule": "0 3 * * *"},
11041
+ {"path": "/api/cron/reset-sandbox", "schedule": "*/15 * * * *"},
11051
11042
  {"path": "/api/cron/sync-currencies", "schedule": "0 18 * * *"}
11052
11043
  ]
11053
11044
  }
@@ -11055,7 +11046,7 @@ Scheduled jobs deploy automatically with the application via `vercel.json`:
11055
11046
 
11056
11047
  | Cron | Schedule (UTC) | maxDuration | Authentication |
11057
11048
  |:--|:--|:--|:--|
11058
- | `/api/cron/reset-sandbox` | `0 3 * * *` (03:00 daily) | 60s | `Authorization: Bearer ${CRON_SECRET}` |
11049
+ | `/api/cron/reset-sandbox` | `*/15 * * * *` (every 15 minutes) | 60s | `Authorization: Bearer ${CRON_SECRET}` |
11059
11050
  | `/api/cron/sync-currencies` | `0 18 * * *` (18:00 daily) | 30s | `Authorization: Bearer ${CRON_SECRET}` |
11060
11051
 
11061
11052
  Purpose: `reset-sandbox` performs R2 clear + `SANDBOX_RESET_SQL` + media normalize + content seed; `sync-currencies` performs Frankfurter FX fetch + currency row update.
@@ -11111,7 +11102,7 @@ flowchart TB
11111
11102
  subgraph CronPipeline["Scheduled Jobs - Vercel Cron"]
11112
11103
  CronDecl[vercel.json crons array]
11113
11104
  CronRuntime[Vercel Cron Dispatcher]
11114
- ResetJob[/api/cron/reset-sandbox<br/>03:00 UTC]
11105
+ ResetJob[/api/cron/reset-sandbox<br/>every 15 min]
11115
11106
  SyncJob[/api/cron/sync-currencies<br/>18:00 UTC]
11116
11107
  end
11117
11108
 
@@ -11285,7 +11276,7 @@ Per §4.12, the following are the codified timing constraints. Breaches surface
11285
11276
  | Sync-currencies maxDuration | 30s | `vercel.json` + route export |
11286
11277
  | Reset-sandbox maxDuration | 60s | `vercel.json` + route export |
11287
11278
  | Currency sync schedule | 18:00 UTC daily | `vercel.json` |
11288
- | Sandbox reset schedule | 03:00 UTC daily | `vercel.json` |
11279
+ | Sandbox reset schedule | every 15 minutes | `vercel.json` |
11289
11280
  | Max source image width | 2560 px | `/api/process-image` |
11290
11281
  | Image derivative widths | 1920, 1280, 768, 384, 128 | `next.config.js` `deviceSizes`/`imageSizes` |
11291
11282
  | Image quality presets | 60, 75 | `next.config.js` `qualities` |
@@ -11300,7 +11291,7 @@ Per §4.12, the following are the codified timing constraints. Breaches surface
11300
11291
  | Vercel function invocations | Vercel dashboard | Auto-scaled; `maxDuration` capping |
11301
11292
  | Vercel bandwidth | Vercel dashboard | 1-year image cache TTL, AVIF compression |
11302
11293
  | Vercel image optimization count | Vercel dashboard | `minimumCacheTTL: 31_536_000` |
11303
- | Supabase DB CPU/IO | Supabase dashboard | RLS indexing (`00000000000007_setup_indexes.sql`) |
11294
+ | Supabase DB CPU/IO | Supabase dashboard | RLS indexing (`02001_baseline_schema.sql`) |
11304
11295
  | Supabase egress | Supabase dashboard | Server-side aggregation; `count` head queries |
11305
11296
  | R2 storage | Cloudflare dashboard | Folder path sanitization prevents duplicates |
11306
11297
  | R2 Class A writes | Cloudflare dashboard | Single direct-upload via presigned URL (vs through server) |
@@ -11411,7 +11402,7 @@ graph TB
11411
11402
  | Review Vercel log stream | Daily / on-alert | Vercel console |
11412
11403
  | Review Supabase Advisors | Weekly | Supabase console |
11413
11404
  | Review feedback inbox | Daily | `feedback@nextblock.dev` |
11414
- | Sandbox reset validation | Automated | `/api/cron/reset-sandbox` nightly at 03:00 UTC |
11405
+ | Sandbox reset validation | Automated | `/api/cron/reset-sandbox` every 15 minutes |
11415
11406
  | FX rate sync | Automated | `/api/cron/sync-currencies` daily at 18:00 UTC |
11416
11407
  | Dependency audit | Per release | `npm audit`, manual review |
11417
11408
  | Security patch review | On advisory | `npm update` + regression testing |
@@ -11543,12 +11534,12 @@ Operators should select tiers based on expected monthly active users, storage fo
11543
11534
  - **Vercel-native, platform-delegated deployment**: no Docker, no Kubernetes, no Terraform, no GitHub Actions — all four absences are intentional ADRs documented in §5.3.7.3 and §3.7.1.
11544
11535
  - **Three managed platform providers** compose the production footprint: Vercel (app + edge + cron + RUM), Supabase (PostgreSQL 17 + Auth + Storage metadata), Cloudflare R2 (S3-compatible object storage).
11545
11536
  - **Hybrid CI/CD** combines Vercel Git integration (for application deployment) with three Node.js release scripts under `tools/scripts/` (for library/CLI/DB releases).
11546
- - **Two Vercel cron schedules** in `vercel.json`: `/api/cron/reset-sandbox` at 03:00 UTC (60s max) and `/api/cron/sync-currencies` at 18:00 UTC (30s max).
11537
+ - **Two Vercel cron schedules** in `vercel.json`: `/api/cron/reset-sandbox` every 15 minutes (60s max) and `/api/cron/sync-currencies` at 18:00 UTC (30s max).
11547
11538
  - **Infrastructure as Code is declarative**, distributed across `vercel.json`, `nx.json`, `libs/db/src/supabase/{migrations/, config.toml, templates/}`, `.env.example`, and per-project `project.json` files.
11548
11539
  - **Library publication uses a Twin Package Strategy** for `@nextblock-cms/ecom`: stub to public npm registry + real module to private GitHub Packages.
11549
11540
  - **11 canonical SQL migrations** compose the database schema; re-running them from a fresh Supabase project deterministically reconstructs the system.
11550
11541
  - **Monitoring is intentionally minimal**: Vercel Speed Insights for RUM, GTM for client analytics, structured `console.warn`/`console.error` logs for server-side events. No Prometheus/Grafana/Datadog/PagerDuty.
11551
- - **Disaster recovery relies on four compounding mechanisms**: Supabase PITR, content revisions with JSON Patch diffs, schema reconstruction from migrations, and nightly sandbox reset as a continuous reconstruction rehearsal.
11542
+ - **Disaster recovery relies on four compounding mechanisms**: Supabase PITR, content revisions with JSON Patch diffs, schema reconstruction from migrations, and 15-minute sandbox reset as a continuous reconstruction rehearsal.
11552
11543
  - **Cost optimization is designed in**: zero egress on R2, platform-delegated scaling on Vercel, 1-year image cache TTL, daily (not per-request) FX sync.
11553
11544
 
11554
11545
  ### 8.11.2 Cross-References to Other Sections
@@ -11582,7 +11573,7 @@ Operators should select tiers based on expected monthly active users, storage fo
11582
11573
  **Files Examined**
11583
11574
 
11584
11575
  - `.env.example` — Authoritative inventory of 40+ environment variables across 7 categories (Platform, Secrets, FX, Supabase, R2, SMTP, Stripe, Freemius)
11585
- - `vercel.json` — Declarative cron schedule definitions (2 crons: reset-sandbox at 03:00 UTC, sync-currencies at 18:00 UTC)
11576
+ - `vercel.json` — Declarative cron schedule definitions (2 crons: reset-sandbox every 15 minutes, sync-currencies at 18:00 UTC)
11586
11577
  - `package.json` (root) — Workspace identity (`nextblock`), 51 npm scripts, Nx plugin versions, dependency overrides, `packageManager`
11587
11578
  - `nx.json` — Workspace orchestration: 6 plugins, `defaultBase: master`, production named-input exclusions, `release.version.preVersionCommand`
11588
11579
  - `apps/nextblock/next.config.js` — Image pipeline config, `compiler.removeConsole`, `transpilePackages`, dynamic `remotePatterns`, `turbopack.resolveAlias`
@@ -11702,15 +11693,15 @@ The following enum types are defined in the eleven canonical SQL migrations loca
11702
11693
 
11703
11694
  | Enum Type | Allowed Values | Migration |
11704
11695
  |:--|:--|:--|
11705
- | `user_role` | `ADMIN`, `WRITER`, `USER` | `00000000000000_setup_foundation_and_enums.sql` |
11706
- | `page_status` | `draft`, `published`, `archived` | `00000000000002_setup_content_tables.sql` |
11707
- | `menu_location` | `header`, `footer`, `sidebar` | `00000000000002_setup_content_tables.sql` |
11708
- | `revision_type` | `snapshot`, `diff` | `00000000000002_setup_content_tables.sql` |
11709
- | `product_type` | `physical`, `digital` | `00000000000003_setup_catalog_and_licensing.sql` |
11710
- | `payment_provider` | `stripe`, `freemius` | `00000000000003_setup_catalog_and_licensing.sql` |
11711
- | `order_status` | `pending`, `paid`, `shipped`, `delivered`, `refunded`, `cancelled` | `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` |
11712
- | `tax_calculation_mode` | `manual`, `automatic` | `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` |
11713
- | `inventory_deduction_method` | `rpc`, `sql-fallback` | `00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` |
11696
+ | `user_role` | `ADMIN`, `WRITER`, `USER` | `02001_baseline_schema.sql` |
11697
+ | `page_status` | `draft`, `published`, `archived` | `02001_baseline_schema.sql` |
11698
+ | `menu_location` | `header`, `footer`, `sidebar` | `02001_baseline_schema.sql` |
11699
+ | `revision_type` | `snapshot`, `diff` | `02001_baseline_schema.sql` |
11700
+ | `product_type` | `physical`, `digital` | `02001_baseline_schema.sql` |
11701
+ | `payment_provider` | `stripe`, `freemius` | `02001_baseline_schema.sql` |
11702
+ | `order_status` | `pending`, `paid`, `shipped`, `delivered`, `refunded`, `cancelled` | `02001_baseline_schema.sql` |
11703
+ | `tax_calculation_mode` | `manual`, `automatic` | `02001_baseline_schema.sql` |
11704
+ | `inventory_deduction_method` | `rpc`, `sql-fallback` | `02001_baseline_schema.sql` |
11714
11705
 
11715
11706
  Cross-reference: Section 6.2 (Database Design), Section 4.11 (Validation Rules and Compliance Checkpoints).
11716
11707
 
@@ -11771,7 +11762,7 @@ Reproduced from Section 4.12 for quick reference during operational work. Values
11771
11762
  | Sync-currencies `maxDuration` | 30 seconds | `api/cron/sync-currencies` |
11772
11763
  | Reset-sandbox `maxDuration` | 60 seconds | `api/cron/reset-sandbox` |
11773
11764
  | Currency sync schedule | `0 18 * * *` (18:00 UTC daily) | `vercel.json` |
11774
- | Sandbox reset schedule | `0 3 * * *` (03:00 UTC daily) | `vercel.json` |
11765
+ | Sandbox reset schedule | `*/15 * * * *` (every 15 minutes) | `vercel.json` |
11775
11766
  | Max source image width | 2560 pixels | `api/process-image` |
11776
11767
  | Image derivative widths | 1920, 1280, 768, 384, 128 | `api/process-image` |
11777
11768
  | Lighthouse performance target | 100/100 | `README.md` |
@@ -12149,7 +12140,7 @@ The glossary defines domain-specific, product-specific, and platform-specific te
12149
12140
 
12150
12141
  **Sandbox Mode** — The demonstration environment activated by `NEXT_PUBLIC_IS_SANDBOX=true`. The `cms.nextblock.dev` deployment operates in sandbox mode with demo credentials `demo@nextblock.dev` / `password`.
12151
12142
 
12152
- **Sandbox Reset** — The nightly 03:00 UTC cron job at `/api/cron/reset-sandbox` that clears R2, bootstraps SQL, normalizes media, and re-seeds content.
12143
+ **Sandbox Reset** — The 15-minute cron job at `/api/cron/reset-sandbox` that clears R2, bootstraps SQL, normalizes media, and re-seeds content.
12153
12144
 
12154
12145
  **Scope Tag** — An Nx project tag (`scope:public` or `scope:premium`) validated by the `@nx/enforce-module-boundaries` ESLint rule. See Section 9.1.5.
12155
12146
 
@@ -12430,10 +12421,10 @@ The appendices above synthesize material from the following sections of this spe
12430
12421
 
12431
12422
  - `libs/environment.d.ts` — Typed `NodeJS.ProcessEnv` augmentation used to enumerate every environment variable listed in Section 9.1.1
12432
12423
  - `.env.example` — Environment variable inventory and sample values cross-checked against the typed augmentation
12433
- - `libs/db/src/supabase/migrations/00000000000000_setup_foundation_and_enums.sql` — `user_role` enum definition
12434
- - `libs/db/src/supabase/migrations/00000000000002_setup_content_tables.sql` — `page_status`, `menu_location`, `revision_type` enum definitions
12435
- - `libs/db/src/supabase/migrations/00000000000003_setup_catalog_and_licensing.sql` — `product_type`, `payment_provider` enum definitions
12436
- - `libs/db/src/supabase/migrations/00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql` — `order_status`, `tax_calculation_mode`, `inventory_deduction_method` enum definitions
12424
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `user_role` enum definition
12425
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `page_status`, `menu_location`, `revision_type` enum definitions
12426
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `product_type`, `payment_provider` enum definitions
12427
+ - `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `order_status`, `tax_calculation_mode`, `inventory_deduction_method` enum definitions
12437
12428
  - `libs/db/src/supabase/config.toml` — Supabase local port allocation reproduced in Section 9.1.9
12438
12429
  - `apps/nextblock/proxy.ts` — Security header set (Section 9.1.12), prefetch priority mapping (Section 9.1.13), CSP nonce, structured cache observability log schema
12439
12430
  - `apps/nextblock/app/layout.tsx` — `PUBLIC_LAYOUT_REVALIDATE_SECONDS` constant reproduced in SLA ledger