Make dynamic websites like it's 2006.
UCE is a direct web runtime: files are pages, templates live next to control flow, nginx fronts the whole thing, and a request can compile into a wasm module on first hit. It is built in the image of PHP's non-architecture, but with an explicit request object, component rendering, and a built-in WebSocket broker.
The old web had the right instincts.
Put code where the page lives. Let runtime state die with the request. Keep deployment boring. Treat server-rendered HTML as the default, and add richer behavior only where it pays for itself.
Inline markup without framework tax
UCE templates live directly inside handler code with escaped and raw output modes, so rendering stays local to the request logic that owns it.
Components and sub-rendering
Components are just .uce files. Props flow through context.props. Full pages can call other units without inventing a second application runtime.
FastCGI for pages, HTTP for sockets
Normal page renders stay on the FastCGI socket. WebSocket upgrades for .uce endpoints are proxied to the built-in HTTP listener.
nginx in front, systemd behind
Static files stay static, nginx does the edge work, and the runtime handles compilation, request execution, and socket fan-out.
It is not trying to save you from the web.
What it leans into
Files on disk, explicit request handlers, template tags, reusable partials, request-scoped state, and operationally plain deployment.
What it resists
Mandatory client-side hydration, giant application shells, hidden routing layers, and pretending every website is a desktop app in a trench coat.
What it adds
Built-in components, markdown rendering, current request data in one place, and a broker-backed WebSocket story that stays inside the same runtime.
Common things in UCE
The examples below are modeled on the current runtime API and the published demo pages. The point is not abstraction depth. The point is that the code stays obvious.
A page with request data
Drop from C++ into markup, escape output by default, and keep request handling next to the HTML it controls.
RENDER(Request& context)
{
String name = first(context.get["name"].to_string(), "guest");
<>
<h1>Hello, <?= name ?>.</h1>
<p>Rendered at <?= time_format_utc("%F %T") ?></p>
</>
}
POST + session flash
Old-school dynamic websites still matter. UCE keeps form handling and page rendering in one file when that is the simplest thing.
RENDER(Request& context)
{
if(context.post.has("email"))
context.session["flash"] = "Saved " + context.post["email"].to_string();
<>
<form method="post">
<input name="email" placeholder="you@example.com"></input>
<button>Save</button>
</form>
<? if(context.session.has("flash")) { ?>
<p><?= context.session["flash"] ?></p>
<? } ?>
</>
}
Reusable UI without a second framework
Components are ordinary .uce files. Pass props through context.props and choose when to render or return markup.
COMPONENT(Request& context)
{
<>
<article class="card">
<h3><?= context.props["title"] ?></h3>
<p><?= context.props["body"] ?></p>
</article>
</>
}
RENDER(Request& context)
{
DValue props;
props["title"] = "Shipping note";
props["body"] = "Components are just .uce files with structured props.";
<>
<?: component("components/card", props, context) ?>
</>
}
Markdown when you want it
UCE also ships a markdown module, so content-heavy pages do not need an external rendering stack.
RENDER(Request& context)
{
String src = "# Release Notes\n\n- fast path\n- docs\n- demos";
<>
<section class="prose">
<?: markdown_to_html(src) ?>
</section>
</>
}
WebSockets with broker-owned state
Any .uce page can render HTML and also accept live socket messages with WS(Request& context). Connection-local state lives on context.connection.
RENDER(Request& context)
{
<>
<script>
const ws = new WebSocket(`ws://${window.location.host}${window.location.pathname}`);
ws.onopen = () => ws.send(JSON.stringify({ type: "join", name: "guest" }));
</script>
</>
}
WS(Request& context)
{
DValue payload = json_decode(context.in);
String connection_id = context.params["WS_CONNECTION_ID"];
context.connection["name"] = payload["name"];
payload["connection_id"] = connection_id;
ws_send(json_encode(payload));
}
nginx wiring
Use FastCGI for ordinary requests and send websocket upgrades to the runtime's built-in HTTP listener.
location ~ \.uce$ {
error_page 418 = @uce_websocket;
if ($http_upgrade = "websocket") { return 418; }
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/uce/fastcgi.sock;
}
location @uce_websocket {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://127.0.0.1:8080;
}
Put nginx in front. Keep the runtime focused.
The intended shape
- nginx serves static files directly from `/var/www/html`
- ordinary `.uce` page loads go through FastCGI
- websocket upgrade traffic for `.uce` paths goes to the built-in HTTP listener
- systemd manages the runtime binary and restart policy
The practical settings
- `FCGI_SOCKET_PATH=/run/uce/fastcgi.sock` for normal requests
- `HTTP_PORT=8080` for `.uce` websocket upgrades
- `scripts/systemd/manage-uce-service.sh` for build and service control
- published root should be `/var/www/html`, not the repo root
Go straight to the useful parts.
What this is and what it is not
Is UCE production ready?
The runtime is still experimental. The point of this site is to explain the model, show the current surface area, and make it easy to evaluate the demos and docs.
Why compare it to PHP?
Because the design instinct is similar: dynamic pages as files, shallow deployment, and a bias toward solving the request in front of you instead of building an application architecture altar.
What makes it different from old PHP?
The request object is explicit, components and named handlers are first-class, markdown support is built in, and WebSockets use a broker-owned connection model inside the runtime.