Quickback Docs

Output Structure

Understanding the files and directories generated by the Quickback compiler.

The compiler generates a complete project structure based on your definitions and provider configuration. Cloudflare Workers is the only supported runtime; what varies is which optional surfaces your config enables.

Warning: Never edit files in src/ directly. They are overwritten on every compile. Make changes in your quickback/ definitions instead.

Output

src/
├── index.ts                    # Hono app entry point (Workers export)
├── env.d.ts                    # Cloudflare bindings TypeScript types
├── db/
│   ├── index.ts                # Database connection factory
│   ├── auth-schema.ts          # Auth table schemas (dual mode)
│   └── features-schema.ts     # Feature table schemas (dual mode)
├── auth/
│   └── schema.ts               # Better Auth table schemas (generated by the Better Auth CLI)
├── features/
│   └── {feature}/
│       ├── schema.ts                    # Drizzle table definition(s)
│       ├── {feature}.ts                 # Table alias — re-exports the table from schema.ts
│       ├── {feature}.resource.ts        # Firewall / access / masking helpers for this resource
│       ├── {feature}.routes.ts          # CRUD + record-bound action endpoints
│       ├── {feature}-actions.routes.ts  # Standalone action endpoints (when an action sets `path:`)
│       ├── actions.ts                   # Aggregator — imports every action's default export
│       ├── actions/
│       │   └── {action}.ts              # One file per authored action
│       └── .quickback/
│           └── define-action.ts         # Typed per-feature `defineAction` helper
├── routes/
│   └── {feature}.ts            # Mount re-export consumed by src/index.ts
├── lib/
│   ├── auth.ts                 # Better Auth instance & config
│   ├── access.ts               # Access control helpers
│   ├── types.ts                # Runtime type definitions
│   ├── masks.ts                # Field masking utilities
│   ├── services.ts             # Service layer
│   ├── audit-wrapper.ts        # Stamps the managed audit columns on write
│   └── security-audit.ts       # Unsafe cross-tenant audit logger (when needed)
└── middleware/
    ├── auth.ts                 # Auth context middleware
    ├── db.ts                   # Database instance middleware
    └── services.ts             # Service injection middleware

quickback/drizzle/
├── auth/                       # Auth migrations (dual mode)
│   ├── meta/
│   │   ├── _journal.json
│   │   └── 0000_snapshot.json
│   └── 0000_initial.sql
├── features/                   # Feature migrations (dual mode)
│   ├── meta/
│   │   ├── _journal.json
│   │   └── 0000_snapshot.json
│   └── 0000_initial.sql
└── audit/                      # Unsafe cross-tenant action audit migrations (when needed)
    ├── meta/
    └── 0000_initial.sql

# Root config files
├── package.json
├── tsconfig.json
├── wrangler.toml               # Cloudflare Workers config
├── drizzle.config.ts           # Features DB drizzle config
├── drizzle.auth.config.ts      # Auth DB drizzle config (dual mode)
└── drizzle.audit.config.ts     # Audit DB drizzle config (when unsafe actions exist)

Not every per-feature file is always emitted:

  • actions.ts, actions/, and .quickback/define-action.ts appear only when the feature has at least one authored action under quickback/features/{feature}/actions/.
  • {feature}-actions.routes.ts appears only when at least one of those actions declares path: (the marker that makes an action standalone).
  • schema.ts, {feature}.ts, and {feature}.resource.ts are skipped for tableless features.
  • A multi-table feature gets one alias file per table ({feature}.ts, {other_table}.ts) alongside the single schema.ts.

Generated Package Dependency Security

For projects using Better Auth, the generated package.json keeps the stable auth@1.6.x development dependency that provides the better-auth executable used by auth:schema. It also emits this npm override:

{
  "overrides": {
    "lodash": "4.18.1"
  }
}

The override lifts the CLI's dev-only Prisma schema-parser chain off Lodash versions affected by the 2026 code-injection and prototype-pollution advisories. It does not change the Better Auth runtime package or generated authentication behavior. Quickback preserves unrelated overrides already in your package manifest, but the compiler-owned Lodash security pin wins if an existing override conflicts.

