Spindle / Dext engine / architecture atlas

Spindle /
Dext Engine

The whole system on three plates: every surface and the host it reaches, the route a turn takes to a committed head, the store and engine underneath, and Spindle for Browser, which runs the same engine inside your browser.

Schema 20 · API v1 · drawn from the source of Spindle for Browser 0.1.7, November 2026
Schema 20
API v1
Browser 0.1.7
20SQLite schema
32 MiBmax step payload
1,048,576admission ceiling
50,000files per workspace
64 MiBobject cap
v1shared API contract
01

Full host map

Seven surfaces and the preview sandbox, one host boundary, one schema-20 store, and the four Dext engine crates underneath. The browser and the web app are first-class here, not side notes.

module boundary model / unlinked app authority / trust boundary engine substrate
01 TRANSPORTS + SURFACESAPI v1 · the web app on /api

CLI / TTY

cli.rs · cli_ui.rs

Operator shell; TTY-side pack forms, selects, confirms, input, and progress.

LOCAL

Rust API

agent.rs · api.rs

Embeddable Session API and stable JSON forms for events, messages, settings, status, and reads.

API v1

JSON-RPC stdio

api/stdio.rs

NDJSON protocol, one worker per session, 64 MiB lines, 4 MiB prompts, 32 queued steers.

STDIO

HTTP /v1 + UI

serve/mod.rs · serve/v1.rs

Loopback default, optional public origin, bearer/device/API tokens, and SSE event rings.

WEB

Browser extension

chrome/ · spindle-embed

MV3 for Chrome, Edge and Brave: side panel, offscreen engine, WASM host, OPFS, page tools, browser Linux, optional host.

0.1.7

Web PWA

store/ui · serve

The host-served web app. Talks to the host's private /api routes and their SSE stream (not part of API v1); its service worker handles notifications, not caching.

SERVED UI

Embed SDK

embed/ · store/src/embedded

WASM/JS host for apps, with Node and OPFS storage and the shared protocol.

EMBED

Preview sandbox

chrome/preview.html · serve/preview.rs

