Live workspace updates
The dashboard opens one same-origin WebSocket for its selected workspace and app view while the browser tab is visible and online. It loads that view through workspace_view, then applies pushed changed records and deletion keys directly to its cache. Routine updates do not call workspace_snapshot. The socket carries safe HQ projections, never commands, secret values, raw provider payloads, or health claims. Browser, CLI, HTTP MCP, scheduled GitHub work, and local reporters reach the same stored change journal.
The indicator distinguishes connecting, live, renewing, reconnecting, offline, paused, and fallback refresh. An unavailable socket uses bounded exponential reconnect backoff and a one-minute workspace_changes fallback for the active view while visible and online. Hidden and offline tabs stop both. A reconnect sends the cached cursor and receives bounded catch-up in its ready frame, closing the bootstrap race without a second bootstrap read. Ordinary updates preserve filters, collapsed goals, history watermarks, and drafts. Fixed Activity pages remain fixed; explicit refresh can revalidate their existing watermark.
Repositories loads projects, repositories, and observations, not Activity, goals, or source configuration. Activity loads repository context and goals, with the grouped journal fetched through its separate paginated commands. Hooks and Monitoring load their repository/project context and their own provider queries. Secrets loads repository context and its dedicated provider workspace. Overview includes source descriptors. The Settings index, membership, automation, and personal preferences need only the workspace shell; each task mounts only its own queries. GitHub evidence and local publisher settings add repositories, observations, and source descriptors. Import watches workspace changes to refresh eligibility without downloading repository records. Repository detail scopes repository records and evidence to its selected repository, adding the selected section's context. VIEW_COLLECTIONS and VIEW_TOPICS in shared/workspace-sync.ts define the exact mapping. Switching app tabs changes the subscription; an off-tab Activity change does not send a Repositories frame or trigger an HTTP read.
This replaces short interval reads for HQ-owned Activity, goals, repository snapshots, source refresh receipts, membership, credentials, imports, connection settings, and operation receipts. Hookrelay and Endpoint Monitor still own their provider state; bounded evidence reads retain their own refresh intervals. Account invitations retain a slow refresh because a recipient is not yet a member of the inviting workspace. Clock display timers do not perform network requests. Neither an open socket nor a heartbeat confirms that a target, scheduler, or agent is healthy.
Connection details
Select the connection indicator in the page header to open Connection details. It separates the live transport from the last accepted view data. An HTTP refresh can fail while the socket remains live; a socket can reconnect while saved view data remains available. Neither condition changes the freshness or completeness of provider evidence. Inspect the affected resource and its exact run receipt for that evidence.
The panel shows the last connection and transport response, bounded authorization renewal, scheduled retry or fallback times, and recent connection events. Dates and times follow your preferences. A connection reaching its update budget or authorization deadline is labelled Renewing live updates, not diagnosed as a service outage. A handshake or heartbeat timeout, rejected message, changed access, or temporary service failure has a separate allowlisted explanation. When a browser supplies only an abnormal close, HQ reports the interruption without guessing at its cause. A brief error/close grace preserves a following access-revocation signal; it does not extend reconnect or fallback limits.
Use Check current view for an explicit read without reloading the page, replacing draft choices, or starting provider collection. Automatic recovery preserves fixed historical Activity pages and completed GitHub receipts; explicit refresh may revalidate their exact existing references. A GitHub history opened without a run ID keeps its first selected receipt even when a newer run appears. Select another receipt deliberately to change it.
Connection events are bounded, in-memory browser diagnostics for the active workspace/view, not a durable incident log or a new server read capability. Reloading, changing the subscribed view, or renewing membership clears that local history. The panel retains only classified reasons, timestamps and numeric close codes, never raw frame bodies, server reason strings or credentials. Its display clock runs only while the panel is open and makes no network requests. For service-level investigation, use the operator diagnostics and authorized Cloudflare logs.
Protocol and authorization
GET /api/events requires a WebSocket upgrade, an exact same-origin Origin, a verified principal, and current workspace read authority. The browser supplies workspaceId, a named view, its cursor, and its bootstrap memberRevision; repository detail also supplies repositoryId. These select a bounded projection, not an authorization grant. Publisher and Reporter credentials cannot subscribe. The Worker creates the internal identity envelope rather than forwarding client-supplied identity headers. Each workspace maps to its own SQLite-backed WorkspaceEvents Durable Object through the reviewed WORKSPACE_EVENTS binding.
Version 2 ready and update frames bind the workspace and view to a notification revision and either a delta or explicit reset. Deltas identify their from and cursor, allowlisted upserts, and deletion keys. The ready frame also carries authorization expiry. Duplicate or older delivery cannot roll a cache back. A missing range, expired journal window, changed membership revision, or oversized response requests a fresh load of that view only. Invalid or wrong-workspace/scope frames cause reconnect without accepting their records. Independent view caches prevent tab switching or overlapping HTTP/socket responses from overwriting newer data.
The grouped Activity feed, provider snapshots, and operation histories retain narrow topic invalidations because their page aggregates or provider-owned observations are separate contracts. Only active queries refetch; historical Activity pages do not move with new events. Repeated invalidations are coalesced per query without cancelling in-flight reads. A change arriving during a read retains one trailing recheck so the final update is not lost. GitHub coverage, refresh history, active receipts and source credentials use a five-second minimum interval between push-triggered reads during a burst; other live aggregates use a shorter interval. Quiet queries do not poll. Hidden/offline tabs and unmounted views discard queued work and catch up on return. Small session/access signals remain app-wide, and genuine changed-access signals recheck fixed queries. This is not a claim of record-delta support for independent provider APIs. The version 1 notification protocol and explicit workspace_snapshot command remain compatible with older clients; the browser uses neither for its routine data path. shared/workspace-push.ts, shared/workspace-sync.ts and src/lib/workspace-query-refresh.ts define the schemas and operational bounds.
The repository overview subscribes to Activity and association signals for its bounded journal and local resource previews. Activity updates do not invalidate resource metadata. Ordinary evidence updates change the repository projection without a new context read; connection configuration or repository identity changes revalidate the relevant metadata. Secrets connection changes publish the existing association topic even when no main view record changed, so metadata-only updates are delivered with an empty record delta rather than silently skipped. Active auxiliary reads revalidate on subscription establishment and recovery to close their own initial-read gap. Hidden repository sections do not keep those queries mounted.
Accepted operational observations for the open repository also key a distinct repository_coverage_get read. This retrieves saved coverage and lease progress from HQ only; it never initiates provider work. The key uses that repository's operational source identity and original observation, receipt and expiry times. GitHub evidence and Activity do not change it. The browser selects the latest generated coverage response without extending evidence deadlines. An accepted update or invalidation withdraws the superseded card's freshness even if the saved-result read fails; earlier details remain visible as last-known evidence. A failed retained read has its own cache-only retry. The initial overview provider check and deliberate Check linked resources still use repository_coverage; source notifications cannot invalidate that provider-check query.
The explicit Refresh view button reloads only the selected view and its active auxiliary queries. This also rechecks derived source descriptors, such as deployment-bound credential availability, which can change without a D1 journal write. Routine socket delivery, reconnect catch-up, and fallback refresh use deltas; provider-owned evidence still requires its own collection or refresh.
GitHub Coverage uses the settings-source projection for filtering and accepted evidence. Its bounded github_coverage auxiliary query reads latest refresh items for the visible repository IDs only. Workspace, source and access changes revalidate that active query; Activity-only changes do not. Opening a receipt mounts the separate exact-run and bounded history reads. Switching to Connections unmounts the coverage read while retaining URL-backed filters, and the local freshness clock makes no network requests.
Overview derives repository problems, coverage gaps and reviews from its existing active-view projection. Its separate operational previews read at most two selected connections. Hook changes revalidate only Hookrelay previews, monitoring changes only Endpoint Monitor previews, and association, operation or access changes revalidate the affected active preview queries. GitHub source progress, ordinary workspace metadata and Activity do not trigger provider reads. A local clock ages the last preview; provider-owned changes outside HQ require a deliberate refresh or a new view read, not an inferred push event.
Hibernation-safe attachments retain the subject, membership revision, optional credential identity, authorization deadline, notification revision, view, and last delivered cursor. Membership and credential authority are checked before reading and again before sending records. Revocation closes the socket, clears affected caches, and refreshes the session. Connections expire no later than the verified credential or Access assertion, with a thirty-minute maximum; an alarm enforces expiry even when nothing changes. Per-workspace and per-subject connection limits, byte-bounded frames, and a notification budget bound fanout. Equivalent view/cursor subscriptions reuse a delta within one dispatch. A slow observer is closed and must catch up through authorized reads.
Heartbeats use the Durable Object automatic-response API without waking a hibernating object or reading the database. The server keeps no interval timer alive. Non-heartbeat client messages are rejected; operations remain reviewed HTTP commands.
When a new observer connects, admission releases expired connections and slots whose last received heartbeat or initial connection time exceeds PUSH_LIMITS.INACTIVE_MS. This recovers abandoned connections without increasing the subject/workspace caps, adding a cleanup timer or disconnecting a responsive idle tab. A stalled tab can reconnect through the normal catch-up path. Older attachments without a connection timestamp retain their authorization deadline until they renew; the server does not invent a last-response time. See the automatic-response timestamp contract.
Durable delivery and recovery
D1 triggers update a workspace revision and pending topic bitset in the same transaction as the changed record. A separate coalesced change-key journal tracks each changed entity's latest cursor, including deleted entities, without copying record bodies or private columns. Source scope, credential availability, and refresh dependencies record the affected public projection keys. The cursor is independent of notification-trigger execution order. Retention covers the bounded SYNC_LIMITS.HISTORY sequence window, with a collection-specific floor; unrelated Activity entries do not consume that journal. Activity and receipts remain the audit records, not the transport journal.
Cursor, retention floor, changed keys, and record projections are read in one D1 batch. Source descriptors use the existing safe formatter with selected IDs; a clock barrier retries if concurrent changes cross that derived read. Tests exercise transaction rollback, source-scope removal, and the read boundary. Catch-up is bounded by changed-key and UTF-8 frame limits; overflow is a view-only reset, not a partial response whose cursor skips undelivered records. A failed view load preserves existing content and exposes its error.
After a successful mutating command, HQ attempts delivery outside the response's critical path. The GitHub runner also publishes saved running and completed states. Acknowledgement clears pending topics only when the stored revision still matches the delivered revision. Interruption leaves the row pending; the minute scheduler retries a bounded set, and a later write can retry its workspace directly. Reconnect always catches up through reads. Without a running Cron, idle outbox repair depends on later writes or reconnects. Live updates describes transport connectivity, not proof that every producer is running. Inspect hq.push.interrupted warnings alongside scheduled invocation evidence.
Back up D1 privately and verify an isolated restore before migration. Install trigger-compatible conditional-write checks before 0014_workspace_push.sql: D1 includes trigger side effects in meta.changes, so an older exact-one check can reject a successful save. Apply 0020_workspace_sync.sql before deploying view-sync code. It adds the key journal, clock, retention floors, and dependency triggers without replacing application tables or copying secrets. Then deploy the reviewed push artifact with workspacePush: true and its existing Durable Object migration. No new hostname, provider binding, namespace, or secret is required.
Preserve both notification and sync schemas, newer application data, and compatible write-count checks during rollback. Once deployed, retain the Durable Object class export, binding, migration history, and Secrets retention safeguards in a recovery build; do not delete the namespace or restore an older whole database to undo UI behavior. A forward recovery can restore authorized view reads while retaining compatible server components. After a deliberate database restoration, reload clients so they establish fresh view baselines rather than trusting pre-restore cursors. Recheck the exact Worker, D1, Access policies, domain, bindings, and schedule after deployment.
Cloudflare Free-plan compatibility
SQLite-backed Durable Objects and WebSocket hibernation are available on Free and Paid. Documented compute allowances are 100,000 Durable Object requests and 13,000 GB-s per day on Free, versus one million included requests and 400,000 GB-s per month on Paid. RPC calls, upgrades, alarms, and billable incoming messages still consume resources; hibernation removes idle duration charges, not every charge. Outgoing messages are not billed as requests, and incoming WebSocket messages use the platform's documented billing ratio. See Durable Objects pricing and WebSocket hibernation.
Notifications and record deltas add D1 clock/key/index writes, bounded retention work, projection reads, acknowledgements, membership checks, and small alarm/storage operations. This exchanges idle dashboard reads and repeated fleet downloads for work proportional to changes and observers; it is not a spending ceiling. Multiple keys affected by a source dependency can share a sequence, and retention remains bounded by the sequence window and enrolled-resource bounds rather than an audit-history promise. No record bodies or private values are logged by this transport.
The scheduled collector already exceeds Free's per-invocation D1 and CPU allowances independently of this transport. Its regression fixture includes notification dispatch in the bounded Paid statement budget; an idle tick adds one outbox query without GitHub calls. Fleet-wide row usage, active-observer fanout, and deployed delta CPU have not been established as Free-compatible. Record those measurements before claiming Free compatibility, even when deploying on Paid. See diagnostics for measured collector and logging limits. Hibernating SQLite-backed sockets alone introduce no Paid-only requirement.