Live Preview System
Real-time preview of agent work through web interfaces, terminals, and streaming activity logs in your browser
The preview system shows you what agents are doing in real-time. Sandboxed web previews, native terminal access, streaming logs — all in the Bloom web dashboard.
Tab-Per-Service Model
Every service in your colony.toml gets its own preview tab. Agent activity and terminal get dedicated tabs too.
# colony.toml
[[services]]
name = "web"
command = "npm start"
port = 4001
[[services]]
name = "api"
command = "node server.js"
port = 4002
You get these tabs:
┌─────┬─────┬───────┬──────────┐
│ Web │ API │ Agent │ Terminal │
└─────┴─────┴───────┴──────────┘
Click any tab to switch views. Each service is accessible at {name}-{port}.colony.local.
Web Preview
Web services run in a sandboxed iframe. Security by default.
URL Pattern
http://web-4001.colony.local/ # Web service
http://api-4002.colony.local/api # API service
Caddy registers these routes when services start and forwards requests to the right network namespace.
Security Model
<iframe
sandbox="allow-scripts allow-forms allow-popups allow-modals"
src="http://web-4001.colony.local/"
></iframe>
The sandbox prevents:
- Cookies/localStorage access from the parent (no
allow-same-origin) - Top-level navigation
- Arbitrary downloads
The preview is read-only. Agents can modify the running service, but the iframe can’t touch Bloom.
DevTools Toolbar
Every web preview gets a toolbar:
| Button | Action |
|---|---|
| Reload | Refresh the iframe (Cmd+R) |
| Open | Open service in new browser tab |
| DevTools | Open browser DevTools for iframe |
It’s always visible above the preview.
Terminal Preview
You get a full-featured terminal using xterm.js v5 with Nerd Font support.
Architecture
Bloom (xterm.js) ←─ Binary WebSocket ─→ Mycelium (PTY manager)
↓
Erlang open_port (PTY)
↓
/bin/bash in namespace
Every terminal session:
- Runs in the colony’s network namespace
- Uses a dedicated binary WebSocket
- Spawns an OTP actor (PTY session) for process management
- Streams stdin/stdout with zero buffering
Terminal Features
- Nerd Font icons — full glyph support for powerline prompts
- True color — 24-bit color
- Mouse input — click, scroll, selection
- Resize handling — responds to panel resizing
- Copy/paste — standard Cmd+C/V
# Inside the terminal, you're in the namespace
$ ip addr show
# Shows namespace-specific IPs
$ curl localhost:4001
# Hits the web service in this namespace
Use the terminal to debug services, inspect the environment, or manually test API endpoints.
Agent Activity Stream
The agent tab shows real-time events from the agent executor (Claude Code CLI). JSON-formatted logs streaming in.
Event Types
| Event | Description |
|---|---|
tool_use | Agent called a tool (Read, Edit, Bash) |
tool_result | Tool execution completed |
text | Agent thinking/explaining |
error | Execution error |
Example stream:
{"type": "tool_use", "name": "Read", "input": {"file_path": "src/app.ts"}}
{"type": "tool_result", "output": "import express from 'express'..."}
{"type": "text", "content": "I'll add the new route handler..."}
{"type": "tool_use", "name": "Edit", "input": {"file_path": "src/app.ts", ...}}
Status Indicator
Live status badge:
- Running (green) — Agent is executing
- Idle (gray) — Waiting for next task
- Error (red) — Execution failed
The stream auto-scrolls. New events appear at the bottom unless you’ve scrolled up to review history.
Log Tailing (SSE)
All colony logs (stdout/stderr from services) stream to Bloom via Server-Sent Events.
Log API
GET /api/colonies/{id}/logs?follow=true
Returns:
data: {"timestamp": "2026-02-16T10:15:30Z", "level": "info", "message": "Server listening on :4001"}
data: {"timestamp": "2026-02-16T10:15:31Z", "level": "info", "message": "GET / 200 12ms"}
We store logs in a ring buffer (ETS in Mycelium). Default size is 10,000 lines. Old logs get evicted automatically.
Integration with Preview
When you’re viewing a service preview, the log stream shows only logs from that service (filtered by service_name). Switch tabs to see other services.
Plugin Architecture
The preview system is extensible. Each plugin implements:
interface PreviewPlugin {
id: string;
label: string;
icon: string;
component: (props: PluginProps) => JSX.Element;
isAvailable: (config: ColonyConfig) => boolean;
}
Built-in Plugins
| Plugin | Purpose |
|---|---|
| WebPreview | Sandboxed iframe for web services |
| Terminal | xterm.js terminal in namespace |
| AgentPreview | Streaming agent activity |
Plugin Registry
Register plugins in bloom/src/lib/preview/registry.ts:
import { registerPlugin } from '@/lib/preview/registry';
registerPlugin({
id: 'custom-tool',
label: 'Custom Tool',
icon: 'tool-icon',
component: CustomToolPreview,
isAvailable: (config) => config.services.some(s => s.name === 'tool'),
});
The registry uses a Map for O(1) lookups. You can register plugins dynamically at runtime.
Future plugins: database query tools, visual diff viewers, network traffic monitors, custom dashboards.
Resizable Panels
Preview panels use @corvu/resizable for flexible layouts:
- Drag the divider to resize the split
- Collapse the preview for full-width focus
- Per-tab resize state (web preview vs terminal might need different sizes)
<Resizable orientation="horizontal" initialSize="50%">
<ResizablePanel id="content">
{/* Colony details, state, controls */}
</ResizablePanel>
<ResizableHandle />
<ResizablePanel id="preview">
{/* Active preview plugin */}
</ResizablePanel>
</Resizable>
Technical Implementation
WebSocket Protocol
message TerminalInput {
string session_id = 1;
bytes data = 2; // Raw stdin bytes
}
message TerminalOutput {
string session_id = 1;
bytes data = 2; // Raw stdout/stderr bytes
}
Binary frames, not JSON. Zero serialization overhead.
PTY Session Management
Each terminal session is an OTP actor that:
- Spawns
/bin/bashvia Erlangopen_portwith{spawn_executable, "/bin/bash"} - Sets
{env, [{"TERM", "xterm-256color"}]}for color support - Monitors the port for exit signals
- Cleans up resources on disconnect
Performance
- WebSocket latency:
<5ms(local) - Terminal responsiveness: Native (no buffering)
- Log throughput: 10,000+ lines/sec (SSE stream)
- Memory: 10MB per preview tab (mostly xterm.js buffer)
Next Steps
- Colony Lifecycle — Understand how services start/stop
- Parallel Agents — How agents avoid conflicts
- Architecture Overview — Technical deep dive