Additional
Browser application preview
Synced from github.com/CoWork-OS/CoWork-OS/docs
The browser application is currently an opt-in development preview. Set COWORK_WEB_ENABLED=1 before starting a CoWork host. Its routes live on the existing Control Plane listener at /app/ and, in desktop mode, on an enabled Web Access listener at /app/. The default deployment policy accepts only a loopback listener and loopback Host header. The native Control Plane protocol and existing Web Access bearer-token API retain their own authentication.
Desktop isolation and local builds
The browser preview must preserve native desktop UI and behavior. Its approval explanation, unavailable-action presentation, sidebar adjustments and browser draft recovery belong only to browser clients. They must not be enabled in desktop merely because COWORK_WEB_ENABLED=1 is set. That environment variable controls host routes; the browser renderer has a separate explicit identity.
An approval explanation in the desktop composer was an unintended shared-component regression. The local correction hides it on desktop without changing approval settings. Related desktop sidebar, composer structure, draft and feedback behavior were restored. This is not a claim that all shared runtime changes have been audited.
Updating a source checkout and rebuilding it changes the app launched from that checkout. It does not replace an installed application bundle. Identify the running process, checkout, profile and build before comparing desktop and browser results; preserve existing data and credentials. The browser QA profile is separate from the normal desktop profile.
Starting the preview
Build the assets with npm run build:web. For a disposable Node host, start node bin/coworkd-node.js --user-data-dir <disposable-dir> --enable-control-plane with COWORK_WEB_ENABLED=1. Its log reports the actual Control Plane address. From a trusted admin client, run node bin/coworkctl.js --url <ws-url> --token <control-plane-token> call web.pair to generate a one-use code, then visit <http-url>/app/ and enter the code. The code expires after 90 seconds. In desktop mode, the enabled Web Access settings panel can generate a code for its own listener. Codes and browser sessions are specific to one listener, profile, and host generation.
Remote exposure requires COWORK_WEB_PUBLIC_ORIGIN set to a canonical HTTPS origin and COWORK_WEB_TRUSTED_PROXY_ADDRESSES set to the exact address of the TLS-terminating proxy. Forward the original Host and X-Forwarded-Proto: https; route /app/ and /api/web/v1/ to the same host listener. Browser routes fail closed when the deployment policy does not match the listener and request. A proxy path prefix must rewrite to these host paths while preserving the same prefix on the client side.
The browser entry now mounts the same React App, styles, sidebar, home screen, composer, and task view used by the desktop application. Pairing and the browser transport initialize before the shared app loads. The browser host adapter provides authorized workspace/task reads and maps core task actions to browser host methods, including workspace changes, task resume, message/step feedback, and integration mentions. The host publishes a closed method manifest for settings, task rename/pin/archive/fork, project creation, managed agents, automations, Inbox, Everyday Agent, and device workflows. Forks require a readable, writable workspace and use replay-safe operation receipts; a stale side-chat fork is archived when the browser host lacks permanent task deletion. Shared navigation follows that manifest instead of a blanket browser lock. Opening a screen is not proof that all of its optional integrations are available. Canvas session restoration is skipped when the host does not expose the required methods, and the interactive Browser Workbench stays unavailable until a browser stream is provided. Configure a working model provider in AI & Models before expecting a task to run. The browser can edit provider settings and models; saved credentials are represented by presence flags, never returned to the browser. Provider saves send changed fields against an opaque host-generated revision, while credential replacements use a protected field and host-managed OAuth values and credential-presence flags stay read-only. A conflict asks the user to reload AI & Models before retrying. Focused host and bridge tests cover secret redaction, protected replacement, rejected unprotected or stale writes, revision refresh after success, and reload-before-retry behavior. Guardrail and permission-runtime reads, admin policy summaries, usage reports, user profile facts, and open commitments are served by the host; report reads enforce current workspace read access. Local model hardware detection and server start/stop controls remain desktop-only and explain that limitation in the settings UI. ChatGPT subscription sign-in now starts from the shared AI & Models screen: the host owns the OAuth flow and token persistence, while the browser receives a session-owned flow ID, authorization URL, expiry, and status. Complete the account login, or paste the complete localhost callback URL into that screen when the redirect cannot reach the host. Requests expire after fifteen minutes and can be cancelled. Storage refusal never reports a connected account; session revocation discards pending credentials. Other OAuth routes and native settings services still require their own adapters. The browser start/cancel path has passed source-checkout smoke; successful personal login, provider execution, and restart recovery remain acceptance gates. The notifications panel uses the host profile service and polls for changes while the paired app is mounted; reads and mutations are filtered through current workspace access. Mailbox and personality change subscriptions poll compact host state while a subscriber is mounted and stop when the view unsubscribes. Mission Control Teams, Reviews, and Standup remain disabled until their complete host services are available, and command-center summaries, planner settings, execution, run history, and core harness review data explain when host methods are absent. Session-wide auto-approval is held only in the current browser page and is not persisted to the host.
Composer drafts are stored locally for the host installation and profile. Mutations serialize across browser tabs using Web Locks; browsers without that facility receive an explicit draft-storage error. Attachment references are opaque, and a failed rekey restores attachment ownership before reporting failure.
Browser appearance and first-run display preferences are stored in the browser for that host profile. They do not change the host's desktop settings or grant host capabilities.
The host API supports pairing, workspace/task summaries, durable task submission with a stable browser request key, task cancellation with a durable receipt, follow-ups backed by a durable message receipt, committed task activity with older-page navigation, pending approval and structured-input decisions, workspace file transfer, task artifact downloads, bounded Git status and diff, selected-file stage/unstage, staged-only commits, and host terminal attachment for eligible workspaces. Git changes require the workspace's current write permission and an unchanged status revision; durable receipts reconcile retries after a lost reply. A task submission, cancellation, or follow-up whose reply is lost can be reconciled by its key. Approval/input decisions can also be checked against their durable outcome after an uncertain reply. Follow-up recovery marks terminal tasks with queued/started receipts for resume and replays a transcript-consumed message when the host stopped between snapshot persistence and receipt advancement. Focused tests cover those restart-state boundaries, including attachment restoration; real-model crash/restart acceptance remains open. Workspace downloads and uploads are capped at 64 MiB per file. Uploads create a new file only; an existing name returns a conflict. Browser file requests use current effective workspace permissions. Artifact downloads use short-lived, single-use handles with current permission checks. Terminal access rechecks the current task/workspace shell policy, retains bounded output for reconnect, and requires a writer attachment for input. The shared desktop terminal dock supports scoped tabs, input, output replay, resize, Stop, and Close. If the host has no configured model provider, the shared app links directly to AI & Models settings. Session-sharing and protected-credential administration remain native services. An attempted shared-app action without a browser host method now reports that it is unavailable in the browser session; it does not claim success or perform a host side effect. Some desktop-only or optional integrations remain unavailable in the browser.
Use npm run qa:web:ui-smoke to drive pairing, shared navigation, all 26 Settings routes and supported secondary tabs, scheduled-task create/live-update/delete, queue-setting interaction/save/restore, project creation, agent save, notification read/clear, Git stage/commit, and capability-disabled Mission Control controls in a disposable browser. It verifies Usage Insights loads and unsupported Integrations/actions explain their unavailable state. It also fails if a tested route invokes a host method that the browser does not provide. It uses a locally available Chromium browser; set COWORK_WEB_UI_BROWSER_EXECUTABLE to select another executable. Run npm run qa:web:smoke for a disposable local host check of pairing, scoped file listing and transfer, Git status/diff/stage/commit replay, terminal detach and output replay, upload/no-overwrite, artifact download with one-use handle, project creation, message feedback persistence, task fork and side-chat replay, task cancellation and receipt replay, traversal denial, and logout. npm run release:smoke also installs the built npm tarball into a clean temporary project and runs that browser workflow through the installed Node daemon launcher. After npm run package:mac:unsigned (or signed macOS packaging), npm run qa:web:desktop-smoke starts the packaged Electron host in a disposable headless profile, pairs through the browser, and checks the packaged app assets and shared CoWork UI. These browser checks create disposable task records and an artifact file; they do not run a real model task or validate a visible native window. The Linux server package and daemon-health smoke passed, but pairing and the full browser workflow against that packaged Linux artifact remain unverified. Timeline replay begins with a bounded recent snapshot, pages older activity on request, and polls committed changes. The mutation journal retains 10,000 changes per task and signals an expired cursor so the client can resnapshot. The browser stores unresolved decision identifiers, but not structured-input answer text. Ordinary free-form task messages can contain user-provided secrets, so pair only trusted clients.
The shared composer now sends workspace-scoped visual attachment descriptors for new tasks and follow-ups. The host verifies the open file, declared size and media signature, captures its bytes, and persists a private copy rather than reopening a mutable workspace path during execution. A message accepts up to five files, with 25 MiB per image, 64 MiB per video and 128 MiB total; simultaneous captures are limited to 256 MiB. The disposable smoke checks an uploaded PNG's admission event, private receipt, hash and exact stored bytes. It does not prove model interpretation of the image. Follow-ups also carry validated integration mentions, assistant quotes and an expected turn; quoted event IDs must identify an assistant message in that task. Real-model turns and historical visual-context recovery still require acceptance checks.
Add tools and the Skill Store use host-backed catalog and status reads. Installed Feature Packs expose desired-state pack and skill toggles through the shared desktop service, with admin policy and quarantine checks. Installation, import and native settings actions require their own browser services. Desktop-only settings, native folder picking, voice capture, and media embedded in generated task artifacts remain separate delivery work. Workspace MP4, MOV, and WebM previews use short-lived session-bound URLs and authenticated byte-range requests; the host rechecks origin, pairing session, workspace permission, and file identity during playback. The interactive Browser Workbench requires the host to advertise browser.interactive; until a browser stream is available, its task action is disabled with the host's reason instead of mounting Electron-only controls. Text, raster-image, PDF, and supported workspace video previews are available; HTML and SVG download as attachments. Do not treat this preview as a complete browser task workflow or a release-quality remote deployment.
Task Queue settings use the same host queue manager as desktop. Writes require host-admin capability, accept only bounded concurrency and timeout values, and persist before applying runtime changes. The browser confirms saved values by reading them back. Failed or refused storage writes report an error and retain the previous runtime settings.
Workspace Memory settings now use the existing host memory services for settings, stats, recent/search/detail/timeline reads, deterministic pasted-text imports, imported-memory recall flags, and individually authorized deletion. The shared Memory screen also supports manual profile facts, relationship records, observation search/detail/timeline and metadata/privacy edits, and stored Chronicle observation summaries without local asset paths. Workspace reads require current read access; edits require write access; deletion requires delete access independently. Failed loads and edits display errors, and delayed settings/observation reads from a previous workspace are ignored. The disposable browser smoke verifies retention save/readback/restore, fact add/pin/delete, text-import persistence and its completion popup, and delayed workspace replies. Host acceptance also verifies observation edits, recall flags, denied deletion followed by authorized deletion in its disposable fixture. External memory setup, autonomy, Chronicle deletion and file imports remain unfinished browser engineering. Real-model recall after edits and memory reads under active streaming load remain unverified.
Pending memory write review uses bounded workspace reads and the existing host approval service. Approve requires current write permission, removal also requires delete permission, and external writes require automatic network access. Replay receives the effective workspace policy, including curated-file guards and archive mirroring restrictions. Reject requires write permission. Detail responses use redacted display records, including legacy summaries. The Memory settings no longer show a Pending Memory Writes card (the review queue is off unless COWORK_MEMORY_WRITE_APPROVAL_MODE is set); disposable host smoke verifies list/count/detail and repeated approval-operation reconciliation. External-provider execution, approvals generated by a real model task, and crash/restart acceptance remain open.
Observation Inspector Promote now calls the same curated-memory service as desktop. The Node daemon initializes that service before queue recovery. Promotion authorizes the stored observation's workspace and applies current filesystem read/write guards; unsuccessful writes display an error, while staged writes explicitly report that review is required. Wake-Up Layers uses the most recent task prompt/agent role, asynchronously prefetches local Box Brain recall, and guards workspace-file reads. Failed preview reads display an error. Browser acceptance imports a disposable memory, rebuilds metadata, searches/selects it, clicks Promote, confirms completion and verifies its title in .cowork/MEMORY.md. Host acceptance verifies promotion content and the layer preview payload. Review-required promotion, subsequent real-task injection and preview performance under active streaming remain unverified.
Workspace Kit status, initialization and project creation now use shared desktop templates, project-file helpers and default scheduling behavior. Browser status reads do not create lifecycle files. Filesystem guards bound reads to 2 MiB and enforce workspace scope, explicit denies and canonical paths, including revision history. The owner setup API can seed only the built-in policy templates; arbitrary policy writes remain protected. Scheduling refusals are reported rather than treated as successful initialization. Markdown indexing uses the same read guard. In the browser, Open USER.md, MEMORY.md and DESIGN.md use the existing file viewer; desktop retains its native open action. Browser smoke verifies Initialize, project-file persistence and USER.md viewing. Host smoke verifies repeated initialization leaves exactly three default jobs. Actual scheduled execution and recovery after a partial initialization remain unverified.
Checking shared controls
After rebuilding or restarting the host, reload /app/; a host restart requires a new pairing code. Use a disposable profile for testing changes.
-
Open Settings → AI & Models. Check the configured provider, choose a model, test its connection, and save. A real model task requires valid host credentials or a reachable local model.
-
Open Notifications. Confirm host notices appear, use Mark all read, then Clear all. The browser only shows notices associated with readable workspaces.
-
Use Projects → Add folder or project. Name a test project and confirm it appears as the current project. The host creates its directory; the browser does not choose arbitrary host paths.
-
Open Agents → Create agent → Start blank. Edit the name, save, reopen, and confirm the saved values.
-
Open Automations. Verify the Library and Builder load, save a draft in the test project, and reload to confirm it persists. In Settings → Automations, confirm Routines loads saved definitions and Edit opens their values without requiring optional hook services. Unsupported engine tabs must explain their availability; a failed routine-list read must not appear as an empty list. Keep test automations disabled until you intend to execute them.
-
Submit a test task, then verify Pin, Rename, Archive, Fork, workspace changes, and Open side chat in its task menu. Use the response thumbs to submit message feedback. With a running task, test step feedback and resume after approving a paused task. Without a configured model, the app should offer AI setup rather than pretend to run the task.
-
Add a small test file to the composer. Confirm it stays visible, type a draft, and reload to confirm text and attachment references recover. Local file bytes are not persisted; select an attachment again if it is no longer available. Sending imports the file into the selected project.
-
In Calm style, open Library, browse a project folder, preview a text file, and download it. HTML and SVG files download as attachments rather than running in the application origin.
-
Open Personality, save the assistant name, leave the screen, and verify the name is retained. Open Devices and check that the fleet view loads; task controls require a saved, connected device.
-
Open an authorized task, expand its side panel, and open the terminal. Type a harmless command, check output, reconnect, then test Stop, Close tab, and New tab. The terminal requires a task whose current access profile permits shell use; the host chooses the workspace-root directory.
-
Open Settings → Skills. Check that Skill Store status loads, search the catalog, and open a result. Open Feature Packs → QA & Testing and confirm its Commands, Skills and Agents tabs show host-backed details. Disable the test pack, reload, check the saved state, then restore it. Repeat for an individual skill. Required or quarantined packs must respect their restrictions. Installation and import controls must explain their availability instead of failing silently.
-
Open Settings → Automations → Task Queue. Note the original limits, change one value, save, and confirm the saved status. Reopen the tab and verify the value; restart the disposable host, pair again, and verify it remains saved. Restore the original limits and save. Load or save failures must appear in the page instead of showing defaults or a false success.
-
Open Git Changes in a disposable Git-backed workspace. Stage one changed file, unstage it, stage it again, and commit with a message. Confirm unrelated unstaged files remain outside the commit. Refresh between actions; a stale revision must prompt a refresh, and retrying the same operation must not create a duplicate commit. Browser commits skip repository hooks.
-
Open Settings → Memory in a disposable project. In What CoWork knows, add a fact, pin it, then delete it. In Settings, change Keep history for, leave and return to confirm the saved value, then restore it. Use Import → From another assistant to paste a disposable memory; confirm Import complete stays visible and the imported record persists after closing. Switch workspaces while data loads and verify only the selected workspace's settings appear. Workspace deletion controls should explain missing delete permission; a permission rejection must never appear as successful deletion.
Run npm run qa:web:smoke for disposable host/service checks. It also checks the shared method manifest, provider/personality/agent/automation reads, optional positional arguments, project creation, and task fork/side-chat replay. This is separate from interactive browser testing, a real-model workflow, and packaged release acceptance.
Current runtime and control audit
qa:web:ui-smoke emits a control ledger to the OS temporary directory as cowork-web-control-audit.json; set COWORK_WEB_CONTROL_AUDIT_PATH to choose a destination. Visible/enabled controls are inventory, not proof of their effects. Nested dialogs and populated-data states remain separate audit work.
Awareness settings and belief actions now use the existing host service. Browser config writes send changed fields and merge against current host state; explicit config/belief changes publish only after persistence succeeds. Workspace beliefs authorize their stored workspace, and deletion requires delete access independently. Browser acceptance saves/restores Private Mode and confirms/forgets a belief generated through ordinary task feedback. Node hosts do not imply desktop device collectors are running. Real-task prompt effects, restart and load acceptance remain open.