Quickback Docs

Changelog

Release notes and version history for the Quickback compiler, CLI, and platform.

Changelog

Release notes for the Quickback compiler, CLI, and platform.


v0.8.7 — April 17, 2026

Security: Hono CVE cluster fix

Hono is bumped to ^4.12.14 across the monorepo, the compiler Docker image's pre-installed deps, and every generator path that emits Hono into compiled projects. Older ranges (^4.0.0 / ^4.5.0 / ^4.6.0 / ^4.7.11) covered the vulnerable window. Recompile to pick up the fix in your generated project's package.json.

Cloudflare Email Sending is now in public beta

Quickback already defaults to the Workers send_email binding for Cloudflare deploys, and Cloudflare Email Sending has now graduated to public beta — production-ready with zero-config, no API keys, no third-party accounts. No action needed; recompiling any existing project continues to wire the default Cloudflare transport. AWS SES remains a fully supported opt-in (email.provider: 'aws-ses') for non-Cloudflare runtimes or existing SES setups. See Email Configuration.

Minor/patch dep sweep

Aligned the whole dep surface — monorepo, compiler Docker image, and generator emissions into compiled projects — so new compiles and existing recompiles converge on the same versions:

  • wrangler@^4.83.0
  • drizzle-orm@^0.45.2, drizzle-kit@^0.31.10
  • zod@^4.3.6
  • @cloudflare/workers-types@^4.20260417.1 (also deduped — was accidentally declared in both dependencies and devDependencies in some paths)
  • @tanstack/react-query@^5.99.0 (Account app)

v0.8.5 — April 15, 2026

Fix: Better Auth 1.5 and @cloudflare/workers-types Compatibility

