XBOS-X
X
BOS-X chat → app surface
ready
◢◤

Describe an app to build. The agent streams its work here; the live preview opens on the right when the build settles.

no preview yet

Your app preview will appear here once the build completes.

BOS-X — Architecture

A lean, self-owned stack that turns a chat message into a live web app: a buildless Surface talks to one Node Runner; the runner resolves an Engine that drives a managed Sandbox to write code, routes every model call through a key-injection proxy so the sandbox never sees a provider key (Invariant 6), persists builds durably, and serves the live preview. State lives in an isolated Supabase.

The system at a glance

 SURFACE  (buildless SPA)        x.dev.bos.pro · Cloudflare Pages, also served by the runner
   built-apps rail │ chat + SSE transcript │ live preview iframe (sandboxed, no same-origin)
        │
        │   POST /api/run (SSE)   +   GET /api/* (apps · messages · models · health)
        ▼
 RUNNER  (one Node/TS service)   x-runner.bos.pro · durable CF tunnel  (laptop+Colima today; VPS = BLK-12)
   server.ts → runTurn → engine registry →  oma            (opencode serve, gated, default)
        │     fallback chain: oma → aider → router-direct
        │                                  aider           (LiteLLM, edits via git diff)
        │                                  router-direct   (1 call, WRITE_FILE)  ← actually runs in deploy
        │
        ├─ KEY-INJECTION PROXY  (Invariant 6):  strip inbound auth → inject the real router Bearer
        │      the sandbox gets ONLY the proxy URL + a non-secret token — never a provider key
        │
        └─ durable builds:  runner/built-apps/{app_id}/  →  GET /apps/{app_id}/*  (survives sandbox reaping)
             │                            │                              │
             ▼                            ▼                              ▼
   SUPABASE (isolated)          MANAGED SANDBOX               MODEL ROUTER (read-only)
   gsuafvvzsdfkfybgfzjv         Daytona (default) / E2B       rundev.cloved.ai/v1
   apps · messages ·            build → preview               bos:* aliases · provider failover
   model_runs · engine_sessions egress allowlist + proxy      no /v1/models · no streaming
   RLS forced · runner writes   URL only (Invariant 6)        (proxy de-streams)
          

Components

Surface

surface/

Buildless HTML/CSS/JS SPA: chat + SSE transcript + a sandboxed preview iframe (no allow-same-origin). Served by the runner and as a Cloudflare Pages copy at x.dev.bos.pro; its config.js pins BOSX_API_BASE → x-runner.bos.pro.

Runner

runner/src/server.ts

The one Node/TS service: every /api/* route, SSE framing, sandbox provisioning, durable build persistence, static serving.

Engine seam

runner/src/engines/*

IAgentEngine registry: oma (opencode, gated, default), aider (LiteLLM, git-diff), router-direct (one call + WRITE_FILE — runs in the live deploy). Fallback oma→aider→router-direct.

Key-injection proxy

runner/src/router/keyInjectionProxy.ts

Invariant 6. Strips inbound auth, injects the real router key, and de-streams (the router rejects stream:true). The sandbox only ever gets the proxy URL + a non-secret token.

Sandbox adapter

runner/src/sandbox/*

One branded SandboxedSpec; Daytona (default) / E2B (alt). All code execution happens here — never on the host (host engines are fail-closed).

Model router

rundev.cloved.ai/v1

Read-only egress; bos:* aliases over provider failover. No /v1/models, no streaming (the proxy de-streams).

Supabase (isolated)

gsuafvvzsdfkfybgfzjv

apps · messages · model_runs · engine_sessions (the last is a stub). RLS forced; the runner writes with the service-role key (host-checked, fail-closed).

Factory (offline)

factory/

Not in the request path: prebro_orchestrator.py (DAG build-waves) + proposal_debate.py / playwright-verify/goal_judge_debate.py (multi-model judge panels via callmodel.py).

Request flow — chat → live app

  1. The Surface POSTs {prompt, model, engine_ref?, session_id, app_id?} to /api/run and opens an SSE stream.
  2. The runner mints run-<id>, validates the model against a server-side allowlist, mints/reuses app-<id>, and emits meta{app_id}.
  3. It provisions a managed sandbox via sandboxedSpec() — which asserts Invariant 6: the sandbox env carries the proxy URL + a token, never a provider key.
  4. It persists the user turn to messages, loads any follow-up history + existing files, then runs the engine (fallback oma→aider→router-direct).
  5. The engine calls the model through the router, parses WRITE_FILE blocks, writes the files; every step streams to the Surface as engine_event.
  6. On success the files are persisted durably to runner/built-apps/{app_id}/ and the runner emits preview{url = /apps/{app_id}/} — a runner-served URL that survives the sandbox being torn down.
  7. The build is recorded in apps, the assistant turn in messages, and every attempt (success/fail) in model_runs; done{status} closes the stream.
  8. The Surface loads the preview URL into the sandboxed iframe (no allow-same-origin — generated apps are untrusted).

Invariants & trust boundaries

  • Invariant 6 — one egress authority. The runner holds the real model-router key; the sandbox never does. The key-injection proxy is the only hop that adds it.
  • One sandbox adapter. All execution is the managed sandbox; no engine spins up its own. (Host engines are fail-closed off until exec co-location ships.)
  • Engine ⊥ model ⊥ memory. The engine is a registry plugin, the model is the router's job, cross-engine continuity is a small summary-handoff.
  • Isolated state. Only the Supabase project gsuafvvzsdfkfybgfzjv; RLS is forced and every write is runner-only (service-role, host-checked).
  • Untrusted preview. The preview iframe deliberately omits allow-same-origin so a generated app can't read the parent/runner origin.

Status & honest gaps

  • The engine that actually runs in the live deploy is router-direct; oma/aider are registered but fail-closed until sandbox exec co-location ships.
  • The runner is hosted on a laptop (Colima + the durable Cloudflare tunnel x-runner.bos.pro); a dedicated Prebro VPS is still pending (BLK-12).
  • engine_sessions exists in the schema but its store is a stub — cross-engine continuity is not yet wired.
  • There is no end-user auth yet: anon read is intentional; all writes are runner-only.
  • Canonical spec = memory/ARCHITECTURE.md (v2.0, “lean self-owned stack”), not the superseded root 01_ARCHITECTURE.md (v1.0).

Сравнение архитектур (RU)

bos-x — намеренно «тонкий» (lean) стек, который уже работает в проде. Ниже — сравнение с двумя эталонными архитектурами того же класса («чат → приложение в песочнице»): исследовательской modular-agentic-system и SaaS-рантаймом vbp-german runner-v2. Для каждой — схема-различие и таблица дельт.

bos-x ↔ modular-agentic-system

modular-agentic-system — это в основном проектная документация плюс три прототипа. Её суть — ядро с двумя реестрами: политика (приём → сессия → оркестратор → расчёт) живёт в крошечном ядре, которое никогда не видит подложку, а две независимые оси — харнесс (петля агента) и окружение (песочница) — подключаются по ref-строке через реестры. Ключевая дисциплина — непрозрачный EnvironmentHandle и CI-grep-гейт, который роняет сборку, если в ядро просочилось слово про подложку (container_id, dockerode, workspace_dir). Песочницы договариваются о возможностях (capability negotiation), а самый сложный момент — публикация порта превью — спрятан за единым контрактом exposePort(port)→url.

bos-x устроен проще: одна ось-реестр (движки oma/aider/router-direct), а песочница — единственный адаптер (Daytona по умолчанию, E2B рядом), а не вторая ось-реестр. Границу гарантируют брендированный SandboxedSpec и Инвариант 6 (прокси-инъекция ключа), а не CI-grep-гейт. Итог: bos-x — тоньше и уже в проде; modular-agentic-system — модульнее и строже к развязке слоёв, но почти не построена (реально запускается лишь sdk × docker).

 bos-x  (в проде)
 ─────────────────────────────
 Surface SPA (buildless)
   │
 Runner (один Node-сервис)
   ├ engine registry (1 ось):
   │     oma · aider · router-direct
   ├ key-injection proxy (Инв.6)
   └ sandbox adapter
         Daytona/E2B — один адаптер,
         НЕ реестр
 Supabase: apps·messages·runs
 превью: durable /apps/{id}/ на runner
            
 modular-agentic-system  (эталон/прототип)
 ─────────────────────────────
 Studio UI (React/Carbon)
   │
 Core-ядро (ТОЛЬКО политика)
   ├ registry ХАРНЕССОВ (ось 1)
   │     sdk · opencode · …
   └ registry ОКРУЖЕНИЙ (ось 2)
         docker · e2b · vercel …
   EnvironmentHandle (непрозрачный)
   exposePort(port)→url
   CI-grep-гейт против утечки подложки
 запускается лишь sdk × docker
            
Аспектbos-xmodular-agentic-systemΔ дельта
Оси подключения1 (движки)2 (харнесс + окружение)MAS модульнее
Песочницаодин адаптер (Daytona/E2B)реестр окружений по refMAS гибче, bos-x проще
Развязка слоёвSandboxedSpec + Инв.6непрозрачный handle + CI-grep-гейту MAS жёстче механически
Зрелостьв продедокументация + 3 прототипаbos-x реальнее
Секрет ключапрокси-инъекция (Инв.6)ключ в ProvisionSpec.env адаптераоба прячут ключ от песочницы

bos-x ↔ vbp-german runner-v2

runner-v2 — полноценный многопользовательский SaaS-рантайм (Express 5, ~28 групп маршрутов). Песочница — облачная Daytona (одна на сессию, hibernate/wake). Состояние сессии двухуровневое: горячее в Redis + долговечное в Supabase с восстановлением после краша. Есть writer-lock (один писатель на проект), 4-канальная сборка контекста (ContextFrame) с аудитом и бюджетом токенов, цепочка BYOK (ключ пользователя → организации → платформы), именованные профили агентов и под-агенты с ограничением глубины (≤4), биллинг/энтайтлменты/маркетплейс и «глубокий» health-чек. Провайдерский ключ резолвится на хосте и в песочницу не попадает — та же дисциплина, что у bos-x.

bos-x намеренно тоньше: один сервис, тонкий surface, движковый seam (3 движка), прокси-инъекция ключа (Инв.6) как первоклассная граница, Daytona/E2B и Supabase с 4 таблицами. Чего у bos-x пока нет: облачный хостинг рантайма (сейчас ноутбук + Colima, VPS — BLK-12), восстановление сессий после краша (состояние в памяти), формальный writer-lock, аудируемая сборка контекста, failover/deep-health, биллинг. Итог: runner-v2 — операционно куда зрелее, но тяжелее (часть абстракций — заглушки: failover, model-health admission, MCP — но deep-health реальный); bos-x — легче, честнее про «построено / заглушка» и с более чистой границей ключа.

 bos-x  (lean, в проде)
 ─────────────────────────────
 один Node-сервис, /api/*
 движки: oma · aider · router-direct
 key-injection proxy (Инв.6)
 sandbox: Daytona/E2B
 сессии: в памяти (engine_sessions=stub)
 одна-запись: не формализовано
 контекст: системный промпт
 health: /api/health
 хост: ноутбук + Colima (VPS=BLK-12)
 биллинга нет
            
 runner-v2  (SaaS-рантайм)
 ─────────────────────────────
 Express 5, ~28 групп маршрутов
 профили агентов + под-агенты (≤4)
 ключ на хосте (в песочницу не идёт)
 sandbox: Daytona облако, 1/сессия, hibernate
 сессии: Redis(горячо)+Supabase(durable)+recovery
 writer-lock (Redis, NX, TTL)
 ContextFrame: 4 канала, аудит, бюджет токенов
 health: deep (пингует Redis), BYOK-проверки
 хост: облако
 биллинг/энтайтлменты/маркетплейс
            
Аспектbos-xrunner-v2Δ дельта
Хост рантайманоутбук + Colima (VPS=BLK-12)облакоrunner-v2 устойчивее
Состояние сессиив памяти (stub)Redis + Supabase + recoveryrunner-v2 переживает краш
Один-писательне формализованоwriter-lock (Redis NX TTL)заимствовать
Сборка контекстасистемный промптContextFrame: 4 канала + аудитзаимствовать
Граница ключапрокси-инъекция (Инв.6, first-class)ключ на хостеу bos-x чище
Объёмодин сервис, тонкийSaaS-плоскость (часть — заглушки)bos-x легче

Что заимствовать · где bos-x лучше / хуже

Заимствовать в bos-x (по приоритету)

Из runner-v2 (операционная зрелость):

  1. Облачный хостинг рантайма + сессионный lifecycle песочницы (одна Daytona-песочница на сессию, hibernate/wake, hygiene-sweep) — bos-x уже использует Daytona для песочниц, но сам рантайм живёт на ноутбуке (Colima); вынос рантайма в облако + пер-сессионный lifecycle убирают корневую причину падений x-runner и закрывают BLK-12.
  2. Двухуровневое состояние сессии: Redis (горячо) + Supabase (durable) + восстановление после краша — переживает рестарт рантайма.
  3. writer-lock на проект (Redis, NX, TTL) — один писатель, без гонок (ложится на модель «строка apps = тред»).
  4. 4-канальный ContextFrame с аудитом и бюджетом токенов — инспектируемая и воспроизводимая сборка контекста.
  5. «Глубокий» health-чек, пингующий зависимости — лечит «врущий /api/health».
  6. Цепочка BYOK (ключ пользователя → организации → платформы), резолв на хосте — расширяет границу ключа bos-x.

Из modular-agentic-system (модульность и строгость развязки):

  1. CI-grep-гейт против утечки подложки в ядро — механически защищает Инвариант 6 и seam песочницы.
  2. Флаги возможностей песочницы + лестница деградации (publicPorts/snapshot/nativeGit) — один кернел ведёт Daytona и E2B с мягкой деградацией.
  3. Контракт exposePort(port)→url, прокси спрятан в адаптере — держит Daytona ↔ E2B взаимозаменяемыми.
  4. Именованный контракт кросс-движковой непрерывности (производное предложение, не фича MAS) — решить, что переносится при смене engine_ref и в fallback-цепочке.

Где bos-x лучше

  • Тоньше и уже в проде — один связный сервис, без SaaS-разрастания (~28 групп маршрутов у runner-v2).
  • Инвариант 6 (прокси-инъекция ключа) — более чистая, первоклассная граница секрета, чем «ключ на хосте».
  • Честность про «построено / заглушка» (блок Status) против необъявленных заглушек failover / model-health admission / MCP у runner-v2.

Где bos-x хуже

  • Хост — ноутбук + Colima (хрупко) против облака; отсюда повторяющиеся падения (BLK-12).
  • Сессии в памяти, без восстановления после краша (у runner-v2 — Redis+Supabase+recovery).
  • Нет формального writer-lock, аудируемой сборки контекста, failover/deep-health и биллинга.