Files
stack/docs/design/MS22-DB-CENTRIC-ARCHITECTURE.md
Jason Woltje 3ceb45c0b2 docs(design): MS22 DB-centric agent fleet architecture
Minimal env vars (DATABASE_URL + MOSAIC_SECRET_KEY), all config in Postgres,
onboarding wizard, breakglass auth, OIDC via settings UI.
2026-03-01 08:17:11 -06:00

9.2 KiB

MS22 Phase 1: DB-Centric Agent Fleet Architecture

Design Principles

  1. Minimal env vars — Only DATABASE_URL and MOSAIC_SECRET_KEY needed to start
  2. DB-centric config — All runtime config lives in Postgres, managed via WebUI
  3. Mosaic is the gatekeeper — Users never talk to OpenClaw directly
  4. Onboarding-first — Breakglass user + wizard on first boot, no manual config files
  5. Generic product — No hardcoded agent names, models, providers, or endpoints

Bootstrap Flow

docker stack deploy (2 env vars)
         │
         ▼
┌─────────────────────┐
│  Postgres migration  │ ← creates tables, no seed data
└─────────────────────┘
         │
         ▼
┌─────────────────────┐
│  User opens WebUI   │ ← detects empty config
└─────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────┐
│           ONBOARDING WIZARD                 │
│                                             │
│  Step 1: Create breakglass admin            │
│          (username + password → bcrypt)      │
│                                             │
│  Step 2: Configure OIDC (optional)          │
│          (provider URL, client ID, secret)  │
│                                             │
│  Step 3: Add LLM provider                   │
│          (type, API key, endpoint, test)    │
│                                             │
│  Step 4: Configure agents                   │
│          (roles, model assignments)         │
│          Auto-generates gateway tokens      │
│                                             │
│  Step 5: Deploy summary + health check      │
└─────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────┐
│  Agents pick up     │ ← GET /api/internal/agent-config/:name
│  config from DB     │
└─────────────────────┘

Auth Layers

Flow Method Details
User → Mosaic WebUI Breakglass (local) or OIDC Breakglass always available as fallback
Mosaic API → OpenClaw Bearer token Auto-generated per agent, stored encrypted in DB
OpenClaw → Mosaic API (config) Bearer token Same agent token, validated by Mosaic
OpenClaw → LLM providers API keys Stored encrypted in DB, delivered via config endpoint
Admin → Settings RBAC Admin role required for provider/agent/OIDC config

Database Schema (new tables)

SystemConfig

Key-value store for global settings (singleton-ish).

model SystemConfig {
  id        String   @id @default(cuid())
  key       String   @unique  // "oidc.issuerUrl", "oidc.clientId", "onboarding.completed", etc.
  value     String             // plaintext or encrypted (prefix: "enc:")
  encrypted Boolean  @default(false)
  updatedAt DateTime @updatedAt
}

LlmProvider

LLM provider configurations.

model LlmProvider {
  id          String   @id @default(cuid())
  name        String   @unique  // "zai", "openai", "anthropic", "ollama-local", etc.
  displayName String             // "Z.ai", "OpenAI", "Local Ollama"
  type        String             // "zai" | "openai" | "anthropic" | "ollama" | "custom"
  baseUrl     String?            // null for built-in providers, URL for custom/ollama
  apiKey      String?            // encrypted
  apiType     String   @default("openai-completions")  // openai-completions | anthropic-messages | etc.
  models      Json     @default("[]")  // available model list [{id, name, contextWindow, maxTokens}]
  isActive    Boolean  @default(true)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  agents      AgentModelAssignment[]
}

AgentConfig

Per-agent configuration (replaces old OpenClawAgent).

model AgentConfig {
  id              String   @id @default(cuid())
  name            String   @unique  // "mosaic-main", "mosaic-projects", etc.
  displayName     String             // "Main Orchestrator", "Projects", etc.
  role            String             // "orchestrator" | "developer" | "researcher" | "operations"
  gatewayUrl      String             // internal Docker URL: "http://mosaic-main:18789"
  gatewayToken    String             // encrypted — auto-generated
  isActive        Boolean  @default(true)
  personality     String?            // SOUL.md content for this agent
  toolPermissions Json     @default("[]")  // allowed tool list
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt
  modelAssignment AgentModelAssignment?
}

