@kardoe/quickback 0.8.1 → 0.8.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.
@@ -7,7 +7,7 @@ export const DOCS = {
7
7
  },
8
8
  "account-ui/environment-variables": {
9
9
  "title": "Environment Variables",
10
- "content": "# Environment Variables\n\nConfigure the Account UI by setting environment variables in your `.env` file or deployment platform.\n\n## Required Variables\n\nThese variables are **required** for the Account UI to function:\n\n### API Configuration\n\n```bash\n# Quickback API URL\nVITE_QUICKBACK_API_URL=https://api.example.com\n\n# Account UI URL (where the Account UI is deployed)\nVITE_ACCOUNT_APP_URL=https://account.example.com\n```\n\n## App Identity\n\nConfigure your application's basic information:\n\n```bash\n# App name (displayed in UI)\nVITE_APP_NAME=My Application\n\n# Tagline (shown on landing pages)\nVITE_APP_TAGLINE=Build faster, ship sooner\n\n# Full description\nVITE_APP_DESCRIPTION=A complete platform for building modern web applications\n\n# Company information\nVITE_COMPANY_NAME=My Company Inc.\nVITE_COMPANY_ADDRESS=123 Main St, Suite 100, San Francisco, CA 94105\n```\n\n## URLs\n\n### Main Application\n\n```bash\n# Your main application URL\n# Users will be redirected here after authentication\nVITE_QUICKBACK_APP_URL=https://app.example.com\n\n# Organization/Tenant URL pattern (optional)\n# Use {slug} as placeholder for organization identifier\n# Examples:\n# /organizations/{slug} -> app.example.com/organizations/acme\n# /workspace/{slug} -> app.example.com/workspace/acme\n# https://{slug}.example.com -> acme.example.com\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\n```\n\n### Support and Legal\n\n```bash\n# Support/help URL\nVITE_SUPPORT_URL=https://support.example.com\n\n# Privacy policy URL\nVITE_PRIVACY_URL=https://example.com/privacy\n\n# Terms of service URL\nVITE_TERMS_URL=https://example.com/terms\n```\n\n## Email Configuration\n\n```bash\n# From address for all emails\nVITE_EMAIL_FROM=noreply@example.com\n\n# Reply-to address\nVITE_EMAIL_REPLY_TO=support@example.com\n\n# Support email address\nVITE_SUPPORT_EMAIL=support@example.com\n\n# AWS Region for SES (if using AWS SES)\nVITE_EMAIL_REGION=us-east-1\n```\n\n## Branding\n\n```bash\n# Primary theme color (hex code)\nVITE_THEME_COLOR=#1e293b\n\n# SEO keywords (comma-separated)\nVITE_SEO_KEYWORDS=saas,authentication,account management\n```\n\n## Feature Flags\n\nEnable or disable features using boolean environment variables:\n\n### Authentication Features\n\n```bash\n# Enable user signup (default: true)\nENABLE_SIGNUP=true\n\n# Enable email verification after signup (default: true)\nENABLE_EMAIL_VERIFICATION=true\n\n# Disable email deliverability checks (default: false)\n# Set to true to skip checking if email addresses are deliverable\nDISABLE_EMAIL_STATUS_CHECK=false\n\n# Enable passkey (WebAuthn) login (default: true)\nENABLE_PASSKEY=true\n\n# Enable email OTP login and signup (default: true)\nENABLE_EMAIL_OTP=true\n\n# Enable email+password login and signup (default: false)\nENABLE_PASSWORD=false\n```\n\n### Account Management Features\n\n```bash\n# Enable file uploads (avatar, etc.) (default: false)\nVITE_ENABLE_FILE_UPLOADS=false\n```\n\n### Organization Features\n\n```bash\n# Enable multi-tenant organizations (default: true)\nENABLE_ORGANIZATIONS=true\n```\n\n### Admin Features\n\n```bash\n# Enable admin panel (default: true)\nENABLE_ADMIN=true\n```\n\n## Complete Example\n\nHere's a complete `.env` file with all common configuration:\n\n```bash title=\".env\"\n# ===== REQUIRED =====\nVITE_QUICKBACK_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\n\n# ===== APP IDENTITY =====\nVITE_APP_NAME=Acme SaaS\nVITE_APP_TAGLINE=Build faster, ship sooner\nVITE_APP_DESCRIPTION=The complete platform for modern web applications\nVITE_COMPANY_NAME=Acme Corporation\nVITE_COMPANY_ADDRESS=123 Main St, Suite 100, San Francisco, CA 94105\n\n# ===== URLS =====\nVITE_QUICKBACK_APP_URL=https://app.acme.com\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\nVITE_SUPPORT_URL=https://help.acme.com\nVITE_PRIVACY_URL=https://acme.com/privacy\nVITE_TERMS_URL=https://acme.com/terms\n\n# ===== EMAIL =====\nVITE_EMAIL_FROM=noreply@acme.com\nVITE_EMAIL_REPLY_TO=support@acme.com\nVITE_SUPPORT_EMAIL=support@acme.com\nVITE_EMAIL_REGION=us-east-1\n\n# ===== BRANDING =====\nVITE_THEME_COLOR=#3b82f6\n\n# ===== SEO =====\nVITE_SEO_KEYWORDS=saas,project management,team collaboration\n\n# ===== FEATURES =====\nENABLE_SIGNUP=true\nENABLE_EMAIL_VERIFICATION=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\nENABLE_PASSWORD=false\nENABLE_ORGANIZATIONS=true\nENABLE_ADMIN=true\nVITE_ENABLE_FILE_UPLOADS=true\nDISABLE_EMAIL_STATUS_CHECK=false\n```\n\n## Environment-Specific Configuration\n\n### Development\n\n```bash title=\".env.development\"\nVITE_QUICKBACK_API_URL=http://localhost:8787\nVITE_ACCOUNT_APP_URL=http://localhost:5173\nVITE_QUICKBACK_APP_URL=http://localhost:3000\nDISABLE_EMAIL_STATUS_CHECK=true\n```\n\n### Staging\n\n```bash title=\".env.staging\"\nVITE_QUICKBACK_API_URL=https://api-staging.example.com\nVITE_ACCOUNT_APP_URL=https://account-staging.example.com\nVITE_QUICKBACK_APP_URL=https://app-staging.example.com\n```\n\n### Production\n\n```bash title=\".env.production\"\nVITE_QUICKBACK_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\nVITE_QUICKBACK_APP_URL=https://app.example.com\nDISABLE_EMAIL_STATUS_CHECK=false\n```\n\n## Cloudflare Pages Setup\n\nWhen deploying to Cloudflare Pages, set environment variables in the dashboard:\n\n1. Go to **Settings** → **Environment Variables**\n2. Add each `VITE_*` variable\n3. Separate values for **Production** and **Preview** branches\n\n## Wrangler Configuration\n\nFor `wrangler dev` and `wrangler deploy`, add variables to `wrangler.toml`:\n\n```toml title=\"wrangler.toml\"\n[env.production.vars]\nVITE_QUICKBACK_API_URL = \"https://api.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account.example.com\"\nVITE_APP_NAME = \"My App\"\n# ... other variables\n```\n\nOr use `.dev.vars` for local development (git-ignored):\n\n```bash title=\".dev.vars\"\nVITE_QUICKBACK_API_URL=http://localhost:8787\nVITE_ACCOUNT_APP_URL=http://localhost:5173\n```\n\n## Runtime Configuration\n\nSome values can be overridden at runtime using the config API:\n\n```ts\n\nsetAppConfig({\n name: 'My Custom App',\n branding: {\n primaryColor: '#ff6600',\n logoUrl: '/custom-logo.png',\n },\n urls: {\n base: 'https://account.custom.com',\n app: 'https://app.custom.com',\n },\n});\n```\n\n## Validation\n\nThe Account UI validates configuration on startup:\n\n- **Missing VITE_QUICKBACK_API_URL**: Throws error\n- **Invalid URLs**: Logs warning\n- **Missing optional fields**: Uses defaults\n\nCheck the browser console for configuration warnings.\n\n## Next Steps\n\n- **[Feature Flags](/account-ui/features)** - Detailed feature configuration\n- **[Customization](/account-ui/customization)** - Customize labels and messages\n- **[Worker Setup](/account-ui/worker)** - Deploy to Cloudflare"
10
+ "content": "# Environment Variables\n\nConfigure the Account UI by setting environment variables in your `.env` file or deployment platform.\n\n## Quick Setup — Single Variable\n\nFor the common \"unified deployment\" where API, Account, and CMS all live on the same Quickback origin, you only need **one** variable:\n\n```bash\n# Points at the Quickback Worker origin\nVITE_QUICKBACK_URL=https://secure.example.com\n```\n\nWhen `VITE_QUICKBACK_URL` is set, it's used as a fallback for the individual URL vars:\n\n| Individual var | Used first | Falls back to |\n|---|---|---|\n| `VITE_QUICKBACK_API_URL` | if set | `${VITE_QUICKBACK_URL}` |\n| `VITE_QUICKBACK_ACCOUNT_URL` | if set | `${VITE_QUICKBACK_URL}/account` |\n| `VITE_QUICKBACK_CMS_URL` | if set | `${VITE_QUICKBACK_URL}/cms` |\n\n## Required Variables\n\nAt minimum, you need one of:\n\n- **`VITE_QUICKBACK_URL`** — catch-all for the Quickback origin (recommended), **or**\n- **`VITE_QUICKBACK_API_URL`** — explicit API URL (use this if your API lives on a different subdomain from Account/CMS)\n\n### Split-subdomain setup\n\nIf your API, Account, and CMS live on separate custom subdomains, set them explicitly:\n\n```bash\n# Quickback API URL\nVITE_QUICKBACK_API_URL=https://api.example.com\n\n# Account UI URL (where the Account UI is deployed)\nVITE_ACCOUNT_APP_URL=https://account.example.com\n\n# Optional: override Account/CMS URLs for cross-origin links\nVITE_QUICKBACK_ACCOUNT_URL=https://auth.example.com\nVITE_QUICKBACK_CMS_URL=https://cms.example.com\n```\n\n## App Identity\n\nConfigure your application's basic information:\n\n```bash\n# App name (displayed in UI)\nVITE_APP_NAME=My Application\n\n# Tagline (shown on landing pages)\nVITE_APP_TAGLINE=Build faster, ship sooner\n\n# Full description\nVITE_APP_DESCRIPTION=A complete platform for building modern web applications\n\n# Company information\nVITE_COMPANY_NAME=My Company Inc.\nVITE_COMPANY_ADDRESS=123 Main St, Suite 100, San Francisco, CA 94105\n```\n\n## URLs\n\n### Main Application\n\n```bash\n# Your main application URL\n# Users will be redirected here after authentication\nVITE_QUICKBACK_APP_URL=https://app.example.com\n\n# Organization/Tenant URL pattern (optional)\n# Use {slug} as placeholder for organization identifier\n# Examples:\n# /organizations/{slug} -> app.example.com/organizations/acme\n# /workspace/{slug} -> app.example.com/workspace/acme\n# https://{slug}.example.com -> acme.example.com\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\n```\n\n### Support and Legal\n\n```bash\n# Support/help URL\nVITE_SUPPORT_URL=https://support.example.com\n\n# Privacy policy URL\nVITE_PRIVACY_URL=https://example.com/privacy\n\n# Terms of service URL\nVITE_TERMS_URL=https://example.com/terms\n```\n\n## Email Configuration\n\n```bash\n# From address for all emails\nVITE_EMAIL_FROM=noreply@example.com\n\n# Reply-to address\nVITE_EMAIL_REPLY_TO=support@example.com\n\n# Support email address\nVITE_SUPPORT_EMAIL=support@example.com\n\n# AWS Region for SES (if using AWS SES)\nVITE_EMAIL_REGION=us-east-1\n```\n\n## Branding\n\n```bash\n# Primary theme color (hex code)\nVITE_THEME_COLOR=#1e293b\n\n# SEO keywords (comma-separated)\nVITE_SEO_KEYWORDS=saas,authentication,account management\n```\n\n## Feature Flags\n\nEnable or disable features using boolean environment variables:\n\n### Authentication Features\n\n```bash\n# Enable user signup (default: true)\nENABLE_SIGNUP=true\n\n# Enable email verification after signup (default: true)\nENABLE_EMAIL_VERIFICATION=true\n\n# Disable email deliverability checks (default: false)\n# Set to true to skip checking if email addresses are deliverable\nDISABLE_EMAIL_STATUS_CHECK=false\n\n# Enable passkey (WebAuthn) login (default: true)\nENABLE_PASSKEY=true\n\n# Enable email OTP login and signup (default: true)\nENABLE_EMAIL_OTP=true\n\n# Enable email+password login and signup (default: false)\nENABLE_PASSWORD=false\n```\n\n### Account Management Features\n\n```bash\n# Enable file uploads (avatar, etc.) (default: false)\nVITE_ENABLE_FILE_UPLOADS=false\n```\n\n### Organization Features\n\n```bash\n# Enable multi-tenant organizations (default: true)\nENABLE_ORGANIZATIONS=true\n```\n\n### Admin Features\n\n```bash\n# Enable admin panel (default: true)\nENABLE_ADMIN=true\n```\n\n## Complete Example\n\nHere's a complete `.env` file with all common configuration:\n\n```bash title=\".env\"\n# ===== REQUIRED =====\nVITE_QUICKBACK_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\n\n# ===== APP IDENTITY =====\nVITE_APP_NAME=Acme SaaS\nVITE_APP_TAGLINE=Build faster, ship sooner\nVITE_APP_DESCRIPTION=The complete platform for modern web applications\nVITE_COMPANY_NAME=Acme Corporation\nVITE_COMPANY_ADDRESS=123 Main St, Suite 100, San Francisco, CA 94105\n\n# ===== URLS =====\nVITE_QUICKBACK_APP_URL=https://app.acme.com\nVITE_TENANT_URL_PATTERN=/organizations/{slug}\nVITE_SUPPORT_URL=https://help.acme.com\nVITE_PRIVACY_URL=https://acme.com/privacy\nVITE_TERMS_URL=https://acme.com/terms\n\n# ===== EMAIL =====\nVITE_EMAIL_FROM=noreply@acme.com\nVITE_EMAIL_REPLY_TO=support@acme.com\nVITE_SUPPORT_EMAIL=support@acme.com\nVITE_EMAIL_REGION=us-east-1\n\n# ===== BRANDING =====\nVITE_THEME_COLOR=#3b82f6\n\n# ===== SEO =====\nVITE_SEO_KEYWORDS=saas,project management,team collaboration\n\n# ===== FEATURES =====\nENABLE_SIGNUP=true\nENABLE_EMAIL_VERIFICATION=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\nENABLE_PASSWORD=false\nENABLE_ORGANIZATIONS=true\nENABLE_ADMIN=true\nVITE_ENABLE_FILE_UPLOADS=true\nDISABLE_EMAIL_STATUS_CHECK=false\n```\n\n## Environment-Specific Configuration\n\n### Development\n\n```bash title=\".env.development\"\nVITE_QUICKBACK_API_URL=http://localhost:8787\nVITE_ACCOUNT_APP_URL=http://localhost:5173\nVITE_QUICKBACK_APP_URL=http://localhost:3000\nDISABLE_EMAIL_STATUS_CHECK=true\n```\n\n### Staging\n\n```bash title=\".env.staging\"\nVITE_QUICKBACK_API_URL=https://api-staging.example.com\nVITE_ACCOUNT_APP_URL=https://account-staging.example.com\nVITE_QUICKBACK_APP_URL=https://app-staging.example.com\n```\n\n### Production\n\n```bash title=\".env.production\"\nVITE_QUICKBACK_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\nVITE_QUICKBACK_APP_URL=https://app.example.com\nDISABLE_EMAIL_STATUS_CHECK=false\n```\n\n## Cloudflare Pages Setup\n\nWhen deploying to Cloudflare Pages, set environment variables in the dashboard:\n\n1. Go to **Settings** → **Environment Variables**\n2. Add each `VITE_*` variable\n3. Separate values for **Production** and **Preview** branches\n\n## Wrangler Configuration\n\nFor `wrangler dev` and `wrangler deploy`, add variables to `wrangler.toml`:\n\n```toml title=\"wrangler.toml\"\n[env.production.vars]\nVITE_QUICKBACK_API_URL = \"https://api.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account.example.com\"\nVITE_APP_NAME = \"My App\"\n# ... other variables\n```\n\nOr use `.dev.vars` for local development (git-ignored):\n\n```bash title=\".dev.vars\"\nVITE_QUICKBACK_API_URL=http://localhost:8787\nVITE_ACCOUNT_APP_URL=http://localhost:5173\n```\n\n## Runtime Configuration\n\nSome values can be overridden at runtime using the config API:\n\n```ts\n\nsetAppConfig({\n name: 'My Custom App',\n branding: {\n primaryColor: '#ff6600',\n logoUrl: '/custom-logo.png',\n },\n urls: {\n base: 'https://account.custom.com',\n app: 'https://app.custom.com',\n },\n});\n```\n\n## Validation\n\nThe Account UI validates configuration on startup:\n\n- **Missing VITE_QUICKBACK_API_URL**: Throws error\n- **Invalid URLs**: Logs warning\n- **Missing optional fields**: Uses defaults\n\nCheck the browser console for configuration warnings.\n\n## Next Steps\n\n- **[Feature Flags](/account-ui/features)** - Detailed feature configuration\n- **[Customization](/account-ui/customization)** - Customize labels and messages\n- **[Worker Setup](/account-ui/worker)** - Deploy to Cloudflare"
11
11
  },