After recompiling, run your package manager's install command so its lockfile records the patched transitive version. Use npm audit --omit=dev when evaluating the production dependency tree; development-tool advisories are a separate review surface.

For Better Auth projects, quickback compile owns auth-schema generation. The generated build script runs TypeScript only and never rewrites src/auth/schema.ts, so build and deployment preflight checks preserve a clean generated worktree. auth:schema remains an explicit diagnostic command; run quickback compile afterward to restore canonical generated bytes.

On recompilation, Quickback replaces commands for script names it generates, including build, with the current compiler output. Scripts with other names are treated as project customizations and preserved. This lets existing projects acquire build and deployment fixes without losing unrelated tooling.

Optional Output Files

These files are generated only when the corresponding features are configured:

Embeddings

When any feature has embeddings configured:

src/
├── lib/
│   └── embeddings.ts           # Embedding helpers
├── routes/
│   └── embeddings.ts           # POST /api/{v1|v2}/embeddings endpoint
└── queue-consumer.ts           # Queue handler for async embedding jobs

File Storage (R2)

When fileStorage is configured:

src/
└── routes/
    └── storage.ts              # File upload/download endpoints
quickback/drizzle/
└── files/                      # File metadata migrations
    ├── meta/
    └── 0000_*.sql

Webhooks

When webhooks are enabled:

src/
└── lib/
    └── webhooks/
        ├── index.ts            # Webhook module entry
        ├── sign.ts             # Webhook payload signing
        ├── handlers.ts         # Handler registry
        ├── emit.ts             # Queue emission helpers
        ├── routes.ts           # Inbound/outbound endpoints
        └── providers/
            ├── index.ts
            └── stripe.ts       # Stripe webhook handler
quickback/drizzle/
└── webhooks/                   # Webhook schema migrations
    ├── meta/
    └── 0000_*.sql

Security Audit Database (Unsafe Actions)

When any action enables unsafe cross-tenant mode (unsafe.crossTenant: true):

src/
├── db/
│   └── audit-schema.ts         # audit_events table
└── lib/
    └── security-audit.ts       # mandatory audit writer

quickback/drizzle/
└── audit/
    ├── meta/
    └── 0000_*.sql

# Root
└── drizzle.audit.config.ts

Cloudflare output also includes an AUDIT_DB D1 binding in wrangler.toml and migration scripts:

  • db:migrate:audit:local
  • db:migrate:audit:remote

Security Contract Report and Signature

Generated on every compile (unless disabled via compiler.securityContracts.report.enabled: false):

reports/
├── security-contracts.report.json      # Contract evaluation summary + violations
└── security-contracts.report.sig.json  # Signature / digest envelope for the report

The signature file uses HMAC-SHA256 when a signing key is configured, otherwise it falls back to SHA-256 digest mode.
Set compiler.securityContracts.report.signature.required: true to fail compilation when a signing key is missing.

Realtime

When any feature has realtime configured:

src/
├── lib/
│   ├── realtime.ts             # Broadcast helpers
│   ├── Broadcaster.ts          # Durable Object class (inline in main worker)
│   └── ws-ticket.ts            # WebSocket ticket auth utility
└── routes/
    └── ws-ticket.ts            # POST /realtime/v1/ws-ticket endpoint

The Broadcaster DO class is exported from your main worker entry point and runs inline — no separate cloudflare-workers/broadcast/ deployment needed.

Device Authorization

When the deviceAuthorization plugin is enabled:

src/
└── routes/
    └── cli-auth.ts             # Device auth flow endpoints

Database Schemas

Dual Database Mode (D1 default)

The compiler separates schemas into two files:

src/db/auth-schema.ts — Re-exports Better Auth table schemas:

  • users, sessions, accounts
  • organizations, members, invitations (if organizations enabled)
  • Plugin-specific tables (apiKeys, etc.)