AgentModelAssignment

Links agents to providers and models.

model AgentModelAssignment {
  id           String      @id @default(cuid())
  agentId      String      @unique
  agent        AgentConfig @relation(fields: [agentId], references: [id])
  providerId   String
  provider     LlmProvider @relation(fields: [providerId], references: [id])
  primaryModel String      // "glm-5", "claude-sonnet-4-6", "cogito", etc.
  fallbacks    Json        @default("[]")  // [{providerId, model}]
  updatedAt    DateTime    @updatedAt
}

BreakglassUser

Local admin user (no OIDC dependency).

model BreakglassUser {
  id           String   @id @default(cuid())
  username     String   @unique
  passwordHash String             // bcrypt
  isActive     Boolean  @default(true)
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt
}

Internal Config Endpoint

GET /api/internal/agent-config/:agentName

  • Auth: Bearer token (agent's own gateway token)
  • Returns: Complete openclaw.json generated from DB tables
  • Includes: model config, provider credentials (decrypted), tool permissions

Docker Compose (simplified)

services:
  mosaic-api:
    image: mosaic/api:latest
    environment:
      DATABASE_URL: ${DATABASE_URL}
      MOSAIC_SECRET_KEY: ${MOSAIC_SECRET_KEY}

  mosaic-web:
    image: mosaic/web:latest
    environment:
      NEXT_PUBLIC_API_URL: http://mosaic-api:4000

  mosaic-main:
    image: alpine/openclaw:latest
    command: ["/config/entrypoint.sh"]
    environment:
      DATABASE_URL: ${DATABASE_URL}
      MOSAIC_API_URL: http://mosaic-api:4000
      MOSAIC_SECRET_KEY: ${MOSAIC_SECRET_KEY}
      AGENT_NAME: mosaic-main
    volumes:
      - mosaic-main-state:/home/node/.openclaw

  # Additional agents follow same pattern, only AGENT_NAME differs

Entrypoint (simplified)

#!/bin/sh
# Fetch config from Mosaic API, write openclaw.json, start gateway
CONFIG=$(curl -sf "${MOSAIC_API_URL}/api/internal/agent-config/${AGENT_NAME}" \
  -H "Authorization: Bearer ${MOSAIC_SECRET_KEY}")
echo "$CONFIG" > /tmp/openclaw.json
export OPENCLAW_CONFIG_PATH=/tmp/openclaw.json
exec openclaw gateway run --bind lan --auth token

Task Breakdown

Phase 1a: DB Schema + Internal Config API

  • Prisma schema: SystemConfig, LlmProvider, AgentConfig, AgentModelAssignment, BreakglassUser
  • Migration
  • Internal config endpoint: generates openclaw.json from DB
  • Encryption/decryption service for API keys and tokens

Phase 1b: Onboarding Wizard (API)

  • Detect first-boot (no breakglass user exists)
  • POST /api/onboarding/breakglass — create admin
  • POST /api/onboarding/oidc — save OIDC config
  • POST /api/onboarding/provider — add LLM provider + test connection
  • POST /api/onboarding/agents — configure agent fleet
  • POST /api/onboarding/complete — mark onboarding done

Phase 1c: Onboarding Wizard (WebUI)

  • Multi-step wizard component
  • Breakglass user creation form
  • OIDC config form (skip option)
  • LLM provider form with connection test
  • Agent configuration with model picker
  • Summary + deploy health check

Phase 1d: Settings Pages (WebUI)

  • Settings/Providers — CRUD for LLM providers
  • Settings/Agents — model assignments, personalities, status
  • Settings/Auth — OIDC config, breakglass password reset
  • All behind admin RBAC

Phase 1e: Docker Compose + Entrypoint

  • Simplified compose (AGENT_NAME + shared env vars)
  • Entrypoint: curl config from API, write, start
  • Health check integration

Phase 1f: Chat Proxy

  • Mosaic API routes WebUI chat to correct OpenClaw agent
  • SSE streaming pass-through
  • Agent selector in WebUI

Open Questions

  1. Should agents auto-restart when config changes in DB? (webhook/signal vs polling)
  2. Should breakglass user be created via CLI as alternative to WebUI wizard?
  3. Config cache TTL in agents? (avoid hitting API on every request)