Lesson 15: The Generated Web Chat UI
A complete browser chat client - sidebar, streaming, approvals, SQLite store.
The Generated Web Chat UI
A gateways/*.bx entry with exposes: "webui" ships a complete browser chat client
for your agent - a conversation sidebar, streaming with reasoning and tool calls,
human-in-the-loop approvals, per-visitor theming, and a real SQLite store behind it.
// gateways/chat.bx
class {
function configure() {
return {
exposes : "webui",
path : "/chat",
apiKeyEnvVar: "CHAT_UI_API_KEY" // optional - see "Securing it" below
};
}
}
This generates a static <path>/index.html (served directly, no route needed) plus a
dedicated API under <path>/api. The page is dependency-free vanilla HTML/CSS/JS,
pre-built and vendored inside BxAgents itself - bxAgents build never runs npm install, and a generated project never needs Node installed at all.

That's the real, unmodified generated page - this one branded with
examples/webui-agent's
theme/title/icon config from Lesson 13's exposure
concept, taken further. bxAgents build && bxAgents serve in that example gets you the
same page for real.
What the page can do
- Conversation sidebar - switch, rename, delete, or start a new conversation.
- Steer while streaming - the composer stays live during a turn; Send becomes Steer, splicing your message into the run already in flight.
- Stop - posts
/cancelbefore aborting, so the server actually stops spending tokens rather than just abandoning the browser connection. - Reasoning + tool calls - collapsed disclosures fed straight off the streamed response.
- Approvals - a human-in-the-loop pause renders an Approve/Reject card.
Without accounts: one shared workspace
By default the UI has no accounts and no gate - every visitor is anonymous, and every visitor reads and writes the same conversations, preferences and agent memory. This is the zero-ceremony local-dev experience, not a deployment posture.
An open UI has no privacy between visitors. Declare users if the page is
reachable by more than the people who should see the transcripts.
users : [
{ username: "ada", passwordEnvVar: "ACME_ADA_PASSWORD", displayName: "Ada Lovelace" }
]
An account names the environment variable holding its password, or carries an
already-hashed value - a literal password key is a build error, not a warning.
Generate a hash with:
bxAgents hash-password --password="<your-password>"
Branding it
Every key is optional:
theme: {
accent : "0f766e",
radius : "10px",
font : "Inter, system-ui, sans-serif",
dark : { accent : "rgb(45, 212, 191)" }
}
Write hex colors bare, with no leading # - BoxLang begins string interpolation
at # in both quote styles, so the generator adds it back for you.
title, subtitle, icon, welcome, placeholder and footer are also available,
plus a themeFile for anything the tokens don't cover.
Securing it
apiKeyEnvVar is a simple, toggleable gate - not a full login system. Left unset,
<path>/api/* is wide open (fine for local dev). Set it, and every request under
<path>/api/* must carry a matching X-API-Key header. The static shell itself is
deliberately not gated, since a browser's plain page navigation can't send a
custom header.
Try it
bxAgents build
bxAgents serve --port=8080
Open http://localhost:8080/chat in a browser and talk to your agent - the same one
you've been building tools, skills and subagents onto since Lesson 9.
Full reference: The web chat UI.