12
12
  "account-ui/features/admin": {
13
13
  "title": "Admin Panel",
@@ -51,23 +51,23 @@ export const DOCS = {
51
51
  },
52
52
  "account-ui/library-usage": {
53
53
  "title": "Library Usage",
54
- "content": "# Library Usage\n\nInstead of cloning the template, you can install Account UI as an npm package. This gives you automatic updates via `npm update` while configuring the UI through `setAppConfig()`.\n\n## Installation\n\n```bash\nnpm install quickback-better-auth-account-ui\n```\n\n### Peer Dependencies\n\nAccount UI expects these packages in your app:\n\n```bash\nnpm install react react-dom react-router-dom @tanstack/react-query\n```\n\n## Basic Setup\n\n### 1. Import and Configure\n\n```tsx title=\"src/main.tsx\"\n\nsetAppConfig({\n authRoute: 'quickback', // or 'better-auth' for standalone Better Auth\n name: 'My App',\n companyName: 'My Company Inc.',\n tagline: 'Build faster, ship sooner',\n description: 'My app description',\n});\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n </React.StrictMode>\n);\n```\n\n### 2. Set API URLs\n\nThe library reads API URLs from `globalThis` variables. Set these before importing the library:\n\n```tsx title=\"src/main.tsx\"\n// Set API URLs before auth-ui loads\n(globalThis as any).__QUICKBACK_API_URL__ = 'https://api.example.com';\n(globalThis as any).__QUICKBACK_APP_URL__ = 'https://account.example.com';\n\n// Then import dynamically to ensure globals are set first\nconst { AuthApp, setAppConfig } = await import('quickback-better-auth-account-ui');\nawait import('quickback-better-auth-account-ui/styles.css');\n```\n\nOr with Vite environment variables:\n\n```tsx title=\"src/main.tsx\"\n(globalThis as any).__QUICKBACK_API_URL__ = import.meta.env.VITE_QUICKBACK_API_URL;\n(globalThis as any).__QUICKBACK_APP_URL__ = import.meta.env.VITE_QUICKBACK_APP_URL;\n\nPromise.all([\n import('quickback-better-auth-account-ui'),\n import('quickback-better-auth-account-ui/styles.css'),\n]).then(([{ AuthApp, setAppConfig }]) => {\n setAppConfig({\n authRoute: 'quickback',\n name: import.meta.env.VITE_APP_NAME || 'My App',\n companyName: import.meta.env.VITE_COMPANY_NAME || 'My Company',\n tagline: import.meta.env.VITE_APP_TAGLINE || 'Welcome',\n });\n\n ReactDOM.createRoot(document.getElementById('root')!).render(\n \n </React.StrictMode>\n );\n});\n```\n\n## Cloudflare Worker\n\nThe library exports a Cloudflare Worker entry point for SPA routing and static asset serving:\n\n```ts title=\"src/worker.ts\"\nexport { default } from 'quickback-better-auth-account-ui/worker';\n```\n\nOr extend it with custom logic:\n\n```ts title=\"src/worker.ts\"\n\nexport default createAuthWorker();\n```\n\n## Configuration via `setAppConfig()`\n\nIn library mode, all configuration happens through `setAppConfig()` instead of environment variables.\n\n### Auth Route Mode\n\n```ts\nsetAppConfig({\n authRoute: 'quickback', // Quickback API: /auth/v1, /api/v1, /storage/v1\n // or\n authRoute: 'better-auth', // Better Auth default: /api/auth\n});\n```\n\n### Branding\n\n```ts\nsetAppConfig({\n name: 'Acme SaaS',\n companyName: 'Acme Corporation',\n tagline: 'Build faster, ship sooner',\n description: 'The complete platform for modern web applications',\n branding: {\n primaryColor: '#3b82f6',\n logoUrl: '/logo.png',\n faviconUrl: '/favicon.ico',\n },\n});\n```\n\n### Labels and Messages\n\n```ts\nsetAppConfig({\n labels: {\n organizations: 'Workspaces',\n terms: 'Terms and Conditions',\n privacy: 'Privacy Notice',\n },\n messages: {\n noOrganizations: \"You haven't joined any workspaces yet.\",\n createOrganization: 'Create Workspace',\n },\n});\n```\n\n### Routes\n\n```ts\nsetAppConfig({\n routes: {\n public: {\n login: '/sign-in',\n signup: '/sign-up',\n },\n organizations: {\n list: '/workspaces',\n create: '/workspaces/new',\n },\n },\n});\n```\n\n### Password and Session\n\n```ts\nsetAppConfig({\n auth: {\n passwordRequirements: {\n minLength: 12,\n requireSymbols: true,\n },\n sessionDuration: 7 * 24 * 60 * 60, // 7 days\n },\n});\n```\n\nSee [Customization](/account-ui/customization) for the full reference of all configurable options.\n\n## Exports\n\nThe library provides the following exports:\n\n### Main Entry (`quickback-better-auth-account-ui`)\n\n| Export | Type | Description |\n|--------|------|-------------|\n| `AuthApp` | Component | The main React application component |\n| `authClient` | Object | Pre-configured Better Auth client |\n| `appConfig` | Object | Current app configuration |\n| `setAppConfig` | Function | Set app configuration overrides |\n| `createAppConfig` | Function | Create a new config object |\n| `getApiBase` | Function | Get the current API base URL |\n| `setApiBase` | Function | Override the API base URL |\n| `getAuthApiUrl` | Function | Get the auth API URL |\n| `getDataApiUrl` | Function | Get the data API URL |\n| `getStorageApiUrl` | Function | Get the storage API URL |\n\n### Styles (`quickback-better-auth-account-ui/styles.css`)\n\nThe compiled CSS for the Account UI. Must be imported for the UI to render correctly.\n\n### Worker (`quickback-better-auth-account-ui/worker`)\n\n| Export | Type | Description |\n|--------|------|-------------|\n| `default` | Worker | Cloudflare Worker with SPA routing |\n| `createAuthWorker` | Function | Factory for creating a worker instance |\n\n## Custom Styles\n\nOverride styles by importing your CSS after the library styles:\n\n```ts\n\n```\n\n```css title=\"my-overrides.css\"\n:root {\n --primary: 59 130 246;\n --primary-foreground: 255 255 255;\n}\n```\n\n## Updating\n\nUpdate to the latest version:\n\n```bash\nnpm update quickback-better-auth-account-ui\n```\n\nThe library follows semantic versioning. Check the [changelog](https://github.com/Kardoe-com/quickback-better-auth-account-ui/releases) for breaking changes.\n\n## Next Steps\n\n- **[Customization](/account-ui/customization)** — Full configuration reference\n- **[Feature Flags](/account-ui/features)** — Enable and disable features\n- **[Worker Setup](/account-ui/worker)** — Deploy to Cloudflare Workers\n- **[With Quickback](/account-ui/with-quickback)** — Quickback-specific configuration"
54
+ "content": "# Library Usage\n\nInstead of cloning the template, you can install Account UI as an npm package. This gives you automatic updates via `npm update` while configuring the UI through `setAppConfig()`.\n\n## Installation\n\n```bash\nnpm install quickback-better-auth-account-ui\n```\n\n### Peer Dependencies\n\nAccount UI expects these packages in your app:\n\n```bash\nnpm install react react-dom react-router-dom @tanstack/react-query\n```\n\n## Basic Setup\n\n### 1. Import and Configure\n\n```tsx title=\"src/main.tsx\"\n\nsetAppConfig({\n authRoute: 'quickback', // or 'better-auth' for standalone Better Auth\n name: 'My App',\n companyName: 'My Company Inc.',\n tagline: 'Build faster, ship sooner',\n description: 'My app description',\n});\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n \n </React.StrictMode>\n);\n```\n\n### 2. Set API URLs\n\nThe library reads API URLs from `globalThis` variables. Set these before importing the library:\n\n```tsx title=\"src/main.tsx\"\n// Single catch-all: API + Account + CMS URLs are all derived from this origin.\n(globalThis as any).__QUICKBACK_URL__ = 'https://secure.example.com';\n(globalThis as any).__QUICKBACK_APP_URL__ = 'https://app.example.com';\n\n// Then import dynamically to ensure globals are set first\nconst { AuthApp, setAppConfig } = await import('quickback-better-auth-account-ui');\nawait import('quickback-better-auth-account-ui/styles.css');\n```\n\nIf your API lives on a different subdomain, set `__QUICKBACK_API_URL__` explicitly instead (or alongside) — it takes precedence over the catch-all:\n\n```tsx\n(globalThis as any).__QUICKBACK_URL__ = 'https://secure.example.com'; // catch-all\n(globalThis as any).__QUICKBACK_API_URL__ = 'https://api.example.com'; // explicit override\n```\n\nOr with Vite environment variables:\n\n```tsx title=\"src/main.tsx\"\n(globalThis as any).__QUICKBACK_URL__ = import.meta.env.VITE_QUICKBACK_URL;\n(globalThis as any).__QUICKBACK_API_URL__ = import.meta.env.VITE_QUICKBACK_API_URL;\n(globalThis as any).__QUICKBACK_APP_URL__ = import.meta.env.VITE_QUICKBACK_APP_URL;\n\nPromise.all([\n import('quickback-better-auth-account-ui'),\n import('quickback-better-auth-account-ui/styles.css'),\n]).then(([{ AuthApp, setAppConfig }]) => {\n setAppConfig({\n authRoute: 'quickback',\n name: import.meta.env.VITE_APP_NAME || 'My App',\n companyName: import.meta.env.VITE_COMPANY_NAME || 'My Company',\n tagline: import.meta.env.VITE_APP_TAGLINE || 'Welcome',\n });\n\n ReactDOM.createRoot(document.getElementById('root')!).render(\n \n </React.StrictMode>\n );\n});\n```\n\n## Cloudflare Worker\n\nThe library exports a Cloudflare Worker entry point for SPA routing and static asset serving:\n\n```ts title=\"src/worker.ts\"\nexport { default } from 'quickback-better-auth-account-ui/worker';\n```\n\nOr extend it with custom logic:\n\n```ts title=\"src/worker.ts\"\n\nexport default createAuthWorker();\n```\n\n## Configuration via `setAppConfig()`\n\nIn library mode, all configuration happens through `setAppConfig()` instead of environment variables.\n\n### Auth Route Mode\n\n```ts\nsetAppConfig({\n authRoute: 'quickback', // Quickback API: /auth/v1, /api/v1, /storage/v1\n // or\n authRoute: 'better-auth', // Better Auth default: /api/auth\n});\n```\n\n### Branding\n\n```ts\nsetAppConfig({\n name: 'Acme SaaS',\n companyName: 'Acme Corporation',\n tagline: 'Build faster, ship sooner',\n description: 'The complete platform for modern web applications',\n branding: {\n primaryColor: '#3b82f6',\n logoUrl: '/logo.png',\n faviconUrl: '/favicon.ico',\n },\n});\n```\n\n### Labels and Messages\n\n```ts\nsetAppConfig({\n labels: {\n organizations: 'Workspaces',\n terms: 'Terms and Conditions',\n privacy: 'Privacy Notice',\n },\n messages: {\n noOrganizations: \"You haven't joined any workspaces yet.\",\n createOrganization: 'Create Workspace',\n },\n});\n```\n\n### Routes\n\n```ts\nsetAppConfig({\n routes: {\n public: {\n login: '/sign-in',\n signup: '/sign-up',\n },\n organizations: {\n list: '/workspaces',\n create: '/workspaces/new',\n },\n },\n});\n```\n\n### Password and Session\n\n```ts\nsetAppConfig({\n auth: {\n passwordRequirements: {\n minLength: 12,\n requireSymbols: true,\n },\n sessionDuration: 7 * 24 * 60 * 60, // 7 days\n },\n});\n```\n\nSee [Customization](/account-ui/customization) for the full reference of all configurable options.\n\n## Exports\n\nThe library provides the following exports:\n\n### Main Entry (`quickback-better-auth-account-ui`)\n\n| Export | Type | Description |\n|--------|------|-------------|\n| `AuthApp` | Component | The main React application component |\n| `authClient` | Object | Pre-configured Better Auth client |\n| `appConfig` | Object | Current app configuration |\n| `setAppConfig` | Function | Set app configuration overrides |\n| `createAppConfig` | Function | Create a new config object |\n| `getApiBase` | Function | Get the current API base URL |\n| `setApiBase` | Function | Override the API base URL |\n| `getAuthApiUrl` | Function | Get the auth API URL |\n| `getDataApiUrl` | Function | Get the data API URL |\n| `getStorageApiUrl` | Function | Get the storage API URL |\n\n### Styles (`quickback-better-auth-account-ui/styles.css`)\n\nThe compiled CSS for the Account UI. Must be imported for the UI to render correctly.\n\n### Worker (`quickback-better-auth-account-ui/worker`)\n\n| Export | Type | Description |\n|--------|------|-------------|\n| `default` | Worker | Cloudflare Worker with SPA routing |\n| `createAuthWorker` | Function | Factory for creating a worker instance |\n\n## Custom Styles\n\nOverride styles by importing your CSS after the library styles:\n\n```ts\n\n```\n\n```css title=\"my-overrides.css\"\n:root {\n --primary: 59 130 246;\n --primary-foreground: 255 255 255;\n}\n```\n\n## Updating\n\nUpdate to the latest version:\n\n```bash\nnpm update quickback-better-auth-account-ui\n```\n\nThe library follows semantic versioning. Check the [changelog](https://github.com/Kardoe-com/quickback-better-auth-account-ui/releases) for breaking changes.\n\n## Next Steps\n\n- **[Customization](/account-ui/customization)** — Full configuration reference\n- **[Feature Flags](/account-ui/features)** — Enable and disable features\n- **[Worker Setup](/account-ui/worker)** — Deploy to Cloudflare Workers\n- **[With Quickback](/account-ui/with-quickback)** — Quickback-specific configuration"
55
55
  },
56
56
  "account-ui/standalone": {
57
57
  "title": "Standalone Usage",
58
- "content": "Account UI can be used with **any Better Auth-powered backend**, not just Quickback-compiled projects. This makes it a drop-in authentication frontend for any app using Better Auth.\n\n## Requirements\n\n- A backend running [Better Auth](https://better-auth.com) with API endpoints\n- Node.js 18+ and npm/pnpm/bun\n- A Cloudflare account (for deployment)\n\n## Setup\n\n### 1. Clone or Install the Account UI\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### 2. Configure the Auth Route Mode\n\nThe key difference from Quickback mode is the `VITE_AUTH_ROUTE` variable. Set it to `better-auth` to use Better Auth's default API paths:\n\n```bash title=\".env\"\n# Auth route mode — use 'better-auth' for standalone\nVITE_AUTH_ROUTE=better-auth\n\n# Your Better Auth backend URL\nVITE_QUICKBACK_API_URL=https://api.example.com\n\n# Where this Account UI is hosted\nVITE_ACCOUNT_APP_URL=https://account.example.com\n```\n\nThis configures the auth client to use `/api/auth` as the base path (Better Auth's default), instead of Quickback's `/auth/v1`.\n\n### 3. Configure Your App\n\n```bash title=\".env\"\n# App identity\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Welcome to My App\n\n# Redirect after login\nVITE_QUICKBACK_APP_URL=https://app.example.com\n\n# Feature flags\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\n```\n\nSee [Environment Variables](/account-ui/environment-variables) for the complete reference.\n\n### 4. Build and Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n## Auth Route Modes\n\nAccount UI supports three routing modes, controlled by `VITE_AUTH_ROUTE`:\n\n| Mode | Value | Auth Path | Data Path | Storage Path |\n|------|-------|-----------|-----------|--------------|\n| Better Auth | `better-auth` | `/api/auth` | — | — |\n| Quickback | `quickback` | `/auth/v1` | `/api/v1` | `/storage/v1` |\n| Custom | `custom` | Custom | Custom | Custom |\n\n### Better Auth Mode (Default)\n\nRoutes all auth requests to `/api/auth/*` — the standard Better Auth convention:\n\n```bash\nVITE_AUTH_ROUTE=better-auth\n```\n\n### Custom Mode\n\nFor non-standard setups, use custom mode with explicit paths:\n\n```bash\nVITE_AUTH_ROUTE=custom\nVITE_AUTH_BASE_PATH=/my-auth\nVITE_DATA_BASE_PATH=/my-api\nVITE_STORAGE_BASE_PATH=/my-storage\n```\n\n## Backend Requirements\n\nYour Better Auth backend must have the following plugins enabled for full Account UI functionality:\n\n| Feature | Required Plugin | Required For |\n|---------|----------------|--------------|\n| Organizations | `organization` | Multi-tenant org management |\n| Admin panel | `admin` | User management dashboard |\n| API keys | `apiKey` | API key generation UI |\n| Passkeys | `@better-auth/passkey` | WebAuthn authentication |\n| Email OTP | `emailOTP` | One-time password login |\n| CLI login | `deviceAuthorization` | `quickback login` flow |\n\nOnly enable the features that match your backend's plugin configuration. Disable features for plugins you haven't installed:\n\n```bash\n# If your backend doesn't have the organization plugin:\nENABLE_ORGANIZATIONS=false\n\n# If you haven't set up passkeys:\nENABLE_PASSKEYS=false\n```\n\n## CORS Configuration\n\nYour Better Auth backend must allow requests from the Account UI's domain. Configure CORS to include:\n\n```typescript\n// In your Better Auth config\ntrustedOrigins: [\n \"https://account.example.com\", // Account UI domain\n],\n```\n\n## Differences from Quickback Mode\n\n| Aspect | Standalone | With Quickback |\n|--------|-----------|----------------|\n| Auth routes | `/api/auth/*` | `/auth/v1/*` |\n| Data API | Not used | `/api/v1/*` |\n| Storage API | Not used | `/storage/v1/*` |\n| File uploads | Manual setup | Auto-configured |\n| Backend | Any Better Auth app | Quickback-compiled |\n\n## Next Steps\n\n- [Environment Variables](/account-ui/environment-variables) — Complete configuration reference\n- [Customization](/account-ui/customization) — Branding, labels, and theming\n- [Worker Setup](/account-ui/worker) — Cloudflare Workers deployment\n- [Feature Flags](/account-ui/features) — Enable and disable features"
58
+ "content": "Account UI can be used with **any Better Auth-powered backend**, not just Quickback-compiled projects. This makes it a drop-in authentication frontend for any app using Better Auth.\n\n## Requirements\n\n- A backend running [Better Auth](https://better-auth.com) with API endpoints\n- Node.js 18+ and npm/pnpm/bun\n- A Cloudflare account (for deployment)\n\n## Setup\n\n### 1. Clone or Install the Account UI\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### 2. Configure the Auth Route Mode\n\nThe key difference from Quickback mode is the `VITE_AUTH_ROUTE` variable. Set it to `better-auth` to use Better Auth's default API paths:\n\n```bash title=\".env\"\n# Auth route mode — use 'better-auth' for standalone\nVITE_AUTH_ROUTE=better-auth\n\n# Your Better Auth backend URL (single catch-all — see below)\nVITE_QUICKBACK_URL=https://secure.example.com\n\n# Where this Account UI is hosted\nVITE_ACCOUNT_APP_URL=https://account.example.com\n```\n\n`VITE_QUICKBACK_URL` is the recommended one-variable setup. If your API lives on a separate subdomain, set `VITE_QUICKBACK_API_URL` explicitly — it takes precedence.\n\nThis configures the auth client to use `/api/auth` as the base path (Better Auth's default), instead of Quickback's `/auth/v1`.\n\n### 3. Configure Your App\n\n```bash title=\".env\"\n# App identity\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Welcome to My App\n\n# Redirect after login\nVITE_QUICKBACK_APP_URL=https://app.example.com\n\n# Feature flags\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\nENABLE_PASSKEY=true\nENABLE_EMAIL_OTP=true\n```\n\nSee [Environment Variables](/account-ui/environment-variables) for the complete reference.\n\n### 4. Build and Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n## Auth Route Modes\n\nAccount UI supports three routing modes, controlled by `VITE_AUTH_ROUTE`:\n\n| Mode | Value | Auth Path | Data Path | Storage Path |\n|------|-------|-----------|-----------|--------------|\n| Better Auth | `better-auth` | `/api/auth` | — | — |\n| Quickback | `quickback` | `/auth/v1` | `/api/v1` | `/storage/v1` |\n| Custom | `custom` | Custom | Custom | Custom |\n\n### Better Auth Mode (Default)\n\nRoutes all auth requests to `/api/auth/*` — the standard Better Auth convention:\n\n```bash\nVITE_AUTH_ROUTE=better-auth\n```\n\n### Custom Mode\n\nFor non-standard setups, use custom mode with explicit paths:\n\n```bash\nVITE_AUTH_ROUTE=custom\nVITE_AUTH_BASE_PATH=/my-auth\nVITE_DATA_BASE_PATH=/my-api\nVITE_STORAGE_BASE_PATH=/my-storage\n```\n\n## Backend Requirements\n\nYour Better Auth backend must have the following plugins enabled for full Account UI functionality:\n\n| Feature | Required Plugin | Required For |\n|---------|----------------|--------------|\n| Organizations | `organization` | Multi-tenant org management |\n| Admin panel | `admin` | User management dashboard |\n| API keys | `apiKey` | API key generation UI |\n| Passkeys | `@better-auth/passkey` | WebAuthn authentication |\n| Email OTP | `emailOTP` | One-time password login |\n| CLI login | `deviceAuthorization` | `quickback login` flow |\n\nOnly enable the features that match your backend's plugin configuration. Disable features for plugins you haven't installed:\n\n```bash\n# If your backend doesn't have the organization plugin:\nENABLE_ORGANIZATIONS=false\n\n# If you haven't set up passkeys:\nENABLE_PASSKEYS=false\n```\n\n## CORS Configuration\n\nYour Better Auth backend must allow requests from the Account UI's domain. Configure CORS to include:\n\n```typescript\n// In your Better Auth config\ntrustedOrigins: [\n \"https://account.example.com\", // Account UI domain\n],\n```\n\n## Differences from Quickback Mode\n\n| Aspect | Standalone | With Quickback |\n|--------|-----------|----------------|\n| Auth routes | `/api/auth/*` | `/auth/v1/*` |\n| Data API | Not used | `/api/v1/*` |\n| Storage API | Not used | `/storage/v1/*` |\n| File uploads | Manual setup | Auto-configured |\n| Backend | Any Better Auth app | Quickback-compiled |\n\n## Next Steps\n\n- [Environment Variables](/account-ui/environment-variables) — Complete configuration reference\n- [Customization](/account-ui/customization) — Branding, labels, and theming\n- [Worker Setup](/account-ui/worker) — Cloudflare Workers deployment\n- [Feature Flags](/account-ui/features) — Enable and disable features"
59
59
  },
60
60
  "account-ui/with-quickback": {
61
61
  "title": "With Quickback",
62
- "content": "When using Account UI with a Quickback-compiled backend, the recommended approach is to let the compiler build and embed the SPA directly into your Worker.\n\n## Compiler-Embedded (Recommended)\n\nAdd `account` to your `quickback.config.ts`:\n\n```typescript title=\"quickback/quickback.config.ts\"\nexport default {\n name: \"my-app\",\n template: \"hono\",\n account: {\n domain: \"auth.example.com\",\n name: \"My App\",\n companyName: \"My Company\",\n auth: {\n password: true,\n passkey: true,\n organizations: true,\n admin: true,\n },\n },\n // ...providers\n};\n```\n\nThen compile and deploy:\n\n```bash\nquickback compile\nnpx wrangler deploy\n```\n\n### What Happens During Compilation\n\n1. The compiler reads your `account` config and generates a `.env` file with the correct Vite environment variables (`VITE_QUICKBACK_API_URL`, `VITE_ENABLE_PASSWORD`, branding, etc.)\n2. The Account SPA source is copied to a temp directory, with disabled feature routes excluded\n3. Pre-installed dependencies are symlinked (no `npm install` during build)\n4. Vite builds the SPA with your settings baked in\n5. Built assets (content-hashed filenames) are placed in the output directory\n\nWhen CMS is also enabled, Account assets go to `src/apps/account/` and are served at `/account/`. When Account is the only SPA, assets go to `src/apps/` and are served at root.\n\n### Custom Domains\n\nWith a custom domain, the Account SPA is served at root (`/`) on that domain:\n\n```typescript\naccount: { domain: \"auth.example.com\" }\n```\n\nAdd an admin domain to serve the admin panel on its own subdomain:\n\n```typescript\naccount: {\n domain: \"auth.example.com\",\n adminDomain: \"admin.example.com\",\n}\n```\n\nSee [Multi-Domain Architecture](/compiler/config/domains) for details on hostname routing and cross-subdomain cookies.\n\n### Auth Feature Flags\n\nFeature flags control which pages are included in the build:\n\n```typescript\nauth: {\n password: true, // Email/password login + inline password change\n emailOTP: false, // Email OTP login flow\n passkey: true, // WebAuthn passkey setup + management\n organizations: true, // Dashboard, org management, invitations\n admin: true, // Admin panel (user management)\n}\n```\n\nDisabled features are excluded at compile time — their route files are removed before the Vite build. See [Configuration](/compiler/config) for all options.\n\n## Manual Deployment\n\nIf you need Account UI as a separate Worker (not embedded in the API Worker), you can deploy it standalone.\n\n### 1. Clone the Template\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### 2. Configure for Quickback\n\nSet the auth route mode to `quickback` to match Quickback's API paths:\n\n```bash title=\".env\"\nVITE_AUTH_ROUTE=quickback\nVITE_QUICKBACK_API_URL=https://api.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\nVITE_QUICKBACK_APP_URL=https://app.example.com\n```\n\nIn Quickback mode, Account UI routes requests to three API paths:\n\n| Path | Purpose | Examples |\n|------|---------|---------|\n| `/auth/v1/*` | Authentication | Login, signup, sessions, passkeys |\n| `/api/v1/*` | Data API | Organizations, API keys |\n| `/storage/v1/*` | File storage | Avatar uploads, file management |\n\n### 3. Configure Features\n\nMatch your `quickback.config.ts` plugins:\n\n```bash title=\".env\"\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Build faster, ship sooner\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\nENABLE_PASSKEY=true\nENABLE_ADMIN=true\n```\n\n### 4. Build and Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n## Library Mode\n\nInstall as a dependency instead of cloning:\n\n```bash\nnpm install quickback-better-auth-account-ui\n```\n\n```tsx\n\nsetAppConfig({\n authRoute: 'quickback',\n name: 'My App',\n companyName: 'My Company',\n});\n```\n\nSee [Library Usage](/account-ui/library-usage) for the complete guide.\n\n## Next Steps\n\n- [Configuration](/compiler/config) — CMS and Account config reference\n- [Multi-Domain Architecture](/compiler/config/domains) — Custom domains and hostname routing\n- [Environment Variables](/account-ui/environment-variables) — Complete variable reference\n- [Feature Flags](/account-ui/features) — Enable and disable features\n- [Customization](/account-ui/customization) — Branding, labels, and theming"
62
+ "content": "When using Account UI with a Quickback-compiled backend, the recommended approach is to let the compiler build and embed the SPA directly into your Worker.\n\n## Compiler-Embedded (Recommended)\n\nAdd `account` to your `quickback.config.ts`:\n\n```typescript title=\"quickback/quickback.config.ts\"\nexport default {\n name: \"my-app\",\n template: \"hono\",\n account: {\n domain: \"auth.example.com\",\n name: \"My App\",\n companyName: \"My Company\",\n auth: {\n password: true,\n passkey: true,\n organizations: true,\n admin: true,\n },\n },\n // ...providers\n};\n```\n\nThen compile and deploy:\n\n```bash\nquickback compile\nnpx wrangler deploy\n```\n\n### What Happens During Compilation\n\n1. The compiler reads your `account` config and generates a `.env` file with the correct Vite environment variables (`VITE_QUICKBACK_URL`, `VITE_QUICKBACK_API_URL`, `VITE_ENABLE_PASSWORD`, branding, etc.)\n2. The Account SPA source is copied to a temp directory, with disabled feature routes excluded\n3. Pre-installed dependencies are symlinked (no `npm install` during build)\n4. Vite builds the SPA with your settings baked in\n5. Built assets (content-hashed filenames) are placed in the output directory\n\nWhen CMS is also enabled, Account assets go to `src/apps/account/` and are served at `/account/`. When Account is the only SPA, assets go to `src/apps/` and are served at root.\n\n### Custom Domains\n\nWith a custom domain, the Account SPA is served at root (`/`) on that domain:\n\n```typescript\naccount: { domain: \"auth.example.com\" }\n```\n\nAdd an admin domain to serve the admin panel on its own subdomain:\n\n```typescript\naccount: {\n domain: \"auth.example.com\",\n adminDomain: \"admin.example.com\",\n}\n```\n\nSee [Multi-Domain Architecture](/compiler/config/domains) for details on hostname routing and cross-subdomain cookies.\n\n### Auth Feature Flags\n\nFeature flags control which pages are included in the build:\n\n```typescript\nauth: {\n password: true, // Email/password login + inline password change\n emailOTP: false, // Email OTP login flow\n passkey: true, // WebAuthn passkey setup + management\n organizations: true, // Dashboard, org management, invitations\n admin: true, // Admin panel (user management)\n}\n```\n\nDisabled features are excluded at compile time — their route files are removed before the Vite build. See [Configuration](/compiler/config) for all options.\n\n## Manual Deployment\n\nIf you need Account UI as a separate Worker (not embedded in the API Worker), you can deploy it standalone.\n\n### 1. Clone the Template\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### 2. Configure for Quickback\n\nSet the auth route mode to `quickback` to match Quickback's API paths:\n\n```bash title=\".env\"\nVITE_AUTH_ROUTE=quickback\n# One variable points at the Quickback origin — API, Account, and CMS are all derived from it.\nVITE_QUICKBACK_URL=https://secure.example.com\nVITE_ACCOUNT_APP_URL=https://account.example.com\nVITE_QUICKBACK_APP_URL=https://app.example.com\n```\n\nIn Quickback mode, Account UI routes requests to three API paths:\n\n| Path | Purpose | Examples |\n|------|---------|---------|\n| `/auth/v1/*` | Authentication | Login, signup, sessions, passkeys |\n| `/api/v1/*` | Data API | Organizations, API keys |\n| `/storage/v1/*` | File storage | Avatar uploads, file management |\n\n### 3. Configure Features\n\nMatch your `quickback.config.ts` plugins:\n\n```bash title=\".env\"\nVITE_APP_NAME=My App\nVITE_APP_TAGLINE=Build faster, ship sooner\nENABLE_SIGNUP=true\nENABLE_ORGANIZATIONS=true\nENABLE_PASSKEY=true\nENABLE_ADMIN=true\n```\n\n### 4. Build and Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n## Library Mode\n\nInstall as a dependency instead of cloning:\n\n```bash\nnpm install quickback-better-auth-account-ui\n```\n\n```tsx\n\nsetAppConfig({\n authRoute: 'quickback',\n name: 'My App',\n companyName: 'My Company',\n});\n```\n\nSee [Library Usage](/account-ui/library-usage) for the complete guide.\n\n## Next Steps\n\n- [Configuration](/compiler/config) — CMS and Account config reference\n- [Multi-Domain Architecture](/compiler/config/domains) — Custom domains and hostname routing\n- [Environment Variables](/account-ui/environment-variables) — Complete variable reference\n- [Feature Flags](/account-ui/features) — Enable and disable features\n- [Customization](/account-ui/customization) — Branding, labels, and theming"
63
63
  },
64
64
  "account-ui/worker": {
65
65
  "title": "Worker Setup",
66
- "content": "# Worker Setup\n\nDeploy the Account UI as a Cloudflare Worker with static asset serving.\n\n## Quick Start\n\n### Using the Template\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### Using the Library\n\nIf you're consuming Account UI as an npm package, re-export the worker from the library:\n\n```ts title=\"src/worker.ts\"\nexport { default } from 'quickback-better-auth-account-ui/worker';\n```\n\nThen continue with the Wrangler configuration below.\n\n### 2. Configure Wrangler\n\nThe template includes a `wrangler.toml` pre-configured for Cloudflare Workers:\n\n```toml title=\"wrangler.toml\"\nname = \"my-account-app\"\ncompatibility_date = \"2025-01-01\"\ncompatibility_flags = [\"nodejs_compat\"]\n\n# Custom domain (uncomment and set your domain)\n# routes = [\n# { pattern = \"account.example.com\", custom_domain = true }\n# ]\n\n# Worker entry point\nmain = \"src/worker.ts\"\n\n# Static assets\n[assets]\nbinding = \"ASSETS\"\ndirectory = \"dist/client\"\n```\n\n### 3. Build and Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n## Worker Entry Point\n\nThe worker is at `src/worker.ts`. It serves static assets and handles SPA routing:\n\n- Static asset serving via the `ASSETS` binding\n- SPA routing (all routes serve `index.html`)\n- Health check endpoint (`/health`)\n\n### Custom Worker Logic\n\nEdit `src/worker.ts` directly to add custom logic:\n\n```ts title=\"src/worker.ts\"\nexport default {\n async fetch(request: Request, env: Env): Promise\n\n### Multiple Environments\n\n```toml title=\"wrangler.toml\"\n# Development\n[env.dev]\nname = \"my-account-app-dev\"\nroute = \"account-dev.example.com/*\"\n\n[env.dev.vars]\nVITE_QUICKBACK_API_URL = \"https://api-dev.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account-dev.example.com\"\n\n# Staging\n[env.staging]\nname = \"my-account-app-staging\"\nroute = \"account-staging.example.com/*\"\n\n[env.staging.vars]\nVITE_QUICKBACK_API_URL = \"https://api-staging.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account-staging.example.com\"\n\n# Production\n[env.production]\nname = \"my-account-app\"\nroute = \"account.example.com/*\"\n\n[env.production.vars]\nVITE_QUICKBACK_API_URL = \"https://api.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account.example.com\"\n```\n\nDeploy to specific environment:\n\n```bash\nwrangler deploy --env staging\nwrangler deploy --env production\n```\n\n## Cloudflare Pages Alternative\n\nYou can also deploy to Cloudflare Pages instead of Workers:\n\n### 1. Configure Build\n\n```toml title=\"wrangler.toml\"\n# Remove [assets] section - Pages handles this\n\nname = \"my-account-app\"\npages_build_output_dir = \"dist/client\"\n```\n\n### 2. Deploy to Pages\n\n```bash\n# First time setup\nwrangler pages project create my-account-app\n\n# Deploy\nwrangler pages deploy dist/client\n```\n\n### 3. Configure Environment Variables\n\nSet environment variables in Cloudflare Pages dashboard:\n1. Go to your Pages project\n2. **Settings** → **Environment Variables**\n3. Add `VITE_*` variables\n4. Redeploy\n\n## Local Development\n\n### Option 1: Vite Dev Server\n\nThe fastest way to develop locally:\n\n```bash\nnpx vite dev\n```\n\nThis starts the Vite dev server with hot module replacement on `localhost:5173`.\n\n### Option 2: Wrangler Dev\n\nTest the worker locally with `wrangler dev`:\n\n```bash\nnpm run build && wrangler dev\n```\n\nThis runs the worker on `localhost:8787`.\n\n## Health Check\n\nThe worker includes a built-in health check endpoint:\n\n```bash\ncurl https://account.example.com/health\n```\n\nResponse:\n\n```json\n{\n \"status\": \"ok\",\n \"app\": \"auth-ui\"\n}\n```\n\nUse this for:\n- Uptime monitoring\n- Load balancer health checks\n- Deployment verification\n\n## Security Headers\n\nAdd security headers by editing `src/worker.ts`:\n\n```ts title=\"src/worker.ts\"\nexport default {\n async fetch(request: Request, env: Env): Promise<Response> {\n // ... routing logic ...\n\n // Add security headers to response\n const response = await env.ASSETS.fetch(request);\n\n const headers = new Headers(response.headers);\n headers.set('X-Frame-Options', 'DENY');\n headers.set('X-Content-Type-Options', 'nosniff');\n headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');\n headers.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n },\n};\n```\n\n## Custom 404 Page\n\nServe a custom 404 page for unmatched routes:\n\n```ts title=\"src/worker.ts\"\nexport default {\n async fetch(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url);\n\n // Try to serve static asset\n const assetResponse = await env.ASSETS.fetch(request);\n\n if (assetResponse.status !== 404) {\n return assetResponse;\n }\n\n // Serve index.html for SPA routes\n if (!url.pathname.startsWith('/api/')) {\n const indexRequest = new Request(`${url.origin}/index.html`, request);\n return env.ASSETS.fetch(indexRequest);\n }\n\n // Custom 404 for API routes\n return new Response('Not Found', { status: 404 });\n },\n};\n```\n\n## Deployment Checklist\n\nBefore deploying to production:\n\n- [ ] Set all required environment variables (`VITE_QUICKBACK_API_URL`, etc.)\n- [ ] Configure custom domain in Cloudflare\n- [ ] Add SSL certificate (automatic with Cloudflare)\n- [ ] Test all authentication flows\n- [ ] Configure DNS records\n- [ ] Set up monitoring/health checks\n- [ ] Test email sending\n- [ ] Verify passkey functionality (requires HTTPS)\n- [ ] Check CORS configuration on API\n- [ ] Review security headers\n- [ ] Test organization invitations\n- [ ] Verify redirection to main app works\n\n## Troubleshooting\n\n### Assets Not Loading\n\n**Problem**: 404 errors for static assets (JS, CSS)\n\n**Solution**: Check `directory` path in `wrangler.toml`:\n\n```toml\n[assets]\nbinding = \"ASSETS\"\ndirectory = \"dist/client\" # Must point to Vite's client build output\n```\n\n### Environment Variables Not Working\n\n**Problem**: Config values are undefined\n\n**Solution**: Remember that `VITE_*` variables are build-time. Either:\n1. Set them in `.env.production` before building\n2. Use `setAppConfig()` in `src/main.tsx` for runtime overrides\n3. Edit `src/config/app.ts` defaults directly\n\n### SPA Routing Issues\n\n**Problem**: Refreshing on `/profile` returns 404\n\n**Solution**: Ensure your worker serves `index.html` for all non-asset routes:\n\n```ts\nif (assetResponse.status === 404 && !url.pathname.startsWith('/api/')) {\n return env.ASSETS.fetch(new Request(`${url.origin}/index.html`, request));\n}\n```\n\n## Next Steps\n\n- **[Environment Variables](/account-ui/environment-variables)** - Configure your deployment\n- **[Feature Flags](/account-ui/features)** - Enable features\n- **[Customization](/account-ui/customization)** - Brand your UI"
66
+ "content": "# Worker Setup\n\nDeploy the Account UI as a Cloudflare Worker with static asset serving.\n\n## Quick Start\n\n### Using the Template\n\n```bash\nnpx degit Kardoe-com/quickback-better-auth-account-ui my-account-app\ncd my-account-app\nnpm install\n```\n\n### Using the Library\n\nIf you're consuming Account UI as an npm package, re-export the worker from the library:\n\n```ts title=\"src/worker.ts\"\nexport { default } from 'quickback-better-auth-account-ui/worker';\n```\n\nThen continue with the Wrangler configuration below.\n\n### 2. Configure Wrangler\n\nThe template includes a `wrangler.toml` pre-configured for Cloudflare Workers:\n\n```toml title=\"wrangler.toml\"\nname = \"my-account-app\"\ncompatibility_date = \"2025-01-01\"\ncompatibility_flags = [\"nodejs_compat\"]\n\n# Custom domain (uncomment and set your domain)\n# routes = [\n# { pattern = \"account.example.com\", custom_domain = true }\n# ]\n\n# Worker entry point\nmain = \"src/worker.ts\"\n\n# Static assets\n[assets]\nbinding = \"ASSETS\"\ndirectory = \"dist/client\"\n```\n\n### 3. Build and Deploy\n\n```bash\nnpm run build\nnpx wrangler deploy\n```\n\n## Worker Entry Point\n\nThe worker is at `src/worker.ts`. It serves static assets and handles SPA routing:\n\n- Static asset serving via the `ASSETS` binding\n- SPA routing (all routes serve `index.html`)\n- Health check endpoint (`/health`)\n\n### Custom Worker Logic\n\nEdit `src/worker.ts` directly to add custom logic:\n\n```ts title=\"src/worker.ts\"\nexport default {\n async fetch(request: Request, env: Env): Promise\n\n### Multiple Environments\n\n```toml title=\"wrangler.toml\"\n# Development\n[env.dev]\nname = \"my-account-app-dev\"\nroute = \"account-dev.example.com/*\"\n\n[env.dev.vars]\nVITE_QUICKBACK_URL = \"https://secure-dev.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account-dev.example.com\"\n\n# Staging\n[env.staging]\nname = \"my-account-app-staging\"\nroute = \"account-staging.example.com/*\"\n\n[env.staging.vars]\nVITE_QUICKBACK_URL = \"https://secure-staging.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account-staging.example.com\"\n\n# Production\n[env.production]\nname = \"my-account-app\"\nroute = \"account.example.com/*\"\n\n[env.production.vars]\nVITE_QUICKBACK_URL = \"https://secure.example.com\"\nVITE_ACCOUNT_APP_URL = \"https://account.example.com\"\n```\n\nDeploy to specific environment:\n\n```bash\nwrangler deploy --env staging\nwrangler deploy --env production\n```\n\n## Cloudflare Pages Alternative\n\nYou can also deploy to Cloudflare Pages instead of Workers:\n\n### 1. Configure Build\n\n```toml title=\"wrangler.toml\"\n# Remove [assets] section - Pages handles this\n\nname = \"my-account-app\"\npages_build_output_dir = \"dist/client\"\n```\n\n### 2. Deploy to Pages\n\n```bash\n# First time setup\nwrangler pages project create my-account-app\n\n# Deploy\nwrangler pages deploy dist/client\n```\n\n### 3. Configure Environment Variables\n\nSet environment variables in Cloudflare Pages dashboard:\n1. Go to your Pages project\n2. **Settings** → **Environment Variables**\n3. Add `VITE_*` variables\n4. Redeploy\n\n## Local Development\n\n### Option 1: Vite Dev Server\n\nThe fastest way to develop locally:\n\n```bash\nnpx vite dev\n```\n\nThis starts the Vite dev server with hot module replacement on `localhost:5173`.\n\n### Option 2: Wrangler Dev\n\nTest the worker locally with `wrangler dev`:\n\n```bash\nnpm run build && wrangler dev\n```\n\nThis runs the worker on `localhost:8787`.\n\n## Health Check\n\nThe worker includes a built-in health check endpoint:\n\n```bash\ncurl https://account.example.com/health\n```\n\nResponse:\n\n```json\n{\n \"status\": \"ok\",\n \"app\": \"auth-ui\"\n}\n```\n\nUse this for:\n- Uptime monitoring\n- Load balancer health checks\n- Deployment verification\n\n## Security Headers\n\nAdd security headers by editing `src/worker.ts`:\n\n```ts title=\"src/worker.ts\"\nexport default {\n async fetch(request: Request, env: Env): Promise<Response> {\n // ... routing logic ...\n\n // Add security headers to response\n const response = await env.ASSETS.fetch(request);\n\n const headers = new Headers(response.headers);\n headers.set('X-Frame-Options', 'DENY');\n headers.set('X-Content-Type-Options', 'nosniff');\n headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');\n headers.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n },\n};\n```\n\n## Custom 404 Page\n\nServe a custom 404 page for unmatched routes:\n\n```ts title=\"src/worker.ts\"\nexport default {\n async fetch(request: Request, env: Env): Promise<Response> {\n const url = new URL(request.url);\n\n // Try to serve static asset\n const assetResponse = await env.ASSETS.fetch(request);\n\n if (assetResponse.status !== 404) {\n return assetResponse;\n }\n\n // Serve index.html for SPA routes\n if (!url.pathname.startsWith('/api/')) {\n const indexRequest = new Request(`${url.origin}/index.html`, request);\n return env.ASSETS.fetch(indexRequest);\n }\n\n // Custom 404 for API routes\n return new Response('Not Found', { status: 404 });\n },\n};\n```\n\n## Deployment Checklist\n\nBefore deploying to production:\n\n- [ ] Set all required environment variables (`VITE_QUICKBACK_URL` or `VITE_QUICKBACK_API_URL`, etc.)\n- [ ] Configure custom domain in Cloudflare\n- [ ] Add SSL certificate (automatic with Cloudflare)\n- [ ] Test all authentication flows\n- [ ] Configure DNS records\n- [ ] Set up monitoring/health checks\n- [ ] Test email sending\n- [ ] Verify passkey functionality (requires HTTPS)\n- [ ] Check CORS configuration on API\n- [ ] Review security headers\n- [ ] Test organization invitations\n- [ ] Verify redirection to main app works\n\n## Troubleshooting\n\n### Assets Not Loading\n\n**Problem**: 404 errors for static assets (JS, CSS)\n\n**Solution**: Check `directory` path in `wrangler.toml`:\n\n```toml\n[assets]\nbinding = \"ASSETS\"\ndirectory = \"dist/client\" # Must point to Vite's client build output\n```\n\n### Environment Variables Not Working\n\n**Problem**: Config values are undefined\n\n**Solution**: Remember that `VITE_*` variables are build-time. Either:\n1. Set them in `.env.production` before building\n2. Use `setAppConfig()` in `src/main.tsx` for runtime overrides\n3. Edit `src/config/app.ts` defaults directly\n\n### SPA Routing Issues\n\n**Problem**: Refreshing on `/profile` returns 404\n\n**Solution**: Ensure your worker serves `index.html` for all non-asset routes:\n\n```ts\nif (assetResponse.status === 404 && !url.pathname.startsWith('/api/')) {\n return env.ASSETS.fetch(new Request(`${url.origin}/index.html`, request));\n}\n```\n\n## Next Steps\n\n- **[Environment Variables](/account-ui/environment-variables)** - Configure your deployment\n- **[Feature Flags](/account-ui/features)** - Enable features\n- **[Customization](/account-ui/customization)** - Brand your UI"
67
67
  },
68
68
  "changelog": {
69
69
  "title": "Changelog",
70
- "content": "# Changelog\n\nRelease notes for the Quickback compiler, CLI, and platform.\n\n---\n\n## v0.8.1 — April 9, 2026\n\n### Role Hierarchy with `+` Suffix\n\nYou can now define a role hierarchy in your config and use the `+` suffix in access rules to mean \"this role and above\":\n\n```typescript\n// quickback.config.ts\nauth: {\n roleHierarchy: ['member', 'admin', 'owner'],\n}\n\n// In resource definitions\ncrud: {\n list: { access: { roles: [\"member+\"] } }, // member, admin, owner\n create: { access: { roles: [\"admin+\"] } }, // admin, owner\n delete: { access: { roles: [\"owner\"] } }, // owner only\n}\n```\n\nRoles are expanded at compile time. No `+` = exact match. See [Access - Role Hierarchy](/compiler/definitions/access#role-hierarchy) for details.\n\n---\n\n## v0.8.0 — April 9, 2026\n\n### Breaking: VITE URL Environment Variables Renamed\n\nAll Quickback-generated URL environment variables are now namespaced under `VITE_QUICKBACK_*` to avoid collisions with other Vite projects.\n\n| Old | New |\n|-----|-----|\n| `VITE_API_URL` | `VITE_QUICKBACK_API_URL` |\n| `VITE_ACCOUNT_URL` | `VITE_QUICKBACK_ACCOUNT_URL` |\n| `VITE_APP_URL` | `VITE_QUICKBACK_APP_URL` |\n| `VITE_CMS_URL` | `VITE_QUICKBACK_CMS_URL` |\n\n**Non-URL variables are unchanged:** `VITE_ENABLE_*` feature flags, branding vars (`VITE_APP_NAME`, etc.), and `VITE_STRIPE_PUBLISHABLE_KEY` stay as-is.\n\n**Action required:**\n- **Compiled projects** — re-run `quickback compile`. The compiler generates the new names automatically.\n- **Standalone deploys** — update `.env`, `.env.production`, and `wrangler.toml` files manually.\n- **Library users** — the `__QUICKBACK_API_URL__` / `__QUICKBACK_APP_URL__` globalThis names are unchanged, but if you pipe Vite env vars into them, update the var names.\n\nSee [Environment Variables](/account-ui/environment-variables) for the full reference.\n\n### CMS Access Control\n\nThe `cms.access` config option now enforces access at the CMS level, not just link visibility in Account UI.\n\n```typescript\ncms: { access: \"admin\" }\n```\n\nWhen set to `\"admin\"`, non-admin users (`user.role !== \"admin\"`) see an \"Access Denied\" screen with a sign-out button instead of the CMS. When set to `\"user\"` (the default), any authenticated user can access the CMS.\n\nSee [CMS Connecting](/cms/connecting) and [Compiler Config](/compiler/config) for details.\n\n### SPA Sub-Route Fix\n\nFixed a bug where hard-refreshing or directly navigating to SPA sub-routes (e.g., `/account/profile`, `/cms/tables/users`) returned a blank white screen. The Worker now correctly serves `index.html` for all SPA paths, allowing the client-side router to handle routing.\n\n---\n\n## v0.5.11 — February 24, 2026\n\n### Email OTP & Auth Fixes\n\n- Fixed email-OTP magic links not working correctly\n- Removed deprecated `/internal/validate` endpoint — use standard Better Auth session validation instead\n- Auth is now required for all API routes when running locally (previously some routes were unprotected in dev)\n\n### Multiple Table Exports Fix\n\n- Fixed a compiler error when a single file exports multiple Drizzle tables alongside a `defineTable()` default export\n- The CLI now properly detects and reports this with a clear error message pointing to the fix\n\n### Headless Drizzle Rename Hints\n\n- Added `compiler.migrations.renames` configuration for CI/CD environments where Drizzle's interactive rename prompts would block compilation\n- Compile errors now include explicit rename key paths and fail fast on malformed rename config keys\n- See [Configuration](/compiler/config) for details\n\n### Security Contract Report Artifacts\n\n- Added generated security contract artifacts to compiler output:\n - `reports/security-contracts.report.json`\n - `reports/security-contracts.report.sig.json`\n- Added config-driven signing controls:\n - `compiler.securityContracts.report.signature.enabled`\n - `compiler.securityContracts.report.signature.required`\n - `compiler.securityContracts.report.signature.key` / `keyEnv` / `keyId`\n- Missing required signing keys now fail loudly with explicit remediation guidance\n- Added strict config validation for report/signature paths and signing options\n\n### Mandatory Unsafe Action Audit Trail\n\n- Added structured unsafe action config (`unsafe: { reason, adminOnly, crossTenant, targetScope }`)\n- Cross-tenant unsafe actions now require Better Auth authentication plus platform admin role (`ctx.userRole === \"admin\"`)\n- Added mandatory audit logging for unsafe cross-tenant actions (success, denial, and error paths)\n- Cloudflare output now includes optional `AUDIT_DB` wiring, `drizzle.audit.config.ts`, and `db:migrate:audit:*` scripts when unsafe actions are present\n- Added compile-time raw SQL guard for actions and handlers (`allowRawSql: true` required per action)\n\n---\n\n## v0.5.10 — February 19, 2026\n\n### Compiler Page Parsing & CLI Output\n\n- Improved compiler page parsing for `definePage()` definitions\n- Better CLI output formatting during compilation\n- Added `apiPath` to schema registry for CMS integration\n\n### Bug Fixes\n\n- Fixed hyphenated action names not being properly quoted in generated code (e.g., `mark-complete` now generates valid JavaScript)\n- API key authentication (`x-api-key` header) is now handled separately from session tokens (`Bearer` header)\n\n---\n\n## v0.5.9 — February 16, 2026\n\n### CMS Pages & CLI Page Support\n\n- Added `definePage()` support for CMS-managed pages\n- Auth middleware improvements for page routes\n- CLI now supports page definitions alongside table and action definitions\n\n---\n\n## v0.5.8 — February 14, 2026\n\n### CMS App\n\n- Introduced the Quickback CMS — a schema-driven admin panel that connects to your generated API\n- CMS namespace added to actions for admin-specific operations\n- Fixed `guard` → `access` naming inconsistency in CMS action definitions\n\n### Schema Registry & Firewall Improvements\n\n- Added schema registry generator — the compiler now outputs a JSON schema registry used by the CMS\n- Firewall error modes: choose between `reveal` (403 with details) and `hide` (opaque 404) for security-sensitive deployments\n\n### Bug Fixes\n\n- Fixed anonymous user email format generation\n- Organization selector improvements in Account UI\n- Config validation now catches more errors at compile time\n- Better CRUD error handling with structured error responses\n- Fixed masking to use representative star counts instead of fixed formatting\n\n---\n\n## v0.5.7 — February 12, 2026\n\n### Scoped Database for Actions\n\nActions now receive a **security-scoped database** instead of a raw Drizzle instance. The compiler generates a proxy wrapper that automatically enforces org isolation, owner filtering, and soft-delete visibility — the same protections that CRUD routes have always had.\n\nThe scoped DB uses duck-typed column detection at runtime:\n\n| Column Detected | SELECT / UPDATE / DELETE | INSERT |\n|---|---|---|\n| `organizationId` | Adds `WHERE organizationId = ?` | Auto-injects `organizationId` from context |\n| `ownerId` | Adds `WHERE ownerId = ?` | Auto-injects `ownerId` from context |\n| `deletedAt` | Adds `WHERE deletedAt IS NULL` | — |\n\nThis means **every action is secure by default** — no manual `WHERE` clauses needed.\n\n```typescript\ndefineActions(todos, {\n complete: {\n type: \"record\",\n execute: async ({ db, ctx, record, input }) => {\n // db is scoped — only sees records in user's org, excludes soft-deleted\n const siblings = await db.select().from(todos);\n // ↑ automatically filtered to ctx.activeOrgId + deletedAt IS NULL\n },\n },\n});\n```\n\n#### Unsafe Mode\n\nActions that intentionally need to bypass security (admin reports, cross-org queries, migrations) can declare `unsafe: true` to receive a raw, unscoped database handle:\n\n```typescript\ndefineActions(analytics, {\n globalReport: {\n unsafe: true,\n execute: async ({ db, rawDb, ctx, input }) => {\n // db → still scoped (safety net)\n // rawDb → bypasses all security filters\n const allOrgs = await rawDb.select().from(organizations);\n },\n },\n});\n```\n\nWithout `unsafe: true`, `rawDb` is `undefined`.\n\n**Related docs:** [Actions](/compiler/definitions/actions), [Actions API](/compiler/using-the-api/actions-api)\n\n---\n\n### Cascading Soft Delete\n\nSoft-deleting a parent record now **automatically cascades** to child and junction tables within the same feature. The compiler detects foreign key references at build time and generates cascade UPDATE statements.\n\n```\nDELETE /api/v1/projects/:id\n```\n\nGenerated behavior:\n```typescript\n// 1. Soft delete the parent\nawait db.update(projects)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projects.id, id));\n\n// 2. Auto-cascade to children (compiler-generated)\nawait db.update(projectMembers)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectMembers.projectId, id));\n\nawait db.update(projectTasks)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectTasks.projectId, id));\n```\n\nRules:\n- Only applies to **soft delete** (the default). Hard delete relies on database-level `ON DELETE CASCADE`.\n- Only cascades within the **same feature** — cross-feature references are not affected.\n- Child tables must have `deletedAt` / `deletedBy` columns (auto-added by the compiler's audit fields).\n\n**Related docs:** [Actions API — Cascading Soft Delete](/compiler/using-the-api/actions-api#cascading-soft-delete)\n\n---\n\n### Advanced Query Parameters\n\nNew query parameter capabilities for all list endpoints:\n\n- **Field selection** — `?fields=id,name,status` returns only the columns you need\n- **Multi-sort** — `?sort=status:asc,createdAt:desc` sorts by multiple fields\n- **Total count** — `?count=true` returns total matching records in response headers (`X-Total-Count`)\n- **Full-text search** — `?search=keyword` searches across all text columns\n\n```bash\n# Get only names and statuses, sorted by status then date, with total count\nGET /api/v1/todos?fields=id,name,status&sort=status:asc,createdAt:desc&count=true\n\n# Search across all text fields\nGET /api/v1/todos?search=urgent\n```\n\n**Related docs:** [Query Parameters](/compiler/using-the-api/query-params)\n\n---\n\n### Audit Field Improvements\n\n- `deletedAt` and `deletedBy` fields are now **always injected** by the compiler for tables with soft delete enabled — no need to define them in your schema\n- All audit fields (`createdAt`, `createdBy`, `modifiedAt`, `modifiedBy`, `deletedAt`, `deletedBy`) are auto-managed\n\n---\n\n## v0.5.6 — February 8, 2026\n\n### Database Naming Conventions\n\n- Default table and column naming changed to **snake_case** with `usePlurals: false`\n- Table names derived from generated Better Auth schema for consistency\n- Removed legacy single-database mode — split databases (auth + features) is now the standard\n\n### Auth Variable Shadowing Fix\n\n- Fixed `member` variable in auth middleware that shadowed the Drizzle `member` table import\n- Renamed to `sessionMember` to avoid conflicts in generated routes\n\n---\n\n## v0.5.5 — February 5, 2026\n\n### Better Auth Plugins\n\n- Published `@kardoe/better-auth-upgrade-anonymous` v1.1.0 — post-passkey email collection flow\n- Published `@kardoe/better-auth-combo-auth` — combined email + password + OTP authentication\n- Published `@kardoe/better-auth-aws-ses` — AWS SES email provider for Better Auth\n\n### OpenAPI Spec Generation\n\n- Generated APIs now include a full OpenAPI specification at `/openapi.json`\n- Better Auth endpoints included in the spec\n- Runtime route: `GET /openapi.json`\n\n### Security Hardening\n\n- Global error handler prevents leaking internal error details\n- Security headers middleware (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)\n- `BETTER_AUTH_SECRET` properly passed to generated config\n\n---\n\n## v0.5.4 — January 30, 2026\n\n### Account UI\n\n- Pre-built authentication UI deployed as Cloudflare Workers\n- Features: sessions, organizations, passkeys, passwordless, admin panel, API keys\n- Dual-mode: standalone (degit template) or embedded with Quickback projects\n\n### Webhook System\n\n- Inbound webhook endpoints with signature verification\n- Outbound webhooks via Cloudflare Queues with automatic retries\n- Configurable per-feature webhook events\n\n### Realtime & Vector Search\n\n- Durable Objects + WebSocket realtime subscriptions\n- Vector embeddings via Cloudflare Vectorize\n- KV and R2 storage integrations\n\n---\n\n## v0.5.0 — January 2026\n\n### Initial Release\n\n- **Quickback Compiler** — TypeScript-first backend compiler\n- **Four Security Pillars** — Firewall, Access, Guards, Masking\n- **`defineTable()`** — Schema + security configuration in a single file\n- **Templates** — Cloudflare Workers, Bun standalone, B2B SaaS\n- **Cloud Compiler** — Remote compilation via `compiler.quickback.dev`\n- **CLI** — `quickback create`, `quickback compile`, `quickback init`\n- **Better Auth Integration** — Organizations, roles, sessions\n- **Drizzle ORM** — Schema-first with automatic migrations\n- **Cloudflare D1** — Split database support (auth + features)"
70
+ "content": "# Changelog\n\nRelease notes for the Quickback compiler, CLI, and platform.\n\n---\n\n## v0.8.4 — April 14, 2026\n\n### New `generateId: \"short\"` Strategy\n\nAdded a compact ID option for tables where short, shareable codes matter more than collision resistance at scale:\n\n```typescript\nproviders: {\n database: defineDatabase(\"cloudflare-d1\", {\n generateId: \"short\",\n }),\n}\n```\n\nGenerates 6-character alphanumeric IDs (e.g. `a3F9xK`) using nanoid's `customAlphabet` with the full 62-char alphabet (`0-9A-Za-z`). Requires the `nanoid` package in your project (same as the existing `\"nanoid\"` strategy).\n\n**When to use:** room codes, invite links, share URLs, ephemeral records — anywhere the ID appears in a URL or needs to be human-typeable.\n\n**When not to use:** high-volume tables. 62^6 ≈ 56.8B combinations means ~238K rows before a 50% collision probability (birthday bound). For large tables stick with `\"uuid\"`, `\"cuid\"`, or `\"prefixed\"`.\n\nSee [Providers - ID Generation Options](/compiler/config/providers#id-generation-options) for the full list of strategies.\n\n---\n\n## v0.8.3 — April 14, 2026\n\n### Fix: `wrangler.toml` Route Placement and Binding IDs\n\nTwo bugs in the generated `wrangler.toml` that blocked production deployment without manual edits:\n\n**Routes were nested under `[dev]`.** The `routes = [...]` array was emitted after the `[dev]` section header, so TOML parsed it as `dev.routes` and wrangler ignored the custom domain (warning: `Unexpected fields found in dev field: \"routes\"`). Routes now emit as a top-level key.\n\n**D1 and KV bindings used placeholder IDs.** Despite `databaseId`, `filesDatabaseId`, and `kvId` being set in the quickback config, the generated `wrangler.toml` wrote `\"local-features\"`, `\"local-files\"`, and `\"local\"` instead of the real UUIDs. The compiler now resolves IDs from multiple config sources:\n\n| Binding | Config sources checked (in order) |\n|---------|-----------------------------------|\n| `AUTH_DB` | `database.config.authDatabaseId` |\n| `DB` | `database.config.featuresDatabaseId`, `database.config.databaseId` |\n| `FILES_DB` | `fileStorage.config.filesDatabaseId`, `database.config.filesDatabaseId` |\n| `KV` | `database.config.kvNamespaceId`, `storage.config.namespaceId`, `auth.config.kvId` |\n\nSee [Providers - Database Options](/compiler/config/providers#database-options) for the full list of supported ID properties.\n\n### Fix: Account SPA Post-Login Redirect on Unified Domain\n\nWhen the Account SPA was served at `/account/` on the unified quickback domain (e.g. `secure.example.com/account/`), post-login redirects dropped the basepath and sent users to `/dashboard` instead of `/account/dashboard`. Sign-out redirects had the same issue.\n\nThe SPA now shares a basepath module across the router and all raw `window.location.href` assignments, so login, signup, email OTP, welcome, and sign-out flows all stay under the correct path prefix regardless of whether the SPA is on the unified domain or its own dedicated auth subdomain.\n\nNo config changes required — recompile to pick up the fix.\n\n---\n\n## v0.8.2 — April 11, 2026\n\n### Catch-All `VITE_QUICKBACK_URL` Environment Variable\n\nInstead of setting four separate URL env vars, you can now point at a single Quickback origin:\n\n```bash\n# One variable — done.\nVITE_QUICKBACK_URL=https://secure.example.com\n```\n\nWhen set, it's used as a fallback for the individual URLs:\n\n| Individual var (explicit) | Fallback when unset |\n|---|---|\n| `VITE_QUICKBACK_API_URL` | `${VITE_QUICKBACK_URL}` |\n| `VITE_QUICKBACK_ACCOUNT_URL` | `${VITE_QUICKBACK_URL}/account` |\n| `VITE_QUICKBACK_CMS_URL` | `${VITE_QUICKBACK_URL}/cms` |\n\n`VITE_QUICKBACK_APP_URL` stays independent — it points at the tenant's own frontend, not the Quickback server.\n\n**Non-breaking.** Individual vars still take precedence when set, so existing configurations work unchanged. The compiler now also emits `VITE_QUICKBACK_URL` into the generated SPA `.env` files, and library users can set `__QUICKBACK_URL__` on `globalThis` for the same effect.\n\nSee [Environment Variables](/account-ui/environment-variables) for the full reference.\n\n---\n\n## v0.8.1 — April 9, 2026\n\n### Role Hierarchy with `+` Suffix\n\nYou can now define a role hierarchy in your config and use the `+` suffix in access rules to mean \"this role and above\":\n\n```typescript\n// quickback.config.ts\nauth: {\n roleHierarchy: ['member', 'admin', 'owner'],\n}\n\n// In resource definitions\ncrud: {\n list: { access: { roles: [\"member+\"] } }, // member, admin, owner\n create: { access: { roles: [\"admin+\"] } }, // admin, owner\n delete: { access: { roles: [\"owner\"] } }, // owner only\n}\n```\n\nRoles are expanded at compile time. No `+` = exact match. See [Access - Role Hierarchy](/compiler/definitions/access#role-hierarchy) for details.\n\n---\n\n## v0.8.0 — April 9, 2026\n\n### Breaking: VITE URL Environment Variables Renamed\n\nAll Quickback-generated URL environment variables are now namespaced under `VITE_QUICKBACK_*` to avoid collisions with other Vite projects.\n\n| Old | New |\n|-----|-----|\n| `VITE_API_URL` | `VITE_QUICKBACK_API_URL` |\n| `VITE_ACCOUNT_URL` | `VITE_QUICKBACK_ACCOUNT_URL` |\n| `VITE_APP_URL` | `VITE_QUICKBACK_APP_URL` |\n| `VITE_CMS_URL` | `VITE_QUICKBACK_CMS_URL` |\n\n**Non-URL variables are unchanged:** `VITE_ENABLE_*` feature flags, branding vars (`VITE_APP_NAME`, etc.), and `VITE_STRIPE_PUBLISHABLE_KEY` stay as-is.\n\n**Action required:**\n- **Compiled projects** — re-run `quickback compile`. The compiler generates the new names automatically.\n- **Standalone deploys** — update `.env`, `.env.production`, and `wrangler.toml` files manually.\n- **Library users** — the `__QUICKBACK_API_URL__` / `__QUICKBACK_APP_URL__` globalThis names are unchanged, but if you pipe Vite env vars into them, update the var names.\n\nSee [Environment Variables](/account-ui/environment-variables) for the full reference.\n\n### CMS Access Control\n\nThe `cms.access` config option now enforces access at the CMS level, not just link visibility in Account UI.\n\n```typescript\ncms: { access: \"admin\" }\n```\n\nWhen set to `\"admin\"`, non-admin users (`user.role !== \"admin\"`) see an \"Access Denied\" screen with a sign-out button instead of the CMS. When set to `\"user\"` (the default), any authenticated user can access the CMS.\n\nSee [CMS Connecting](/cms/connecting) and [Compiler Config](/compiler/config) for details.\n\n### SPA Sub-Route Fix\n\nFixed a bug where hard-refreshing or directly navigating to SPA sub-routes (e.g., `/account/profile`, `/cms/tables/users`) returned a blank white screen. The Worker now correctly serves `index.html` for all SPA paths, allowing the client-side router to handle routing.\n\n---\n\n## v0.5.11 — February 24, 2026\n\n### Email OTP & Auth Fixes\n\n- Fixed email-OTP magic links not working correctly\n- Removed deprecated `/internal/validate` endpoint — use standard Better Auth session validation instead\n- Auth is now required for all API routes when running locally (previously some routes were unprotected in dev)\n\n### Multiple Table Exports Fix\n\n- Fixed a compiler error when a single file exports multiple Drizzle tables alongside a `defineTable()` default export\n- The CLI now properly detects and reports this with a clear error message pointing to the fix\n\n### Headless Drizzle Rename Hints\n\n- Added `compiler.migrations.renames` configuration for CI/CD environments where Drizzle's interactive rename prompts would block compilation\n- Compile errors now include explicit rename key paths and fail fast on malformed rename config keys\n- See [Configuration](/compiler/config) for details\n\n### Security Contract Report Artifacts\n\n- Added generated security contract artifacts to compiler output:\n - `reports/security-contracts.report.json`\n - `reports/security-contracts.report.sig.json`\n- Added config-driven signing controls:\n - `compiler.securityContracts.report.signature.enabled`\n - `compiler.securityContracts.report.signature.required`\n - `compiler.securityContracts.report.signature.key` / `keyEnv` / `keyId`\n- Missing required signing keys now fail loudly with explicit remediation guidance\n- Added strict config validation for report/signature paths and signing options\n\n### Mandatory Unsafe Action Audit Trail\n\n- Added structured unsafe action config (`unsafe: { reason, adminOnly, crossTenant, targetScope }`)\n- Cross-tenant unsafe actions now require Better Auth authentication plus platform admin role (`ctx.userRole === \"admin\"`)\n- Added mandatory audit logging for unsafe cross-tenant actions (success, denial, and error paths)\n- Cloudflare output now includes optional `AUDIT_DB` wiring, `drizzle.audit.config.ts`, and `db:migrate:audit:*` scripts when unsafe actions are present\n- Added compile-time raw SQL guard for actions and handlers (`allowRawSql: true` required per action)\n\n---\n\n## v0.5.10 — February 19, 2026\n\n### Compiler Page Parsing & CLI Output\n\n- Improved compiler page parsing for `definePage()` definitions\n- Better CLI output formatting during compilation\n- Added `apiPath` to schema registry for CMS integration\n\n### Bug Fixes\n\n- Fixed hyphenated action names not being properly quoted in generated code (e.g., `mark-complete` now generates valid JavaScript)\n- API key authentication (`x-api-key` header) is now handled separately from session tokens (`Bearer` header)\n\n---\n\n## v0.5.9 — February 16, 2026\n\n### CMS Pages & CLI Page Support\n\n- Added `definePage()` support for CMS-managed pages\n- Auth middleware improvements for page routes\n- CLI now supports page definitions alongside table and action definitions\n\n---\n\n## v0.5.8 — February 14, 2026\n\n### CMS App\n\n- Introduced the Quickback CMS — a schema-driven admin panel that connects to your generated API\n- CMS namespace added to actions for admin-specific operations\n- Fixed `guard` → `access` naming inconsistency in CMS action definitions\n\n### Schema Registry & Firewall Improvements\n\n- Added schema registry generator — the compiler now outputs a JSON schema registry used by the CMS\n- Firewall error modes: choose between `reveal` (403 with details) and `hide` (opaque 404) for security-sensitive deployments\n\n### Bug Fixes\n\n- Fixed anonymous user email format generation\n- Organization selector improvements in Account UI\n- Config validation now catches more errors at compile time\n- Better CRUD error handling with structured error responses\n- Fixed masking to use representative star counts instead of fixed formatting\n\n---\n\n## v0.5.7 — February 12, 2026\n\n### Scoped Database for Actions\n\nActions now receive a **security-scoped database** instead of a raw Drizzle instance. The compiler generates a proxy wrapper that automatically enforces org isolation, owner filtering, and soft-delete visibility — the same protections that CRUD routes have always had.\n\nThe scoped DB uses duck-typed column detection at runtime:\n\n| Column Detected | SELECT / UPDATE / DELETE | INSERT |\n|---|---|---|\n| `organizationId` | Adds `WHERE organizationId = ?` | Auto-injects `organizationId` from context |\n| `ownerId` | Adds `WHERE ownerId = ?` | Auto-injects `ownerId` from context |\n| `deletedAt` | Adds `WHERE deletedAt IS NULL` | — |\n\nThis means **every action is secure by default** — no manual `WHERE` clauses needed.\n\n```typescript\ndefineActions(todos, {\n complete: {\n type: \"record\",\n execute: async ({ db, ctx, record, input }) => {\n // db is scoped — only sees records in user's org, excludes soft-deleted\n const siblings = await db.select().from(todos);\n // ↑ automatically filtered to ctx.activeOrgId + deletedAt IS NULL\n },\n },\n});\n```\n\n#### Unsafe Mode\n\nActions that intentionally need to bypass security (admin reports, cross-org queries, migrations) can declare `unsafe: true` to receive a raw, unscoped database handle:\n\n```typescript\ndefineActions(analytics, {\n globalReport: {\n unsafe: true,\n execute: async ({ db, rawDb, ctx, input }) => {\n // db → still scoped (safety net)\n // rawDb → bypasses all security filters\n const allOrgs = await rawDb.select().from(organizations);\n },\n },\n});\n```\n\nWithout `unsafe: true`, `rawDb` is `undefined`.\n\n**Related docs:** [Actions](/compiler/definitions/actions), [Actions API](/compiler/using-the-api/actions-api)\n\n---\n\n### Cascading Soft Delete\n\nSoft-deleting a parent record now **automatically cascades** to child and junction tables within the same feature. The compiler detects foreign key references at build time and generates cascade UPDATE statements.\n\n```\nDELETE /api/v1/projects/:id\n```\n\nGenerated behavior:\n```typescript\n// 1. Soft delete the parent\nawait db.update(projects)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projects.id, id));\n\n// 2. Auto-cascade to children (compiler-generated)\nawait db.update(projectMembers)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectMembers.projectId, id));\n\nawait db.update(projectTasks)\n .set({ deletedAt: now, deletedBy: userId })\n .where(eq(projectTasks.projectId, id));\n```\n\nRules:\n- Only applies to **soft delete** (the default). Hard delete relies on database-level `ON DELETE CASCADE`.\n- Only cascades within the **same feature** — cross-feature references are not affected.\n- Child tables must have `deletedAt` / `deletedBy` columns (auto-added by the compiler's audit fields).\n\n**Related docs:** [Actions API — Cascading Soft Delete](/compiler/using-the-api/actions-api#cascading-soft-delete)\n\n---\n\n### Advanced Query Parameters\n\nNew query parameter capabilities for all list endpoints:\n\n- **Field selection** — `?fields=id,name,status` returns only the columns you need\n- **Multi-sort** — `?sort=status:asc,createdAt:desc` sorts by multiple fields\n- **Total count** — `?count=true` returns total matching records in response headers (`X-Total-Count`)\n- **Full-text search** — `?search=keyword` searches across all text columns\n\n```bash\n# Get only names and statuses, sorted by status then date, with total count\nGET /api/v1/todos?fields=id,name,status&sort=status:asc,createdAt:desc&count=true\n\n# Search across all text fields\nGET /api/v1/todos?search=urgent\n```\n\n**Related docs:** [Query Parameters](/compiler/using-the-api/query-params)\n\n---\n\n### Audit Field Improvements\n\n- `deletedAt` and `deletedBy` fields are now **always injected** by the compiler for tables with soft delete enabled — no need to define them in your schema\n- All audit fields (`createdAt`, `createdBy`, `modifiedAt`, `modifiedBy`, `deletedAt`, `deletedBy`) are auto-managed\n\n---\n\n## v0.5.6 — February 8, 2026\n\n### Database Naming Conventions\n\n- Default table and column naming changed to **snake_case** with `usePlurals: false`\n- Table names derived from generated Better Auth schema for consistency\n- Removed legacy single-database mode — split databases (auth + features) is now the standard\n\n### Auth Variable Shadowing Fix\n\n- Fixed `member` variable in auth middleware that shadowed the Drizzle `member` table import\n- Renamed to `sessionMember` to avoid conflicts in generated routes\n\n---\n\n## v0.5.5 — February 5, 2026\n\n### Better Auth Plugins\n\n- Published `@kardoe/better-auth-upgrade-anonymous` v1.1.0 — post-passkey email collection flow\n- Published `@kardoe/better-auth-combo-auth` — combined email + password + OTP authentication\n- Published `@kardoe/better-auth-aws-ses` — AWS SES email provider for Better Auth\n\n### OpenAPI Spec Generation\n\n- Generated APIs now include a full OpenAPI specification at `/openapi.json`\n- Better Auth endpoints included in the spec\n- Runtime route: `GET /openapi.json`\n\n### Security Hardening\n\n- Global error handler prevents leaking internal error details\n- Security headers middleware (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)\n- `BETTER_AUTH_SECRET` properly passed to generated config\n\n---\n\n## v0.5.4 — January 30, 2026\n\n### Account UI\n\n- Pre-built authentication UI deployed as Cloudflare Workers\n- Features: sessions, organizations, passkeys, passwordless, admin panel, API keys\n- Dual-mode: standalone (degit template) or embedded with Quickback projects\n\n### Webhook System\n\n- Inbound webhook endpoints with signature verification\n- Outbound webhooks via Cloudflare Queues with automatic retries\n- Configurable per-feature webhook events\n\n### Realtime & Vector Search\n\n- Durable Objects + WebSocket realtime subscriptions\n- Vector embeddings via Cloudflare Vectorize\n- KV and R2 storage integrations\n\n---\n\n## v0.5.0 — January 2026\n\n### Initial Release\n\n- **Quickback Compiler** — TypeScript-first backend compiler\n- **Four Security Pillars** — Firewall, Access, Guards, Masking\n- **`defineTable()`** — Schema + security configuration in a single file\n- **Templates** — Cloudflare Workers, Bun standalone, B2B SaaS\n- **Cloud Compiler** — Remote compilation via `compiler.quickback.dev`\n- **CLI** — `quickback create`, `quickback compile`, `quickback init`\n- **Better Auth Integration** — Organizations, roles, sessions\n- **Drizzle ORM** — Schema-first with automatic migrations\n- **Cloudflare D1** — Split database support (auth + features)"
71
71
  },
72
72
  "cms/actions": {
73
73
  "title": "Actions",
@@ -155,7 +155,7 @@ export const DOCS = {
155
155
  },
156
156
  "compiler/config/providers": {
157
157
  "title": "Providers",
158
- "content": "Providers configure which services your compiled backend targets.\n\n## Runtime Providers\n\n| Provider | Description |\n|----------|-------------|\n| `cloudflare` | Cloudflare Workers (Hono) |\n| `bun` | Bun runtime (Hono) |\n| `node` | Node.js runtime (Hono) |\n\n```typescript\n\nproviders: {\n runtime: defineRuntime(\"cloudflare\"),\n}\n```\n\n## Database Providers\n\n| Provider | Description |\n|----------|-------------|\n| `cloudflare-d1` | Cloudflare D1 (SQLite) |\n| `better-sqlite3` | SQLite via better-sqlite3 (Bun/Node) |\n| `libsql` | LibSQL / Turso |\n| `neon` | Neon (PostgreSQL) |\n| `supabase` | Supabase (PostgreSQL) |\n\n```typescript\n\nproviders: {\n database: defineDatabase(\"cloudflare-d1\", {\n generateId: \"prefixed\", // \"uuid\" | \"cuid\" | \"nanoid\" | \"prefixed\" | \"serial\" | false\n namingConvention: \"snake_case\", // \"camelCase\" | \"snake_case\"\n usePlurals: false, // Auth table names: \"users\" vs \"user\"\n }),\n}\n```\n\n### Database Options\n\n| Option | Type | Default (D1) | Description |\n|--------|------|---------|-------------|\n| `generateId` | `string \\| false` | `\"prefixed\"` | ID generation strategy |\n| `namingConvention` | `string` | `\"snake_case\"` | Column naming convention |\n| `usePlurals` | `boolean` | `false` | Pluralize auth table names (e.g. `user` vs `users`) |\n| `splitDatabases` | `boolean` | `true` | Separate auth and features databases |\n| `authBinding` | `string` | `\"AUTH_DB\"` | Binding name for auth database |\n| `featuresBinding` | `string` | `\"DB\"` | Binding name for features database |\n\n### ID Generation Options\n\n| Value | Description | Example |\n|-------|-------------|---------|\n| `\"uuid\"` | Server generates UUID | `550e8400-e29b-41d4-a716-446655440000` |\n| `\"cuid\"` | Server generates CUID | `clh2v8k9g0000l508h5gx8j1a` |\n| `\"nanoid\"` | Server generates nanoid | `V1StGXR8_Z5jdHi6B-myT` |\n| `\"prefixed\"` | Prefixed ID from table name | `room_abc123` |\n| `\"serial\"` | Database auto-increments | `1`, `2`, `3` |\n| `false` | Client provides ID (enables PUT/upsert) | Any string |\n\n## Auth Providers\n\n| Provider | Description |\n|----------|-------------|\n| `better-auth` | Better Auth with plugins |\n| `supabase-auth` | Supabase Auth |\n| `external` | External auth via Cloudflare service binding |\n\n```typescript\n\nproviders: {\n auth: defineAuth(\"better-auth\", {\n session: {\n expiresInDays: 7,\n updateAgeInDays: 1,\n },\n rateLimit: {\n enabled: true,\n window: 60,\n max: 100,\n },\n }),\n}\n```\n\n### Better Auth Options\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `session.expiresInDays` | `number` | Session expiration in days |\n| `session.updateAgeInDays` | `number` | Session refresh interval in days |\n| `rateLimit.enabled` | `boolean` | Enable rate limiting |\n| `rateLimit.window` | `number` | Rate limit window in seconds |\n| `rateLimit.max` | `number` | Max requests per window |\n| `socialProviders` | `object` | Social login providers (`google`, `github`, `discord`) |\n| `debugLogs` | `boolean` | Enable auth debug logging |\n\n### Better Auth Plugins\n\nWhen `features: [\"organizations\"]` is set in your config, the compiler automatically enables organization-related plugins. Additional plugins can be configured:\n\n| Plugin | Description |\n|--------|-------------|\n| `organization` | Multi-tenant organizations |\n| `admin` | Admin panel access |\n| `apiKey` | API key authentication |\n| `anonymous` | Anonymous sessions |\n| `upgradeAnonymous` | Convert anonymous to full accounts |\n| `twoFactor` | Two-factor authentication |\n| `passkey` | WebAuthn passkey login |\n| `magicLink` | Email magic link login |\n| `emailOtp` | Email one-time password |\n| `deviceAuthorization` | Device auth flow (CLI tools) |\n| `jwt` | JWT token support |\n| `openAPI` | OpenAPI spec generation |\n\n## Storage Providers\n\n```typescript\n\nproviders: {\n storage: defineStorage(\"kv\", {\n binding: \"KV_STORE\",\n }),\n fileStorage: defineFileStorage(\"r2\", {\n binding: \"FILES\",\n maxFileSize: \"10mb\",\n allowedTypes: [\"image/png\", \"image/jpeg\", \"application/pdf\"],\n publicDomain: \"files.example.com\",\n }),\n}\n```\n\n### Storage Types\n\n| Type | Description |\n|------|-------------|\n| `kv` | Key-value storage (Cloudflare KV, Redis) |\n| `r2` | Object storage (Cloudflare R2) |\n| `memory` | In-memory storage (development only) |\n| `redis` | Redis storage |\n\n### File Storage Options\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `binding` | `string` | R2 bucket binding name |\n| `maxFileSize` | `string` | Maximum file size (e.g. `\"10mb\"`) |\n| `allowedTypes` | `string[]` | Allowed MIME types |\n| `publicDomain` | `string` | Public domain for file URLs |\n| `userScopedBuckets` | `boolean` | Scope files by user |"
158
+ "content": "Providers configure which services your compiled backend targets.\n\n## Runtime Providers\n\n| Provider | Description |\n|----------|-------------|\n| `cloudflare` | Cloudflare Workers (Hono) |\n| `bun` | Bun runtime (Hono) |\n| `node` | Node.js runtime (Hono) |\n\n```typescript\n\nproviders: {\n runtime: defineRuntime(\"cloudflare\"),\n}\n```\n\n## Database Providers\n\n| Provider | Description |\n|----------|-------------|\n| `cloudflare-d1` | Cloudflare D1 (SQLite) |\n| `better-sqlite3` | SQLite via better-sqlite3 (Bun/Node) |\n| `libsql` | LibSQL / Turso |\n| `neon` | Neon (PostgreSQL) |\n| `supabase` | Supabase (PostgreSQL) |\n\n```typescript\n\nproviders: {\n database: defineDatabase(\"cloudflare-d1\", {\n generateId: \"prefixed\", // \"uuid\" | \"cuid\" | \"nanoid\" | \"prefixed\" | \"serial\" | false\n namingConvention: \"snake_case\", // \"camelCase\" | \"snake_case\"\n usePlurals: false, // Auth table names: \"users\" vs \"user\"\n }),\n}\n```\n\n### Database Options\n\n| Option | Type | Default (D1) | Description |\n|--------|------|---------|-------------|\n| `generateId` | `string \\| false` | `\"prefixed\"` | ID generation strategy |\n| `namingConvention` | `string` | `\"snake_case\"` | Column naming convention |\n| `usePlurals` | `boolean` | `false` | Pluralize auth table names (e.g. `user` vs `users`) |\n| `splitDatabases` | `boolean` | `true` | Separate auth and features databases |\n| `authBinding` | `string` | `\"AUTH_DB\"` | Binding name for auth database |\n| `featuresBinding` | `string` | `\"DB\"` | Binding name for features database |\n| `databaseId` | `string` | — | D1 database ID for the features database (used in generated wrangler.toml) |\n| `authDatabaseId` | `string` | — | D1 database ID for the auth database |\n| `featuresDatabaseId` | `string` | — | Explicit features DB ID (falls back to `databaseId`) |\n| `filesDatabaseId` | `string` | — | D1 database ID for the files metadata database |\n| `webhooksDatabaseId` | `string` | — | D1 database ID for the webhooks database |\n\n#### Setting Database IDs for Deployment\n\nWhen deploying to production, set your D1 database IDs so the compiler generates a ready-to-deploy `wrangler.toml`:\n\n```typescript\nproviders: {\n database: defineDatabase(\"cloudflare-d1\", {\n authDatabaseId: \"fe6c77c4-2131-40be-a8fc-3649eef2a38c\",\n databaseId: \"bba22981-cb6e-43d6-bf32-4193513c60b1\",\n filesDatabaseId: \"c3b8866b-cc4f-4e51-8382-0af244584d93\",\n }),\n}\n```\n\nIf these are omitted, the compiler uses local-dev placeholders and you'll need to manually edit `wrangler.toml` before deploying.\n\n### ID Generation Options\n\n| Value | Description | Example |\n|-------|-------------|---------|\n| `\"uuid\"` | Server generates UUID | `550e8400-e29b-41d4-a716-446655440000` |\n| `\"cuid\"` | Server generates CUID | `clh2v8k9g0000l508h5gx8j1a` |\n| `\"nanoid\"` | Server generates nanoid (21-char, URL-safe) | `V1StGXR8_Z5jdHi6B-myT` |\n| `\"short\"` | 6-char alphanumeric (nanoid `customAlphabet`) | `a3F9xK` |\n| `\"prefixed\"` | Prefixed ID from table name | `room_abc123` |\n| `\"serial\"` | Database auto-increments | `1`, `2`, `3` |\n| `false` | Client provides ID (enables PUT/upsert) | Any string |\n\n## Auth Providers\n\n| Provider | Description |\n|----------|-------------|\n| `better-auth` | Better Auth with plugins |\n| `supabase-auth` | Supabase Auth |\n| `external` | External auth via Cloudflare service binding |\n\n```typescript\n\nproviders: {\n auth: defineAuth(\"better-auth\", {\n session: {\n expiresInDays: 7,\n updateAgeInDays: 1,\n },\n rateLimit: {\n enabled: true,\n window: 60,\n max: 100,\n },\n }),\n}\n```\n\n### Better Auth Options\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `session.expiresInDays` | `number` | Session expiration in days |\n| `session.updateAgeInDays` | `number` | Session refresh interval in days |\n| `rateLimit.enabled` | `boolean` | Enable rate limiting |\n| `rateLimit.window` | `number` | Rate limit window in seconds |\n| `rateLimit.max` | `number` | Max requests per window |\n| `socialProviders` | `object` | Social login providers (`google`, `github`, `discord`) |\n| `debugLogs` | `boolean` | Enable auth debug logging |\n\n### Better Auth Plugins\n\nWhen `features: [\"organizations\"]` is set in your config, the compiler automatically enables organization-related plugins. Additional plugins can be configured:\n\n| Plugin | Description |\n|--------|-------------|\n| `organization` | Multi-tenant organizations |\n| `admin` | Admin panel access |\n| `apiKey` | API key authentication |\n| `anonymous` | Anonymous sessions |\n| `upgradeAnonymous` | Convert anonymous to full accounts |\n| `twoFactor` | Two-factor authentication |\n| `passkey` | WebAuthn passkey login |\n| `magicLink` | Email magic link login |\n| `emailOtp` | Email one-time password |\n| `deviceAuthorization` | Device auth flow (CLI tools) |\n| `jwt` | JWT token support |\n| `openAPI` | OpenAPI spec generation |\n\n## Storage Providers\n\n```typescript\n\nproviders: {\n storage: defineStorage(\"kv\", {\n binding: \"KV_STORE\",\n namespaceId: \"7ed0f824a8c54c388713d9fdf63cd004\",\n }),\n fileStorage: defineFileStorage(\"r2\", {\n binding: \"FILES\",\n maxFileSize: \"10mb\",\n allowedTypes: [\"image/png\", \"image/jpeg\", \"application/pdf\"],\n publicDomain: \"files.example.com\",\n }),\n}\n```\n\n### Storage Types\n\n| Type | Description |\n|------|-------------|\n| `kv` | Key-value storage (Cloudflare KV, Redis) |\n| `r2` | Object storage (Cloudflare R2) |\n| `memory` | In-memory storage (development only) |\n| `redis` | Redis storage |\n\n### File Storage Options\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `binding` | `string` | R2 bucket binding name |\n| `maxFileSize` | `string` | Maximum file size (e.g. `\"10mb\"`) |\n| `allowedTypes` | `string[]` | Allowed MIME types |\n| `publicDomain` | `string` | Public domain for file URLs |\n| `userScopedBuckets` | `boolean` | Scope files by user |"
159
159
  },
160
160
  "compiler/config/single-tenant": {
161
161
  "title": "Single-Tenant Mode",
@@ -343,11 +343,11 @@ export const DOCS = {
343
343
  },
344
344
  "stack/auth/jwt-optimization": {
345
345
  "title": "JWT Optimization",
346
- "content": "Quickback automatically optimizes authenticated API requests using JWT tokens. After the first request (which uses session cookies), subsequent requests skip the database entirely by sending a signed JWT that encodes the full auth context.\n\n## How It Works\n\n### Request Flow\n\n```\nFirst request (no JWT cached):\n Browser → Cookie auth → 2 DB queries → Response + set-auth-token header\n Browser caches JWT in localStorage\n\nSubsequent requests (JWT cached):\n Browser → Bearer JWT → Signature check only → Response (0 DB queries)\n```\n\n### What's in the JWT\n\nThe JWT encodes everything needed for auth middleware:\n\n| Claim | Description |\n|-------|-------------|\n| `sub` | User ID |\n| `orgId` | Active organization ID |\n| `role` | Organization membership role (`owner`, `admin`, `member`) |\n| `userRole` | Global user role (when admin panel is enabled) |\n| `email` | User email |\n| `name` | User display name |\n| `iat` | Issued-at timestamp |\n| `exp` | Expiry (15 minutes from issue) |\n\n### Signing\n\nJWTs are signed with HMAC-SHA256 using your `BETTER_AUTH_SECRET`. No additional configuration is needed — this uses the same secret you already set for Better Auth.\n\n---\n\n## Token Lifecycle\n\n### Automatic Minting\n\nWhen a request arrives without a JWT (or with an expired one), the auth middleware:\n\n1. Authenticates via session cookie (existing Better Auth flow)\n2. Signs a JWT with the full auth context\n3. Returns it in the `set-auth-token` response header\n\nThe Quickback API client (`quickback-client.ts`) automatically captures this header and stores the JWT in `localStorage`.\n\n### Re-minting on Org Switch\n\nWhen a user switches organizations, the JWT must be re-minted because `orgId` and `role` change. The auth UI handles this automatically:\n\n```typescript\n// This happens automatically in the org switcher\nawait authClient.organization.setActive({ organizationSlug: slug });\n\n// Fetch fresh JWT with new org context\nconst res = await fetch('/api/v1/token', {\n method: 'POST',\n credentials: 'include',\n});\nconst { token } = await res.json();\nlocalStorage.setItem('bearer_token', token);\n```\n\n### Invalidation on Role Changes\n\nWhen an admin changes a user's role or removes them from an organization, the server broadcasts an `auth:token-invalidated` event via WebSocket. The client automatically clears the cached JWT, forcing the next request to fall back to session auth and mint a fresh token.\n\n```\nAdmin changes user role\n → databaseHooks fires on member update\n → Broadcasts auth:token-invalidated via realtime\n → Client clears localStorage JWT\n → Next request uses session cookie → gets fresh JWT\n```\n\n### Expiry\n\nJWTs expire after 15 minutes. This is a safety net — in practice, JWTs are re-minted well before expiry through the `set-auth-token` response header on session-fallback requests.\n\nWhen a JWT expires or is invalid:\n1. The middleware silently falls back to session cookie auth\n2. A fresh JWT is minted and returned in the response header\n3. The client stores the new JWT for subsequent requests\n\n---\n\n## Token Endpoint\n\n`POST /api/v1/token` mints a fresh JWT from the current session. This endpoint goes through normal auth middleware, so the user must have a valid session cookie.\n\n**Request:**\n```bash\ncurl -X POST https://your-api.example.com/api/v1/token \\\n -H \"Content-Type: application/json\" \\\n --cookie \"better-auth.session_token=...\"\n```\n\n**Response:**\n```json\n{\n \"token\": \"eyJhbGciOiJIUzI1NiJ9...\"\n}\n```\n\nUse this endpoint after operations that change auth context (like switching organizations) to get a JWT that reflects the new state.\n\n---\n\n## Client Integration\n\n### Quickback API Client\n\nThe `quickback-client.ts` API client handles JWT auth automatically:\n\n- **Sends** the cached JWT as `Authorization: Bearer <token>` on every request\n- **Captures** refreshed JWTs from `set-auth-token` response headers\n- **Clears** the JWT on `401` responses (falls back to session cookie)\n\nNo additional client configuration is needed.\n\n### Custom API Calls\n\nIf you make direct `fetch()` calls to your API (outside the Quickback client), include the JWT:\n\n```typescript\nconst jwt = localStorage.getItem('bearer_token');\n\nconst res = await fetch('https://your-api.example.com/api/v1/things', {\n headers: {\n 'Authorization': `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n },\n credentials: 'include', // Fallback to session cookie if no JWT\n});\n\n// Capture refreshed token\nconst newToken = res.headers.get('set-auth-token');\nif (newToken) {\n localStorage.setItem('bearer_token', newToken);\n}\n```\n\n### Sign Out\n\nClear the JWT on sign-out to prevent stale tokens:\n\n```typescript\nlocalStorage.removeItem('bearer_token');\nawait authClient.signOut();\n```\n\n---\n\n## Security\n\n### Threat Model\n\n| Threat | Mitigation |\n|--------|------------|\n| Token theft (XSS) | 15-minute expiry limits exposure window. `httpOnly` cookies protect the session. |\n| Token replay | Short expiry + `set-auth-token` rotation on session fallback |\n| Stale permissions | WebSocket `auth:token-invalidated` broadcast on role changes |\n| Token forgery | HMAC-SHA256 signature verified with `BETTER_AUTH_SECRET` |\n| Timing attacks | `crypto.subtle.verify()` provides constant-time comparison |\n\n### Key Points\n\n- JWTs are a **performance optimization**, not a replacement for session auth\n- Session cookies remain the source of truth — JWTs are derived from them\n- If a JWT is lost, stolen, or cleared, the system gracefully falls back to cookie auth\n- No additional secrets or configuration needed — uses your existing `BETTER_AUTH_SECRET`\n- The JWT contains no sensitive data beyond what's already in the auth context\n\n---\n\n## Files Worker\n\nThe files worker (for Cloudflare R2 file uploads) also supports JWT authentication. When a request includes a JWT `Authorization` header, the worker verifies it directly instead of calling back to the auth API — eliminating a network round-trip for file operations.\n\n---\n\n## Related\n\n- [Auth Overview](/stack/auth) — Setup and configuration\n- [Auth Security](/stack/auth/security) — Cookie security, rate limiting, CORS\n- [API Keys](/stack/auth/api-keys) — Programmatic API access"
346
+ "content": "Quickback automatically optimizes authenticated API requests using JWT tokens. After the first request (which uses session cookies), subsequent requests skip the database entirely by sending a signed JWT that encodes the full auth context.\n\n## How It Works\n\n### Request Flow\n\n```\nFirst request (no JWT cached):\n Browser → Cookie auth → 2 DB queries → Response + set-auth-token header\n Browser caches JWT in localStorage\n\nSubsequent requests (JWT cached):\n Browser → Bearer JWT → Signature check only → Response (0 DB queries)\n```\n\n### What's in the JWT\n\nThe JWT encodes everything needed for auth middleware:\n\n| Claim | Description |\n|-------|-------------|\n| `sub` | User ID |\n| `orgId` | Active organization ID |\n| `role` | Organization membership role (`owner`, `admin`, `member`) |\n| `userRole` | Global user role (when admin panel is enabled) |\n| `email` | User email |\n| `name` | User display name |\n| `iat` | Issued-at timestamp |\n| `exp` | Expiry (15 minutes from issue) |\n\n### Signing\n\nJWTs are signed with HMAC-SHA256 using your `BETTER_AUTH_SECRET`. No additional configuration is needed — this uses the same secret you already set for Better Auth.\n\n---\n\n## Server-Side Validation\n\n**Every authenticated API request is validated server-side before it reaches your handler.** There is no \"trust the client\" shortcut — the middleware runs full signature verification on each request.\n\nValidation steps (in generated `src/middleware/auth.ts`):\n\n1. Extract the `Authorization: Bearer <token>` header\n2. Call `verifyJwt(token, BETTER_AUTH_SECRET)` from `src/lib/jwt.ts`\n3. Decode header + payload, recompute the HMAC-SHA256 signature, and compare via `crypto.subtle.verify()` (constant-time — no timing-attack surface)\n4. Check `exp` against the current time\n5. On success: hydrate `AppContext` from claims (`sub`, `orgId`, `role`, `userRole`, `email`, `name`) — no DB query\n6. On failure (bad signature, expired, malformed): **silently fall back** to Better Auth's session cookie auth; if that succeeds, mint a fresh JWT and return it in the `set-auth-token` response header\n\nThe Files worker (R2 uploads) performs the same verification using the shared secret — no network round-trip to the auth API for file operations.\n\n---\n\n## Token Lifecycle\n\n### Automatic Minting\n\nWhen a request arrives without a JWT (or with an expired one), the auth middleware:\n\n1. Authenticates via session cookie (existing Better Auth flow)\n2. Signs a JWT with the full auth context\n3. Returns it in the `set-auth-token` response header\n\nThe Quickback API client (`quickback-client.ts`) automatically captures this header and stores the JWT in `localStorage`.\n\n### Re-minting on Org Switch\n\nWhen a user switches organizations, the JWT must be re-minted because `orgId` and `role` change. The auth UI handles this automatically:\n\n```typescript\n// This happens automatically in the org switcher\nawait authClient.organization.setActive({ organizationSlug: slug });\n\n// Fetch fresh JWT with new org context\nconst res = await fetch('/api/v1/token', {\n method: 'POST',\n credentials: 'include',\n});\nconst { token } = await res.json();\nlocalStorage.setItem('bearer_token', token);\n```\n\n### Invalidation on Role Changes\n\nWhen an admin changes a user's role or removes them from an organization, the server broadcasts an `auth:token-invalidated` event via WebSocket. The client automatically clears the cached JWT, forcing the next request to fall back to session auth and mint a fresh token.\n\n```\nAdmin changes user role\n → databaseHooks fires on member update\n → Broadcasts auth:token-invalidated via realtime\n → Client clears localStorage JWT\n → Next request uses session cookie → gets fresh JWT\n```\n\n### Expiry\n\nJWTs expire after 15 minutes. This is a safety net — in practice, JWTs are re-minted well before expiry through the `set-auth-token` response header on session-fallback requests.\n\nWhen a JWT expires or is invalid:\n1. The middleware silently falls back to session cookie auth\n2. A fresh JWT is minted and returned in the response header\n3. The client stores the new JWT for subsequent requests\n\n---\n\n## Token Endpoint\n\n`POST /api/v1/token` mints a fresh JWT from the current session. This endpoint goes through normal auth middleware, so the user must have a valid session cookie.\n\n**Request:**\n```bash\ncurl -X POST https://your-api.example.com/api/v1/token \\\n -H \"Content-Type: application/json\" \\\n --cookie \"better-auth.session_token=...\"\n```\n\n**Response:**\n```json\n{\n \"token\": \"eyJhbGciOiJIUzI1NiJ9...\"\n}\n```\n\nUse this endpoint after operations that change auth context (like switching organizations) to get a JWT that reflects the new state.\n\n---\n\n## Client Integration\n\n### Quickback API Client\n\nThe `quickback-client.ts` API client handles JWT auth automatically:\n\n- **Sends** the cached JWT as `Authorization: Bearer <token>` on every request\n- **Captures** refreshed JWTs from `set-auth-token` response headers\n- **Clears** the JWT on `401` responses (falls back to session cookie)\n\nNo additional client configuration is needed.\n\n### Custom API Calls\n\nIf you make direct `fetch()` calls to your API (outside the Quickback client), include the JWT:\n\n```typescript\nconst jwt = localStorage.getItem('bearer_token');\n\nconst res = await fetch('https://your-api.example.com/api/v1/things', {\n headers: {\n 'Authorization': `Bearer ${jwt}`,\n 'Content-Type': 'application/json',\n },\n credentials: 'include', // Fallback to session cookie if no JWT\n});\n\n// Capture refreshed token\nconst newToken = res.headers.get('set-auth-token');\nif (newToken) {\n localStorage.setItem('bearer_token', newToken);\n}\n```\n\n### Sign Out\n\nClear the JWT on sign-out to prevent stale tokens:\n\n```typescript\nlocalStorage.removeItem('bearer_token');\nawait authClient.signOut();\n```\n\n---\n\n## Security\n\n### Threat Model\n\n| Threat | Mitigation |\n|--------|------------|\n| Token theft (XSS) | 15-minute expiry limits exposure window. `httpOnly` cookies protect the session. |\n| Token replay | Short expiry + `set-auth-token` rotation on session fallback |\n| Stale permissions | WebSocket `auth:token-invalidated` broadcast on role changes |\n| Token forgery | HMAC-SHA256 signature verified with `BETTER_AUTH_SECRET` |\n| Timing attacks | `crypto.subtle.verify()` provides constant-time comparison |\n\n### Key Points\n\n- JWTs are a **performance optimization**, not a replacement for session auth\n- Session cookies remain the source of truth — JWTs are derived from them\n- If a JWT is lost, stolen, or cleared, the system gracefully falls back to cookie auth\n- No additional secrets or configuration needed — uses your existing `BETTER_AUTH_SECRET`\n- The JWT contains no sensitive data beyond what's already in the auth context\n\n---\n\n## Files Worker\n\nThe files worker (for Cloudflare R2 file uploads) also supports JWT authentication. When a request includes a JWT `Authorization` header, the worker verifies it directly instead of calling back to the auth API — eliminating a network round-trip for file operations.\n\n---\n\n## Relation to Better Auth's JWT Plugin\n\nQuickback's JWT fast-path (documented above) is a **custom HMAC-SHA256 implementation**, separate from Better Auth's [`jwt` plugin](https://www.better-auth.com/docs/plugins/jwt). The distinction matters if you're comparing options.\n\n### What Quickback ships by default\n\n- Custom HMAC-SHA256 JWTs minted by the compiled API at `POST /api/v1/token`\n- Payload includes `sub`, `orgId`, `role`, `userRole`, `email`, `name` — everything the middleware needs to reconstruct `AppContext` with zero DB queries\n- Signed with `BETTER_AUTH_SECRET`\n- Used by the generated auth middleware as a fast-path ahead of `getSession()`\n\n### What Better Auth's JWT plugin gives you (opt-in)\n\nEnable it with `plugins: [\"jwt\"]` in your auth config:\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"jwt\"],\n}),\n```\n\nYou get:\n- JWKS endpoint at `/auth/v1/jwks` for asymmetric public-key verification by external services\n- BA's own `/auth/v1/token` endpoint (asymmetric-signed, EdDSA by default)\n- Standard OIDC-style claims via BA's `definePayload` hook\n\n### Why Quickback doesn't use BA's plugin internally\n\nBetter Auth's `definePayload` hook only receives `{ user }`. It cannot embed the active organization context (`orgId`, org-member `role`) that Quickback's Firewall and Access layers need. Without that context in the JWT, the middleware would have to do a DB lookup per request — defeating the whole point of the fast-path.\n\n### When to enable BA's JWT plugin anyway\n\nTurn it on if **external services** need to verify tokens without sharing `BETTER_AUTH_SECRET` — e.g. a third-party worker that wants to confirm a user's identity via JWKS. The BA-issued tokens won't have Quickback's org context, so they're useful for identity assertions, not for hitting the Quickback API directly.\n\nEnabling BA's plugin does **not** change how the compiled API authenticates requests — the middleware keeps using the custom HMAC JWT regardless.\n\n---\n\n## Related\n\n- [Auth Overview](/stack/auth) — Setup and configuration\n- [Auth Security](/stack/auth/security) — Cookie security, rate limiting, CORS\n- [API Keys](/stack/auth/api-keys) — Programmatic API access"
347
347
  },
348
348
  "stack/auth/plugins": {
349
349
  "title": "Auth Plugins",
350
- "content": "Quickback ships with a curated set of Better Auth plugins and helper wiring so your auth stack is production-ready by default. Enable these in `quickback.config.ts` under `providers.auth`.\n\n## Available Plugins\n\n### Email OTP (with AWS SES)\n\nQuickback wires the Better Auth Email OTP flow to AWS SES. When `emailOtp` is enabled, the compiler emits the SES plugin and combo-auth email flow (magic link + OTP).\n\n**Enable in config:**\n\n```ts\n\nexport default defineConfig({\n name: \"quickback-api\",\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\", {\n vars: {\n AWS_SES_REGION: \"us-east-2\",\n EMAIL_FROM: \"noreply@yourdomain.com\",\n EMAIL_FROM_NAME: \"Your App | Account Services\",\n APP_NAME: \"Your App\",\n APP_URL: \"https://account.yourdomain.com\",\n BETTER_AUTH_URL: \"https://api.yourdomain.com\",\n },\n }),\n auth: defineAuth(\"better-auth\", {\n emailAndPassword: { enabled: true },\n plugins: [\"emailOtp\"],\n }),\n },\n});\n```\n\n**Endpoints:**\n- `POST /auth/v1/email-otp/send-verification-otp`\n- `POST /auth/v1/email-otp/verify-otp`\n\n**Email readiness check:**\n\nUse this to show UI warnings when SES isn't configured.\n\n- `GET /api/v1/system/email-status`\n- Response: `{ \"emailConfigured\": true|false }`\n\n### Upgrade Anonymous\n\nAdds a first-class endpoint to convert an anonymous user into a full user. This flips `isAnonymous` to `false` and refreshes the session cache immediately.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n emailAndPassword: { enabled: true },\n plugins: [\"anonymous\", \"upgradeAnonymous\"],\n}),\n```\n\n**Endpoint:**\n- `POST /auth/v1/upgrade-anonymous`\n\n**Note:** If you send `Content-Type: application/json`, include a body (an empty `{}` is fine). This avoids request parsing errors in some clients.\n\n### AWS SES Plugin\n\nThis is a Quickback-provided Better Auth plugin used by Email OTP. It handles SES signing and delivery and is auto-included when `emailOtp` is enabled.\n\nRequired vars (use Wrangler secrets for credentials):\n- `AWS_ACCESS_KEY_ID` (secret)\n- `AWS_SECRET_ACCESS_KEY` (secret)\n- `AWS_SES_REGION`\n- `EMAIL_FROM`\n\nOptional vars:\n- `EMAIL_FROM_NAME`\n- `EMAIL_REPLY_TO` - Reply-to address for emails (defaults to `EMAIL_FROM`)\n- `APP_NAME`\n- `APP_URL`\n- `BETTER_AUTH_URL`\n\n### Magic Links\n\nMagic links provide passwordless authentication by sending a unique login link to the user's email. When clicked, the link authenticates the user without requiring a password.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"magicLink\"],\n}),\n```\n\n**Endpoints:**\n- `POST /auth/v1/magic-link/send` - Send magic link email\n- `GET /auth/v1/magic-link/verify` - Verify magic link token\n\n**Email customization:**\n\nMagic link emails use the same AWS SES configuration as Email OTP. Customize the email template via environment variables:\n\n- `APP_NAME` - Application name in email subject\n- `APP_URL` - Base URL for magic link redirect\n- `EMAIL_FROM` - Sender email address\n- `EMAIL_FROM_NAME` - Sender display name\n\n**Frontend integration:**\n\n```ts\n// Request magic link\nawait fetch('/auth/v1/magic-link/send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: 'user@yourdomain.com' })\n});\n\n// User clicks link in email, which redirects to your app\n// The token is verified automatically via the callback URL\n```\n\n### Passkeys\n\nPasskeys provide passwordless authentication using WebAuthn/FIDO2. Users authenticate with biometrics (fingerprint, face) or device PIN instead of passwords.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"passkey\"],\n}),\n```\n\n**Required environment variable:**\n- `ACCOUNT_URL` - Your account/frontend URL (used as the relying party origin for WebAuthn)\n\n**Endpoints:**\n- `POST /auth/v1/passkey/register/options` - Get registration challenge\n- `POST /auth/v1/passkey/register/verify` - Complete registration\n- `POST /auth/v1/passkey/authenticate/options` - Get authentication challenge\n- `POST /auth/v1/passkey/authenticate/verify` - Complete authentication\n- `GET /auth/v1/passkey/list-user-passkeys` - List user's registered passkeys\n- `POST /auth/v1/passkey/delete-passkey` - Delete a registered passkey\n\n**Browser support:**\n\nPasskeys are supported in all modern browsers:\n- Chrome 67+\n- Safari 14+\n- Firefox 60+\n- Edge 79+\n\n**Registration flow:**\n\n```ts\n// 1. Get registration options\nconst optionsRes = await fetch('/auth/v1/passkey/register/options', {\n method: 'POST',\n credentials: 'include'\n});\nconst options = await optionsRes.json();\n\n// 2. Create credential using WebAuthn API\nconst credential = await navigator.credentials.create({\n publicKey: options\n});\n\n// 3. Send credential to server\nawait fetch('/auth/v1/passkey/register/verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(credential)\n});\n```\n\n**Authentication flow:**\n\n```ts\n// 1. Get authentication options\nconst optionsRes = await fetch('/auth/v1/passkey/authenticate/options', {\n method: 'POST'\n});\nconst options = await optionsRes.json();\n\n// 2. Get credential using WebAuthn API\nconst credential = await navigator.credentials.get({\n publicKey: options\n});\n\n// 3. Verify credential\nawait fetch('/auth/v1/passkey/authenticate/verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(credential)\n});\n```\n\n## Where This Runs\n\nAll Better Auth plugin routes are served under your auth base path:\n\n```\n/auth/v1/*\n```"
350
+ "content": "Quickback ships with a curated set of Better Auth plugins and helper wiring so your auth stack is production-ready by default. Enable these in `quickback.config.ts` under `providers.auth`.\n\n## Available Plugins\n\n### Email OTP (with AWS SES)\n\nQuickback wires the Better Auth Email OTP flow to AWS SES. When `emailOtp` is enabled, the compiler emits the SES plugin and combo-auth email flow (magic link + OTP).\n\n**Enable in config:**\n\n```ts\n\nexport default defineConfig({\n name: \"quickback-api\",\n providers: {\n runtime: defineRuntime(\"cloudflare\"),\n database: defineDatabase(\"cloudflare-d1\", {\n vars: {\n AWS_SES_REGION: \"us-east-2\",\n EMAIL_FROM: \"noreply@yourdomain.com\",\n EMAIL_FROM_NAME: \"Your App | Account Services\",\n APP_NAME: \"Your App\",\n APP_URL: \"https://account.yourdomain.com\",\n BETTER_AUTH_URL: \"https://api.yourdomain.com\",\n },\n }),\n auth: defineAuth(\"better-auth\", {\n emailAndPassword: { enabled: true },\n plugins: [\"emailOtp\"],\n }),\n },\n});\n```\n\n**Endpoints:**\n- `POST /auth/v1/email-otp/send-verification-otp`\n- `POST /auth/v1/email-otp/verify-otp`\n\n**Email readiness check:**\n\nUse this to show UI warnings when SES isn't configured.\n\n- `GET /api/v1/system/email-status`\n- Response: `{ \"emailConfigured\": true|false }`\n\n### Upgrade Anonymous\n\nAdds a first-class endpoint to convert an anonymous user into a full user. This flips `isAnonymous` to `false` and refreshes the session cache immediately.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n emailAndPassword: { enabled: true },\n plugins: [\"anonymous\", \"upgradeAnonymous\"],\n}),\n```\n\n**Endpoint:**\n- `POST /auth/v1/upgrade-anonymous`\n\n**Note:** If you send `Content-Type: application/json`, include a body (an empty `{}` is fine). This avoids request parsing errors in some clients.\n\n### AWS SES Plugin\n\nThis is a Quickback-provided Better Auth plugin used by Email OTP. It handles SES signing and delivery and is auto-included when `emailOtp` is enabled.\n\nRequired vars (use Wrangler secrets for credentials):\n- `AWS_ACCESS_KEY_ID` (secret)\n- `AWS_SECRET_ACCESS_KEY` (secret)\n- `AWS_SES_REGION`\n- `EMAIL_FROM`\n\nOptional vars:\n- `EMAIL_FROM_NAME`\n- `EMAIL_REPLY_TO` - Reply-to address for emails (defaults to `EMAIL_FROM`)\n- `APP_NAME`\n- `APP_URL`\n- `BETTER_AUTH_URL`\n\n### Magic Links\n\nMagic links provide passwordless authentication by sending a unique login link to the user's email. When clicked, the link authenticates the user without requiring a password.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"magicLink\"],\n}),\n```\n\n**Endpoints:**\n- `POST /auth/v1/magic-link/send` - Send magic link email\n- `GET /auth/v1/magic-link/verify` - Verify magic link token\n\n**Email customization:**\n\nMagic link emails use the same AWS SES configuration as Email OTP. Customize the email template via environment variables:\n\n- `APP_NAME` - Application name in email subject\n- `APP_URL` - Base URL for magic link redirect\n- `EMAIL_FROM` - Sender email address\n- `EMAIL_FROM_NAME` - Sender display name\n\n**Frontend integration:**\n\n```ts\n// Request magic link\nawait fetch('/auth/v1/magic-link/send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: 'user@yourdomain.com' })\n});\n\n// User clicks link in email, which redirects to your app\n// The token is verified automatically via the callback URL\n```\n\n### Passkeys\n\nPasskeys provide passwordless authentication using WebAuthn/FIDO2. Users authenticate with biometrics (fingerprint, face) or device PIN instead of passwords.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"passkey\"],\n}),\n```\n\n**Required environment variable:**\n- `ACCOUNT_URL` - Your account/frontend URL (used as the relying party origin for WebAuthn)\n\n**Endpoints:**\n- `POST /auth/v1/passkey/register/options` - Get registration challenge\n- `POST /auth/v1/passkey/register/verify` - Complete registration\n- `POST /auth/v1/passkey/authenticate/options` - Get authentication challenge\n- `POST /auth/v1/passkey/authenticate/verify` - Complete authentication\n- `GET /auth/v1/passkey/list-user-passkeys` - List user's registered passkeys\n- `POST /auth/v1/passkey/delete-passkey` - Delete a registered passkey\n\n**Browser support:**\n\nPasskeys are supported in all modern browsers:\n- Chrome 67+\n- Safari 14+\n- Firefox 60+\n- Edge 79+\n\n**Registration flow:**\n\n```ts\n// 1. Get registration options\nconst optionsRes = await fetch('/auth/v1/passkey/register/options', {\n method: 'POST',\n credentials: 'include'\n});\nconst options = await optionsRes.json();\n\n// 2. Create credential using WebAuthn API\nconst credential = await navigator.credentials.create({\n publicKey: options\n});\n\n// 3. Send credential to server\nawait fetch('/auth/v1/passkey/register/verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(credential)\n});\n```\n\n**Authentication flow:**\n\n```ts\n// 1. Get authentication options\nconst optionsRes = await fetch('/auth/v1/passkey/authenticate/options', {\n method: 'POST'\n});\nconst options = await optionsRes.json();\n\n// 2. Get credential using WebAuthn API\nconst credential = await navigator.credentials.get({\n publicKey: options\n});\n\n// 3. Verify credential\nawait fetch('/auth/v1/passkey/authenticate/verify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(credential)\n});\n```\n\n### JWT Plugin\n\nBetter Auth's JWT plugin adds asymmetric-signed tokens and a JWKS endpoint for external services that need to verify user identity without sharing a shared secret.\n\n**Enable in config:**\n\n```ts\nauth: defineAuth(\"better-auth\", {\n plugins: [\"jwt\"],\n}),\n```\n\n**Endpoints:**\n- `GET /auth/v1/jwks` — public keys for verifying BA-issued tokens\n- `POST /auth/v1/token` — mint an asymmetric-signed JWT from the current session\n\n**When to enable:** you have external services (non-Quickback workers, third-party integrations) that need to verify a user's identity via JWKS without holding `BETTER_AUTH_SECRET`. The BA-issued tokens won't carry Quickback's `orgId`/`role` claims, so they're for identity, not API access.\n\n## Where This Runs\n\nAll Better Auth plugin routes are served under your auth base path:\n\n```\n/auth/v1/*\n```"
351
351
  },
352
352
  "stack/auth/security": {
353
353
  "title": "Authentication Security",
@@ -359,7 +359,7 @@ export const DOCS = {
359
359
  },
360
360
  "stack/database/d1": {
361
361
  "title": "D1 Database",
362
- "content": "Cloudflare D1 is SQLite at the edge. Quickback uses D1 as the primary database for Cloudflare deployments, with a multi-database pattern for separation of concerns.\n\n## What is D1?\n\nD1 is Cloudflare's serverless SQL database built on SQLite:\n\n- **Edge-native** - Runs in Cloudflare's global network\n- **SQLite compatible** - Use familiar SQL syntax\n- **Zero configuration** - No connection strings or pooling\n- **Automatic replication** - Read replicas at every edge location\n\n## Multi-Database Pattern\n\nQuickback generates separate D1 databases for different concerns:\n\n| Database | Binding | Purpose |\n|----------|---------|---------|\n| `AUTH_DB` | `AUTH_DB` | Better Auth tables (user, session, account) |\n| `DB` | `DB` | Your application data |\n| `FILES_DB` | `FILES_DB` | File metadata for R2 uploads |\n| `WEBHOOKS_DB` | `WEBHOOKS_DB` | Webhook delivery tracking |\n\nThis separation provides:\n- **Independent scaling** - Auth traffic doesn't affect app queries\n- **Isolation** - Auth schema changes don't touch your data\n- **Clarity** - Clear ownership of each database\n\n## Drizzle ORM Integration\n\nQuickback uses [Drizzle ORM](https://orm.drizzle.team/) for type-safe database access:\n\n```ts\n// schema/tables.ts - Your schema definition\n\nexport const posts = sqliteTable(\"posts\", {\n id: text(\"id\").primaryKey(),\n title: text(\"title\").notNull(),\n content: text(\"content\"),\n authorId: text(\"author_id\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp\" }),\n});\n```\n\nThe compiler generates Drizzle queries based on your security rules:\n\n```ts\n// Generated query with firewall applied\nconst result = await db\n .select()\n .from(posts)\n .where(eq(posts.authorId, userId)); // Firewall injects ownership\n```\n\n## Migrations\n\nQuickback generates migrations automatically at compile time based on your schema changes:\n\n```bash\n# Compile your project (generates migrations)\nquickback compile\n\n# Apply migrations locally\nwrangler d1 migrations apply DB --local\n\n# Apply to production D1\nwrangler d1 migrations apply DB --remote\n```\n\nMigration files are generated in `quickback/drizzle/` and version-controlled with your code. You never need to manually generate migrations—just define your schema and compile.\n\n## wrangler.toml Bindings\n\nConfigure D1 bindings in `wrangler.toml`:\n\n```toml\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"my-app-db\"\ndatabase_id = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n\n[[d1_databases]]\nbinding = \"AUTH_DB\"\ndatabase_name = \"my-app-auth\"\ndatabase_id = \"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\"\n\n[[d1_databases]]\nbinding = \"FILES_DB\"\ndatabase_name = \"my-app-files\"\ndatabase_id = \"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz\"\n```\n\nCreate databases via Wrangler:\n\n```bash\nwrangler d1 create my-app-db\nwrangler d1 create my-app-auth\nwrangler d1 create my-app-files\n```\n\n## Accessing Data via API\n\nAll data access in Quickback goes through the generated API endpoints—never direct database queries. This ensures security rules (firewall, access, guards, masking) are always enforced.\n\n### CRUD Operations\n\n```bash\n# List posts (firewall automatically filters by ownership)\nGET /api/v1/posts\n\n# Get a single post\nGET /api/v1/posts/:id\n\n# Create a post (guards validate allowed fields)\nPOST /api/v1/posts\n{ \"title\": \"Hello World\", \"content\": \"...\" }\n\n# Update a post (guards validate updatable fields)\nPATCH /api/v1/posts/:id\n{ \"title\": \"Updated Title\" }\n\n# Delete a post\nDELETE /api/v1/posts/:id\n```\n\n### Filtering and Pagination\n\n```bash\n# Filter by field\nGET /api/v1/posts?status=published\n\n# Pagination\nGET /api/v1/posts?limit=10&offset=20\n\n# Sort\nGET /api/v1/posts?sort=createdAt&order=desc\n```\n\n### Why No Direct Database Access?\n\nDirect database queries bypass Quickback's security layers:\n- **Firewall** - Data isolation by user/org/team\n- **Access** - Role-based permissions\n- **Guards** - Field-level create/update restrictions\n- **Masking** - Sensitive data redaction\n\nAlways use the API endpoints. For custom business logic, use [Actions](/compiler/definitions/actions).\n\n## Local Development\n\nD1 works locally with Wrangler:\n\n```bash\n# Start local dev server with D1\nwrangler dev\n\n# D1 data persists in .wrangler/state/\n```\n\nLocal D1 uses SQLite files in `.wrangler/state/v3/d1/`, which you can inspect with any SQLite client.\n\n## Security Architecture\n\nFor all Quickback-generated code, D1's application-layer security provides equivalent protection to Supabase RLS. The key difference is where enforcement happens — and this matters if you write custom routes.\n\n### How Security Works\n\n| Component | Enforcement | Notes |\n|-----------|-------------|-------|\n| CRUD endpoints | ✅ Firewall auto-applied | All generated routes enforce security |\n| Actions | ✅ Firewall auto-applied | Both standalone and record-based |\n| Manual routes | ⚠️ Must apply firewall | Use `withFirewall` helper |\n\n### Why D1 is Secure\n\n1. **No external database access** - D1 can only be queried through your Worker. There's no connection string or external endpoint.\n2. **Generated code enforces rules** - All CRUD and Action endpoints automatically apply firewall, access, guards, and masking.\n3. **Single entry point** - Every request flows through your API where security is enforced.\n\nUnlike Supabase where PostgreSQL RLS provides database-level enforcement (protecting data even if application code has bugs), D1's security comes from architecture: the database is inaccessible except through your Worker, and all generated routes apply the four security pillars. The trade-off is that **custom routes you write outside Quickback must manually apply security** — there is no database-level safety net.\n\n### Comparison with Supabase RLS\n\n| Scenario | Supabase | D1 |\n|----------|----------|-----|\n| CRUD endpoints | ✅ Secure (RLS + App) | ✅ Secure (App) |\n| Actions | ✅ Secure (RLS + App) | ✅ Secure (App) |\n| Manual routes | ✅ RLS still protects | ⚠️ Must apply firewall |\n| External DB access | ⚠️ Possible with credentials | ✅ Not possible |\n| Dashboard queries | Via Supabase Studio | ⚠️ Admin only (audit logged) |\n\n### Writing Manual Routes\n\nIf you write custom routes outside of Quickback compilation (e.g., custom reports, integrations), use the generated `withFirewall` helper to ensure security:\n\n```ts\n\napp.get('/reports/monthly', async (c) => {\n return withFirewall(c, async (ctx, firewall) => {\n const results = await db.select()\n .from(invoices)\n .where(firewall);\n return c.json(results);\n });\n});\n```\n\nThe `withFirewall` helper:\n- Validates authentication\n- Builds the correct WHERE conditions for the current user/org\n- Returns 401 if not authenticated\n\n### Best Practices\n\n1. **Use generated endpoints** - Prefer CRUD and Actions over manual routes\n2. **Always apply firewall** - When writing manual routes, always use `withFirewall`\n3. **Avoid raw SQL** - Raw SQL bypasses application security; use Drizzle ORM\n4. **Review custom code** - Manual routes should be code-reviewed for security\n\n## Limitations\n\nD1 is built on SQLite, which means:\n\n- **No stored procedures** - Business logic lives in your Worker\n- **Single-writer** - One write connection at a time (reads scale horizontally)\n- **Size limits** - 10GB per database (free tier: 500MB)\n- **No PostgreSQL extensions** - Use Supabase if you need PostGIS, etc.\n\nFor most applications, these limits are non-issues. D1's edge distribution and zero-config setup outweigh the constraints."
362
+ "content": "Cloudflare D1 is SQLite at the edge. Quickback uses D1 as the primary database for Cloudflare deployments, with a multi-database pattern for separation of concerns.\n\n## What is D1?\n\nD1 is Cloudflare's serverless SQL database built on SQLite:\n\n- **Edge-native** - Runs in Cloudflare's global network\n- **SQLite compatible** - Use familiar SQL syntax\n- **Zero configuration** - No connection strings or pooling\n- **Automatic replication** - Read replicas at every edge location\n\n## Multi-Database Pattern\n\nQuickback generates separate D1 databases for different concerns:\n\n| Database | Binding | Purpose |\n|----------|---------|---------|\n| `AUTH_DB` | `AUTH_DB` | Better Auth tables (user, session, account) |\n| `DB` | `DB` | Your application data |\n| `FILES_DB` | `FILES_DB` | File metadata for R2 uploads |\n| `WEBHOOKS_DB` | `WEBHOOKS_DB` | Webhook delivery tracking |\n\nThis separation provides:\n- **Independent scaling** - Auth traffic doesn't affect app queries\n- **Isolation** - Auth schema changes don't touch your data\n- **Clarity** - Clear ownership of each database\n\n## Drizzle ORM Integration\n\nQuickback uses [Drizzle ORM](https://orm.drizzle.team/) for type-safe database access:\n\n```ts\n// schema/tables.ts - Your schema definition\n\nexport const posts = sqliteTable(\"posts\", {\n id: text(\"id\").primaryKey(),\n title: text(\"title\").notNull(),\n content: text(\"content\"),\n authorId: text(\"author_id\").notNull(),\n createdAt: integer(\"created_at\", { mode: \"timestamp\" }),\n});\n```\n\nThe compiler generates Drizzle queries based on your security rules:\n\n```ts\n// Generated query with firewall applied\nconst result = await db\n .select()\n .from(posts)\n .where(eq(posts.authorId, userId)); // Firewall injects ownership\n```\n\n## Migrations\n\nQuickback generates migrations automatically at compile time based on your schema changes:\n\n```bash\n# Compile your project (generates migrations)\nquickback compile\n\n# Apply migrations locally\nwrangler d1 migrations apply DB --local\n\n# Apply to production D1\nwrangler d1 migrations apply DB --remote\n```\n\nMigration files are generated in `quickback/drizzle/` and version-controlled with your code. You never need to manually generate migrations—just define your schema and compile.\n\n## wrangler.toml Bindings\n\nThe compiler generates D1 bindings in `wrangler.toml` automatically. To get production-ready IDs in the output, set them in your `quickback.config.ts`:\n\n```typescript\nproviders: {\n database: defineDatabase(\"cloudflare-d1\", {\n databaseId: \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\", // Features DB → binding \"DB\"\n authDatabaseId: \"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\", // Auth DB → binding \"AUTH_DB\"\n filesDatabaseId: \"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz\", // Files DB → binding \"FILES_DB\"\n }),\n}\n```\n\nThis generates:\n\n```toml\n[[d1_databases]]\nbinding = \"AUTH_DB\"\ndatabase_name = \"my-app-auth\"\ndatabase_id = \"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\"\n\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"my-app-features\"\ndatabase_id = \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n\n[[d1_databases]]\nbinding = \"FILES_DB\"\ndatabase_name = \"my-app-files\"\ndatabase_id = \"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz\"\n```\n\nIf database IDs are omitted, the compiler uses local-dev placeholders — you'll need to replace them manually before deploying.\n\nCreate databases via Wrangler:\n\n```bash\nwrangler d1 create my-app-features\nwrangler d1 create my-app-auth\nwrangler d1 create my-app-files\n```\n\n## Accessing Data via API\n\nAll data access in Quickback goes through the generated API endpoints—never direct database queries. This ensures security rules (firewall, access, guards, masking) are always enforced.\n\n### CRUD Operations\n\n```bash\n# List posts (firewall automatically filters by ownership)\nGET /api/v1/posts\n\n# Get a single post\nGET /api/v1/posts/:id\n\n# Create a post (guards validate allowed fields)\nPOST /api/v1/posts\n{ \"title\": \"Hello World\", \"content\": \"...\" }\n\n# Update a post (guards validate updatable fields)\nPATCH /api/v1/posts/:id\n{ \"title\": \"Updated Title\" }\n\n# Delete a post\nDELETE /api/v1/posts/:id\n```\n\n### Filtering and Pagination\n\n```bash\n# Filter by field\nGET /api/v1/posts?status=published\n\n# Pagination\nGET /api/v1/posts?limit=10&offset=20\n\n# Sort\nGET /api/v1/posts?sort=createdAt&order=desc\n```\n\n### Why No Direct Database Access?\n\nDirect database queries bypass Quickback's security layers:\n- **Firewall** - Data isolation by user/org/team\n- **Access** - Role-based permissions\n- **Guards** - Field-level create/update restrictions\n- **Masking** - Sensitive data redaction\n\nAlways use the API endpoints. For custom business logic, use [Actions](/compiler/definitions/actions).\n\n## Local Development\n\nD1 works locally with Wrangler:\n\n```bash\n# Start local dev server with D1\nwrangler dev\n\n# D1 data persists in .wrangler/state/\n```\n\nLocal D1 uses SQLite files in `.wrangler/state/v3/d1/`, which you can inspect with any SQLite client.\n\n## Security Architecture\n\nFor all Quickback-generated code, D1's application-layer security provides equivalent protection to Supabase RLS. The key difference is where enforcement happens — and this matters if you write custom routes.\n\n### How Security Works\n\n| Component | Enforcement | Notes |\n|-----------|-------------|-------|\n| CRUD endpoints | ✅ Firewall auto-applied | All generated routes enforce security |\n| Actions | ✅ Firewall auto-applied | Both standalone and record-based |\n| Manual routes | ⚠️ Must apply firewall | Use `withFirewall` helper |\n\n### Why D1 is Secure\n\n1. **No external database access** - D1 can only be queried through your Worker. There's no connection string or external endpoint.\n2. **Generated code enforces rules** - All CRUD and Action endpoints automatically apply firewall, access, guards, and masking.\n3. **Single entry point** - Every request flows through your API where security is enforced.\n\nUnlike Supabase where PostgreSQL RLS provides database-level enforcement (protecting data even if application code has bugs), D1's security comes from architecture: the database is inaccessible except through your Worker, and all generated routes apply the four security pillars. The trade-off is that **custom routes you write outside Quickback must manually apply security** — there is no database-level safety net.\n\n### Comparison with Supabase RLS\n\n| Scenario | Supabase | D1 |\n|----------|----------|-----|\n| CRUD endpoints | ✅ Secure (RLS + App) | ✅ Secure (App) |\n| Actions | ✅ Secure (RLS + App) | ✅ Secure (App) |\n| Manual routes | ✅ RLS still protects | ⚠️ Must apply firewall |\n| External DB access | ⚠️ Possible with credentials | ✅ Not possible |\n| Dashboard queries | Via Supabase Studio | ⚠️ Admin only (audit logged) |\n\n### Writing Manual Routes\n\nIf you write custom routes outside of Quickback compilation (e.g., custom reports, integrations), use the generated `withFirewall` helper to ensure security:\n\n```ts\n\napp.get('/reports/monthly', async (c) => {\n return withFirewall(c, async (ctx, firewall) => {\n const results = await db.select()\n .from(invoices)\n .where(firewall);\n return c.json(results);\n });\n});\n```\n\nThe `withFirewall` helper:\n- Validates authentication\n- Builds the correct WHERE conditions for the current user/org\n- Returns 401 if not authenticated\n\n### Best Practices\n\n1. **Use generated endpoints** - Prefer CRUD and Actions over manual routes\n2. **Always apply firewall** - When writing manual routes, always use `withFirewall`\n3. **Avoid raw SQL** - Raw SQL bypasses application security; use Drizzle ORM\n4. **Review custom code** - Manual routes should be code-reviewed for security\n\n## Limitations\n\nD1 is built on SQLite, which means:\n\n- **No stored procedures** - Business logic lives in your Worker\n- **Single-writer** - One write connection at a time (reads scale horizontally)\n- **Size limits** - 10GB per database (free tier: 500MB)\n- **No PostgreSQL extensions** - Use Supabase if you need PostGIS, etc.\n\nFor most applications, these limits are non-issues. D1's edge distribution and zero-config setup outweigh the constraints."
363
363
  },
364
364
  "stack/database": {
365
365
  "title": "Database",
@@ -407,7 +407,7 @@ export const DOCS = {
407
407
  },
408
408
  "stack/storage/kv": {
409
409
  "title": "KV Storage",
410
- "content": "Cloudflare Workers KV is a global key-value store optimized for read-heavy workloads. Quickback uses KV for session storage, caching, and fast lookups.\n\n## What is KV?\n\nWorkers KV is a distributed key-value store:\n\n- **Global distribution** - Data replicated to 300+ edge locations\n- **Low-latency reads** - Cached at the edge, sub-millisecond access\n- **Eventually consistent** - Writes propagate globally within 60 seconds\n- **Simple API** - Get, put, delete, list\n\n## Use Cases in Quickback\n\n| Use Case | Description |\n|----------|-------------|\n| **Sessions** | Better Auth stores sessions in KV for fast validation |\n| **Cache** | Cache expensive database queries or API responses |\n| **Rate Limiting** | Track request counts per user/IP |\n| **Feature Flags** | Store configuration that changes infrequently |\n\n## Namespace Setup\n\nCreate a KV namespace via Wrangler:\n\n```bash\n# Create namespace\nwrangler kv namespace create \"SESSIONS\"\n\n# Output:\n# Add the following to your wrangler.toml:\n# [[kv_namespaces]]\n# binding = \"SESSIONS\"\n# id = \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n```\n\nAdd to `wrangler.toml`:\n\n```toml\n[[kv_namespaces]]\nbinding = \"SESSIONS\"\nid = \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n\n[[kv_namespaces]]\nbinding = \"CACHE\"\nid = \"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\"\n```\n\n## Reading and Writing Values\n\n### Basic Operations\n\n```ts\n// Write a value\nawait env.CACHE.put(\"user:123\", JSON.stringify({ name: \"Alice\" }));\n\n// Read a value\nconst data = await env.CACHE.get(\"user:123\");\nconst user = data ? JSON.parse(data) : null;\n\n// Delete a value\nawait env.CACHE.delete(\"user:123\");\n\n// Check if key exists\nconst exists = await env.CACHE.get(\"user:123\") !== null;\n```\n\n### With Metadata\n\nKV supports metadata attached to keys:\n\n```ts\n// Write with metadata\nawait env.CACHE.put(\"user:123\", JSON.stringify(userData), {\n metadata: { updatedAt: Date.now(), version: 1 },\n});\n\n// Read with metadata\nconst { value, metadata } = await env.CACHE.getWithMetadata(\"user:123\");\n```\n\n### List Keys\n\n```ts\n// List all keys with prefix\nconst list = await env.CACHE.list({ prefix: \"user:\" });\n\nfor (const key of list.keys) {\n console.log(key.name, key.metadata);\n}\n\n// Paginate through results\nlet cursor = undefined;\ndo {\n const result = await env.CACHE.list({ prefix: \"user:\", cursor });\n // Process result.keys\n cursor = result.cursor;\n} while (cursor);\n```\n\n## Expiration and TTL\n\nSet automatic expiration on keys:\n\n```ts\n// Expire in 1 hour (3600 seconds)\nawait env.CACHE.put(\"session:abc\", sessionData, {\n expirationTtl: 3600,\n});\n\n// Expire at specific timestamp\nawait env.CACHE.put(\"session:abc\", sessionData, {\n expiration: Math.floor(Date.now() / 1000) + 3600,\n});\n```\n\nCommon TTL patterns:\n\n| Use Case | TTL |\n|----------|-----|\n| Session tokens | 24 hours (86400) |\n| API cache | 5 minutes (300) |\n| Rate limit counters | 1 minute (60) |\n| Feature flags | No expiration |\n\n## Session Storage\n\nBetter Auth uses KV for session storage in Cloudflare deployments:\n\n```ts\n// Quickback configures this automatically\nauth: defineAuth(\"better-auth\", {\n session: {\n storage: \"kv\", // Uses SESSIONS namespace\n },\n}),\n```\n\nSessions are stored as:\n- Key: `session:{sessionId}`\n- Value: Serialized session object\n- TTL: Configured session expiration\n\n## Caching Patterns\n\n### Cache-Aside Pattern\n\n```ts\nasync function getUser(userId: string) {\n // Check cache first\n const cached = await env.CACHE.get(`user:${userId}`);\n if (cached) {\n return JSON.parse(cached);\n }\n\n // Fetch from database\n const user = await db.select().from(users).where(eq(users.id, userId));\n\n // Store in cache\n await env.CACHE.put(`user:${userId}`, JSON.stringify(user), {\n expirationTtl: 300, // 5 minutes\n });\n\n return user;\n}\n```\n\n### Cache Invalidation\n\n```ts\n// Invalidate on update\nasync function updateUser(userId: string, data: UserUpdate) {\n await db.update(users).set(data).where(eq(users.id, userId));\n await env.CACHE.delete(`user:${userId}`);\n}\n```\n\n## wrangler.toml Reference\n\n```toml\n# Production namespace\n[[kv_namespaces]]\nbinding = \"SESSIONS\"\nid = \"abc123...\"\n\n# Preview namespace (for wrangler dev)\n[[kv_namespaces]]\nbinding = \"SESSIONS\"\nid = \"def456...\"\npreview_id = \"ghi789...\"\n```\n\n## Limitations\n\n- **Value size** - Maximum 25 MB per value\n- **Key size** - Maximum 512 bytes\n- **Write limits** - 1 write per second per key\n- **Eventual consistency** - Changes may take up to 60 seconds to propagate\n- **Not for hot writes** - Use Durable Objects for high-write scenarios\n\nKV is optimized for read-heavy workloads. For write-heavy use cases or strong consistency, consider [Durable Objects](/stack/realtime/durable-objects)."
410
+ "content": "Cloudflare Workers KV is a global key-value store optimized for read-heavy workloads. Quickback uses KV for session storage, caching, and fast lookups.\n\n## What is KV?\n\nWorkers KV is a distributed key-value store:\n\n- **Global distribution** - Data replicated to 300+ edge locations\n- **Low-latency reads** - Cached at the edge, sub-millisecond access\n- **Eventually consistent** - Writes propagate globally within 60 seconds\n- **Simple API** - Get, put, delete, list\n\n## Use Cases in Quickback\n\n| Use Case | Description |\n|----------|-------------|\n| **Sessions** | Better Auth stores sessions in KV for fast validation |\n| **Cache** | Cache expensive database queries or API responses |\n| **Rate Limiting** | Track request counts per user/IP |\n| **Feature Flags** | Store configuration that changes infrequently |\n\n## Namespace Setup\n\nCreate a KV namespace via Wrangler:\n\n```bash\n# Create namespace\nwrangler kv namespace create \"KV\"\n\n# Output:\n# Add the following to your wrangler.toml:\n# [[kv_namespaces]]\n# binding = \"KV\"\n# id = \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n```\n\n### Setting the namespace ID in config\n\nTo have the compiler generate `wrangler.toml` with your KV namespace ID, set it in your provider config:\n\n```typescript\nproviders: {\n storage: defineStorage(\"kv\", {\n namespaceId: \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\n }),\n}\n```\n\nThe `namespaceId` can also be set as `kvId` on the auth provider config, since KV is primarily used for auth session storage. The compiler checks both locations.\n\n## Reading and Writing Values\n\n### Basic Operations\n\n```ts\n// Write a value\nawait env.CACHE.put(\"user:123\", JSON.stringify({ name: \"Alice\" }));\n\n// Read a value\nconst data = await env.CACHE.get(\"user:123\");\nconst user = data ? JSON.parse(data) : null;\n\n// Delete a value\nawait env.CACHE.delete(\"user:123\");\n\n// Check if key exists\nconst exists = await env.CACHE.get(\"user:123\") !== null;\n```\n\n### With Metadata\n\nKV supports metadata attached to keys:\n\n```ts\n// Write with metadata\nawait env.CACHE.put(\"user:123\", JSON.stringify(userData), {\n metadata: { updatedAt: Date.now(), version: 1 },\n});\n\n// Read with metadata\nconst { value, metadata } = await env.CACHE.getWithMetadata(\"user:123\");\n```\n\n### List Keys\n\n```ts\n// List all keys with prefix\nconst list = await env.CACHE.list({ prefix: \"user:\" });\n\nfor (const key of list.keys) {\n console.log(key.name, key.metadata);\n}\n\n// Paginate through results\nlet cursor = undefined;\ndo {\n const result = await env.CACHE.list({ prefix: \"user:\", cursor });\n // Process result.keys\n cursor = result.cursor;\n} while (cursor);\n```\n\n## Expiration and TTL\n\nSet automatic expiration on keys:\n\n```ts\n// Expire in 1 hour (3600 seconds)\nawait env.CACHE.put(\"session:abc\", sessionData, {\n expirationTtl: 3600,\n});\n\n// Expire at specific timestamp\nawait env.CACHE.put(\"session:abc\", sessionData, {\n expiration: Math.floor(Date.now() / 1000) + 3600,\n});\n```\n\nCommon TTL patterns:\n\n| Use Case | TTL |\n|----------|-----|\n| Session tokens | 24 hours (86400) |\n| API cache | 5 minutes (300) |\n| Rate limit counters | 1 minute (60) |\n| Feature flags | No expiration |\n\n## Session Storage\n\nBetter Auth uses KV for session storage in Cloudflare deployments:\n\n```ts\n// Quickback configures this automatically\nauth: defineAuth(\"better-auth\", {\n session: {\n storage: \"kv\", // Uses SESSIONS namespace\n },\n}),\n```\n\nSessions are stored as:\n- Key: `session:{sessionId}`\n- Value: Serialized session object\n- TTL: Configured session expiration\n\n## Caching Patterns\n\n### Cache-Aside Pattern\n\n```ts\nasync function getUser(userId: string) {\n // Check cache first\n const cached = await env.CACHE.get(`user:${userId}`);\n if (cached) {\n return JSON.parse(cached);\n }\n\n // Fetch from database\n const user = await db.select().from(users).where(eq(users.id, userId));\n\n // Store in cache\n await env.CACHE.put(`user:${userId}`, JSON.stringify(user), {\n expirationTtl: 300, // 5 minutes\n });\n\n return user;\n}\n```\n\n### Cache Invalidation\n\n```ts\n// Invalidate on update\nasync function updateUser(userId: string, data: UserUpdate) {\n await db.update(users).set(data).where(eq(users.id, userId));\n await env.CACHE.delete(`user:${userId}`);\n}\n```\n\n## wrangler.toml Reference\n\n```toml\n# Production namespace\n[[kv_namespaces]]\nbinding = \"SESSIONS\"\nid = \"abc123...\"\n\n# Preview namespace (for wrangler dev)\n[[kv_namespaces]]\nbinding = \"SESSIONS\"\nid = \"def456...\"\npreview_id = \"ghi789...\"\n```\n\n## Limitations\n\n- **Value size** - Maximum 25 MB per value\n- **Key size** - Maximum 512 bytes\n- **Write limits** - 1 write per second per key\n- **Eventual consistency** - Changes may take up to 60 seconds to propagate\n- **Not for hot writes** - Use Durable Objects for high-write scenarios\n\nKV is optimized for read-heavy workloads. For write-heavy use cases or strong consistency, consider [Durable Objects](/stack/realtime/durable-objects)."
411
411
  },
412
412
  "stack/storage/r2": {
413
413
  "title": "File Storage (R2)",
@@ -1 +1 @@
1
- {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/docs/content.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,0DAA0D;AAO1D,MAAM,CAAC,MAAM,IAAI,GAA6B;IAC5C,0BAA0B,EAAE;QAC1B,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,woRAAwoR;KACppR;IACD,kCAAkC,EAAE;QAClC,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,+xPAA+xP;KAC3yP;IACD,2BAA2B,EAAE;QAC3B,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,8sFAA8sF;KAC1tF;IACD,8BAA8B,EAAE;QAC9B,OAAO,EAAE,oBAAoB;QAC7B,SAAS,EAAE,kgBAAkgB;KAC9gB;IACD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,SAAS;QAClB,SAAS,EAAE,slBAAslB;KAClmB;IACD,mCAAmC,EAAE;QACnC,OAAO,EAAE,mBAAmB;QAC5B,SAAS,EAAE,irBAAirB;KAC7rB;IACD,qBAAqB,EAAE;QACrB,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,+8LAA+8L;KAC39L;IACD,mCAAmC,EAAE;QACnC,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,48BAA48B;KACx9B;IACD,8BAA8B,EAAE;QAC9B,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,qwBAAqwB;KACjxB;IACD,kCAAkC,EAAE;QAClC,OAAO,EAAE,6BAA6B;QACtC,SAAS,EAAE,+uBAA+uB;KAC3vB;IACD,8BAA8B,EAAE;QAC9B,OAAO,EAAE,oBAAoB;QAC7B,SAAS,EAAE,0hBAA0hB;KACtiB;IACD,YAAY,EAAE;QACZ,OAAO,EAAE,YAAY;QACrB,SAAS,EAAE,o5KAAo5K;KACh6K;IACD,0BAA0B,EAAE;QAC1B,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,25MAA25M;KACv6M;IACD,uBAAuB,EAAE;QACvB,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,6zIAA6zI;KACz0I;IACD,2BAA2B,EAAE;QAC3B,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,g/IAAg/I;KAC5/I;IACD,mBAAmB,EAAE;QACnB,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,y7NAAy7N;KACr8N;IACD,WAAW,EAAE;QACX,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,8/hBAA8/hB;KAC1giB;IACD,aAAa,EAAE;QACb,OAAO,EAAE,SAAS;QAClB,SAAS,EAAE,qnOAAqnO;KACjoO;IACD,gBAAgB,EAAE;QAChB,OAAO,EAAE,sBAAsB;QAC/B,SAAS,EAAE,2ybAA2yb;KACvzb;IACD,gBAAgB,EAAE;QAChB,OAAO,EAAE,YAAY;QACrB,SAAS,EAAE,g0KAAg0K;KAC50K;IACD,eAAe,EAAE;QACf,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,iuDAAiuD;KAC7uD;IACD,KAAK,EAAE;QACL,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,2jNAA2jN;KACvkN;IACD,oBAAoB,EAAE;QACpB,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,6nMAA6nM;KACzoM;IACD,WAAW,EAAE;QACX,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,i9PAAi9P;KAC79P;IACD,oBAAoB,EAAE;QACpB,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,wwKAAwwK;KACpxK;IACD,mBAAmB,EAAE;QACnB,OAAO,EAAE,yBAAyB;QAClC,SAAS,EAAE,kvYAAkvY;KAC9vY;IACD,qBAAqB,EAAE;QACrB,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,29OAA29O;KACv+O;IACD,cAAc,EAAE;QACd,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,0iMAA0iM;KACtjM;IACD,iBAAiB,EAAE;QACjB,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,88KAA88K;KAC19K;IACD,wCAAwC,EAAE;QACxC,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,2nEAA2nE;KACvoE;IACD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,y0NAAy0N;KACr1N;IACD,mCAAmC,EAAE;QACnC,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,ipCAAipC;KAC7pC;IACD,yBAAyB,EAAE;QACzB,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,myEAAmyE;KAC/yE;IACD,wCAAwC,EAAE;QACxC,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,+tFAA+tF;KAC3uF;IACD,yCAAyC,EAAE;QACzC,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,g/DAAg/D;KAC5/D;IACD,yBAAyB,EAAE;QACzB,OAAO,EAAE,2BAA2B;QACpC,SAAS,EAAE,s8LAAs8L;KACl9L;IACD,iBAAiB,EAAE;QACjB,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,k4XAAk4X;KAC94X;IACD,wBAAwB,EAAE;QACxB,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,w3VAAw3V;KACp4V;IACD,2BAA2B,EAAE;QAC3B,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,++LAA++L;KAC3/L;IACD,+BAA+B,EAAE;QAC/B,OAAO,EAAE,oBAAoB;QAC7B,SAAS,EAAE,y7GAAy7G;KACr8G;IACD,2BAA2B,EAAE;QAC3B,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,+xLAA+xL;KAC3yL;IACD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,gDAAgD;QACzD,SAAS,EAAE,2vTAA2vT;KACvwT;IACD,8BAA8B,EAAE;QAC9B,OAAO,EAAE,SAAS;QAClB,SAAS,EAAE,gnoBAAgnoB;KAC5noB;IACD,+BAA+B,EAAE;QAC/B,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,slIAAslI;KAClmI;IACD,+BAA+B,EAAE;QAC/B,OAAO,EAAE,2BAA2B;QACpC,SAAS,EAAE,+3MAA+3M;KAC34M;IACD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,mCAAmC;QAC5C,SAAS,EAAE,g+MAAg+M;KAC5+M;IACD,sBAAsB,EAAE;QACtB,OAAO,EAAE,sBAAsB;QAC/B,SAAS,EAAE,w7NAAw7N;KACp8N;IACD,8BAA8B,EAAE;QAC9B,OAAO,EAAE,2BAA2B;QACpC,SAAS,EAAE,ohKAAohK;KAChiK;IACD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,kqdAAkqd;KAC9qd;IACD,iCAAiC,EAAE;QACjC,OAAO,EAAE,YAAY;QACrB,SAAS,EAAE,q6BAAq6B;KACj7B;IACD,4BAA4B,EAAE;QAC5B,OAAO,EAAE,+BAA+B;QACxC,SAAS,EAAE,6iQAA6iQ;KACzjQ;IACD,sCAAsC,EAAE;QACtC,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,ysHAAysH;KACrtH;IACD,uCAAuC,EAAE;QACvC,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,s5YAAs5Y;KACl6Y;IACD,uCAAuC,EAAE;QACvC,OAAO,EAAE,oBAAoB;QAC7B,SAAS,EAAE,y/JAAy/J;KACrgK;IACD,0BAA0B,EAAE;QAC1B,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,mhLAAmhL;KAC/hL;IACD,mCAAmC,EAAE;QACnC,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,qjQAAqjQ;KACjkQ;IACD,wCAAwC,EAAE;QACxC,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,o+NAAo+N;KACh/N;IACD,uCAAuC,EAAE;QACvC,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,2wIAA2wI;KACvxI;IACD,8CAA8C,EAAE;QAC9C,OAAO,EAAE,qBAAqB;QAC9B,SAAS,EAAE,0gNAA0gN;KACthN;IACD,yCAAyC,EAAE;QACzC,OAAO,EAAE,6BAA6B;QACtC,SAAS,EAAE,ovHAAovH;KAChwH;IACD,oCAAoC,EAAE;QACpC,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,4zJAA4zJ;KACx0J;IACD,UAAU,EAAE;QACV,OAAO,EAAE,oBAAoB;QAC7B,SAAS,EAAE,qmLAAqmL;KACjnL;IACD,kCAAkC,EAAE;QAClC,OAAO,EAAE,oBAAoB;QAC7B,SAAS,EAAE,wjGAAwjG;KACpkG;IACD,uBAAuB,EAAE;QACvB,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,6rDAA6rD;KACzsD;IACD,4BAA4B,EAAE;QAC5B,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,4iGAA4iG;KACxjG;IACD,gCAAgC,EAAE;QAChC,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,suFAAsuF;KAClvF;IACD,0BAA0B,EAAE;QAC1B,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,65HAA65H;KACz6H;IACD,oCAAoC,EAAE;QACpC,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,g2QAAg2Q;KAC52Q;IACD,yCAAyC,EAAE;QACzC,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,gtRAAgtR;KAC5tR;IACD,gCAAgC,EAAE;QAChC,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,4xGAA4xG;KACxyG;IACD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,8okBAA8okB;KAC1pkB;IACD,+BAA+B,EAAE;QAC/B,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,wvMAAwvM;KACpwM;IACD,wBAAwB,EAAE;QACxB,OAAO,EAAE,eAAe;QACxB,SAAS,EAAE,w/CAAw/C;KACpgD;IACD,gCAAgC,EAAE;QAChC,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,o9GAAo9G;KACh+G;IACD,qCAAqC,EAAE;QACrC,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,o2LAAo2L;KACh3L;IACD,kCAAkC,EAAE;QAClC,OAAO,EAAE,WAAW;QACpB,SAAS,EAAE,6xIAA6xI;KACzyI;IACD,OAAO,EAAE;QACP,OAAO,EAAE,yBAAyB;QAClC,SAAS,EAAE,+3HAA+3H;KAC34H;IACD,2CAA2C,EAAE;QAC3C,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,w0HAAw0H;KACp1H;IACD,8CAA8C,EAAE;QAC9C,OAAO,EAAE,mBAAmB;QAC5B,SAAS,EAAE,ulEAAulE;KACnmE;IACD,qDAAqD,EAAE;QACrD,OAAO,EAAE,0BAA0B;QACnC,SAAS,EAAE,y2JAAy2J;KACr3J;IACD,iCAAiC,EAAE;QACjC,OAAO,EAAE,oBAAoB;QAC7B,SAAS,EAAE,iiRAAiiR;KAC7iR;IACD,eAAe,EAAE;QACf,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,+tBAA+tB;KAC3uB;IACD,qBAAqB,EAAE;QACrB,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,kfAAkf;KAC9f;IACD,wBAAwB,EAAE;QACxB,OAAO,EAAE,sBAAsB;QAC/B,SAAS,EAAE,6jBAA6jB;KACzkB;IACD,YAAY,EAAE;QACZ,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,0lEAA0lE;KACtmE;IACD,6BAA6B,EAAE;QAC7B,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,8tSAA8tS;KAC1uS;IACD,oBAAoB,EAAE;QACpB,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,w9MAAw9M;KACp+M;IACD,qBAAqB,EAAE;QACrB,OAAO,EAAE,yBAAyB;QAClC,SAAS,EAAE,gtZAAgtZ;KAC5tZ;IACD,uBAAuB,EAAE;QACvB,OAAO,EAAE,YAAY;QACrB,SAAS,EAAE,4oCAA4oC;KACxpC;IACD,mBAAmB,EAAE;QACnB,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,gpQAAgpQ;KAC5pQ;IACD,gBAAgB,EAAE;QAChB,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,upBAAupB;KACnqB;IACD,qBAAqB,EAAE;QACrB,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,ymEAAymE;KACrnE;IACD,yBAAyB,EAAE;QACzB,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,02BAA02B;KACt3B;IACD,OAAO,EAAE;QACP,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,yrHAAyrH;KACrsH;IACD,uBAAuB,EAAE;QACvB,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,stQAAstQ;KACluQ;IACD,cAAc,EAAE;QACd,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,+0DAA+0D;KAC31D;IACD,2BAA2B,EAAE;QAC3B,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,s5JAAs5J;KACl6J;IACD,gCAAgC,EAAE;QAChC,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,qpbAAqpb;KACjqb;IACD,gBAAgB,EAAE;QAChB,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,2iGAA2iG;KACvjG;IACD,+BAA+B,EAAE;QAC/B,OAAO,EAAE,gBAAgB;QACzB,SAAS,EAAE,y4LAAy4L;KACr5L;IACD,eAAe,EAAE;QACf,OAAO,EAAE,SAAS;QAClB,SAAS,EAAE,0tBAA0tB;KACtuB;IACD,kBAAkB,EAAE;QAClB,OAAO,EAAE,YAAY;QACrB,SAAS,EAAE,2/JAA2/J;KACvgK;IACD,kBAAkB,EAAE;QAClB,OAAO,EAAE,mBAAmB;QAC5B,SAAS,EAAE,88NAA88N;KAC19N;IACD,wBAAwB,EAAE;QACxB,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,+lEAA+lE;KAC3mE;IACD,wBAAwB,EAAE;QACxB,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,ozDAAozD;KACh0D;IACD,yBAAyB,EAAE;QACzB,OAAO,EAAE,sBAAsB;QAC/B,SAAS,EAAE,m2fAAm2f;KAC/2f;IACD,cAAc,EAAE;QACd,OAAO,EAAE,aAAa;QACtB,SAAS,EAAE,21DAA21D;KACv2D;IACD,+BAA+B,EAAE;QAC/B,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,60KAA60K;KACz1K;IACD,wBAAwB,EAAE;QACxB,OAAO,EAAE,kBAAkB;QAC3B,SAAS,EAAE,mmKAAmmK;KAC/mK;IACD,gBAAgB,EAAE;QAChB,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,81CAA81C;KAC12C;IACD,yBAAyB,EAAE;QACzB,OAAO,EAAE,mBAAmB;QAC5B,SAAS,EAAE,myOAAmyO;KAC/yO;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,0BAA0B;IAC1B,kCAAkC;IAClC,2BAA2B;IAC3B,8BAA8B;IAC9B,6BAA6B;IAC7B,mCAAmC;IACnC,qBAAqB;IACrB,mCAAmC;IACnC,8BAA8B;IAC9B,kCAAkC;IAClC,8BAA8B;IAC9B,YAAY;IACZ,0BAA0B;IAC1B,uBAAuB;IACvB,2BAA2B;IAC3B,mBAAmB;IACnB,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,eAAe;IACf,KAAK;IACL,oBAAoB;IACpB,WAAW;IACX,oBAAoB;IACpB,mBAAmB;IACnB,qBAAqB;IACrB,cAAc;IACd,iBAAiB;IACjB,wCAAwC;IACxC,6BAA6B;IAC7B,mCAAmC;IACnC,yBAAyB;IACzB,wCAAwC;IACxC,yCAAyC;IACzC,yBAAyB;IACzB,iBAAiB;IACjB,wBAAwB;IACxB,2BAA2B;IAC3B,+BAA+B;IAC/B,2BAA2B;IAC3B,6BAA6B;IAC7B,8BAA8B;IAC9B,+BAA+B;IAC/B,+BAA+B;IAC/B,6BAA6B;IAC7B,sBAAsB;IACtB,8BAA8B;IAC9B,6BAA6B;IAC7B,iCAAiC;IACjC,4BAA4B;IAC5B,sCAAsC;IACtC,uCAAuC;IACvC,uCAAuC;IACvC,0BAA0B;IAC1B,mCAAmC;IACnC,wCAAwC;IACxC,uCAAuC;IACvC,8CAA8C;IAC9C,yCAAyC;IACzC,oCAAoC;IACpC,UAAU;IACV,kCAAkC;IAClC,uBAAuB;IACvB,4BAA4B;IAC5B,gCAAgC;IAChC,0BAA0B;IAC1B,oCAAoC;IACpC,yCAAyC;IACzC,gCAAgC;IAChC,6BAA6B;IAC7B,+BAA+B;IAC/B,wBAAwB;IACxB,gCAAgC;IAChC,qCAAqC;IACrC,kCAAkC;IAClC,OAAO;IACP,2CAA2C;IAC3C,8CAA8C;IAC9C,qDAAqD;IACrD,iCAAiC;IACjC,eAAe;IACf,qBAAqB;IACrB,wBAAwB;IACxB,YAAY;IACZ,6BAA6B;IAC7B,oBAAoB;IACpB,qBAAqB;IACrB,uBAAuB;IACvB,mBAAmB;IACnB,gBAAgB;IAChB,qBAAqB;IACrB,yBAAyB;IACzB,OAAO;IACP,uBAAuB;IACvB,cAAc;IACd,2BAA2B;IAC3B,gCAAgC;IAChC,gBAAgB;IAChB,+BAA+B;IAC/B,eAAe;IACf,kBAAkB;IAClB,kBAAkB;IAClB,wBAAwB;IACxB,wBAAwB;IACxB,yBAAyB;IACzB,cAAc;IACd,+BAA+B;IAC/B,wBAAwB;IACxB,gBAAgB;IAChB,yBAAyB;CAC1B,CAAC"}
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import pc from "picocolors";
12
12
  // Version injected at build time by scripts/inject-version.ts
13
- const CLI_VERSION = "0.8.1";
13
+ const CLI_VERSION = "0.8.2";
14
14
  function getPackageVersion() {
15
15
  return CLI_VERSION;
16
16
  }
@@ -592,6 +592,7 @@ Built on Better Auth with multi-tenant organization support.
592
592
  - Session storage in KV namespace
593
593
  - All auth routes at `/auth/v1/*`
594
594
  - Extensible via Better Auth plugins
595
+ - **JWT fast-path** — authenticated API requests validate via HMAC-SHA256 JWT (0 DB queries). Tokens mint and rotate automatically; session cookies are the source of truth. Separate from Better Auth's `jwt` plugin (opt-in, for JWKS/external identity). See `quickback docs stack/auth/jwt-optimization`
595
596
  - **Account UI** — pre-built SPA for login, signup, profile, orgs, admin (see Account UI section above)
596
597
 
597
598
  ```typescript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kardoe/quickback",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "CLI for Quickback - one-shot backend generator",
5
5
  "author": "Paul Stenhouse",
6
6
  "license": "MIT",
@@ -592,6 +592,7 @@ Built on Better Auth with multi-tenant organization support.
592
592
  - Session storage in KV namespace
593
593
  - All auth routes at `/auth/v1/*`
594
594
  - Extensible via Better Auth plugins
595
+ - **JWT fast-path** — authenticated API requests validate via HMAC-SHA256 JWT (0 DB queries). Tokens mint and rotate automatically; session cookies are the source of truth. Separate from Better Auth's `jwt` plugin (opt-in, for JWKS/external identity). See `quickback docs stack/auth/jwt-optimization`
595
596
  - **Account UI** — pre-built SPA for login, signup, profile, orgs, admin (see Account UI section above)
596
597
 
597
598
  ```typescript