Architecture

System design overview covering Kubernetes orchestration, the config sync pipeline, and infrastructure.

Jarble is a monorepo containing a Next.js 15 frontend, an Express + tRPC API backend, shared packages, and infrastructure configuration. Users interact with the frontend, which communicates with the API via type-safe tRPC calls. The API orchestrates Kubernetes pods that run bot runtimes with MCP servers for rich UI rendering.

System Overview

Monorepo Structure
==================

develop-monorepo/
  |-- Jarble-mvp/              Next.js 15 frontend (App Router, React 19)
  |-- jarble-api-main/         Express + tRPC API backend
  |-- shared/
  |   |-- component-manifest/  Single source of truth for component metadata
  |-- scripts/                 CI/build scripts
  |-- infrastructure/          Terraform IaC + Auth0 config
  |-- runtimes/                Bot runtime implementations

Architecture Diagram

                         +------------------+
                         |     Browser      |
                         |  (Next.js SSR +  |
                         |   React 19 SPA)  |
                         +--------+---------+
                                  |
                     tRPC (SuperJSON) + SSE streams
                                  |
                         +--------+---------+
                         |   Next.js 15     |
                         |   App Router     |
                         |   (port 3000)    |
                         +--------+---------+
                                  |
                         tRPC HTTP + REST
                                  |
                         +--------+---------+
                         |  Express + tRPC  |
                         |    API Server    |
                         |   (port 3001)    |
                         +---+----+----+----+
                             |    |    |
                  +----------+    |    +----------+
                  |               |               |
           +------+------+  +----+----+  +-------+-------+
           |   Drizzle   |  |  Stripe |  |   Auth0 JWKS  |
           |  ORM + DB   |  |   API   |  |  Verification |
           +-------------+  +---------+  +---------------+
                  |
        MySQL / PostgreSQL / SQLite
                             |
                    kubectl exec / K8s API
                             |
              +--------------+--------------+
              |         K8s Cluster         |
              |        (K3s / Hetzner)      |
              |                             |
              |  +------------------------+ |
              |  | Pod: dep-<id>          | |
              |  |  +------------------+  | |
              |  |  | OpenClaw Runtime |  | |
              |  |  | (Node.js 22)     |  | |
              |  |  +--------+---------+  | |
              |  |           |            | |
              |  |  +--------+---------+  | |
              |  |  | MCP stdio Server |  | |
              |  |  | (jarble-ui-      |  | |
              |  |  |  server.js)      |  | |
              |  |  +------------------+  | |
              |  |                        | |
              |  |  PVC: /data/ (20Gi)    | |
              |  |  Secret: LLM keys +   | |
              |  |    platform tokens     | |
              |  +------------------------+ |
              +-----------------------------+

Deployment Flow

When a user creates a new bot deployment, the system provisions four Kubernetes resources and boots the pod. The entire flow is orchestrated by the deployment.create tRPC mutation.

1. User submits wizard form (runtime, template, LLM provider, model)
       |
2. API creates deployment record in DB (status: "creating")
       |
3. API provisions 4 K8s resources in the "jarble" namespace:
       |-- Deployment: dep-<id>  (1 replica, port 18789)
       |-- Secret: secret-<id>  (LLM keys, platform tokens, metadata)
       |-- PVC: pvc-<id>        (Longhorn, 20Gi RWO, mounted at /data)
       |-- Service: ClusterIP   (inter-pod communication)
       |
4. Pod boots:
       |-- Container image: ghcr.io/jarble-ai/openclaw:latest
       |-- npm install (first boot only, skipped if .initialized exists)
       |-- MCP server starts on stdio
       |-- OpenClaw gateway starts on port 18789
       |
5. API polls readiness probe (TCP 18789, 30 attempts x 2s = 60s max)
       |
