Architecture
The modern app/public split, the full project tree, and how a request flows from browser to database and back.
Architecture
CBGenesis follows ColdBox's modern template layout: application code is fully separated from the public webroot, so nothing under app/ is ever directly web-accessible.
Layered overview
flowchart TD
A["Browser Request"] --> B
subgraph B["public/ โ Webroot"]
B1["Application.bx โ entry point, bootstraps ColdBox + ORM"]
B2["index.bxm โ front controller"]
B3["includes/ โ Vite compiled assets"]
end
B --> C
subgraph C["app/ โ Application code (not web-accessible)"]
C1["config/ โ ColdBox, Router, CacheBox, WireBox, Scheduler"]
C2["handlers/ โ Controllers, auth, admin, audit log"]
C3["models/ โ Entities, Services"]
C4["views/ + layouts/ โ BXM templates"]
C5["email_templates/ โ Token-based email bodies"]
C6["interceptors/ โ Audit logging hook"]
end
C --> D
subgraph D["resources/ โ Source assets"]
D1["assets/js/ โ Alpine components + stores"]
D2["assets/scss/ โ Bootstrap + custom SCSS"]
D3["database/ โ Migrations + seeders"]
end
D --> E
subgraph E["lib/ โ Dependencies (not source-controlled)"]
E1["coldbox/, testbox/, modules/ โ qb, cbsecurity, cborm, ..."]
end
Anything an attacker could otherwise browse to directly - handler source, config, view templates - simply doesn't live under the webroot. app/Application.bx is a one-line abort; guard, kept only so the framework convention holds even if a web server is ever misconfigured to serve app/ directly.
Request lifecycle
Every request enters through public/Application.bx, which bootstraps ColdBox before handing off to the router and, eventually, your handler:
sequenceDiagram
Browser->>+public/Application.bx: HTTP Request
public/Application.bx->>+ColdBox Bootstrap: loadColdbox()
ColdBox Bootstrap->>+Main Handler: onRequestStart
Main Handler->>+Router: Match route
Router->>+Target Handler: Dispatch event
Target Handler->>+Service Layer: Business logic
Service Layer->>+ORM / qb: Data access
Target Handler->>+View / Layout: Render response
View / Layout-->>-Browser: HTML + Vite assets
Main.bx (app/handlers/Main.bx) is the implicit-event handler wired up in app/config/Coldbox.bx:
onAppInit- runssettingService.preFlightCheck(), seeding any missing app settings into the databaseonRequestStart- loadsprc.settingsandprc.authUserfor every requestonException- the app-wide exception handler
Interceptors
app/config/Coldbox.bx registers three application interceptors, in this order:
app/interceptors/AuditLogger.bx writes to the audit trail on four interception points:
| Point | Recorded |
|---|---|
postAuthentication | A successful sign-in |
preLogout | A sign-out |
cbSecurity_onInvalidAuthentication | A request that required a session and had none |
cbSecurity_onInvalidAuthorization | An authenticated user missing the required permission |
app/interceptors/RateLimiter.bx fires on preProcess - before routing, before any handler - and throttles five unauthenticated Auth actions (login, register, forgot/reset password, invitation activation) by client IP. See Rate limiting for the settings and how it works.
app/interceptors/SSOAuthorization.bx handles cbSSO's
CBSSOAuthorization interception point. It connects a verified provider
identity to the local user model, enforces login-versus-linking policy,
provisions users when allowed, and creates the cbauth session. See
Single sign-on for the callback flow and
the reason cbGenesis uses a custom handler instead of cbSSO's generic cbAuth
integration.
Add your own to the variables.interceptors array in Coldbox.bx; they fire in declaration order.
Scheduled tasks
app/config/Scheduler.bx registers three daily background tasks, each onOneServer() and withNoOverlaps() so a multi-instance deployment runs each one exactly once:
| Task | Runs | Deletes | Governed by |
|---|---|---|---|
| Purge Expired API Tokens | 03:00 | user_api_tokens rows past their expiration | Token lifetime set at issue time from cbApiTokenMaxValidityMonths (default 12 months) - see App Settings |
| Purge Expired Remember Tokens | 03:15 | user_remember_tokens rows past their expiration | Fixed expiration set when the token is issued (SecurityService/RememberTokenService) |
| Purge Old Audit Logs | 03:30 | audit_logs rows older than the retention window | cbAuditLogRetentionDays (default 90; 0 disables the purge) - see App Settings |
All three call a purgeExpiredTokens()/purgeOlderThan() method on the owning service rather than querying the table directly, so the same purge logic is reachable (and testable) outside the scheduler. Add a new task the same way - see Extending the App.
Full project tree
cbgenesis/
โโโ app/ Application code
โ โโโ Application.bx Abort-only gate (prevents direct /app access)
โ โโโ config/
โ โ โโโ Coldbox.bx Framework settings, environments, logging
โ โ โโโ Router.bx All application routes
โ โ โโโ CacheBox.bx Cache regions (default, template, sessions, rateLimit)
โ โ โโโ WireBox.bx DI container configuration
โ โ โโโ Scheduler.bx Scheduled tasks
โ โ โโโ modules/ Per-module settings (cbsecurity, cbauth, cborm, ...)
โ โโโ handlers/ Controllers (Auth, Dashboard, Users, Roles, ...)
โ โโโ layouts/ Admin, AuthCenter, AuthSplit, Main
โ โโโ models/
โ โ โโโ BaseEntity.bx ORM base: timestamps, soft delete, memento
โ โ โโโ BaseService.bx Service base: cborm + qb + cache + validation
โ โ โโโ security/ Role, Permission, APIToken, RememberToken,
โ โ โ Passkey, UserActionToken, SecurityService, ...
โ โ โโโ system/ User, Setting, AuditLog + their services
โ โโโ views/ BXM templates, one folder per handler
โ โ โโโ _components/ Reusable UI partials (app, auth, ui)
โ โโโ email_templates/ Token-based email body templates
โ โโโ helpers/ ApplicationHelper.bxm โ global view helpers
โ โโโ interceptors/ AuditLogger, RateLimiter, SSOAuthorization
โโโ public/
โ โโโ Application.bx Entry point โ ColdBox + ORM bootstrap
โ โโโ index.bxm Front controller placeholder
โ โโโ includes/ Vite production build output
โโโ resources/
โ โโโ assets/js/ Alpine entry, stores, components
โ โโโ assets/scss/ Bootstrap + custom SCSS
โ โโโ database/
โ โโโ migrations/ Schema migrations (cfmigrations)
โ โโโ seeds/ AdminData seeder
โโโ tests/
โ โโโ specs/integration/ Full HTTP-level specs
โ โโโ specs/unit/ Entity + service specs
โโโ lib/ Dependencies (gitignored, installed by `box install`)
โโโ runtime/ BoxLang engine config (boxlang.json)
โโโ server.json CommandBox server config (engine, webroot, aliases)
โโโ box.json Package manifest โ deps, scripts
โโโ package.json NPM โ Alpine, Bootstrap, Vite, ESLint
โโโ vite.config.mjs Vite + coldbox-vite-plugin
โโโ .env.example Environment template
The stack
| Layer | Technology |
|---|---|
| Runtime | BoxLang 1.0+ (JVM) |
| Framework | ColdBox HMVC (bleeding edge) |
| CLI / Server | CommandBox + BoxLang MiniServer |
| Dependency Injection | WireBox |
| Security | cbsecurity + cbauth (session-based + JWT) |
| Database | MySQL, MariaDB, PostgreSQL, and MSSQL via Hibernate ORM (cborm); all four database targets are supported and covered by the project's database testing workflow |
| Query Builder | qb (fluent SQL) |
| Migrations | cfmigrations |
| Validation | cbvalidation |
| cbmailservices | |
| Serialization | mementifier |
| Frontend | Bootstrap 5.3 ยท Alpine.js 3.x ยท Vite 6 |
| Icons | Phosphor Duotone |
| Tooltips | Tippy.js |