Generated API code failed to typecheck after Better Auth bumped to 1.5 and @cloudflare/workers-types tightened BufferSource to ArrayBufferView<ArrayBuffer>. Four unrelated surfaces broke:

  • databaseHooks.member.update.after was removed in Better Auth 1.5. The compiler now emits the member role-change broadcast hook as organizationHooks.afterUpdateMemberRole inside the organization() plugin call, which is the current supported shape.
  • REALTIME_URL and ACCESS_TOKEN are always emitted as optional CloudflareBindings fields when the organization plugin is active, so the broadcast hook typechecks even when realtimeMode is "inline" (or realtime isn't configured at all).
  • auth.api.listOrganizations / getFullOrganization / session.session.activeOrganizationId — third-party Better Auth plugins narrow the inferred auth.api type and hide organization-plugin endpoints. The generated middleware and /admin/v1/organizations route now cast through any for these calls.
  • crypto.subtle.sign / verify no longer accept Uint8Array<ArrayBufferLike> directly. Generated jwt.ts casts encoded payloads to BufferSource.

No config changes required — recompile to pick up the fixes.

CMS Admin Gate Now Enforced Server-Side

cms: { access: "admin" } previously only hid the CMS link in Account UI and rendered an "Access Denied" screen inside the SPA — the static assets, /api/v1/schema, and /api/v1/custom_view CRUD were reachable by any authenticated user. A non-admin with an API token or cookie could bypass the UI and read schema metadata or saved CMS views directly.

This release adds server-side enforcement at every CMS-facing surface:

  • CMS SPA shell — the Worker checks ctx.userRole === "admin" before serving index.html. Unauthenticated requests redirect to login; authenticated non-admins get 403 Admin access required. Static bundles (JS/CSS) remain reachable so browser caches aren't invalidated.
  • /api/v1/schema — returns 403 to non-admins.
  • custom_view CRUD — now gated on the new userRole: ["admin"] primitive rather than org membership.
  • In-SPA gate — unchanged; defense in depth behind the server-side gates.

Your resource CRUD endpoints are unaffected — they keep their per-resource crud.access.roles.

New userRole Access Primitive

Added userRole?: string[] to the declarative Access config, evaluated against ctx.userRole (the Better Auth admin-plugin user.role column). This is distinct from roles, which matches ctx.roles (org membership).

// Old: in multi-tenant this meant org-admin, not platform-admin
access: { roles: ['admin'] }

// New: explicitly require user.role === 'admin' regardless of org membership
access: { userRole: ['admin'] }

// "Org owner OR platform admin"
access: { or: [{ roles: ['owner'] }, { userRole: ['admin'] }] }

// Platform admins see SSNs across every org
masking: { ssn: { type: 'ssn', show: { userRole: ['admin'] } } }

userRole works everywhere Access is accepted — CRUD, actions, views, masking, and inside or/and combinators. See Access - userRole vs roles.

Fix: ReferenceError: customAlphabet is not defined in generated POST handlers

Projects with providers.database.config.generateId: "short" (or "nanoid" / "cuid") compiled routes that called the ID helper but never imported it. Every generated src/features/**/*.routes.ts had customAlphabet('0123...', 6)() in its POST body but no matching import { customAlphabet } from 'nanoid'; at the top of the file. Every POST /api/v1/<resource> and batch-create returned 500 with ReferenceError: customAlphabet is not defined on the first write.

The compiler now emits the correct import alongside the ID expression for all three modes: nanoid, customAlphabet, and createId from @paralleldrive/cuid2.

No config changes required — recompile to pick up the fix.

Fix: Dangling export const in generated actions.ts breaks tsc

When a feature authored actions as export const xActions = defineActions(...) (instead of export default defineActions(...)), the compiler's preamble extractor leaked the assignment's left-hand side into the generated src/features/<feature>/actions.ts. The output contained a dangling export const xActions = with no right-hand side, and tsc --noEmit failed with TS1109: Expression expected. Runtime happened to work because the unused placeholder was never evaluated, but typecheck was red.

The preamble extractor now anchors its boundary to the start of the top-level statement containing defineActions(...), covering export default, export const X =, const X =, and bare call forms. Local Zod helper declarations above the call continue to be preserved.

Fix: Files worker database_id reverted to "local-files" on every compile

Projects using the two-worker R2 architecture had their files worker's wrangler.toml (apps/backend/cloudflare-workers/files/wrangler.toml) rewritten with database_id = "local-files" on every quickback compile, even when providers.database.config.filesDatabaseId was set to a real D1 UUID. The main worker's binding was correct — only the files worker fell through. Remote deploys of the files worker bound to a non-existent D1, so file serving and access-control reads failed with a binding error.

The files-worker wrangler generator now reads filesDatabaseId (and filesDatabaseName) from providers.database.config as a fallback when they aren't set on providers.fileStorage.config, matching the precedence chain already used by the main worker. Local dev is unaffected — wrangler dev --local uses .wrangler/state/ regardless of the configured ID.

No config changes required — recompile and redeploy the files worker.


v0.8.6 — April 16, 2026

Fix: Audit columns silently dropped from tables with a same-line index callback

Tables authored with the composite-index shape Drizzle / prettier produces —

export const episodeTags = sqliteTable("episode_tags", {
  id: text("id").primaryKey(),
  episodeId: text("episode_id").notNull(),
  tagId: text("tag_id").notNull(),
}, (t) => [                                  // <-- "}, (t) =>" on one line
  uniqueIndex("episode_tag_unique").on(t.episodeId, t.tagId),
]);

never received audit field injection. A regex in injectAuditFields required a literal \n between the columns-object }, and the (t) => index callback, so same-line closers were silently skipped and the function returned the source unchanged. drizzle-kit then diffed the audit-less schema against the previous snapshot and emitted ALTER TABLE ... DROP COLUMN created_at/modified_at/deleted_at/created_by/modified_by/deleted_by for every affected table. Any project that applied those migrations had production CRUD return 500 because the generated firewall helper still referenced deletedAt.

The regex is now whitespace-flexible and the identifier class accepts any valid JS identifier (t, table, _t, tbl2). A defensive guard throws at compile time if all three terminator patterns fail on a recognizable Drizzle table — no more silent drops.

