Rapid development platform built by AI

The brief: take the fastest way to build a web application and bring it completely up to date. The answer is an opinionated platform pairing CFML, which humans can easily understand with Rust, which agents verify and machines run at lightning speed with minimal footprint.

Everything a modern application needs is already included. Less to install and for an agent to get wrong.

MIT License Linux · macOS · Windows · Docker · WebAssembly
One file

Every layer above is already inside the binary you download. Extend capabilities with native Rust or .rcx extensions

2–3.5× throughput vs Lucee 7
~60 MB memory under load
instant startup

5 ways to deploy including Cloudflare edge

The WASM target has its own limits: databases go through Hyperdrive, and some of the standard library is unavailable.

A second CFML runtime, written by AI

Lucee is the open-source CFML engine most people run, and it is the reference here: every test has to pass on Lucee first. BoxLang went the other way, onto the JVM. This is the CFML you already write with no virtual machine under it, in one binary that starts instantly and holds around 60 MB under load. Where it runs short you write the missing piece in Rust: an .rcx file dropped in a directory arrives as an ordinary function, or compiles into the binary itself.

Almost all of it was written by AI, which works better than it sounds. Rust will not compile the kind of code a generator gets wrong, and CFML is small enough to have one obvious way to do most things. Between them an agent gets to a right answer and stays there. You are reading a page it served.

How it was built

A component is an MCP server

One CFC under /mcp/ is a server that Claude Code, or any other agent runtime, can discover and call. It is served from the same process and port as your pages, or launched over stdio as a subprocess. No sidecar, no SDK, no second runtime to keep alive.

Annotate a method with tool= and the JSON Schema is read off the CFML signature: types, defaults, which arguments are genuinely required, and descriptions from the doc comment. Results are shaped on the way out too, so a query arrives as an array of row objects rather than the {COLUMNS, DATA} envelope no MCP client understands.

Resources, prompts, progress and logging over SSE are all there, and secured gates a tool on the same contract that gates a WebSocket handler. A tool can also call back: mcp().sample() borrows the client's own model, so no API key of yours is involved. In the other direction, mcpConnect() makes anyone else's MCP server callable from a page.

Tools, resources and the client

mcp/crm.cfc
// One CFC under /mcp/ is one server. There is no other configuration.
component mcp="crm" description="Deal lookups for agents" {

    /** @region.description Sales region to report on */
    function topDeals( required string region, numeric limit = 10 )
        tool        = "top_deals"
        description = "Highest value open deals in a region"
        readOnly    = true
    {
        return queryExecute(
              "select name, value from deals where region = :region limit :limit"
            , { region = arguments.region, limit = arguments.limit }
        );
    }
}
$ rustcfml --serve ./webroot   # http://localhost:8500/mcp/crm
$ rustcfml mcp crm ./webroot   # …or over stdio, as a subprocess

Realtime on the same port as your pages

No Node sidecar, no separate container, no broker to run. A component with a socket attribute is a channel, and the lifecycle methods are the whole API: rooms, presence, auth at the handshake, lastEventId resumability and multi-node fan-out. A return value from onMessage becomes the client's ack.

Both transports are served: raw WebSocket and socket.io, so an existing socket.io client connects without changes. Underneath it, cfthread runs on real OS threads rather than a green-thread pool.

Channels, rooms and fan-out

websockets/chat.cfc
// The whole channel. There is no other configuration.
component socket="/chat" encoding="json" {

    function onConnect( socket ) {
        socket.join( "lobby" );
        socket.emit( "welcome", { id = socket.id() } );
    }

    function onMessage( socket, message ) {
        io().to( "lobby" ).emit( "message", {
              from = socket.id()
            , text = message.text
        } );
        return { delivered = true };   // the client's ack
    }
}

The Java you forgot you depended on

Applications reach into Java more than their authors remember: a StringBuilder in a loop, a ConcurrentHashMap behind a cache. There is no JVM here to answer them, so over a hundred classes are shimmed instead. There are enough of them that CFWheels configures itself from an adapter it ships upstream, and Preside runs with no application changes.

Every shimmed class, and where they stop

legacy.cfm
// The Java already in your codebase, unchanged.
sb = createObject( "java", "java.lang.StringBuilder" ).init();

for ( row in leads ) {
    sb.append( row.region ).append( ", " );
}

id = createObject( "java", "java.util.UUID" )
         .randomUUID().toString();

// A real pool, from the library you already know.
pool = createObject( "java"
     , "java.util.concurrent.Executors" )
         .newFixedThreadPool( 4 );

Drop to Rust for the hot path. Keep writing CFML.

Every high-level language needs an escape hatch, and the quality of that hatch decides how far the language gets. Declare your functions in one macro, build, and drop an .rcx file in a directory: a stock binary loads it at start-up and they arrive as ordinary built-ins.

Writing an extension

src/lib.rs
// One macro is the whole declaration.
module! {
    name: "match",
    version: "0.1.0",
    bifs: { "hammingDistance" => hamming },
}

fn hamming<'a>( ctx: &'a Ctx, args: &[ Value<'a> ] )
    -> Result<Value<'a>> { /* … */ }
$ rustcfml ext build .
  ✓ match-0.1.0-macos-aarch64.rcx

// match.cfm: now it is just a function.
score = hammingDistance( left, right )

You can see inside it in production

Four layers of tooling, every one of them off until you ask, and costing nothing at all while it is off.

Debugging & observability

The classic debug footer is still there: queries, template times, exceptions and scopes, gated four ways so it never leaks to the public. Above it sits a threshold-gated sampling profiler in the FusionReactor mould, which only starts snapshotting a request's CFML call stack once that request has already run too long. A request that stays under the threshold costs one relaxed atomic load per line.

Above that, OpenTelemetry traces over OTLP and Prometheus RED metrics, so a slow request in production lands in Tempo, Jaeger, Honeycomb or Datadog like anything else on your estate. Export runs on a background batch thread and never sits on the request path.

The footer and the profiler are in every build. Traces, metrics and the --profile flamegraph come from a build with the obs-otel and obs-pprof features, and neither is available in the WebAssembly target.

One request, decomposed into spans
GET /api/deals 42.1ms
render api/deals.cfm 39.6ms
DealReport.valueByRegion 32.0ms
SELECT crm.leads 20.2ms
SELECT crm.regions 7.6ms
command
# RED metrics, scraped straight off the engine.
$ curl -s localhost:8500/__rustcfml/metrics | head -3
rustcfml_http_requests_total{route="/api/deals"} 12841
rustcfml_http_request_duration_seconds_bucket{le="0.05"} 12604
rustcfml_db_query_duration_seconds_sum 41.907

Run a page in the next two minutes

No trial, no account, no administrator to log into. Point one flag at a directory of templates and it serves them.