What's New With 8.2.0
September 22, 2026
ColdBox 8.2.0 is a feature release focused on request-level composition and control: route-scoped middleware, standards-based HTTP caching, first-class Server-Sent Events, and conversational context for AI routing — plus a security hardening fix, a handful of correctness fixes, and hot-path performance work in the Renderer and routing layers.
Major Highlights
🧵 Route-Scoped Middleware — .middleware()
Routes can now carry their own middleware chain instead of relying solely on app-wide interceptors. .middleware() attaches a closure, a WireBox ID, or any object with a method named after the interception point (preProcess by default, or postProcess) to a single route — reusing the same dispatch mechanism ColdBox interceptors already use, just scoped to one route.
route( "/admin/:action" )
.middleware( ( event, rc, prc ) => {
if ( !auth.isLoggedIn() ) {
event.relocate( "login" );
return true; // short-circuits the rest of this route's middleware
}
} )
.toHandler( "admin" );Two companion features round this out:
middlewareGroup( name, [ ...targets ] )— register a named, reusable bundle once, then reference it by name from.middleware()or agroup()'smiddlewareoption, instead of repeating the same target list everywhere..withoutMiddleware( target )— opt a single route out of middleware it would otherwise inherit, by target name, by the group name it expanded from, or"*"for everything.
middlewareGroup( "api", [ "RequireApiKey", "RateLimiter" ] );
group( { pattern : "/api", middleware : [ "api" ] }, () => {
route( "/users" ).toHandler( "users" ); // runs "api"
route( "/health" ).withoutMiddleware( "api" ).toHandler( "health" ); // opts out
} );🗄️ HTTP Caching Primitives — ETag, Last-Modified, Cache-Control
Standards-based conditional-GET support, usable from any handler:
event.etag()/event.lastModified()— set the corresponding header and short-circuit with a304 Not Modifiedon a match. Never short-circuits an unsafe HTTP method.event.cacheControl()— build aCache-Controlheader from a directives struct.Response.withETag()/Response.withCacheControl()— the same primitives, as fluent methods on the RESTResponseobject.An already
cache="true"event handler action can opt into an automatically computedETag/Last-Modifiedwith newetag/etagWeak/lastModified/cacheControlannotations, piggybacking on event caching with no extra per-request work.
See HTTP Caching.
📡 First-Class Server-Sent Events (BoxLang)
Streaming is now a first-class concern instead of something bolted onto AI routing. event.sse() takes over the response and hands your callback an SSEEmitter with send(), sendView(), sendData(), sendError(), keep-alives, and graceful disconnect handling:
For routes that always stream, Router.toSSE() mirrors toResponse(). Three new interception points (preSSEConnection, postSSEConnection, onSSEError) let you reject a connection before it opens, observe stream completion, or react to mid-stream errors, and a new this.sse settings block controls keep-alive interval, reconnect hints, and CORS defaults.
See Server-Sent Events and Streaming Routes (SSE).
🤖 AI Routing Gets Conversational Context
toAi()'s invoke, stream, and batch sub-routes now resolve userId, conversationId, and threadId from the request body and thread them through to the runnable via options:
userIddefaults to the framework's own session/request tracking identifier when not suppliedconversationIdis passed through only if supplied - no default is inventedthreadIdis generated if not supplied, and is always echoed back - in the JSON response, anX-Thread-Idheader, and a leadingevent: threadSSE frame on/stream(since browserEventSourceclients can't read response headers)
This release also corrects the toAi() reference documentation, which had drifted from the actual run()/stream()-based IAiRunnable interface and request/response shapes. See AI Routing.
🚪 AI Gateway Routing — toAiGateway()
A third AI terminator joins toAi() and toMCP(), covering the direction they don't: a platform talking to your agent, on the platform's terms. One declaration mounts everything a BoxLang AI Gateway needs over HTTP.
POST
{pattern}[/:gateway]/events
An inbound platform event
GET
{pattern}[/:gateway]/events
The platform's URL verification handshake
GET
{pattern}/interactions/:requestID
Poll a pending human-in-the-loop approval
POST
{pattern}/interactions/:requestID/decisions
Submit a human's decision
GET
{pattern}/info
What this mount serves
GETandPOSTshare/eventson purpose: a platform is given one URL and verifies it with aGETbefore it willPOSTto it.Pin a mount with
toAiGateway( "slack" ), or leave the name out and one mount serves every gateway registered inaiGatewayRegistry(). A pinned name always wins over the URL placeholder.Pass a
sessionand every inbound message is dispatched as an agent turn and acked202immediately, without waiting on the turn - a platform webhook times out in seconds, an agent turn does not. The thread each message landed on comes back in the response (and asX-Thread-Id) so you can correlate the reply. Without a session, events are verified and parsed only.Signatures are verified by the gateway itself before anything is parsed or dispatched.
BoxLang + bx-ai only. See AI Gateway Routing.
🔒 Hardened HTTP Method Spoofing
The _method form-field override (GET/POST browsers use to fake PUT/PATCH/DELETE) is now only honored when the original transport-level request is a POST. Previously, a plain GET request carrying ?_method=DELETE was silently treated as a DELETE — enabling CSRF-style attacks via a link, an <img> tag, a crawler, or a browser prefetch. A new event.getOriginalHTTPMethod() returns the raw, un-spoofed verb when you need it. See HTTP Method Spoofing.
🗄️ Route-Level Cache Rules — Router.withCache()
A route-scoped alternative to handler cache="true" annotations, so caching can be declared where the URL is declared instead of buried on the handler action. .withCache() accepts the same knobs as the handler annotations — timeout, provider, suffix, include/exclude/filter — plus the Tier 1 HTTP caching primitives (etag, etagWeak, lastModified, cacheControl). A route that opts in takes full precedence over that event's handler-level cache annotations; routes that don't opt in fall through unchanged.
See Router.withCache().
⏰ Scheduler Timing Fixes
Two related scheduler bugs are fixed:
A task combining
every()withstartOnTime()/between()now waits for the next aligned period boundary instead of firing immediately on registration (or every server/container restart).A task combining
withNoOverlaps()withbetween()/startOnTime()no longer misreads itsspacedDelayunit — a1-minute task no longer re-fires every second.
🌐 Full BoxLang Null-Runtime Support
ColdBox now runs cleanly under BoxLang with enableNullSupport enabled, closing out the framework-wide gaps in null handling this setting exposes.
🧭 Routing & Annotation Fixes
Fluent routes now correctly preserve their domain when grouped or resolved through nested routes.
Handler metadata annotations nested under other annotations are now read correctly on BoxLang.
⚡ Renderer & Routing Performance
A new
viewDiscoveryCachingsetting (on by default, independent ofviewCaching) caches the filesystem work that locates view/layout files, benefiting every render regardless of whether view output caching is enabled. See View Discovery Caching.Several hot-path costs eliminated in
HandlerService,RoutingService, andRouter— a per-request settings lookup that should have been cached at configuration time, a redundant filesystem check in view dispatch detection, and route response placeholders now pre-parsed once at registration instead of re-parsed by regex on every matching request.The interceptor chain now reports short-circuiting:
announce()returnstrueon the synchronous path when an interceptor short-circuited the chain,falseotherwise - previously this was undetectable. See Announcing Interceptions.
Release Notes
New Features
COLDBOX-1415 HTTP caching primitives - ETag, Last-Modified, Cache-Control
COLDBOX-1416 Route-scoped middleware via existing interceptor points
COLDBOX-1417 Conversational context (userId/conversationId/threadId) on toAi() routes
First-class Server-Sent Events streaming - event.sse(), SSEEmitter, Router.toSSE(), interception points, this.sse settings (#676)
AI Gateway routing - Router.toAiGateway() mounts a BoxLang AI Gateway's inbound events, verification handshake, and human-in-the-loop approval endpoints (COLDBOX-1435, #694)
COLDBOX-1418 Route-level cache rules via Router.withCache()
Improvements
COLDBOX-1406 HTTP method spoofing restricted to POST requests only; new getOriginalHTTPMethod()
New viewDiscoveryCaching setting plus Renderer hot-path caching for view/layout discovery (#664)
Hot-path performance optimizations in HandlerService, RoutingService, and Router (#665)
COLDBOX-1447 Interceptor chain now reports and honors short-circuiting via announce()'s return value; new getRouteDefinitionKeys() route-table introspection helper (#677)
COLDBOX-1440 Full BoxLang null-runtime support (enableNullSupport)
COLDBOX-1389 New body argument on test methods for mocking incoming request bodies
COLDBOX-1399 Scheduled-task stack traces are now logged when a task fails to load
COLDBOX-1403 More defensive bootstrap and session-start handling
COLDBOX-1409 Response.setData() gains message and location arguments
COLDBOX-1412 Response object fluent builders and utility methods
COLDBOX-1396 Scheduler and task-registration performance overhaul
isAjax now detects Fetch metadata, not just X-Requested-With (#662)
COLDBOX-1397 Global performance optimizations across startup and interception hot paths (in addition to PRs #664/#665)
Bugs
COLDBOX-1407 url.results collision breaking cache.getOrSet() on any page loaded with ?results=...
COLDBOX-1411 this.EVENT_CACHE_SUFFIX closures were evaluated once and frozen instead of per-request, letting different requests share a cache key
COLDBOX-1421 Scheduled tasks using every() + startOnTime()/between() fired immediately instead of aligning to the next period boundary
COLDBOX-1434 ScheduledTask combining withNoOverlaps() with a daily start time misread the spacedDelay unit, causing rapid re-fire
COLDBOX-1438 Fluent route domain grouping/nesting not preserved
COLDBOX-1439 Nested handler annotations not read correctly
COLDBOX-1446 addRoute() missing ai/aiRunnable/mcp/mcpServer parameters
COLDBOX-1448 DataMarshaller component made thread-safe
COLDBOX-1449 appHash property missing default value
Fixed a startup typo in Bootstrap.cfc (#667)
COLDBOX-1390 Whoops zero-position Right() error when normalizing file content for error displays
Whoops source panel indentation fix (#661)
COLDBOX-1392 Regression when using Application Bootstrap via inheritance (app hash included app name before the application existed)
COLDBOX-1398 contentType() null guard during event caching on some servlet deployments
COLDBOX-1401 Scheduled task missing getTimezon() getter (BIF collision)
COLDBOX-1402 Scheduled task delay() not honored
COLDBOX-1408 BoxLang prime issue when storing handler metadata
Renderer.renderLayout LayoutNotFoundException on empty layout (regression vs 7.x) (#657)
Module convention routing no longer applies the optional-action fallback to mandatory-action routes (#695)
Scheduler onOneServer() lock sized to the task's real cadence instead of the daily poll (#696)
Wrong ForgeBox slug in the BoxLang AI module error message (#656)
ObjectPopulator (ORM) and SchemaInfo null-prime/null-cast fixes for BoxLang runtime
Tasks
COLDBOX-1450 Deprecation markers for legacy tools and Adobe 2023 static scope ahead of v9
Bugs
COLDBOX-1420 Explicit WireBox mapping was deleted when its first metadata lookup failed
Bugs
COLDBOX-1422 BoxLangProvider did not convert CacheBox's minute-based timeouts, causing objects to expire 60x too soon
Bugs
COLDBOX-1400 Root appender could be called before LogBox finished initializing during loading
Last updated
Was this helpful?