Recovery path:

  • Most projects: recompile. drizzle-kit will detect the audit columns are missing from the DB snapshot and emit an ADD COLUMN migration to restore them. Apply it.
  • Projects that already hand-patched their schema and ran a manual ADD COLUMN migration (like the podcast-app workaround in commit 98d8758): the next compile will regenerate a matching ADD COLUMN migration that will fail with "duplicate column name" against a DB that already has the columns. Mark the new migration as applied without running it (e.g. INSERT INTO __drizzle_migrations in the features DB) or hand-edit the generated SQL to a no-op, then continue normally.

Fix: firewall.exception generated isNull(<table>.deletedAt) against a missing column

Resources with firewall: { exception: true } always emitted return isNull(<table>.deletedAt); inside buildFirewallConditions(), regardless of whether the table actually had a deletedAt column. Projects that disabled audit injection globally (compiler.features.auditFields: false) or authored a schema without deletedAt ended up with Drizzle resolving <table>.deletedAt to undefined at query time — every GET /api/v1/<resource> returned 500.

The three concerns are now fully independent:

  • firewall.exception: true — only disables tenant scoping (org/user/team WHERE clauses). It does not gate audit fields or soft-delete.
  • Audit fields — always injected unless globally opted out. Unchanged.
  • Soft-delete filter — emitted only when the schema actually has deletedAt or the user explicitly set firewall.softDelete.

In the default configuration (audit injection on), behavior is unchanged: exception resources still filter deletedAt IS NULL. Only the broken case changes — exception resources on a schema without deletedAt now return undefined (no firewall), matching the column reality.

No config changes required — recompile to pick up the fix.


v0.8.4 — April 14, 2026

New generateId: "short" Strategy

Added a compact ID option for tables where short, shareable codes matter more than collision resistance at scale:

providers: {
  database: defineDatabase("cloudflare-d1", {
    generateId: "short",
  }),
}

Generates 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).

When to use: room codes, invite links, share URLs, ephemeral records — anywhere the ID appears in a URL or needs to be human-typeable.

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".

See Providers - ID Generation Options for the full list of strategies.


v0.8.3 — April 14, 2026

Fix: wrangler.toml Route Placement and Binding IDs

Two bugs in the generated wrangler.toml that blocked production deployment without manual edits:

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.

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:

BindingConfig sources checked (in order)
AUTH_DBdatabase.config.authDatabaseId
DBdatabase.config.featuresDatabaseId, database.config.databaseId
FILES_DBfileStorage.config.filesDatabaseId, database.config.filesDatabaseId
KVdatabase.config.kvNamespaceId, storage.config.namespaceId, auth.config.kvId

See Providers - Database Options for the full list of supported ID properties.

Fix: Account SPA Post-Login Redirect on Unified Domain

When 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.

The 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.

No config changes required — recompile to pick up the fix.


v0.8.2 — April 11, 2026

Catch-All VITE_QUICKBACK_URL Environment Variable

Instead of setting four separate URL env vars, you can now point at a single Quickback origin:

# One variable — done.
VITE_QUICKBACK_URL=https://secure.example.com

When set, it's used as a fallback for the individual URLs:

Individual var (explicit)Fallback when unset
VITE_QUICKBACK_API_URL${VITE_QUICKBACK_URL}
VITE_QUICKBACK_ACCOUNT_URL${VITE_QUICKBACK_URL}/account
VITE_QUICKBACK_CMS_URL${VITE_QUICKBACK_URL}/cms

VITE_QUICKBACK_APP_URL stays independent — it points at the tenant's own frontend, not the Quickback server.

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.

See Environment Variables for the full reference.


v0.8.1 — April 9, 2026

Role Hierarchy with + Suffix

You can now define a role hierarchy in your config and use the + suffix in access rules to mean "this role and above":

// quickback.config.ts
auth: {
  roleHierarchy: ['member', 'admin', 'owner'],
}

// In resource definitions
crud: {
  list:   { access: { roles: ["member+"] } },  // member, admin, owner
  create: { access: { roles: ["admin+"] } },    // admin, owner
  delete: { access: { roles: ["owner"] } },      // owner only
}

Roles are expanded at compile time. No + = exact match. See Access - Role Hierarchy for details.