src/db/features-schema.ts — Re-exports your feature schemas:

  • Every table declared with feature() or defineTable()
  • The managed audit / soft-delete columns you declared on each table — ...q.audit() / ...q.softDelete() on the q path, the canonical Drizzle lines on the raw-Drizzle path

Single Database Mode (splitDatabases: false)

src/db/schema.ts — Combined re-export of all schemas (auth + features).

Generated Routes

For each feature, the compiler generates a routes file at src/features/{name}/routes.ts containing:

RouteGenerated When
GET /crud.list configured
GET /:idcrud.get configured
POST /crud.create configured
PATCH /:idcrud.update configured
DELETE /:idcrud.delete configured
PUT /:idcrud.put configured
POST /batchcrud.create exists (auto-enabled)
PATCH /batchcrud.update exists (auto-enabled)
DELETE /batchcrud.delete exists (auto-enabled)
PUT /batchcrud.put exists (auto-enabled)
GET /views/{name}views configured
POST /:id/{action}Record-based actions defined
POST /{action}Standalone actions defined

Routes are mounted under /api/v1/{feature} for the default contract or /api/v2/{feature} when contract: { version: "v2" } is selected.

Migrations

The compiler runs the project-local drizzle-kit generate binary during compilation to produce SQL migration files. On subsequent compiles, it uses existingFiles (your current migration state) to generate only incremental changes.

If a compiler response returns migration generation as a post-compile command, the command matches build.packageManager: npm exec, pnpm exec, yarn exec, or bun run. These forms resolve the binary from the generated project's dependencies. The npm form runs offline and declines npm's missing package install prompt, so Quickback never downloads an unpinned Drizzle CLI as a fallback. If the local binary is missing, install the generated project's dependencies and compile again.

The CLI loads migration meta JSON from one location only — <project>/quickback/drizzle/:

  • quickback/drizzle/auth/meta/
  • quickback/drizzle/features/meta/
  • quickback/drizzle/files/meta/
  • quickback/drizzle/webhooks/meta/
  • quickback/drizzle/audit/meta/
  • quickback/drizzle/meta/ (single-database mode)

Quickback owns this state — nothing under a project-root drizzle/ folder is ever read or written.

Migration and report artifacts are written to the quickback/ state directory:

  • quickback/drizzle/... for Drizzle migration SQL/meta artifacts
  • quickback/reports/... for security contract report artifacts

Migration history is never wiped

Unlike src/, the quickback/drizzle/ tree is not hermetic — it is history, and history is only ever appended to or superseded.

To keep compile payloads small, the CLI uploads every migration .sql and _journal.json but only the newest NNNN_snapshot.json per meta directory. The compiler's response is therefore a partial view by construction, and a file missing from it carries no instruction. Compile removes a file only when the compiler was actually shown it and left it out; anything never uploaded — every historical snapshot — is preserved untouched.

When something is removed, the previous tree is archived to quickback/.archives/drizzle-<timestamp>/ (the three most recent are kept) and the compile prints what it removed and what it preserved.

If quickback migrations doctor reports snapshot drift — more journal entries than snapshot files — that drift came from your project's history, not from compiling. Compile does not delete snapshots it was never given.

For non-interactive environments (cloud compile, CI), table/column renames must be declared with compiler.migrations.renames in quickback.config.ts. This avoids interactive rename prompts during migration generation.

Migration files follow the Drizzle Kit naming convention:

0000_initial.sql
0001_add_status_column.sql
0002_create_orders_table.sql

CMS and Account UI Assets

When cms and/or account are enabled, the compiler builds the SPAs from source at compile time and includes the assets in the output.

Each SPA is placed in its own subdirectory under src/apps/:

src/apps/                          # Root assets directory
├── cms/                           # CMS SPA (served at /cms/ on unified domain)
│   ├── index.html
│   └── assets/
│       ├── app.abc123.js         # Content-hashed filenames from Vite
│       └── app.xyz456.css
└── account/                      # Account SPA (served at /account/ on unified domain)
    ├── index.html
    └── assets/
        ├── app.def789.js
        └── app.ghi012.css