6. API writes config files to PVC via kubectl exec:
       |-- /data/config/openclaw.json  (channel configs, agent model)
       |-- /data/config/soul.md        (system prompt + service snippets)
       |-- /data/skills/*.json         (installed skill configs)
       |
7. API updates DB status to "running"

Chat Architecture

The chat system streams bot responses via Server-Sent Events using the AG-UI event protocol. Messages flow through the API, which proxies them to the bot pod via kubectl exec against the OpenClaw gateway.

User types message in browser
       |
POST /api/tambo-agent { deploymentId, message, threadId }
       |
API authenticates request (Auth0 JWT)
       |
API finds pod for deployment (kubectl get pods)
       |
API opens exec session to pod (kubectl exec)
       |
Bot processes message through LLM (Anthropic/OpenAI/Google/OpenRouter)
       |
Bot calls MCP tools (render_ui, define_component, etc.)
       |
API streams response as SSE events:
  |-- TEXT_MESSAGE_START / CONTENT / END
  |-- UI_BLOCK_START / PROPS / END
  |-- RUN_FINISHED
       |
Frontend renders text into chat bubbles and UI blocks onto the canvas

Canvas System

The canvas system renders rich UI components (charts, tables, 3D visualizations, forms) inline in the chat conversation. Components are defined in a shared manifest and rendered through a multi-stage pipeline.

Bot calls render_ui MCP tool with component type + props
       |
Response contains jarble_ui fenced blocks in markdown
       |
uiBlockParser extracts blocks (brace-depth JSON parser)
       |
Server-side validation: library URLs checked against CDN allowlist
       |
Frontend receives parsed UI blocks via SSE
       |
autoFixProps runs 20 repair rules
       |
Zod schema validation (per-component schemas from manifest)
       |
CanvasRenderer renders component with error boundary
       |
SimpleCanvasGrid positions cards in responsive CSS grid

The system supports 37 active component types plus 1 alias (canvas resolves to sandbox). Categories include Display (card, stat_grid, data_table, chart, tabs, accordion), Charts (recharts-based), Interactive (button_group, form), Media (video, audio, image_gallery), and Specialized (code_editor, map, sandbox).

Config Sync Pipeline

The configSync service synchronizes configuration between the database and the pod's persistent volume. It is triggered by credential saves, skill installations, service installations, and deployment updates.

Trigger: credential save, skill install, service install, deployment update
       |
syncConfigsToPvc(deploymentId)
       |
buildDeploymentFields()
  |-- Load deployment from DB
  |-- Load platform credentials (decrypt)
  |-- Load installed skills
  |-- Load service instruction snippets
       |
renderConfigs()
  |-- soul.md: system prompt + service instruction sections
  |-- openclaw.json: channel configs, agent model
  |-- skills/*.json: one file per installed skill
       |
writeConfigsToPvc() via kubectl exec
       |
updateDeploymentSecret() (K8s Secret with env vars)
       |
restartDeployment() (scale to 0, then back to 1)
       |
Poll for readiness (30 attempts x 2s = 60s max)

MCP Server

Each bot pod runs a custom MCP (Model Context Protocol) server via stdio. The server exposes tools that allow the LLM to render rich UI, define custom components, and interact with the component registry.

ToolDescription
render_uiRenders a UI component by type and props. Outputs a jarble_ui fenced block.
define_componentDefines a reusable custom component and saves it to /data/components/.
list_componentsLists all available components (built-in + custom) with descriptions and prop schemas.
component_referenceReturns detailed reference for a specific component type including its Zod schema.
save_canvas_fileSaves canvas component data to /data/files/ for persistence across sessions.

Pods also fetch platform skills dynamically from the API on boot. After a 3-second delay, the MCP server calls GET /debug/platform-skills to get the latest skill definitions. If the API is unreachable, it falls back to a PVC-cached version, and finally to baked-in defaults.

Database

The API uses Drizzle ORM with support for three database backends. MySQL is the production database, PostgreSQL is an alternative, and SQLite is used for local development.

TablePurpose
usersAuth0 user ID, Stripe customer ID, email verification, free trial tracking
deploymentsBot instances with K8s state, LLM config, subscription links
runtimeCatalogAvailable runtimes with hardware specs, pricing tiers, feature flags
platformCredentialsAES-256-GCM encrypted messaging platform tokens
skillsCatalogGlobal skill catalog (Web Search, Weather, Calculator, etc.)
deploymentSkillsMany-to-many: skills installed on deployments
marketplaceComponentsPublished components with manifest, code, author, pricing, moderation status
componentVersionsVersion history for marketplace components
componentInstallsComponent installations on deployments
componentReviewsRatings and text reviews for components
componentPurchasesPurchase records for paid components
creatorProfilesMarketplace creator profiles
marketplaceServicesPublished services (self-hosted and remote)
serviceComponentsMany-to-many: services to bundled components
serviceSkillsMany-to-many: services to bundled skills
serviceInstallsService installations on deployments
serviceCredentialsPer-install HMAC signing secrets for hosted services
processedWebhookEventsStripe webhook idempotency tracking

Infrastructure

Production infrastructure runs on Hetzner Cloud managed by Terraform. The Kubernetes cluster uses K3s (lightweight K8s distribution) with Longhorn for persistent storage.

ComponentTechnologyDetails
Cloud providerHetzner CloudEuropean cloud with competitive pricing
IaCTerraformReproducible provisioning of servers, networks, DNS
KubernetesK3sLightweight K8s for edge and single-node deployments
StorageLonghornDistributed storage; each deployment gets 20Gi RWO PVC
Container registryGitHub Container RegistryImages at ghcr.io/jarble-ai/openclaw:latest
NamespacejarbleAll pods, secrets, PVCs, and services

Tech Stack Summary

LayerTechnologyNotes
Frontend frameworkNext.js (App Router)v15 with React 19
StylingTailwind CSS + shadcn/uiTailwind v4 with CSS variable tokens
AnimationFramer MotionLayout animations, page transitions
ChartsRechartsUnified chart component
Chat framework@assistant-ui/reactExternalStoreRuntime for thread-based chat
Node graph@xyflow/react + dagreInteractive deployment graph
Code editorMonaco EditorVS Code engine for code_editor component
MapsLeafletInteractive maps with markers and polygons
API layerExpress + tRPC + SuperJSONType-safe RPC
ORMDrizzleMySQL, PostgreSQL, SQLite drivers
AuthenticationAuth0RS256 JWT + JWKS verification
PaymentsStripeDynamic pricing, webhook processing
MCP serverCustom stdio serverjarble-ui-server.js in bot pods
Error trackingSentryClient + server error tracking
AnalyticsPostHogProduct analytics and feature flags
HTML sanitizationDOMPurifyClient-side sanitization
Schema validationZodv4 (frontend) / v3 (API)
Architecture | Jarble Wiki