v0.8.0 — April 9, 2026

Breaking: VITE URL Environment Variables Renamed

All Quickback-generated URL environment variables are now namespaced under VITE_QUICKBACK_* to avoid collisions with other Vite projects.

OldNew
VITE_API_URLVITE_QUICKBACK_API_URL
VITE_ACCOUNT_URLVITE_QUICKBACK_ACCOUNT_URL
VITE_APP_URLVITE_QUICKBACK_APP_URL
VITE_CMS_URLVITE_QUICKBACK_CMS_URL

Non-URL variables are unchanged: VITE_ENABLE_* feature flags, branding vars (VITE_APP_NAME, etc.), and VITE_STRIPE_PUBLISHABLE_KEY stay as-is.

Action required:

  • Compiled projects — re-run quickback compile. The compiler generates the new names automatically.
  • Standalone deploys — update .env, .env.production, and wrangler.toml files manually.
  • 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.

See Environment Variables for the full reference.

CMS Access Control

The cms.access config option now enforces access at the CMS level, not just link visibility in Account UI.

cms: { access: "admin" }

When 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.

See CMS Connecting and Compiler Config for details.

SPA Sub-Route Fix

Fixed 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.


v0.5.11 — February 24, 2026

Email OTP & Auth Fixes

  • Fixed email-OTP magic links not working correctly
  • Removed deprecated /internal/validate endpoint — use standard Better Auth session validation instead
  • Auth is now required for all API routes when running locally (previously some routes were unprotected in dev)

Multiple Table Exports Fix

  • Fixed a compiler error when a single file exports multiple Drizzle tables alongside a defineTable() default export
  • The CLI now properly detects and reports this with a clear error message pointing to the fix

Headless Drizzle Rename Hints

  • Added compiler.migrations.renames configuration for CI/CD environments where Drizzle's interactive rename prompts would block compilation
  • Compile errors now include explicit rename key paths and fail fast on malformed rename config keys
  • See Configuration for details

Security Contract Report Artifacts

  • Added generated security contract artifacts to compiler output:
    • reports/security-contracts.report.json
    • reports/security-contracts.report.sig.json
  • Added config-driven signing controls:
    • compiler.securityContracts.report.signature.enabled
    • compiler.securityContracts.report.signature.required
    • compiler.securityContracts.report.signature.key / keyEnv / keyId
  • Missing required signing keys now fail loudly with explicit remediation guidance
  • Added strict config validation for report/signature paths and signing options

Mandatory Unsafe Action Audit Trail

  • Added structured unsafe action config (unsafe: { reason, adminOnly, crossTenant, targetScope })
  • Cross-tenant unsafe actions now require Better Auth authentication plus platform admin role (ctx.userRole === "admin")
  • Added mandatory audit logging for unsafe cross-tenant actions (success, denial, and error paths)
  • Cloudflare output now includes optional AUDIT_DB wiring, drizzle.audit.config.ts, and db:migrate:audit:* scripts when unsafe actions are present
  • Added compile-time raw SQL guard for actions and handlers (allowRawSql: true required per action)

v0.5.10 — February 19, 2026

Compiler Page Parsing & CLI Output

  • Improved compiler page parsing for definePage() definitions
  • Better CLI output formatting during compilation
  • Added apiPath to schema registry for CMS integration

Bug Fixes

  • Fixed hyphenated action names not being properly quoted in generated code (e.g., mark-complete now generates valid JavaScript)
  • API key authentication (x-api-key header) is now handled separately from session tokens (Bearer header)

v0.5.9 — February 16, 2026

CMS Pages & CLI Page Support

  • Added definePage() support for CMS-managed pages
  • Auth middleware improvements for page routes
  • CLI now supports page definitions alongside table and action definitions

v0.5.8 — February 14, 2026

CMS App

  • Introduced the Quickback CMS — a schema-driven admin panel that connects to your generated API
  • CMS namespace added to actions for admin-specific operations
  • Fixed guardaccess naming inconsistency in CMS action definitions

