← Docs · Core Concepts

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:

ButtonAction
ReloadRefresh the iframe (Cmd+R)
OpenOpen service in new browser tab
DevToolsOpen 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

EventDescription
tool_useAgent called a tool (Read, Edit, Bash)
tool_resultTool execution completed
textAgent thinking/explaining
errorExecution 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

PluginPurpose
WebPreviewSandboxed iframe for web services
Terminalxterm.js terminal in namespace
AgentPreviewStreaming 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/bash via Erlang open_port with {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