Invisible AI watchdog for every flow: - Read-only access to context buffer - Cannot emit messages to pipeline - Agents have no way to detect or probe it - Alerts via control plane (email, UI, auto-stop) - Runs on cheap models (Mistral/Mixtral) Watches for: endless loops, goal drift, prompt injection, sandbox escape attempts, token budget exhaustion. Added to Phase 2 (core safety feature). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
49 KiB
BloxServer SaaS Platform — Architecture Design Document
Version: 1.0 (Draft) Date: January 2026 Status: Planning
Executive Summary
BloxServer is a SaaS platform for building AI agent workflows using the xml-pipeline library. Users visually design message flows on a canvas, which generates the underlying YAML configuration. Flows run on isolated container instances with support for built-in tools, marketplace components, and custom WASM modules.
Key Differentiators
- Visual flow builder with real-time YAML synchronization
- Turing-complete message routing (self-iteration, conditionals, parallel execution)
- WASM sandboxing for custom code (no Python upload = secure)
- Marketplace for sharing tools and complete flows
- Anti-paperclipper design with user-controlled memory
System Overview
┌─────────────────────────────────────────────────────────────────────────┐
│ USERS │
│ (Browser / API Clients) │
└───────────────────────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ VERCEL (Frontend) │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Next.js Application │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Flow Canvas │ │ YAML Tab │ │ Monaco │ │ │
│ │ │ (React Flow)│ │ (Preview) │ │ (WASM) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Dashboard │ │ Marketplace │ │ Settings │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────┬─────────────────────────────────────────┘
│ REST / GraphQL
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ RENDER (Backend) │
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Control Plane (FastAPI) │ │
│ │ • User management (via Clerk webhooks) │ │
│ │ • Flow CRUD (organism.yaml storage) │ │
│ │ • Pump orchestration (start/stop/scale) │ │
│ │ • Trigger routing (webhooks → pump injection) │ │
│ │ • Marketplace catalog │ │
│ │ • WASM module registry │ │
│ │ • Billing integration (Stripe) │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Orchestrates │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Pump │ │ Pump │ │ Pump │ │
│ │ Container │ │ Container │ │ Container │ │
│ │ (Flow A) │ │ (Flow B) │ │ (Flow C) │ │
│ │ │ │ │ │ │ │
│ │ StreamPump │ │ StreamPump │ │ StreamPump │ │
│ │ + WASM RT │ │ + WASM RT │ │ + WASM RT │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └─────────────────┼─────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Redis (Shared State) │ │
│ │ • Context buffers: tenant:{id}:flow:{id}:buffer:* │ │
│ │ • Thread registry: tenant:{id}:flow:{id}:registry:* │ │
│ │ • Project memory: tenant:{id}:flow:{id}:memory:* (opt-in) │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ PostgreSQL (Persistent) │ │
│ │ • Users, organizations │ │
│ │ • Flows (organism.yaml stored as text) │ │
│ │ • Marketplace listings │ │
│ │ • WASM modules (metadata, S3 refs) │ │
│ │ • Billing records │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Clerk │ │ Stripe │ │ LLM APIs │ │ S3 │ │
│ │ (Auth) │ │ (Billing)│ │ (xAI,etc)│ │ (WASM) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Tier Model
| Tier | Price | Flows | Tools | Custom Code | Features |
|---|---|---|---|---|---|
| Free | $0 | 1 | Built-in only | ❌ | Community support |
| Paid | $X/mo | Multiple | + Marketplace | ❌ | Email support |
| Pro | $XX/mo | Unlimited | + Marketplace | ✅ WASM/WIT | Priority support |
| Enterprise | Custom | Unlimited | + Private | ✅ WASM/WIT | SSO, roles, SLA |
Limits (TBD)
| Resource | Free | Paid | Pro | Enterprise |
|---|---|---|---|---|
| Flows | 1 | 10 | Unlimited | Unlimited |
| Executions/month | 1,000 | 10,000 | 100,000 | Custom |
| WASM modules | 0 | 0 | 10 | Unlimited |
| Project memory | ❌ | 10MB | 100MB | Custom |
| Team members | 1 | 1 | 1 | Unlimited |
Component Architecture
Frontend (Next.js on Vercel)
Tech Stack
- Framework: Next.js 14+ (App Router)
- UI Generation: Vercel v0
- Components: shadcn/ui + Tailwind CSS
- Flow Canvas: React Flow (Xyflow)
- Code Editor: Monaco Editor (TypeScript mode)
- State: Zustand or Jotai
- API Client: tRPC or React Query
Code Editor Architecture (Pro+ WASM)
AssemblyScript editing uses Monaco's built-in TypeScript language service — no separate language server required. Since AssemblyScript is a strict TypeScript subset, this provides:
- Syntax highlighting
- Autocomplete / IntelliSense
- Type checking
- Error diagnostics
The AssemblyScript type definitions (.d.ts) are loaded into Monaco at startup.
User writes code in Monaco (TypeScript mode)
│
▼
Real-time feedback from TS language service
(syntax, types, autocomplete)
│
▼
User clicks "Build" / "Deploy"
│
▼
Backend runs `asc` compiler
│
├── Success → .wasm file stored, module registered
│
└── Errors → Returned to UI with line numbers
(Monaco shows error markers)
Why no AssemblyScript Language Server (asls)?
- Monaco TypeScript covers 90%+ of editing needs
- The
asccompiler catches AS-specific errors accurately at build time - Eliminates $7+/month infrastructure cost
- Zero cold-start latency (runs in browser)
AI-Assisted WASM Coding (Pro)
The Monaco editor includes AI assistance for writing AssemblyScript — like Claude Code, but for WASM tools.
┌─────────────────────────────────────────────────────────────────┐
│ Monaco Editor (Pro WASM Tab) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ // User types or asks: │
│ // "Help me write a function that parses CSV" │
│ │
│ export function parseCSV(input: string): string[][] { │
│ █ │
│ ← AI suggests completion here │
│ } │
│ │
├─────────────────────────────────────────────────────────────────┤
│ [💬 Ask AI] [▶ Build] [Save] │
└─────────────────────────────────────────────────────────────────┘
Two modes:
| Mode | UX | Like |
|---|---|---|
| Inline completion | Tab to accept suggestions | GitHub Copilot |
| Chat panel | "Write a JSON validator" → generates code | Claude Code |
Implementation:
- Frontend calls Claude/GPT API directly (no backend round-trip needed)
- Context includes: AssemblyScript types, WIT interface, user's code
- Suggestions inserted into Monaco editor
Why AI-assisted (not autonomous):
- Human reviews code before building
- User learns AssemblyScript over time
- Debugging is tractable
- No surprise compute costs
- WASM sandbox is safe, but user oversight builds trust
Ships with Phase 3 (Monaco integration) — just an API call from the browser.
Key Pages
| Route | Purpose |
|---|---|
/ |
Landing page |
/dashboard |
Flow list, usage stats |
/flow/[id] |
Flow canvas editor |
/flow/[id]/yaml |
YAML editor view |
/flow/[id]/runs |
Execution history |
/marketplace |
Browse tools/flows |
/settings |
Account, billing, API keys |
Flow Canvas Features
┌─────────────────────────────────────────────────────────────────┐
│ [Save] [Run] [Stop] [YAML] [Canvas] [Split] │
├─────────────────────────────────────────────────────────────────┤
│ ┌───────────┐ │
│ │ Palette │ ┌─────────┐ ┌─────────┐ │
│ │ │ │ Webhook │ ───▶ │ LLM │ ──┐ │
│ │ [Built-in]│ │ Trigger │ │ Call │ │ │
│ │ [Market] │ └─────────┘ └─────────┘ │ │
│ │ [Custom] │ │ │
│ │ │ ┌─────────┐ │ │
│ │ 📦 Trigger│ │ Code │ ◀─┘ │
│ │ 📦 LLM │ │ Block │ │
│ │ 📦 HTTP │ └────┬────┘ │
│ │ 📦 Code │ │ │
│ │ 📦 Branch │ ▼ │
│ │ ... │ ┌─────────┐ │
│ └───────────┘ │ Output │ │
│ └─────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Minimap │ Zoom: 100% │ Nodes: 4 │ Status: Saved │
└─────────────────────────────────────────────────────────────────┘
Node Types
| Node | Visual | Maps To |
|---|---|---|
| Trigger | 🎯 Circle | Injection endpoint |
| LLM Call | 🤖 Box | Agent listener |
| HTTP Request | 🌐 Box | HTTP tool |
| Code Block | 📝 Box | WASM handler |
| Conditional | ◇ Diamond | Branch logic |
| Output | 📤 Box | Terminal handler |
| Loop | ↻ Arrow back | Self-iteration |
Flow Lifecycle Controls
Simple two-state model: Stopped ↔ Running
┌─────────────────────────────────────────────────────────────┐
│ Flow States │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ [▶ Run] ┌──────────┐ │
│ │ Stopped │ ─────────────▶ │ Running │ │
│ │ │ ◀───────────── │ │ │
│ └──────────┘ [■ Stop] └──────────┘ │
│ │ │ │
│ │ Edit allowed │ Edit blocked │
│ │ Triggers ignored │ Triggers processed │
│ │
└─────────────────────────────────────────────────────────────┘
UI Controls:
[▶ Run] [■ Stop] [Save] ← Action bar
State Transitions:
| Action | From | To | Behavior |
|---|---|---|---|
| Run | Stopped | Running | Start pump container, enable triggers |
| Stop | Running | Stopped | Kill container, lose in-flight threads |
| Save | Stopped | Stopped | Persist YAML to database |
| Save | Running | — | Blocked (must stop first) |
Why no Pause?
- Simpler state machine
- Matches user expectations (Zapier, n8n, Make all work this way)
- Flows should be stateless — restart is safe:
- Webhooks retry automatically (HTTP behavior)
- Schedules catch next tick
- Project memory (Pro) survives restart
Why no Hot Edit?
- Modifying a swarm mid-execution risks undefined behavior
- Agent could be mid-reasoning when peers list changes
- Stop → Edit → Start is safer and predictable
Future consideration (Pro): Graceful Stop
- Stop accepting new triggers
- Wait up to N seconds for in-flight threads to complete
- Force-stop after timeout
Control Plane (FastAPI on Render)
Tech Stack
- Framework: FastAPI
- ORM: SQLAlchemy 2.0 + asyncpg
- Validation: Pydantic v2
- Task Queue: (Optional) Celery or arq
- Container Orchestration: Render Native (or Docker API)
API Endpoints
Authentication (via Clerk)
───────────────────────────
POST /webhooks/clerk # Clerk webhook for user sync
Flows
───────────────────────────
GET /api/flows # List user's flows
POST /api/flows # Create flow
GET /api/flows/{id} # Get flow details
PUT /api/flows/{id} # Update flow (canvas → YAML)
DELETE /api/flows/{id} # Delete flow
POST /api/flows/{id}/start # Start pump container
POST /api/flows/{id}/stop # Stop pump container
GET /api/flows/{id}/status # Pump status
GET /api/flows/{id}/logs # Stream logs
Triggers
───────────────────────────
POST /api/triggers/{flow_id}/webhook/{token} # Webhook ingress
POST /api/triggers/{flow_id}/inject # Manual injection
Marketplace
───────────────────────────
GET /api/marketplace/tools # Browse tools
GET /api/marketplace/flows # Browse flow templates
POST /api/marketplace/publish # Publish to marketplace
WASM Modules (Pro+)
───────────────────────────
GET /api/modules # List user's modules
POST /api/modules # Upload WASM module
DELETE /api/modules/{id} # Delete module
Database Schema (PostgreSQL)
-- Users (synced from Clerk)
CREATE TABLE users (
id UUID PRIMARY KEY,
clerk_id TEXT UNIQUE NOT NULL,
email TEXT NOT NULL,
tier TEXT DEFAULT 'free',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Flows
CREATE TABLE flows (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
name TEXT NOT NULL,
description TEXT,
organism_yaml TEXT NOT NULL, -- The actual config
canvas_state JSONB, -- React Flow state
status TEXT DEFAULT 'stopped', -- stopped, starting, running, error
container_id TEXT, -- Render container ID
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- WASM Modules
CREATE TABLE wasm_modules (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
name TEXT NOT NULL,
description TEXT,
s3_key TEXT NOT NULL, -- S3 path to .wasm file
wit_interface TEXT, -- WIT definition
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Marketplace Listings
CREATE TABLE marketplace_listings (
id UUID PRIMARY KEY,
author_id UUID REFERENCES users(id),
type TEXT NOT NULL, -- 'tool' or 'flow'
name TEXT NOT NULL,
description TEXT,
content JSONB NOT NULL, -- Tool def or flow template
downloads INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Execution History
CREATE TABLE executions (
id UUID PRIMARY KEY,
flow_id UUID REFERENCES flows(id),
trigger_type TEXT, -- webhook, manual, schedule
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
status TEXT, -- success, error
error_message TEXT
);
Pump Containers (Render)
Each flow gets its own container running:
- StreamPump (from xml-pipeline)
- WASM runtime (wasmtime)
- Redis connection (shared state)
Container Image
FROM python:3.11-slim
# Install xml-pipeline
COPY requirements.txt .
RUN pip install -r requirements.txt
# Install wasmtime
RUN pip install wasmtime
# Copy entrypoint
COPY entrypoint.py .
# Environment variables provided by orchestrator:
# - FLOW_ID
# - ORGANISM_YAML (base64 encoded)
# - REDIS_URL
# - TENANT_PREFIX
CMD ["python", "entrypoint.py"]
Entrypoint
# entrypoint.py
import os
import base64
import asyncio
from xml_pipeline.message_bus.stream_pump import StreamPump
from xml_pipeline.config.loader import load_config_from_string
from xml_pipeline.memory.shared_backend import get_shared_backend, BackendConfig
async def main():
# Load config from environment
yaml_content = base64.b64decode(os.environ["ORGANISM_YAML"]).decode()
config = load_config_from_string(yaml_content)
# Configure shared backend with tenant prefix
backend_config = BackendConfig(
backend_type="redis",
redis_url=os.environ["REDIS_URL"],
redis_prefix=os.environ["TENANT_PREFIX"],
)
backend = get_shared_backend(backend_config)
# Start pump
pump = StreamPump(config, backend=backend)
await pump.start()
# Keep running
try:
while True:
await asyncio.sleep(1)
except asyncio.CancelledError:
await pump.shutdown()
if __name__ == "__main__":
asyncio.run(main())
Trigger System
Triggers inject messages into running pumps.
Trigger Types
| Trigger | Implementation |
|---|---|
| Webhook | Control plane receives POST, forwards to pump via Redis pub/sub |
| Schedule | Celery beat or Render Cron, injects at scheduled times |
| Manual | "Run" button in UI, calls control plane API |
| (Future) IMAP polling service |
Webhook Flow
External Service
│
│ POST /api/triggers/{flow_id}/webhook/{token}
▼
┌─────────────────┐
│ Control Plane │
│ │
│ 1. Validate │
│ 2. Find pump │
│ 3. Publish │
└────────┬────────┘
│ Redis PUBLISH trigger:{flow_id}
▼
┌─────────────────┐
│ Pump Container │
│ │
│ 1. Subscribe │
│ 2. Inject msg │
│ 3. Process │
└─────────────────┘
Security Model
Multi-Tenancy Isolation
┌─────────────────────────────────────────────────────────────┐
│ Tenant A │
│ ┌────────────┐ ┌────────────┐ │
│ │ Flow 1 │ │ Flow 2 │ │
│ │ │ │ │ │
│ │ Redis: │ │ Redis: │ │
│ │ tenantA: │ │ tenantA: │ │
│ │ flow1:* │ │ flow2:* │ │
│ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Tenant B │
│ ┌────────────┐ │
│ │ Flow 3 │ ← Cannot access tenantA:* keys │
│ │ │ │
│ │ Redis: │ │
│ │ tenantB: │ │
│ │ flow3:* │ │
│ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
WASM Sandboxing
Custom code runs in WASM, which provides:
- Memory isolation — Cannot access host memory
- No filesystem — Only WIT-defined host functions
- No network — Must use provided HTTP tool
- CPU limits — Fuel-based execution limits
- Deterministic — Same input → same output
Memory Safety (Anti-Paperclipper)
┌─────────────────────────────────────────────────────────────┐
│ Memory Tiers │
├─────────────────────────────────────────────────────────────┤
│ Thread Memory (automatic) │
│ ├── Per-execution context buffer │
│ ├── Pruned when thread completes │
│ └── Swarm cannot prevent deletion │
├─────────────────────────────────────────────────────────────┤
│ Project Memory (opt-in, Pro+) │
│ ├── User explicitly enables per flow │
│ ├── Size limits enforced │
│ ├── User can view/delete anytime │
│ └── Cleared on flow deletion │
├─────────────────────────────────────────────────────────────┤
│ Cross-Flow Memory (FORBIDDEN) │
│ ├── Flow A cannot read Flow B's memory │
│ ├── Even same user, different flows = isolated │
│ └── Prevents swarm coordination across boundaries │
└─────────────────────────────────────────────────────────────┘
Monitor AI (Shadow Watchdog)
Every flow has an invisible AI watchdog that observes but cannot interfere. Agents have no way to detect, probe, or influence the monitor.
┌─────────────────────────────────────────────────────────────────┐
│ User's Flow │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Agent A │───▶│ Agent B │───▶│ Agent C │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Context Buffer │ (append-only log) │
│ └────────┬────────┘ │
└───────────────────────│─────────────────────────────────────────┘
│ Read-only tap (invisible to agents)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Monitor AI (Shadow) │
│ │
│ Capabilities: │
│ ├── READ: All messages in context buffer │
│ ├── ALERT: Email user, UI notification, webhook │
│ └── STOP: Trigger flow stop via control plane │
│ │
│ Cannot: │
│ ├── Emit messages to pipeline │
│ ├── Modify context buffer │
│ └── Be detected by agents (no probe surface) │
│ │
│ Watches for: │
│ ├── Endless loops (repeating message patterns) │
│ ├── Token budget approaching limit │
│ ├── Goal drift (agent pursuing unintended objectives) │
│ ├── Prompt injection attempts │
│ ├── Sandbox escape attempts (probing for filesystem, etc.) │
│ └── Anomalous conversation patterns │
│ │
│ Runs on: Cheap models (Mistral, Mixtral, Llama) │
└─────────────────────────────────────────────────────────────────┘
│
▼ Out-of-band alerts (not via message bus)
┌─────────────────┐
│ Control Plane │
│ • Email user │
│ • UI alert │
│ • Auto-stop │
│ • Audit log │
└─────────────────┘
Why this architecture:
| Property | Benefit |
|---|---|
| Asymmetric visibility | Monitor sees agents; agents can't see monitor |
| No message bus access | Can't be poisoned via prompt injection |
| Separate trust domain | Even compromised agents can't disable watchdog |
| Out-of-band control | Stops flows via API, not messages |
| Cheap to run | Mistral/Mixtral at ~$0.0002/1K tokens |
Alert thresholds (configurable):
| Condition | Default | Action |
|---|---|---|
| Same message pattern 3x | Warn | UI alert |
| Same message pattern 5x | Critical | Auto-stop + email |
| Token usage > 80% budget | Warn | UI alert |
| Token usage > 95% budget | Critical | Auto-stop |
| Suspected goal drift | Warn | Flag for review |
| Escape attempt detected | Critical | Auto-stop + audit log |
Implementation:
- Monitor runs as sidecar process in pump container
- Subscribes to Redis buffer updates (read-only)
- Periodically summarizes recent messages and checks patterns
- Alerts via control plane API (never via message bus)
Data Flow Examples
User Creates Flow
1. User drags nodes on canvas
2. Frontend converts to organism.yaml
3. PUT /api/flows/{id} with YAML
4. Control plane validates YAML
5. Saves to PostgreSQL
6. Returns success
User Starts Flow
1. User clicks "Start"
2. POST /api/flows/{id}/start
3. Control plane:
a. Fetches YAML from DB
b. Creates Render container
c. Passes YAML + Redis config as env vars
d. Updates flow.status = 'starting'
4. Container starts, pump initializes
5. Pump reports ready via Redis
6. Control plane updates flow.status = 'running'
7. Frontend shows green "Running" status
Webhook Triggers Flow
1. External service POSTs to webhook URL
2. Control plane receives at /api/triggers/{flow_id}/webhook/{token}
3. Control plane validates token
4. Control plane publishes to Redis: PUBLISH trigger:{flow_id} {payload}
5. Pump container (subscribed) receives message
6. Pump injects message into StreamPump
7. Pipeline processes, handlers execute
8. Results logged to execution history
Canvas ↔ YAML Synchronization
Canvas → YAML
// Frontend: Convert React Flow state to organism.yaml
function canvasToYaml(nodes, edges) {
const listeners = nodes
.filter(n => n.type !== 'trigger')
.map(node => ({
name: node.data.name,
handler: node.data.handler,
payload_class: node.data.payloadClass,
description: node.data.description,
agent: node.data.isAgent || false,
peers: edges
.filter(e => e.source === node.id)
.map(e => findNode(e.target).data.name),
}));
return yaml.dump({
organism: { name: flowName },
listeners,
});
}
YAML → Canvas
// Frontend: Convert organism.yaml to React Flow state
function yamlToCanvas(yamlContent) {
const config = yaml.load(yamlContent);
const nodes = config.listeners.map((listener, i) => ({
id: listener.name,
type: getNodeType(listener),
position: calculatePosition(i),
data: {
name: listener.name,
handler: listener.handler,
payloadClass: listener.payload_class,
description: listener.description,
isAgent: listener.agent,
},
}));
const edges = config.listeners.flatMap(listener =>
(listener.peers || []).map(peer => ({
id: `${listener.name}-${peer}`,
source: listener.name,
target: peer,
}))
);
return { nodes, edges };
}
Marketplace
Publishing a Tool
1. User creates WASM module (Pro+)
2. User clicks "Publish to Marketplace"
3. Frontend sends:
- Module metadata
- Description, icon, category
- Pricing (free or paid)
4. Control plane:
- Validates module
- Creates marketplace listing
- Makes module available to others
Installing a Tool
1. User browses marketplace
2. User clicks "Install" on tool
3. Control plane:
- Adds tool to user's available tools
- Copies WASM module reference
4. Tool appears in user's palette under "Marketplace" tab
Publishing a Flow Template
1. User creates working flow
2. User clicks "Publish as Template"
3. Frontend sends:
- Flow YAML (sanitized)
- Description, use case
4. Control plane creates listing
5. Other users can "Use Template" to clone flow
Monitoring & Observability
Metrics (Prometheus/Grafana)
| Metric | Description |
|---|---|
nextra_flows_total |
Total flows by status |
nextra_executions_total |
Executions by flow, status |
nextra_pump_memory_bytes |
Memory per pump container |
nextra_pump_messages_total |
Messages processed |
nextra_api_requests_total |
API requests by endpoint |
Logging
- Control Plane: Structured JSON logs → CloudWatch/Datadog
- Pump Containers: Stream to Redis → Viewable in UI
- Execution History: Stored in PostgreSQL
Alerting
| Alert | Condition |
|---|---|
| Pump crash | Container exits unexpectedly |
| High error rate | >5% executions failing |
| Memory pressure | Pump using >80% memory |
| Stuck flow | No messages processed in 5min |
Scaling Considerations
Render Service Types
| Component | Render Service | Scaling |
|---|---|---|
| Control Plane | Web Service | Horizontal (multiple instances) |
| Pump Containers | Private Services | Per-flow, scale-to-zero |
| Redis | Managed Redis | Vertical |
| PostgreSQL | Managed Postgres | Vertical |
Scale-to-Zero (Cost Optimization)
Free tier flows:
- Auto-stop after 15 min idle
- Webhook triggers wake container (~5s cold start)
- User sees "Starting..." briefly
Paid tier flows:
- Keep-alive option
- Faster cold starts (warm pool)
Future: Multi-Region
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ US-East │ │ EU-West │ │ AP-Tokyo │
│ │ │ │ │ │
│ Control Plane│ ←───│ Control Plane│ ←───│ Control Plane│
│ Pump Pool │ │ Pump Pool │ │ Pump Pool │
│ Redis │ │ Redis │ │ Redis │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└───────────────────┼───────────────────┘
▼
┌──────────────────┐
│ Global Postgres │
│ (CockroachDB?) │
└──────────────────┘
AI Flow Builder Assistant
The platform includes an AI assistant that helps users create flows from natural language descriptions. The key insight: the assistant is itself a flow running on BloxServer.
Architecture (Dogfooding)
User: "I want a flow that monitors my website and alerts me on Slack"
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ flow-builder (system flow, runs on BloxServer) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ builder │ ───▶ │ catalog │ │ examples │ │
│ │ (agent) │ ───▶ │ (tool) │ │ (tool) │ │
│ │ │ ───▶ │ │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ │
│ │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ │
│ │ │ ───▶ │ validator │ ───▶ │ responder │ │
│ │ │ │ (tool) │ │ (output) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
│
▼
Returns generated organism.yaml to UI
Tools Available to Builder Agent
| Tool | Purpose |
|---|---|
catalog |
List available nodes (built-in + user's marketplace + custom WASM) |
validator |
Check if generated YAML is valid against schema |
examples |
Search marketplace for similar flows as few-shot examples |
user-modules |
List user's custom WASM modules (Pro) |
Flow Definition
# The AI assistant is itself a flow!
organism:
name: flow-builder-assistant
listeners:
- name: builder
agent: true
prompt: |
You help users create BloxServer flows from natural language.
Process:
1. Call catalog to see available nodes
2. Call examples to find similar flows
3. Generate organism.yaml
4. Call validator to check your work
5. Fix any errors and re-validate
6. Return final YAML via responder
Rules:
- Every flow needs at least one trigger
- Agents can only call declared peers
- WASM code blocks require Pro tier
peers:
- catalog
- validator
- examples
- user-modules
- responder
- name: catalog
handler: nextra.builtin.catalog.list_nodes
description: "Returns available node types with schemas"
- name: validator
handler: nextra.builtin.validator.check_yaml
description: "Validates organism.yaml, returns errors if invalid"
- name: examples
handler: nextra.builtin.examples.search
description: "Search marketplace for example flows"
- name: user-modules
handler: nextra.builtin.modules.list_user
description: "List user's custom WASM modules"
- name: responder
handler: nextra.builtin.respond.to_ui
description: "Return result to the UI"
Benefits of This Approach
| Benefit | Why It Matters |
|---|---|
| No special infrastructure | Same StreamPump, same handlers, same security |
| Always accurate catalog | Queries real available nodes, not static list |
| Self-validating | Checks its own work before returning |
| Learns from marketplace | Uses community flows as few-shot examples |
| Same billing | Just LLM tokens like any agent flow |
| Customizable (Pro) | Users could fork and customize the builder |
Implementation Phases
Phase 1: System prompt + validator tool
- Bake common nodes into prompt
- Validate before returning
- 10-20 curated example flows
Phase 2: Dynamic catalog
catalogtool queries user's actual available nodes- Marketplace examples as RAG source
Phase 3: Learning loop
- Track AI-generated → user-edited corrections
- Use as fine-tuning signal or RAG pairs
Content Strategy
Documentation becomes training data:
- Every doc page = context the AI can reference
- Every marketplace flow = few-shot example
- JSON Schema = ground truth for validation
Good docs help humans AND train the AI — double value.
Implementation Phases
Phase 1: MVP (4-6 weeks)
- Control Plane basic CRUD
- Single pump container (manual start/stop)
- Canvas with basic nodes (LLM, HTTP, Output)
- YAML preview (read-only)
- Clerk authentication
- Free tier only
Phase 2: Core Features (4-6 weeks)
- Automatic pump orchestration
- Webhook triggers
- Execution history
- Canvas ↔ YAML sync
- Monitor AI (shadow watchdog)
- Paid tier + Stripe billing
Phase 3: Pro Features (4-6 weeks)
- WASM module upload
- Monaco editor integration
- Project memory (opt-in)
- Pro tier
Phase 4: Marketplace (4-6 weeks)
- Tool publishing
- Flow templates
- Browse/search/install
- Ratings/reviews
Phase 4.5: AI Flow Builder (2-4 weeks)
- Builder agent flow (system prompt + tools)
- Catalog tool (list available nodes)
- Validator tool (check YAML)
- Examples tool (search marketplace)
- UI integration ("Help me build" button)
Phase 5: Enterprise (TBD)
- Team/org management
- Role-based access
- SSO (SAML)
- SLA dashboard
- Private marketplace
Open Questions
- Pricing specifics — What are the actual price points?
- Execution metering — How to count/limit executions fairly?
- WASM module review — Manual review before marketplace publish?
- Cold start optimization — Warm container pool for paid users?
- Mobile support — Canvas on mobile, or just monitoring?
Appendix: Technology Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Frontend Framework | Next.js | v0 generates it, Vercel hosts it |
| Canvas Library | React Flow | Most popular, good docs, n8n uses it |
| Code Editor | Monaco (TS mode) | No LSP server needed; asc compiler catches AS errors |
| Flow Controls | Run/Stop only | No pause, no hot-edit; stateless flows, safe restarts |
| AI Assistant | Self-hosted flow | Dogfooding: builder is a flow with catalog/validator tools |
| Monitor AI | Shadow sidecar | Read-only watchdog; agents can't detect or influence |
| Monitor Model | Mistral/Mixtral | Cheap (~$0.0002/1K); doesn't need frontier model |
| Control Plane | FastAPI | Matches xml-pipeline, async-native |
| Database | PostgreSQL | Render managed, reliable |
| Cache/Pubsub | Redis | Already needed for xml-pipeline shared backend |
| Auth | Clerk | Free to 10K, great DX, handles OAuth |
| Billing | Stripe | Standard, good APIs |
| Frontend Hosting | Vercel | Built for Next.js |
| Backend Hosting | Render | Simple, good DX, containers |
| WASM Runtime | wasmtime | Best WIT support |
Document generated: January 2026 Next review: After Phase 1 completion