Sandboxed page output, served (by the extension's service worker, or the host) under a sandbox CSP and a Connection-Allowlist, which Chrome 152+ enforces.

BROWSER
shared API v1 · trust boundary · head session
02 HOST RUNTIMEcommit before dispatch

serve + connect

serve/* · serve/connect.rs

V1 routes, pairing, Access, preview, self-edit, sync, threads, tar, disk, pack UI, and PKCE browser tokens.

Session / agent

agent.rs · agent/*

Prompt, steering, resume, provider catalog, durable cost ledger, host tools, app tools, pack turn boundaries.

Turn driver

driver.rs · driver_api.rs

Decides the next step from the committed head and commits it; sends, execution, clocks, and waits belong to the host.

Broker

broker.rs

Reserve attempt, then dispatch model, strict workspace, host tool, image, job, pack, or turn effect. No retry loop.

Effects

jobs.rs · code_mode.rs · code_http.rs

16 jobs running per process, 32 kept per session; 1,024 code calls; 64 HTTP grants; 640 KiB response ceiling.

Pack runtime

pack_runtime.rs

Stateful Pack Runtime v1, ledger anchors, continuation accounting, promoted tools, and UI rounds.

Embedded host

store/src/embedded/* · embed/

Sessions an app drives: backend, code, storage, host, places, WASM JS surface, Node and OPFS adapters.

Rewind / ownership

rewind.rs · ownership.rs

Execution cuts, forks, scope trees, services, waits, suspensions, epoch confirmation.

proposal → revision → attempt → receipt; View reads committed state
03 DURABLE SUBSTRATEschema 20

Schema 20

lib.rs · transition.rs

WAL + FULL, owner lock, migrations from 14–19, previous schema 19, validation watermark, provider refusals.

1M ADMITS

Codec / chunks

codec.rs

Pure-Rust deflate and content-defined 4–64 KiB chunks; 16 KiB inline; every chunk checked by digest.

DEDUP

Objects / files

artifact.rs · workspace.rs

64 MiB object cap, 16 GiB total, 50,000-file maps, 64 MiB files, immutable manifests and staging pins.

IMMUTABLE

View / reads

view.rs · reads.rs

Read-only query_only view, no owner lock; stable message, file, state, and rewind-target reads.

OBSERVE

Durable files

durable.rs

Temp write, fsync, rename, parent sync. Apple uses fullfsync; disk-full is an ambiguous fence.

CRASH-SAFE

Migrations

lib.rs · durable.rs

A native open migrates schema 14–19 in place after copying the store to authority.vN.sqlite; browser stores and imports migrate 19 in one checked transaction.

COPY FIRST
Reference model
not runtime

spindle-reference · schema 2 · 14 operations · 8 entries per record map · 16 commits · no I/O · no live dispatch. It is not a store dependency and cannot authorize work.

AuthorityA proposal becomes a revision and up to eight inert intents in one SQLite transaction. An effect runs only after the broker reserves an attempt.
SurfacesHTTP /v1, stdio, the CLI, the Rust API, the extension and the embed SDK share the API v1 contract; the host-served web app uses the host's private /api. The browser extension runs the embedded host.
ObservationView reads committed history without the owner lock; the writer remains the sole owner of the store.
01b

Turn route

One checked route from transport to head; browser traffic uses the same embedded driver when it runs locally.

01

Enter

CLI, HTTP /v1 (the web app through /api), stdio, Rust API, the browser extension, or the embed SDK.

cli.rs · serve/v1.rs · api/stdio.rs
02

Authorize

Pairing/device tokens; optional Cloudflare Access; browser PKCE connect for a host token.

serve/auth.rs · serve/access.rs · serve/connect.rs
03

Restore

Open the session, take the owner lock, restore the head continuation; reads use View.

agent.rs · driver.rs · view.rs
04

Propose

DTA1 action and next state form a proposal: expected revision, workspace root, up to eight intents.

turn.rs · transition.rs
05

Commit

SQLite checks epoch and scope revision; consume observation, append revision, write intents, move head.

lib.rs · transition.rs
06

Reserve

Broker verifies retained bindings and reserves an attempt. CommittedEffect is the dispatch ticket.

broker.rs
07

Run

Model transport, strict workspace, host tools, jobs, packs, granted HTTP, image read, or embedded host work.

broker.rs · embedded/host.rs
08

Seal

Receipt adoption advances the head; rewind forks the line; jobs die with the process; pack ledger follows ancestry.

rewind.rs · view.rs · pack_runtime.rs
CommitWAL + FULL, one owner lock
Payload32 MiB per step
Admissions1,048,576 maximum
Proposalup to 8 intents
ContractAPI v1 across transports
01w

The Spindle Works

The illustrated host plate: API and browser gate, session/driver, schema-20 vault, codec, object silo, broker, effect yard, rewind, and the Dext engine.

The Spindle Works An isometric works: API and browser gate, session and driver desk, schema-20 authority vault, codec press, object silo, broker gate, jobs/packs/HTTP effect yard, Dext engine tower, and a separate reference model. PLATE 01 / THE SPINDLE WORKS / REV 20 RUST SOURCE / NOT TO SCALE THE SPINDLE WORKS proposal → revision → reserve → effect → receipt → head SCHEMA 20 / API V1 32 MiB / 1M ADMITS REFERENCE MODEL / NO RUNTIME EDGE schema 2 · 14 operations 8 entries/map · 16 commits MODEL ONLY / NOT A HOST PUBLIC ORIGIN / ACCESS PAIRING / ACCESS / CONNECT API v1 API + BROWSER GATE CLI · HTTP · STDIO · RUST WEB PWA · CHROME · EMBED SESSION + DRIVER head · cost · steer TURN CONTINUATION AUTHORITY AUTHORITY.SQLITE / SCHEMA 20 32 MiB PAYLOAD · 1,048,576 ADMITS WAL + FULL · ONE OWNER.LOCK MIGRATIONS 14–19 · COPY FIRST CODEC / CHUNKS deflate · 4–64 KiB · 16 KiB inline OBJECTS + FILES 64 MiB object · 16 GiB total 50,000 FILES BROKER RESERVE → RUN attempt before effect JOBS 16/32 PACKS HTTP EFFECT YARD reserved effects · ledger · grants PROVIDER HTTPS CORE TRANSCRIPT PROVIDER NATIVE DEXT ENGINE / 4 CRATES APP NOT LINKED r1 r2 HEAD REVISION RAIL REWIND fork / cut / resume solid = code path · dashed = model or external edge
Scroll horizontally to inspect the plate
01 / GATE

Every surface, one gate

CLI, HTTP, stdio, Rust API, the extension and embed share the API v1 contract; the web app talks to the host's /api. Pairing/device tokens and optional Access sit in front.

02 / VAULT

Schema 20

WAL + FULL, owner lock, migrations from 14–19 (a copy kept first), 32 MiB steps, one million admissions.

03 / AUTHORITY

Commit, then reserve

Proposal → revision and intents → broker attempt → effect → receipt → head. The driver sends and runs nothing itself: the host does.

Host vs browser

The host plate shows the shared session/store/engine machine. The browser plate is a distinct local-first extension running the embedded WASM host in OPFS.

Conservative contract

CAPABILITIES still advertises no native execution, no durable rewind, and no power-loss certification. The modules exist; the published contract stays narrower.

02

Spindle for Browser

The browser extension is not the served web app. It is a local-first WASM Spindle running in the browser profile, beside a host-served PWA and a reusable embed package.

FACE 01 / EXTENSION

Spindle for Browser

A standalone side panel in Chrome, Edge or Brave. A WASM host, OPFS sessions, encrypted vault, page tools, local Linux, and optional remote host.

chrome/ · chrome/dist/
FACE 02 / SERVED PWA

Host web UI

Loaded from spindle serve. Uses the host's private /api and its SSE stream for a host session; its service worker handles notifications, not caching.

store/ui/
FACE 03 / EMBED

Spindle inside an app

The reusable WASM/JS host package: backend, protocol, places, Node VFS and browser OPFS.

embed/ · store/src/embedded/
Spindle for Browser architecture A Chrome side panel, active tab, and sandboxed preview connect through a service worker and offscreen engine worker to a WASM host over OPFS, a vault, providers, local Linux, and an optional remote Spindle host. SPINDLE FOR BROWSER / 0.1.7 MV3 / CHROMIUM 116+ BROWSER ARCHITECTURE panel → BroadcastChannel → offscreen engine → WASM host / OPFS → executors → events BROWSER / EXTENSION SHELL SIDE PANEL panel.html · panel.mjs thread list settings · spend · vault chat / workspace files · previews page access switches provider / host sign-in selection local or remote thread BroadcastChannel turns continue while closed panel ↔ engine worker ACTIVE TAB chrome.debugger page tools / gated PREVIEW preview.html · sandbox opaque origin allow-scripts no same-origin Connection-Allowlist Chrome 152+ enforces the list older: scripts wait for you served by the worker SERVICE WORKER background.js page reading / acting switches chrome.debugger actions tabs · screenshots · network preview CSP + allowlist only engine.html may ask tools page-tools.mjs · shared.mjs 28 s per tool · unknown, never repeated OFFSCREEN / ENGINE engine-worker.mjs one Dedicated Worker, all threads BroadcastChannel event fanout operations serialize per thread spindle_embed_bg.wasm embedded host · schema-20 sessions driver · store · API methods OPFS pool threads/ vault.mjs AES-GCM / PBKDF2 PROVIDERS HTTPS / direct model request from WASM host key added by the worker stream events back to engine no Spindle server required REMOTE HOST /v1 · optional paired Spindle host PKCE browser token threads can move host ↔ browser remote sandbox runs commands serve/connect.rs LOCAL LINUX v86 / wasm Linux commands run in the profile v86 Alpine runs commands plain sh: wasm Linux if built linux-runner.mjs BroadcastChannel page tools page tools preview remote local model + keys solid blue = internal extension rail · red = Chrome API boundary · dashed = host / network / sandbox edge The extension owns the engine; the host-served web app talks to the host's /api. Previews are contained where Chrome enforces Connection-Allowlist (152+).
Scroll horizontally to inspect the browser map

Local-first execution

The side panel never holds the engine. It talks over BroadcastChannel to a dedicated worker in the offscreen document. That worker loads spindle_embed_bg.wasm, keeps sessions in OPFS, and continues running turns while the panel is closed.

Trust boundaries

Reading and acting on pages are separate switches, both off until you turn them on. Page tools are accepted only from engine.html; actions use chrome.debugger. Under Before acting (Auto or Ask), an action you allow is done only while the tab still shows the page, part or dialog the question showed you (new in 0.1.7). Provider keys, host tokens, and site secrets are sealed by AES-GCM in the vault.

Preview containment

preview.html is a manifest-sandboxed page with no extension APIs. The service worker serves it under the sandbox CSP and a Connection-Allowlist, which Chrome 152+ enforces for the page and every frame and worker it makes. On older Chrome a preview shows without its scripts, frames or outside links until you run it, and then runs uncontained.

Remote host is optional

A browser can connect to a running Spindle host through the PKCE /connect flow, obtaining a browser token for /v1. Local threads can move to the host; embedded threads use the browser's WASM host.

02b

Browser route

From panel input to a local OPFS session, then through the selected executor and back as events.

01

Panel

The side panel reads a prompt, files, page mode, vault state, and the thread's local/remote runner.

panel.html · panel.mjs
02

Channel

BroadcastChannel carries requests and event fanout; operations serialize per thread, while turns remain in the worker.

engine-worker.mjs
03

Open session

The embedded host opens the thread directory on the OPFS pool and reads the committed head.

spindle_embed_bg.wasm · OPFS
04

Drive

The shared driver decides the next model or tool need from the committed state; the browser host performs it.

driver.rs · embedded/host.rs
05

Execute

Provider HTTPS, local WASM Linux/v86, page tools through the service worker, or a remote host /v1.

linux-runner.mjs · page-tools.mjs
06

Seal

The embedded store commits the result and the worker sends the event back to every open panel.

embedded/backend.rs
07

Preview

Preview responses come from the service worker with sandbox CSP and a restrictive Connection-Allowlist.

background.js · preview.html
08

Move

A thread can move between browser and a connected host; the host runs its own sandbox when it owns the turn.

host-moves.mjs · serve/threads.rs
02w

The Browser Works

The illustrated browser plate: Chrome shell, service worker, offscreen engine, WASM/OPFS, vault, page tools, preview, local Linux, providers, and remote host.

The Browser Works, Spindle for Browser 0.1.7 An isometric browser cutaway: Chrome side panel and tab feed a service worker and offscreen engine, which loads a WASM host over OPFS, an encrypted vault, providers, local Linux, preview sandbox, and optional remote host. PLATE 02 / THE BROWSER WORKS / 0.1.7 CHROME MV3 / NOT TO SCALE THE BROWSER WORKS panel → BroadcastChannel → offscreen engine → WASM host → executor → events SCHEMA 20 / OPFS CHROMIUM 116+ / 0.1.7 CHROME / SPINDLE FOR BROWSER SIDE PANEL panel.html threads chat · files settings · spend page switches vault · host thread menu BroadcastChannel ACTIVE TAB chrome.debugger page tools · reading/acting actions · screenshots · network PREVIEW sandbox · opaque origin Connection-Allowlist Chrome 152+ containment CHROME WINDOW / LOCAL PROFILE SERVICE WORKER page-tools.mjs permissions / debugger only engine.html asks preview CSP + allowlist OFFSCREEN ENGINE engine-worker.mjs one worker · all threads spindle_embed_bg.wasm driver · schema-20 sessions BroadcastChannel fanout OPFS threads · sessions · state VAULT AES-GCM · PBKDF2 KEYS / HOST TOKEN PROVIDERS PROVIDER HTTPS / KEY FROM VAULT REMOTE HOST /v1 · PKCE browser token THREAD MOVE / SANDBOX LOCAL LINUX v86 / wasm Linux COMMANDS BROADCASTCHANNEL / EVENTS page tools debugger preview model remote local browser engine · WASM + OPFS · sandbox preview · Chrome API boundary
Scroll horizontally to inspect the browser plate
01 / PANEL

Chrome shell

The side panel holds threads, files, settings, spend, page switches, vault state, and the local/remote runner choice.

02 / ENGINE

Offscreen, not in the panel

The dedicated worker runs every thread. It loads spindle_embed_bg.wasm over OPFS and continues while the panel is closed.

03 / EDGES

Page, model, local, remote

Page tools go through the service worker and debugger; models use direct HTTPS; commands use local WASM Linux/v86 or an optional host.

Preview caveat

Service-worker CSP and Connection-Allowlist contain previews where Chrome enforces the list (152+). Older Chrome shows a preview without its scripts until you run it.

Vault model

Device keys are non-extractable IndexedDB keys; passphrase vaults wrap the key with AES-KW and PBKDF2-SHA-256 at 600,000 rounds. Provider, site, and host records are AES-GCM sealed.

03

Store + engine evidence

The hard numbers behind the maps, plus the boundaries that keep the browser, host, embedded package, and engine from being confused.

Schema 20

SCHEMA = 20, LEGACY_SCHEMAS = [14,15,16,17,18,19], PREVIOUS_SCHEMA = 19. A native open migrates in place after copying the store to authority.vN.sqlite; browser stores and imports migrate 19 in one checked transaction. WAL and synchronous=FULL remain the store profile.

Payload32 MiB
Admissions1,048,576
Workspace files50,000
Object64 MiB
Objects total16 GiB
Effects / proposal8

Capabilities contract

CAPABILITIES advertises transactional receipts and revision-pinned file maps, but still says no native execution, no Dext engine, no durable rewind, no power-loss certification, no backup restore. The code exists; the public contract is deliberately conservative.

Schema20
APIv1
Browser0.1.7
Chromium116+
Engine4 + app
Referenceschema 2

The schema-20 pipeline

The host has a shared turn driver and embedded host, reads helpers, protocol/meta/spend modules, connect and thread services, browser PKCE onboarding, and the browser extension's WASM host. The store still owns the atomic admission → revision → intent → attempt → receipt chain.

Boundary checks

The browser extension is not spindle serve; the PWA is not the extension; spindle-reference is not a runtime; dext app is not a spindle-store dependency.

The four below describe Spindle for Browser 0.1.7. What 0.1.7 added to them: approvals held to the page, slot names a crash cannot split, imports checked from disk, a thread too large to bring back left on the host, and background tasks spared by a parent's Stop.

Approvals held to the page

Before acting is Full (the default), Auto (asks before what sends or submits) or Ask (before every action). An action you allow carries what the question showed: the tab, the page's document (where Chrome says which it is; else its address), the part's description, or the dialog. The service worker checks it just before acting; if the page reloaded or moved on, nothing is done and the thread is told.

Names a crash cannot split

Each slot header this version writes carries a CRC-32 right after its path (in the first 512-byte sector, for paths under about 470 bytes), and every rename writes a record of itself first. A crash at any byte or sector of a header write reopens with each file whole under one of its two names and a directory never split: tested by cutting every byte and every set of sectors in a model of the storage, not a physical power-loss proof.

One live copy

A thread moves between browser and host by bundle and lives in one place: sealed where it left, staged where it arrives. A host unpacks an import into a private staging directory and checks every store there, one import at a time, holding no store in memory; it says a thread's size first, and the browser leaves one over 1 GB on the host. Browser tokens see only their own threads.

Unknown stays unknown

A step cut off is recorded as stopped or unknown, never sent again on its own (unless the session opted in, resend_unknown): Retry sends it once, Take back returns its message. A page action interrupted mid-way is reported unknown. In a thread run in the browser, a parent's Stop leaves its background tasks running, their page actions too.

DEXT ENGINE / EVIDENCE4 + 1

dext-core

core/src/*.rs

Pure model wait, turn, response, and recovery decisions. No host I/O; unsafe code forbidden.

dext-transcript

transcript/src/*.rs

Messages, blocks, checkpoints, runtime, continuations, and tool results.

dext-provider

provider/src/*.rs

Catalog, auth/OAuth, wire shaping, request contracts.

dext-native

native/src/*.rs

SSE, streaming, model transport, strict/host runners, jobs, cgroups, process tree, image policy.

dext CLI app

src/main.rs + modules

Full app; not linked by spindle-store.

Engine limits (native/src/limits.rs, strict.rs): 64 MiB files, 2 GiB trees, 1,800 s runs, 16 jobs per process. Toolchain memory and process limits are separate from the Spindle admission model.
Reference model
not runtime

spindle-reference remains the bounded in-memory lifecycle model: schema 2, 14 operations, 8 entries per record map, 16 commits, no live dispatch. It is not the store schema and not the browser engine.