Schema Registry & Firewall Improvements

  • Added schema registry generator — the compiler now outputs a JSON schema registry used by the CMS
  • Firewall error modes: choose between reveal (403 with details) and hide (opaque 404) for security-sensitive deployments

Bug Fixes

  • Fixed anonymous user email format generation
  • Organization selector improvements in Account UI
  • Config validation now catches more errors at compile time
  • Better CRUD error handling with structured error responses
  • Fixed masking to use representative star counts instead of fixed formatting

v0.5.7 — February 12, 2026

Scoped Database for Actions

Actions 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.

The scoped DB uses duck-typed column detection at runtime:

Column DetectedSELECT / UPDATE / DELETEINSERT
organizationIdAdds WHERE organizationId = ?Auto-injects organizationId from context
ownerIdAdds WHERE ownerId = ?Auto-injects ownerId from context
deletedAtAdds WHERE deletedAt IS NULL

This means every action is secure by default — no manual WHERE clauses needed.

defineActions(todos, {
  complete: {
    type: "record",
    execute: async ({ db, ctx, record, input }) => {
      // db is scoped — only sees records in user's org, excludes soft-deleted
      const siblings = await db.select().from(todos);
      // ↑ automatically filtered to ctx.activeOrgId + deletedAt IS NULL
    },
  },
});

Unsafe Mode

Actions that intentionally need to bypass security (admin reports, cross-org queries, migrations) can declare unsafe: true to receive a raw, unscoped database handle:

defineActions(analytics, {
  globalReport: {
    unsafe: true,
    execute: async ({ db, rawDb, ctx, input }) => {
      // db → still scoped (safety net)
      // rawDb → bypasses all security filters
      const allOrgs = await rawDb.select().from(organizations);
    },
  },
});

Without unsafe: true, rawDb is undefined.

Related docs: Actions, Actions API


Cascading Soft Delete

Soft-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.

DELETE /api/v1/projects/:id

Generated behavior:

// 1. Soft delete the parent
await db.update(projects)
  .set({ deletedAt: now, deletedBy: userId })
  .where(eq(projects.id, id));

// 2. Auto-cascade to children (compiler-generated)
await db.update(projectMembers)
  .set({ deletedAt: now, deletedBy: userId })
  .where(eq(projectMembers.projectId, id));

await db.update(projectTasks)
  .set({ deletedAt: now, deletedBy: userId })
  .where(eq(projectTasks.projectId, id));