On custom domains, each SPA is served at root (/) via hostname-based routing. On the unified domain, CMS is at /cms/ and Account is at /account/.

Static Assets (quickback/public/)

Anything you drop under quickback/public/ is copied verbatim into src/apps/ on every compile. The compiler does not parse, template, or otherwise touch these files — they are bytes that ride past it into the Cloudflare ASSETS binding.

quickback/public/                  # Source — checked into your repo
├── favicon.ico
├── robots.txt
├── og/
│   └── og-image.png
└── pdfs/
    └── whitepaper.pdf

After compile:

src/apps/                          # Destination — written by the CLI
├── favicon.ico                    # Served at https://your-domain/favicon.ico
├── robots.txt                     # Served at /robots.txt
├── og/og-image.png                # Served at /og/og-image.png
├── pdfs/whitepaper.pdf
├── cms/                           # Compiler-emitted SPA (don't shadow)
└── account/                       # Compiler-emitted SPA (don't shadow)

Notes:

  • The folder is optional. If it doesn't exist, this step is a no-op.
  • Files removed from quickback/public/ disappear from src/apps/ on the next compile — src/ is wiped and rewritten each run.
  • Hidden entries (.DS_Store, .git/, dotfiles) are skipped.
  • A file in quickback/public/ that would overwrite a compiler-emitted path (e.g. quickback/public/cms/index.html) fails the compile with a clear error rather than silently shadowing the SPA shell.
  • Binaries (images, fonts, PDFs) are copied byte-for-byte — there is no string round-trip.

Source Apps (quickback/apps/)

For hand-authored TypeScript apps and prebuilt SPA bundles that live alongside the compiler-emitted SPA shells, drop them under quickback/apps/<name>/. The CLI copies the tree into src/apps/<name>/ on every compile, byte-for-byte, after the compiler has finished writing. Bind a directory to a hostname via config.apps — the compiler emits the matching Worker middleware automatically.

quickback/apps/                    # Source — checked into your repo
├── m/                             # A hand-authored mobile companion app
│   ├── index.tsx
│   ├── components/Button.tsx
│   └── lib/utils.ts
└── admin/                         # A hand-authored admin tool
    └── index.tsx

After compile:

src/apps/
├── m/index.tsx                    # Hand-authored — preserved across compiles
├── m/components/Button.tsx
├── m/lib/utils.ts
├── admin/index.tsx
├── cms/                           # Compiler-emitted SPA (don't shadow)
└── account/                       # Compiler-emitted SPA (don't shadow)

When to choose apps/ vs public/:

quickback/public/quickback/apps/
Use forStatic binaries (logos, fonts, OG images, PDFs, robots.txt)Hand-authored TS/TSX/JS app source
SkipsHidden entries only (.DS_Store, .git/)Hidden entries + node_modules/, dist/, build/, .next/, .turbo/, .cache/
Folder layoutFiles at any depth — flat or nestedApps under named subfolders (m/, admin/, …)

Notes:

  • The folder is optional. If it doesn't exist, this step is a no-op.
  • Files removed from quickback/apps/<name>/ disappear from src/apps/<name>/ on the next compile — src/ is wiped and rewritten each run.
  • A file in quickback/apps/ that would overwrite a compiler-emitted path (e.g. quickback/apps/cms/index.tsx while cms: true) fails the compile with a clear error.
  • Migrating from hand-edited src/apps/<name>/: move the entire folder under quickback/apps/<name>/ once, then trust the compile cycle. The hermetic src/ contract holds again.

Generated Wrangler Assets Config

The compiler always generates run_worker_first = true when SPAs are enabled — the Worker handles all SPA routing (per hostname and per path prefix):

[assets]
binding = "ASSETS"
directory = "src/apps"
not_found_handling = "none"
run_worker_first = true

See Multi-Domain Architecture for details on hostname routing.

See Also

On this page