Architecture
bos-y is an agentic coding "chat-to-app" platform: a client POSTs a prompt, the runner executes an LLM agent
inside a hardened Docker sandbox that can only reach the model through one logged egress path, streams typed
events back over SSE, and serves the reply as a previewable app when it is self-contained HTML.
The architectural spine is runner-as-authority (DEC-013): the kernel gates and settles,
the store is the sole writer, and engines are event-emitters with no DB handle and no network.
Version: 0.11.1.
How it works — numbered run flow
- Client POST
/api/runswith{prompt, model, messages[]}→ handler athttpServer.ts:277. - Admission-shaping: default
engine_ref="oai-compat", validate model (400 on unknown), mintrun_id="http-"+uuid, prependBUILDER_PREAMBLEfor single-turn free-form prompts. - Track + 202:
trackRunStartrecords the run; handler returns{run_id}with HTTP 202 immediately — kernel runs without await. - Kernel pipeline (
kernel.ts:82):admit → getEngine → createRun → EventReducer → engine.execute. - Hardened floor:
OpenAICompatEnginedelegates toSandboxedEngineTransport→createSandboxedContainer(the ONE allowed callsite, CI-asserted) — appliesHARDENED_PROFILE:User "1000:1000",CapDrop:ALL,no-new-privileges,ReadonlyRootfs,NetworkMode:none,PidsLimit:64,Memory:128MB. Container starts; ★ SANDBOX START. - Host-side
stagetelemetry: transport emitsimage → sandbox-starting → sandbox-ready → engine-activeevents the untrusted agent cannot forge. - In-sandbox agent (
openai-agent.js) POSTs to/egress/egress.sock(unix named volume) → trusted sidecar injectsBearer key→ model-router primary → OpenRouter fallback on 5xx/timeout. - NDJSON events stream back (
stream_start → usage_delta → final_text → terminal); kernel folds each throughEventReducer(SOLE writer), which validates and settles. - SSE fan-out:
BusAwareStorerepublishes every appended event to the eventBus → SSE stream → client timeline. - Terminal: tracking subscriber captures tokens, extracts HTML artifact, flips run status, calls
ingestRunToBosx(fire-and-forget mirror tomodels.dev.bos.pro). - Preview: client fetches
GET /apps/:runId→ runner serves captured HTML into the preview iframe.
Modularity — the one engine seam (IAgentEngine)
The seam is IAgentEngine (engine/types.ts:212): engine_ref, capabilities, execute(input): AsyncIterable<EngineEvent>, cancel(run_id). The stream must end with exactly one terminal event.
Engines emit; the runner gates + settles. The kernel (kernel.ts) is the single authority: it runs the tool gate on every tool_call before execution and owns settlement. Engines never see the store — EventReducer is the ONLY caller of write methods.
| Pluggable (behind a seam) | Fixed (the authority spine) |
|---|---|
The engine (IAgentEngine impls: oai-compat, e2b, stub/tool-loop) | Kernel loop, tool gate, settlement, closed TERMINAL_CAUSES |
The store backend (RunStore → InMemoryRunStore now; SupabaseRunStore is a drop-in) | EventReducer as the sole writer |
Sandbox image + command (SandboxedEngineSpec) | Hardened floor profile (createSandboxedContainer, ONE callsite) |
| Provider backend (model-router / OpenRouter) | ONE egress path (sidecar socket / authenticated proxy) |
Swapping / adding an agent engine
- Implement
IAgentEngine:engine_ref,capabilities,execute()yieldingEngineEvents ending in oneterminal, andcancel(). UseSandboxedEngineTransportto get the hardened floor for free. - Register at boot via
registerEngine(engine)(engine/registry.ts:23). - The client may send
engine_refonPOST /api/runs; the server defaults to"oai-compat". Adding an engine = implement + register; routing already works. - The engine is folded by the kernel's
EventReducer; it reaches the model ONLY via the egress seam; it has no store handle by construction.
Enforced capability scope (OS-level)
createSandboxedContainer (sandbox/createSandboxedContainer.ts:198, the ONLY docker.createContainer callsite) applies HARDENED_PROFILE:
Config.User "1000:1000"(non-root — note: notHostConfig.User, which is a silent root-drop)CapDrop: ["ALL"]SecurityOpt: ["no-new-privileges"]ReadonlyRootfs: true+ tmpfs/workNetworkMode: "none"PidsLimit: 64,Memory: 128 MB- No key file mounts;
assertNoKeyEnvdeny-scans env vars at boot - CI callsite gate allows exactly 2 files; fails on any third (
assert-sandbox-callsites.sh)
vs bos-x advisory booleans: bos-x registered bos-y with adapter_kind=isolated_external and boolean capability flags — declared metadata. bos-y's isolation is structural (Docker config the engine cannot override), enforced at the OS boundary, never trusted as declared.
Router connection
- Repo:
agiens/model-router - Endpoint:
https://rundev.cloved.ai/v1/chat/completions— OpenAI-compatible chat-completions only. Note: bareGET /v1/modelsreturns 404; the live surface is the POST completions endpoint. No/v1/messages(no Anthropic passthrough → claude-code-sdk deferred). - Backend selection (
selectSidecarBackends): PRIMARY = model-router whenMODEL_ROUTER_API_KEYis set; FALLBACK = OpenRouter on 5xx/timeout (~60s gateway timeout on slow app-gen runs; DEC-023). - Key-blind engine: provider key lives ONLY in the sidecar's env; the floor engine receives only
EGRESS_SOCKET_PATH. The sidecar injectsAuthorization: Bearerhost-side. - Every call logged: sidecar writes to
.egress/calls.jsonland logs served backend +fellBack/primaryStatus. Startup log:Using backend: model-router (fallback: openrouter).
Forks & open decisions
| Fork | Decision made | Alternative not taken | Status |
|---|---|---|---|
| Warm-pool vs spawn-per-run | Spawn-per-run (fresh transport per call, torn down in finally) | Pre-warmed container pool | Open perf option; simplicity won |
| Docker floor vs E2B microVM | Both, two-tier: Docker for trusted engines (DEC-005/008); E2B Firecracker for untrusted codegen (DEC-015) | Single sandbox tier | Ratified |
| Router primary vs OpenRouter-only | Router primary + OpenRouter fallback (DEC-023) | OpenRouter-only | Ratified; OpenRouter-only is the degraded mode when router key is absent |
| Stateless runner vs server session store | Stateless per-run; client owns conversation via localStorage + sends messages[] each run (DEC-025) | Server-side session store | Ratified |
| In-memory tracker vs Supabase | InMemoryRunStore (resets on restart); SupabaseRunStore exists behind the seam | Supabase-backed live tracker | Open; tracked |
What to improve architecturally (honest, tracked)
- Persist the run tracker.
MODEL_RUNSis in-memory and resets on restart.SupabaseRunStoreis built and RLS-clean but the live server wiresInMemoryRunStore. Wire the durable store (theRunStoreseam already supports it). - A 2nd real sandboxed engine. Hot-swap proven at the seam with stub/tool-loop/oai-contract engines; a real external engine (e.g. Aider / opencode after the decoupling spike) would give judge-grade proof.
cancel()for the per-call transport.OpenAICompatEngine.cancelis a no-op because a fresh transport is built perexecute. Cache the transport so cooperative cancel actually stops the container.- E2B-path fail-closed assert + E2B-network re-gate (DEC-018 items b/d) — still tracked.
Done: the fail-closed boot-asserts (assertSandboxProfileOrCrash + assertEgressOnlySidecarPath) are now wired into main() and run at every production boot — the "refuse to start unless sandboxed" gate is LIVE (verified in the prod boot journal). Per-container hardening + the CI callsite-assert remain as defense-in-depth.
Note: egress rate-limiting is DONE and wired — egressRateLimiter is live in httpServer.ts:409 and :446. It is not an open item.
How bos-y differs from bos-x (x.dev.bos.pro)
Both are lean, key-blind, single-egress chat-to-app runners. bos-x is hardened by delegation (managed Daytona/E2B sandbox + branded SandboxedSpec + HTTP key-injection proxy). bos-y is hardened by OS-level enforcement (self-run Docker floor: non-root uid 1000, CapDrop:ALL, no-new-privileges, ReadonlyRootfs, NetworkMode:none in ONE helper + CI callsite gate, stateless runner, deny-default RLS, unforgeable host-side stage telemetry).
Important correction: bos-x is NOT insecure. The "3 divergent callsites / warm-pool bypass / long-lived auth.json" flaws belong to the OLD vbp-mvp runner-service, NOT bos-x. bos-x's current code has ONE SandboxAdapter.create() seam, a branded SandboxedSpec, and no auth.json write.
| Dimension | bos-x | bos-y |
|---|---|---|
| Sandbox | Managed (Daytona/E2B); Linux primitives owned by the provider | Self-run Docker floor; OS-enforced ONE helper, CI callsite gate |
| Capability scope | Advisory feature-booleans; isolation delegated to managed sandbox | Denied by the kernel at OS boundary; advisory caps not depended on for isolation |
| Key egress | In-process HTTP key-injection proxy (loopback) | Key-blind sidecar over Docker named-volume unix socket; engine has NetworkMode:none |
| State / session | Stateful; own session/apps/runs stores in own Supabase | Stateless per-run; client owns messages[] via localStorage |
| RLS / data | Own Supabase; inherited migrations carried 21 security-lint findings | Deny-default RLS from row 1; 0 security lints vs 21 in old project |
| Telemetry | De-stream shim (router rejects stream:true; proxy re-emits SSE) | Live NDJSON EngineEvent stream + unforgeable host-side stage events |
All runners & factories compared
| Stack | Isolation / sandbox | Engine seam | Egress authority | Status |
|---|---|---|---|---|
bos-ybosy.cloved.ai |
Self-run Docker, OS-enforced floor: uid 1000, CapDrop ALL, no-new-privs, ReadonlyRootfs, NetworkMode none; ONE helper + CI callsite gate; E2B microVM transport built for the untrusted tier (live hot-swap tracked) | IAgentEngine; caps advisory, runner enforces regardless; hot-swap proven at the SEAM (oai-contract + tool-loop, 0 kernel diff); oai-compat live in the floor; 2nd real engine + live E2B tracked | ONE key-blind sidecar over named-volume unix socket; engine NetworkMode:none; model-router primary + OpenRouter fallback | Live, 8/8 goals + model-selector DONE; ~180 bun tests + Playwright; judge-accepted |
bos-xx.dev.bos.pro |
Managed sandbox (Daytona/E2B); ONE SandboxAdapter + branded SandboxedSpec; no self-managed warm-pool | Same IAgentEngine shape; 5 advisory booleans; router-direct / oma / aider engines + Claude Code draft (toolGating:false, deferred) | ONE in-process key-injection proxy (HTTP); sandbox gets base URL + placeholder, real key injected at proxy | Active, lean v2.0; model-selector + multi-chat shipped; models.dev.bos.pro dashboard built |
| vbp-mvp original (runner-service) |
Self-managed DinD, NOT hardened: root UID, writable FS, 3+ divergent create callsites incl. warm-pool bypassing helper | IAgentEngine origin (316 LOC); OMA/OMS; caps advisory; in_process not boot-banned | model-router gateway exists but not mandatory; parallel raw-key paths + auth.json key-file (live security break) | Mature but "at the edge of evolution"; ~201K prod LOC; the break bos-x/bos-y were chartered to escape |
| runner-v2 (vbp-german) |
Daytona managed VM; no uid/seccomp/CapDrop floor in that codebase | Claude Agents SDK native; 84-LOC hand-rolled sub-agent orchestrator; billing entangled in orchestration | Direct OpenRouter via Anthropic SDK + static failover = parallel egress/control plane | Reference implementation — studied, not adopted |
agiens/model-routerrundev.cloved.ai/v1 |
N/A — egress gateway, not a sandbox | N/A — not an engine. OpenAI-compat POST /v1/chat/completions; no /v1/messages; streaming rejected | IS the shared egress optimizer: 36 providers, AA-index + cheapest-capable routing, ordered fallback, writes RouteTrace | Live, shared service — reused read-only by bos-x and bos-y |
Goals
Functionality that must work — copy into a goal-loop agent to verify.
Sourced from BOSY_LAUNCH_PROMPT.md (reference only; not executed here).
Status badges reflect memory/state/STATE.md as of v0.11.1.
da3e9494 (BOS-Y, active, isolated_external, launch_url=bosy.cloved.ai); Playwright sign-in screenshots in runner/reports/.90a122d); 2 registration fixes applied (primary_parent_id + manual_login_url).b7bb79a, screenshot runner/reports/tetris-judge.png; free-form prompt fix DEC-021 (v0.7.0 BUILDER_PREAMBLE).2b594a7, oai-compat-contract → summary-handoff → tool-loop (distinct engine, real gated tool_call), kernel.ts 0 diff. 125/0 tests, flake hardened.73bab4a; SIDECAR_LOG confirms model-router primary + OpenRouter fallback on 5xx/timeout (DEC-023); egress rate-limiter wired (egressRateLimiter, httpServer.ts:409+446).76141b9; Playwright 12/12 live; WCAG AA contrast 16.84/9.12/6.19; SVG-only icons; focus-visible; reduced-motion. Note: prior redesign regressed goal-3 — reverted; new design applied incrementally + re-verified.test/lib/appJudge.ts; playwright.config present.94a7d6c); 8-model catalog; 5 models tested by Playwright; 4/5 catalog judges accept.Model Router
Repo: agiens/model-router |
Endpoint: rundev.cloved.ai/v1/chat/completions (OpenAI-compatible, POST only;
bare GET /v1/models → 404; no Anthropic /v1/messages passthrough).
bos-y uses model-router as PRIMARY egress (central logging to models.bos.pro) with OpenRouter fallback on 5xx/timeout.
The 8 selector models are model-router models.
Live runs dashboard
Router details
- Repo:
agiens/model-router - Primary endpoint:
https://rundev.cloved.ai/v1/chat/completions— OpenAI-compatible chat-completions only - Backend selection: model-router PRIMARY when
MODEL_ROUTER_API_KEYis set; OpenRouter FALLBACK on 5xx/timeout (~60s gateway timeout on slow app-gen runs; DEC-023) - Key-blind egress: key lives only in the sidecar container; engine has
NetworkMode:noneand reaches egress only via a Docker named-volume unix socket (/egress/egress.sock) - Fallback logging: SIDECAR_LOG records
servedBackend,fellBack,primaryStatusper call. Startup:Using backend: model-router (fallback: openrouter) - Router dashboard:
models.bos.pro— BOS-platform SPA over the sharedmodel_routestable (needs router key to populate) - bos-y run mirror:
models.dev.bos.pro(bos-x's dashboard) — bos-y mirrors only its own tagged rows (engine_ref=bosy-oai-compat) viabosxIngest.ts, env-gated, boundary-safe