Add Nextra SaaS platform architecture document
Comprehensive design doc covering: - System overview and architecture diagrams - Tier model (Free/Paid/Pro/Enterprise) - Component architecture (Next.js, FastAPI, Pump containers) - Database schema (PostgreSQL) - Trigger system (webhooks, schedules, manual) - Security model (tenant isolation, WASM sandboxing, memory safety) - Canvas ↔ YAML synchronization - Marketplace design - Monitoring and observability - Scaling considerations - Implementation phases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
515c738abb
commit
2d05fd3656
1 changed files with 759 additions and 0 deletions
759
docs/nextra-architecture.md
Normal file
759
docs/nextra-architecture.md
Normal file
|
|
@ -0,0 +1,759 @@
|
|||
# Nextra SaaS Platform — Architecture Design Document
|
||||
|
||||
**Version:** 1.0 (Draft)
|
||||
**Date:** January 2026
|
||||
**Status:** Planning
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Nextra 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
|
||||
- **State:** Zustand or Jotai
|
||||
- **API Client:** tRPC or React Query
|
||||
|
||||
#### 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 |
|
||||
|
||||
### 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)
|
||||
|
||||
```sql
|
||||
-- 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
|
||||
|
||||
```dockerfile
|
||||
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
|
||||
|
||||
```python
|
||||
# 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 |
|
||||
| **Email** | (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 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
// 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?) │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
- [ ] 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 5: Enterprise (TBD)
|
||||
|
||||
- [ ] Team/org management
|
||||
- [ ] Role-based access
|
||||
- [ ] SSO (SAML)
|
||||
- [ ] SLA dashboard
|
||||
- [ ] Private marketplace
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Pricing specifics** — What are the actual price points?
|
||||
2. **Execution metering** — How to count/limit executions fairly?
|
||||
3. **WASM module review** — Manual review before marketplace publish?
|
||||
4. **Cold start optimization** — Warm container pool for paid users?
|
||||
5. **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 |
|
||||
| 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*
|
||||
Loading…
Reference in a new issue