Workbooks Browser (desktop)
The Workbooks Browser is the Tauri desktop shell. Its frontend is one Svelte codebase that talks to the OS, the runtime, and the embedded kernel through a single seam — the Host (the Dock membrane). A target (desktop / web / mobile) is a routing config over that one seam, not a code fork.
Truth lives in desktop/src/lib/platform/ (the Host seam),
desktop/src-tauri/src/ (the local provider commands), and
desktop/src/lib/sdk/ (the Browser SDK). See host vs loaded and
the Dock for the why; /learn/the-seam for the deep page.
The Host seam (one contract, three providers)
The UI calls exactly one thing — the Tauri invoke(cmd, args) seam, generalized
as the Host. Every capability is fulfilled by one of three providers; the UI
never knows which.
| provider | fulfils via | :SRC: |
local | OS via Tauri (desktop) / browser-native (web) | desktop/src-tauri/src/lib.rs:109 (the #[tauri::command] set) |
runtime | a shared server over RCP (HTTP + WS) | desktop/src/lib/engine-api/gen.ts, desktop/src/lib/rcp/ |
kernel | the embedded oql.wasm, in-process, offline | desktop/src-tauri/src/kernel.rs:42 (call/2) |
A target is a routing config mapping capability group → provider. Per-target
mapping (desktop/docs/platform-model.md:35-40):
| capability group | desktop | web page | mobile |
| fs / window / terminal | local (OS) | local (browser)/n.a | local |
| keychain / secrets | local (OS) | runtime / WebCrypto | local |
| agent / chat / data / sync | runtime | runtime | runtime |
| weave / validate / outline | kernel | kernel | kernel |
Honest caveat: one contract does not mean every capability exists everywhere.
The web build has no PTY; the Host reports it unavailable and the UI degrades
(desktop/docs/platform-model.md:48-52).
Embedded kernel commands (provider: kernel)
oql.wasm (the SAME component the Elixir runtime loads, ~414 KB) is compiled
into the shell and run via wasmtime with an empty WASI context — so the app
weaves / tangles / validates a workbook LOCALLY, with no server and no Docker.
This is what makes the browser workbook-native and offline-first.
| command | kernel export | returns | :SRC: |
weave | render | Result<String> | desktop/src-tauri/src/lib.rs:67 |
tangle | tangle-plan | Result<String> | desktop/src-tauri/src/lib.rs:72 |
validate | validate | Result<String> | desktop/src-tauri/src/lib.rs:77 |
lint | lint | Result<String> | desktop/src-tauri/src/lib.rs:82 |
outline | parse-headlines | Result<String> | desktop/src-tauri/src/lib.rs:87 |
The WIT world is pure string → string; the compiled artifact imports WASI 0.2
but is given no preopens, so render never touches the filesystem
(desktop/src-tauri/src/kernel.rs:7-9).
Runtime discovery (provider: runtime)
The shell CONNECTS to an already-running runtime; it does not auto-start the
heavy engine on launch (desktop/src-tauri/src/lib.rs:246-253). runtime_url
reads the local discovery file and returns the localhost URL + per-boot bearer
token + state (up / down).
| field | meaning |
url | <scheme>://127.0.0.1:<port> or "" |
token | per-boot bearer, or "" |
state | up when discovery present, else down |
Connection is brokered over RCP. The route → posture resolver is framework-
agnostic and ships with router adapters (desktop/src/lib/rcp/routing.ts:11;
adapters in desktop/src/lib/rcp/adapters/). Postures: public, gated_data,
gated_route (desktop/src/lib/rcp/routing.ts:14). See /learn/nexus and
RCP wb-uxn.
Daemon / engine control (provider: local)
Local supervision + the first-run install wizard. n/a on the web build.
| command | does |
daemon_status | discovery + /health snapshot |
daemon_up / _down | start / stop the local engine |
daemon_restart | restart the local engine |
engine_detect | probe OS support (krunvm / brew / APFS) |
engine_install_backend | install the VM backend |
engine_boot_local | boot a local engine container |
engine_probe | health-check a candidate engine |
engine_connect_cloud | attach to a cloud engine |
engine_disconnect_cloud | detach the cloud engine |
A live poll emits a daemon-state event; the tray menu surfaces "Engine:
…" status and a start/restart action (desktop/src-tauri/src/lib.rs:254-296).
Offline-first boot
The native split (desktop/src-tauri/src/lib.rs:7-10):
NATIVE (work offline) — fs / doc / tab / workspace / bookmark / theme / terminal.
RUNTIME (graceful when down) — agent / chat / network / publish, over the control-plane at the discovered URL.
The app boots without the engine; engine state shows in the titlebar/tray, never
as a blocking gate. Closing the window HIDES it; the daemon + tray keep running
(desktop/src-tauri/src/lib.rs:258-265).
local provider command surface
The full registered #[tauri::command] set, grouped, from the
generate_handler! block. Each group's handlers live in the named module.
| group | commands | module |
| kernel / discovery | weave tangle validate lint outline runtime_url | lib.rs |
| daemon / engine | daemon_status daemon_up daemon_down daemon_restart engine_detect engine_install_backend engine_boot_local engine_probe engine_connect_cloud engine_disconnect_cloud | daemon.rs |
| tabs | tab_list tab_open tab_focus tab_close tab_set_dirty | tabs.rs |
| filesystem | fs_tree_walk fs_dir_read fs_read_file fs_write_file fs_reveal fs_rename fs_delete fs_mkdir fs_create_file fs_watch_start fs_watch_stop config_watch_start | fs.rs / fs_ops.rs |
| workbooks / memory | workbook_spec_read memory_source_resolve | workbooks.rs |
| packages | package_list package_get_active package_load package_create package_import_folder package_set_icon package_delete package_add_folder package_remove_folder package_set_active package_refresh_active package_set_layout package_set_view_mode package_set_subtree package_workbooks package_app_workbook package_move_into | packages.rs |
| workspaces | workspace_list workspace_get_active workspace_create workspace_set_active workspace_rename workspace_set_icon workspace_delete workspace_add_package workspace_remove_package workspace_set_subtree | workspaces.rs |
| bookmarks/themes/etc | bookmark_list bookmark_create bookmark_rename bookmark_delete bookmark_set_slot theme_* mcp_* plugins_* | store.rs |
| agent settings | agent_settings_get agent_settings_set | agent_settings.rs |
| keychain | secrets_push keys_* env_vars_* connections_* | keychain.rs |
| identity / WorkOS | identity_load identity_generate identity_set_handle identity_set_workos workspace_package workos_sign_in workos_load_session workos_clear_session | network.rs |
| terminal (PTY) | terminal_spawn terminal_write terminal_resize terminal_kill | terminal.rs |
| setup | setup_status setup_initialize_keychain setup_complete_first_run setup_save_model_key | setup.rs |
Secrets are held in the OS keychain; PTY terminals and plugin:dialog are
desktop-only. plugin:store (workspaces / tabs / bookmarks) is a local-domain KV
backed by Tauri's store plugin (desktop/src-tauri/src/lib.rs:105).
Browser preview host (web, mock providers)
When the frontend runs in a plain browser, window.__TAURI_INTERNALS__.invoke
is absent, so webHost.ts installs a stand-in (window.__WB_MOCK_INVOKE__) that
answers every command from seed data. It is structured as the seed of a real
WebHost, not a throwaway mock: domains are tagged mock (today's default),
http (future RCP routing), local (future browser-native). Run it with
cd desktop && bun run dev (port 5178).
Some commands are stateful in the mock (tabs, bookmarks, package_move_into,
the ?onboarding=fresh flow) so flows are walkable in preview; package_move_into's
real Rust command is tracked as wb-5fl.12 (desktop/src/lib/platform/webHost.ts:322-323).
The dock / extension model
The browser is itself extensible: a toolkit ships a UI, registers it as a dock panel, and drives the browser through the Browser SDK. The host stays primitives-only — it exposes seams; toolkits own behavior. This is "software built in workbooks" applied to the browser's own surface. See EXEC shapes.
Panel registration
registerToolkitPanel(m) maps \{ id, title, icon, entry \} onto dock.register.
The panel's icon appears in the titlebar dock toolbar; clicking opens it in the
right dock. DockHost.svelte renders the active panel.
entry form | trust rung | :SRC: |
\{ iframeSrc \} | default — sandboxed iframe | desktop/docs/browser-sdk.md:12-15 |
\{ component \} | first-party / trusted, DOM-mounted | desktop/src/lib/components/DockHost.svelte:5-7 |
iframe panels inherit the theme via the wb-theme postMessage handshake — the
same path WorkbookView uses (desktop/src/lib/components/DockHost.svelte:45-61).
The membrane (postMessage protocol)
A panel's iframe talks to the host over postMessage. The SDK is the only
channel to the host.
| message | direction | shape | :SRC: |
wb-sdk-call | toolkit → host | \{ id, method, args \} | desktop/src/lib/sdk/protocol.ts:19 |
wb-sdk-reply | host → toolkit | \{ id, ok, result?, error? \} | desktop/src/lib/sdk/protocol.ts:25 |
wb-sdk-event | host → toolkit | \{ event, payload \} | desktop/src/lib/sdk/protocol.ts:32 |
wb-theme | host → toolkit | \{ tokens \} | desktop/docs/browser-sdk.md:18 |
wb-theme-ready | toolkit → host | handshake | desktop/docs/browser-sdk.md:19 |
Host dispatcher: desktop/src/lib/sdk/browserSdk.ts:33 (dispatchSdk).
DockHost turns a thrown error into a wb-sdk-reply error
(desktop/src/lib/components/DockHost.svelte:88-90).
The Browser SDK
A toolkit loading /browser-sdk.js gets window.workbooks.browser plus theme
inheritance for free (desktop/static/browser-sdk.js:14 installs it; client is
~129 lines). Every call returns a Promise. The surface is exactly what the user
can do in the UI — no raw fs / exec / network beyond what the host already
brokers.
Method surface
The SDK_METHODS allow-list, kept in sync with the host dispatcher
(desktop/src/lib/sdk/browserSdk.ts:33-89):
| call | returns | dispatcher :SRC: |
tabs.list() | [\{id, path, title\}] | browserSdk.ts:36 |
tabs.active() | \{id, path, title\} or null | browserSdk.ts:38 |
tabs.open(path) | \{ok\} | browserSdk.ts:42 |
tabs.focus(id) | \{ok\} | browserSdk.ts:45 |
tabs.close(id) | \{ok\} | browserSdk.ts:48 |
bookmarks.list() | [\{id, title, path\}] | browserSdk.ts:53 |
bookmarks.add(title, path) | \{ok\} | browserSdk.ts:55 |
bookmarks.remove(id) | \{ok\} | browserSdk.ts:58 |
workspace.active() | \{id, name\} or null | browserSdk.ts:63 |
workspace.list() | [\{id, name\}] | browserSdk.ts:67 |
package.active() | \{name\} or null | browserSdk.ts:69 |
viewer.current() | \{path, title\} of the focused doc, or null | browserSdk.ts:75 |
nexus.active() | \{name, url, mode\} of the connected runtime | browserSdk.ts:79 |
theme.tokens() | the current token map | browserSdk.ts:83 |
browser.call(method, args) is a forward-compat escape hatch; an unknown method
throws (desktop/src/lib/sdk/browserSdk.ts:86-87).
Events
browser.on(name, fn). Names (SdkEventName): tab-changed, workbook-opened,
theme-changed. This makes "open a workbook → the dock toolkit reacts" a
one-liner (desktop/docs/browser-sdk.md:62-65).
Security model
iframe panels are sandboxed; the SDK is the only channel to the host.
The method surface reuses existing UI commands, so a panel grants no capability the user lacks — no fs/exec/network beyond what the host brokers.
DOM-mounted (
\{ component \}) panels bypass the iframe boundary; reserved for first-party/trusted panels only.
The UI is itself a workbook
The north star is that the app's own UI becomes a workbook: one frontend build,
where a target is just a host routing config + runtime endpoint an agent can
author — "publish anywhere" = choose a preset + a runtime URL
(desktop/docs/platform-model.md:103-115).
Today's reality, stated plainly: the browser is workbook-native (renders the
format locally) and workbook-extensible (toolkits drive it via the SDK). The
shell itself is still hand-authored Svelte talking to the Host seam; the typed
Host interface / WIT component and the agent-authored target-config are
explicitly future decisions (desktop/docs/platform-model.md:80-87). Do not read
"the UI is a workbook" as present-tense.
See also
Deep pages: the seam · the one file · the kernel · the nexus
Canon:
desktop/docs/platform-model.md·desktop/docs/browser-sdk.md