Additional
Browser host architecture
Synced from github.com/CoWork-OS/CoWork-OS/docs
The browser application connects to a running CoWork host. The host remains the authority for profiles, workspaces, task execution, permissions, events, files, and human decisions. A browser tab is a client of that host, not another task runtime.
Desktop compatibility contract
Browser preview is additive and opt-in. Reusing desktop components does not authorize changes to native desktop presentation, defaults, permissions, approval policy, navigation, or task behavior solely to support the browser. Browser-specific UI and recovery behavior must be selected by the renderer's explicit window.coworkBrowserHost === true identity. COWORK_WEB_ENABLED=1 enables host routes; it does not make an Electron renderer a browser client and must not select browser UI there.
Capability helpers preserve native availability when the browser marker is absent or false. Browser-specific styles must be scoped to .browser-host, and native component structure must remain unchanged where browser wrappers are needed. Validate both renderers: an enabled browser host must not add preview notices or disable native tools in the desktop app.
The merged preview also touched shared execution and persistence code. Those changes require independent native regression evidence; browser acceptance, a passing build, or an opt-in route flag cannot establish desktop equivalence. The approval banner regression was corrected locally. A complete native compatibility audit remains open.
Authority and transport
An opt-in WebApplication instance belongs to one host profile and generation. It mounts on the existing Control Plane listener and, in desktop mode, an enabled Web Access listener. Each listener has its own pairing audience and session cookie. Pairing codes are one-use and short-lived; browser sessions bind to the listener, profile, and host generation. Host, Origin, CSRF, WebSocket ticket, and deployment-origin checks run before any browser RPC or file route. The browser calls only named, capability-gated methods; it cannot dispatch arbitrary Electron IPC or Control Plane methods.
The browser bundle has a separate Vite entry and a versioned manifest. The host verifies the manifest and assets before mounting. The renderer checks the API version and host identity on reconnect. A changed host generation requires a new session and state reload.
Durable operations and replay
Task creation carries a stable operation key. The host records admission with the task in SQLite and reconciles an unknown reply against that key before starting work again. Reusing a key with changed task content is a conflict. Closing a browser tab does not cancel the host task. Cancellation requires the observed task status and update timestamp, reserves a durable receipt before dispatch, and rechecks task state on retry. A terminal result describes the state observed after the request; it does not claim the request caused that state. Concurrent browser hosts do not provide exactly-once cancellation dispatch.
Follow-ups use a stable, audience-scoped message ID derived from the browser operation key. The host checks the task's durable user-message receipt after an uncertain reply. The browser stores an unresolved operation key and request fingerprint in profile-scoped local storage until the receipt is confirmed. It does not persist the pending message text. An unresolved key survives sign-out and prevents a new browser session from silently submitting duplicate work. The user can retry the same action in the original paired session to reconcile its receipt; if that session is unavailable, an administrator must reconcile it before browser storage is cleared. Receipt lookup scans that task's user-message history and does not return the message text or claim provider completion. A global cross-task operation-key ledger is not yet part of this preview.
Visual task input uses bounded workspace-relative descriptors. The host captures media through a verified file descriptor, checks the declared size and media signature, and fingerprints the captured bytes and file identity. Private copies are staged before initial task admission, and their opaque references commit with the task and admission receipt. Browser event projections omit those private references and request fingerprints. Follow-up fingerprints include media, quote and option content, so a reused message key cannot silently accept changed input. These storage checks are separate from real-model visual interpretation and recovery acceptance.
Ordinary queued follow-ups journal their input before entering the in-memory queue. Recovery uses the durable receipt and private attachment references when a runtime snapshot is absent. Accepted follow-ups remain recoverable while provider dispatch is pending or started; completion is recorded after a conversation snapshot succeeds. A crash during an external provider request can require another request, so this is an at-least-once recovery boundary, not an exactly-once provider guarantee. Internal dispatch fields are omitted from browser event projections. Real-model interruption and restart acceptance remains unverified.
The canonical task_events table remains the task history. A compact mutation journal, written by SQLite triggers in the same transaction as an event insert, update, or delete, provides a per-task committed cursor. Browser snapshot and page reads go through the existing asynchronous storage facade. The browser starts from a bounded recent snapshot, pages older committed events on request, applies forward mutation pages in cursor order, and resnapshots after an expired cursor. It keeps a bounded visible window and treats updates as event-ID replacements and deletes as tombstones. Transient token previews are not committed replay guarantees.
Approval and structured input methods require workspace/task scope and an expected request version. A decision is checked against the durable request state; duplicate same decisions can be reconciled, while opposite or stale decisions conflict. Browser storage keeps an unresolved operation key and identifiers, but never persists answer text. The task event browser projection removes structured-input answers and approval details from those lifecycle events. Free-form task messages may still contain sensitive text supplied by the user; the browser is intended for trusted authenticated clients.
Files and capability boundaries
Workspace file access resolves the current effective access profile on every request. Downloads open and verify a file descriptor inside the workspace before streaming bytes, with a size cap and no directory traversal. Uploads are create-only, use bounded streaming and atomic publication, and reject path aliases or symlink escape. Staging files are hidden from browser listing and removed after failure or cancellation. Task artifacts expose only scoped metadata in RPC. A download handle is bound to the browser session, expires after 60 seconds by default, works once, and rechecks the current access profile and file identity before streaming. The browser does not receive raw host filesystem APIs. Each browser capability is advertised independently, so incomplete workflows remain unavailable.
The browser entry mounts the existing React desktop application after the authenticated transport and capability-limited bridge are ready. It shares the sidebar, home, composer, and task view with desktop; browser-only handling prevents native controls from acting as if their Electron methods were available. Git reads return bounded summaries or diffs for workspaces allowed by the current effective access profile; repository-configured diff drivers and text conversion are disabled. Terminal attachments use the existing host PTY manager, recheck the effective shell policy, bind to the browser session and task/workspace, enforce one writer, and return bounded output pages with explicit gap markers. The installed npm package passed the disposable Node-host browser smoke. Historical visual-context recovery, Git writes, media artifact previews, packaged desktop/Linux acceptance, and a real-model browser workflow are separate delivery gates; see browser-preview.md and the generated web-capability-matrix.md for the current surface and limits.