Skip to content

Fabric Architecture

Physical layout, transport contract, and session flow as of 2026-05-05. This page summarizes; full topology is in fabric-hetzner-handoff-2026-05-04.md §1.

Physical topology

                                INTERNET

                    ┌──────────────┼──────────────┐
                    │                             │
            fabric.optimal.miami           optimal.miami
            (CF orange-cloud)              (CF orange-cloud)
                    │                             │
                    ▼                             ▼
        CF Tunnel: fabric-prod           CF Tunnel: a6f4487b-…
        UUID 00633812-46c8-4e70-…        (Pi's existing tunnel)
                    │                             │
                    ▼                             ▼
              ┌──────────┐                  ┌──────────┐
              │ HETZNER  │                  │   PI 5   │
              │  CX32    │  ◀── pair ──▶    │  16 GB   │
              │ Debian12 │                  │ Bookworm │
              │ 178.156. │                  │ 100.94.  │
              │ 203.234  │                  │ 77.114   │
              └────┬─────┘                  └────┬─────┘
                   │                             │
              cloudflared                   cloudflared (separate tunnel)
              optimalos.service             optimalos.service (Pi-side, classic)
              caddy.service                 n8n / strapi / openclaw-gateway
              (Caddy unused for             optimal-discord / optimal-docs
               tunnel; bypassed)            Phoenix (docker)

         [SAME bundle on both — gated by hostname (client) +
                 DEPLOYMENT env (server). See "Deployment modes" below.]

The Pi tunnel is untouched. No DNS cutover happened. fabric.optimal.miami is a separate hostname on a separate tunnel. Since 2026-05-05 (commit bf69427), both origins serve the same bundle — the legacy and fabric experiences are decided at runtime, not at build time. See Deployment modes for the full primer.

Deployment modes

Two modes, one bundle:

  • Legacy modeoptimal.miami (Pi). Home / Board / Loom / Settings + tmux attach + [XFER]. No Sessions tab, no fuel meter, no vault. Fabric-only API routes return 410 GONE.
  • Fabric modefabric.optimal.miami (Hetzner). Everything legacy mode has, plus Sessions tab + fuel meter + /vault/* + Cockpit chrome.

The gate runs at request-time:

  • Serverprocess.env.DEPLOYMENT === "hetzner-cloud" in src/server.ts controls whether /api/vault, /api/auth/setup-init, /api/auth/devices, and /api/fuel are mounted. Pi has no DEPLOYMENT env → those return 410.
  • Clientclient/fabric-mode.ts isFabricMode() checks window.location.hostname against the FABRIC_HOSTNAMES set (with fabric.* wildcard + window.__FABRIC_MODE__ override seam). client/main.ts showApp() reads it to conditionally render Sessions tab, fuel meter, and vault routes.

Why this exists, what's gated where, how to add a new fabric origin, and how to debug a leak: Deployment modes.

Hetzner box

PropertyValue
TypeCX32 (€5.83/mo, 2 vCPU, 7.6 GB RAM)
IPv4178.156.203.234
IPv62a01:4ff:f4:699d::/64
OSDebian 12 bookworm, kernel 6.1.0-41-amd64
Disk75 GB (1.3 GB used)
SSHssh -i ~/.ssh/id_ed25519 root@178.156.203.234 (key auth, no password)
UFWpublic 22 + Tailscale 22 (drop public after Tailscale joins)

Filesystem layout:

  • /opt/optimalos/app/ — bundled Bun server.js + Vite client/ assets (~3.5 MB total)
  • /opt/optimalos/workflows/ — Loom workflow modules
  • /opt/optimalos/operations/ — Loom operation modules
  • /opt/optimalos/data/ — runtime data (SQLite)
  • /opt/optimalos/secrets.env — mode 0600 optimal:optimal, holds JWT_SIGNING_KEY, INVITE_PASSWORD, Supabase keys
  • /etc/cloudflared/00633812-…json — per-tunnel credentials
  • /etc/cloudflared/config.ymlfabric.optimal.miamihttp://localhost:3000
  • /etc/systemd/system/optimalos.serviceUser=optimal, runs bun run server.js

Services: caddy.service (port 80, unused for tunnel), optimalos.service (port 3000), cloudflared.service (4 redundant CF edges).

Transport contract

Wire-locked in commit 7f55c3b (Phase 10c-1.1). The contract is intentionally minimal so we can swap NATS or QUIC later without rewriting the application layer.

  • Channel: wss://fabric.optimal.miami/ws/device (or /ws/browser)
  • Encoding: NDJSON, one envelope per line
  • Auth: JWT in Sec-WebSocket-Protocol header (HS256, JWT_SIGNING_KEY rotates server-side)
  • Direction: device dials out (Anthropic / Cursor pattern) — cloud has no inbound device port
  • Heartbeat: device → cloud every 20s, includes capabilities digest
  • Envelope shape: {corrId, type, direction, payload, ts}

corrId reuse rule — child envelopes (session.stdout, session.exit) reuse parent's corrId so the cloud router can pipe them back to the right browser SSE stream. Documented at src/daemon/handlers/session-start.ts:18-25.

JWT TTLs:

Token typeTTL
Browser session12 h
Device JWT30 d
Pairing token10 min
Recovery token1 h

Open issue (T4, P1): the device daemon does not currently cross-check vault_recipients.revoked_at before honoring its 30-day JWT. Cloud soft-revoke happens, but a device with a stale JWT can still serve sessions until rotation. See Phase Status → "Left to test".

Session flow

Browser                 Cloud (Hetzner)                  Device (Pi/laptop)
   │                          │                                │
   │  POST /api/sessions       │                                │
   │  { harness, prompt, ... } │                                │
   ├─────────────────────────▶ │                                │
   │                          │  envelope { type:               │
   │                          │    "session.start",             │
   │                          │    corrId: $X }                 │
   │                          ├──────────────────────────────▶  │
   │                          │                                │ spawn harness
   │                          │   stdout chunks                 │ pipe stdout
   │                          │  ◀──────────────────────────────┤
   │   SSE: data lines         │                                │
   │  ◀────────────────────── ─┤                                │
   │                          │   envelope { type:              │
   │                          │     "session.exit",             │
   │                          │     code: 0 }                   │
   │                          │  ◀──────────────────────────────┤
   │   SSE: close              │                                │
   │  ◀────────────────────── ─┤                                │

Capability routing (Phase 12-1, pending): today, session.start routes to the single available device for the requested harness. Phase 12 promotes that to a RAM-aware scheduler that considers capability score, current load, and command allowlist scope. Stub at src/server/device-router.ts:24.

Storage layout

WhereWhatPersistence
Cloud Supabase (hbfalrpswysryltysonm)vault_entries, vault_recipients, vault_access_log, devices, pairing_tokensPersistent
Cloud SQLite (/opt/optimalos/data/)fabric_sessions, Loom run statePersistent (in-memory v1, promoted in Phase 14)
Device disk~/.config/optimalos/keys/device.key (mode 0600)Persistent
Device RAMDecrypted credentials (15-min TTL, best-effort zero)Ephemeral
Browser localStorageTrusted-device fingerprint markerPer-fingerprint (30-day reset)

Open issue (T7, P1): device → cloud fetch calls use system TLS trust store. No origin pubkey pinning yet. CA compromise + MITM still possible. Remediation persists cloud's TLS pubkey SHA-256 at ~/.config/optimalos/keys/cloud-pin.txt during pairing; checked on every fetch.

  • Charter: ~/.openclaw/workspace/optimalOS/docs/superpowers/specs/2026-05-03-fabric-charter.md (501 lines)
  • Plan: ~/.openclaw/workspace/optimalOS/docs/superpowers/plans/2026-05-03-fabric-implementation.md (880 lines)
  • Decision Ledger: ~/.optimalos/transfers/fabric-design/03-decision-ledger.md (all 25 architectural decisions)
  • Handoff: ~/.optimalos/transfers/fabric-hetzner-handoff-2026-05-04.md (load-bearing on session start)

Built by Carlos Lenis in Miami