Rules:

  • Only applies to soft delete (the default). Hard delete relies on database-level ON DELETE CASCADE.
  • Only cascades within the same feature — cross-feature references are not affected.
  • Child tables must have deletedAt / deletedBy columns (auto-added by the compiler's audit fields).

Related docs: Actions API — Cascading Soft Delete


Advanced Query Parameters

New query parameter capabilities for all list endpoints:

  • Field selection?fields=id,name,status returns only the columns you need
  • Multi-sort?sort=status:asc,createdAt:desc sorts by multiple fields
  • Total count?count=true returns total matching records in response headers (X-Total-Count)
  • Full-text search?search=keyword searches across all text columns
# Get only names and statuses, sorted by status then date, with total count
GET /api/v1/todos?fields=id,name,status&sort=status:asc,createdAt:desc&count=true

# Search across all text fields
GET /api/v1/todos?search=urgent

Related docs: Query Parameters


Audit Field Improvements

  • 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
  • All audit fields (createdAt, createdBy, modifiedAt, modifiedBy, deletedAt, deletedBy) are auto-managed

v0.5.6 — February 8, 2026

Database Naming Conventions

  • Default table and column naming changed to snake_case with usePlurals: false
  • Table names derived from generated Better Auth schema for consistency
  • Removed legacy single-database mode — split databases (auth + features) is now the standard

Auth Variable Shadowing Fix

  • Fixed member variable in auth middleware that shadowed the Drizzle member table import
  • Renamed to sessionMember to avoid conflicts in generated routes

v0.5.5 — February 5, 2026

Better Auth Plugins

  • Published @kardoe/better-auth-upgrade-anonymous v1.1.0 — post-passkey email collection flow
  • Published @kardoe/better-auth-combo-auth — combined email + password + OTP authentication
  • Published @kardoe/better-auth-aws-ses — AWS SES email provider for Better Auth

OpenAPI Spec Generation

  • Generated APIs now include a full OpenAPI specification at /openapi.json
  • Better Auth endpoints included in the spec
  • Runtime route: GET /openapi.json

Security Hardening

  • Global error handler prevents leaking internal error details
  • Security headers middleware (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)
  • BETTER_AUTH_SECRET properly passed to generated config

v0.5.4 — January 30, 2026

Account UI

  • Pre-built authentication UI deployed as Cloudflare Workers
  • Features: sessions, organizations, passkeys, passwordless, admin panel, API keys
  • Dual-mode: standalone (degit template) or embedded with Quickback projects

Webhook System

  • Inbound webhook endpoints with signature verification
  • Outbound webhooks via Cloudflare Queues with automatic retries
  • Configurable per-feature webhook events
  • Durable Objects + WebSocket realtime subscriptions
  • Vector embeddings via Cloudflare Vectorize
  • KV and R2 storage integrations

v0.5.0 — January 2026

Initial Release

  • Quickback Compiler — TypeScript-first backend compiler
  • Four Security Pillars — Firewall, Access, Guards, Masking
  • defineTable() — Schema + security configuration in a single file
  • Templates — Cloudflare Workers, Bun standalone, B2B SaaS
  • Cloud Compiler — Remote compilation via compiler.quickback.dev
  • CLIquickback create, quickback compile, quickback init
  • Better Auth Integration — Organizations, roles, sessions
  • Drizzle ORM — Schema-first with automatic migrations
  • Cloudflare D1 — Split database support (auth + features)

On this page

Changelogv0.8.7 — April 17, 2026Security: Hono CVE cluster fixCloudflare Email Sending is now in public betaMinor/patch dep sweepv0.8.5 — April 15, 2026Fix: Better Auth 1.5 and @cloudflare/workers-types CompatibilityCMS Admin Gate Now Enforced Server-SideNew userRole Access PrimitiveFix: ReferenceError: customAlphabet is not defined in generated POST handlersFix: Dangling export const in generated actions.ts breaks tscFix: Files worker database_id reverted to "local-files" on every compilev0.8.6 — April 16, 2026Fix: Audit columns silently dropped from tables with a same-line index callbackFix: firewall.exception generated isNull(<table>.deletedAt) against a missing columnv0.8.4 — April 14, 2026New generateId: "short" Strategyv0.8.3 — April 14, 2026Fix: wrangler.toml Route Placement and Binding IDsFix: Account SPA Post-Login Redirect on Unified Domainv0.8.2 — April 11, 2026Catch-All VITE_QUICKBACK_URL Environment Variablev0.8.1 — April 9, 2026Role Hierarchy with + Suffixv0.8.0 — April 9, 2026Breaking: VITE URL Environment Variables RenamedCMS Access ControlSPA Sub-Route Fixv0.5.11 — February 24, 2026Email OTP & Auth FixesMultiple Table Exports FixHeadless Drizzle Rename HintsSecurity Contract Report ArtifactsMandatory Unsafe Action Audit Trailv0.5.10 — February 19, 2026Compiler Page Parsing & CLI OutputBug Fixesv0.5.9 — February 16, 2026CMS Pages & CLI Page Supportv0.5.8 — February 14, 2026CMS AppSchema Registry & Firewall ImprovementsBug Fixesv0.5.7 — February 12, 2026Scoped Database for ActionsUnsafe ModeCascading Soft DeleteAdvanced Query ParametersAudit Field Improvementsv0.5.6 — February 8, 2026Database Naming ConventionsAuth Variable Shadowing Fixv0.5.5 — February 5, 2026Better Auth PluginsOpenAPI Spec GenerationSecurity Hardeningv0.5.4 — January 30, 2026Account UIWebhook SystemRealtime & Vector Searchv0.5.0 — January 2026Initial Release