Files
kandev/docs/specs/plugins/system-design/isolated-web-app-contributions.md
T
2026-09-21 23:20:52 +01:00

32 KiB

id, title, status, system, owners, created, last_updated, requirements
id title status system owners created last_updated requirements
plugins-isolated-web-app-contributions-design Plugin web-application contributions system design draft plugins
kandev
2026-08-26 2026-09-21
REQ-PLUGINS-ISOLATED-WEB-APPS-001
REQ-PLUGINS-ISOLATED-WEB-APPS-002
REQ-PLUGINS-ISOLATED-WEB-APPS-003
REQ-PLUGINS-ISOLATED-WEB-APPS-004
REQ-PLUGINS-ISOLATED-WEB-APPS-005
REQ-PLUGINS-ISOLATED-WEB-APPS-006
REQ-PLUGINS-ISOLATED-WEB-APPS-007
REQ-PLUGINS-ISOLATED-WEB-APPS-008
REQ-PLUGINS-ISOLATED-WEB-APPS-009
REQ-PLUGINS-ISOLATED-WEB-APPS-010
REQ-PLUGINS-ISOLATED-WEB-APPS-011
REQ-PLUGINS-ISOLATED-WEB-APPS-012
REQ-PLUGINS-ISOLATED-WEB-APPS-013

Plugin web-application contributions system design

Purpose and boundaries

The Plugins system provides packaged web_app documents beside native bundles. The target runtime uses trusted same-origin iframes, not opaque-origin isolation. It does not import package code into the SPA module registry.

The same-origin decision amends the browser trust boundary. Implementation is complete under the same-origin delivery plan.

This design owns plugin instances, immutable releases, capability grants, runtime tokens, web data access, shared instance state, and live browser events. The Canvases system owns canvas scope changes and user workflows.

The canvas distribution design adds a static distribution profile and an inert retained project-source subtree. Preview images belong to registry listings. The profile reuses this runtime boundary; native plugin package installation does not become a canvas import path.

This design implements ADR-2026-08-26-plugin-backed-web-app-canvases. It extends the data boundary from ADR 0043.

Requirement mapping

Requirement Design sections
REQ-PLUGINS-ISOLATED-WEB-APPS-001 Web-application contribution, Browser host
REQ-PLUGINS-ISOLATED-WEB-APPS-002 Package and release contract, Release activation
REQ-PLUGINS-ISOLATED-WEB-APPS-003 Instance grants, Request authorization
REQ-PLUGINS-ISOLATED-WEB-APPS-004 Browser data protocol
REQ-PLUGINS-ISOLATED-WEB-APPS-005 Shared instance state
REQ-PLUGINS-ISOLATED-WEB-APPS-006 Live event protocol
REQ-PLUGINS-ISOLATED-WEB-APPS-007 Browser security boundary
REQ-PLUGINS-ISOLATED-WEB-APPS-008 Plugin instance model, Compatibility
REQ-PLUGINS-ISOLATED-WEB-APPS-009 Diagnostics and observability
REQ-PLUGINS-ISOLATED-WEB-APPS-010 Artifact storage, Protocol compatibility
REQ-PLUGINS-ISOLATED-WEB-APPS-011 Host appearance protocol
REQ-PLUGINS-ISOLATED-WEB-APPS-012 Runtime startup protocol
REQ-PLUGINS-ISOLATED-WEB-APPS-013 Same-origin transport and trust

Existing plugin contracts

The implementation extends these current boundaries:

  • internal/plugins/manifest owns manifest parsing and validation.
  • internal/plugins/pkgtar owns safe plugin package extraction.
  • internal/plugins owns installation, activation, actions, Host data, state, events, and agent tools.
  • pkg/pluginsdk and proto/kandev/plugin/v1/plugin.proto own the managed backend contract.
  • apps/packages/plugin-sdk owns the trusted native frontend contract.
  • lib/plugins loads native bundles and stores frontend contributions.

The new web-application runtime does not use PluginHostApi or PluginRegistry. Existing native bundles keep those interfaces unchanged.

Web-application contribution

Extend manifest.UISection with web_apps:

ui:
  web_apps:
    - key: main
      title: Task board
      entry: ui/index.html
      placements: [task-canvas, workspace-canvas]
      network_origins: [https://api.example.com]

key is unique inside the plugin. entry is a package-relative HTML file. The first placement vocabulary contains task-canvas and workspace-canvas. network_origins is an optional list of exact HTTPS origins for this web application. It cannot contain paths, credentials, wildcards, query strings, or fragments. Later specifications can add other host placements.

The host owns the displayed navigation label, icon, scope, route, and actions. The supported canvas UI occupies its document rectangle. Trusted same-origin code can also access the parent document; geometry is not a security boundary.

A package can contain these contribution combinations:

Package form Native bundle Web application Managed backend
Existing native plugin optional none required
Static canvas package none required none
Managed web plugin optional required required

A web-application-only package omits runtime. A managed plugin keeps runtime.type: binary and can use declared authenticated actions.

Plugin instance model

Installed plugin records stay the source for package identity and managed backend lifecycle. Add explicit plugin instances for scoped activation.

plugin_instances

  • id
  • plugin_id
  • source_kind as installed or local_canvas
  • scope_kind as instance, workspace, task, session, or repository
  • nullable trusted scope identifiers
  • status as pending, active, disabled, archived, error, or removed
  • nullable active_release_id
  • monotonic grant_generation incremented on scope, release, status, or grant changes
  • timestamps

Only identifiers required by scope_kind can be set. A database constraint and service validation reject incomplete or mixed scopes.

Existing installed plugins receive one implicit instance scope. This compatibility projection does not change current settings, enablement, process lifecycle, or native UI loading.

plugin_releases

  • id
  • plugin_id
  • package_digest
  • source_kind
  • source_actor_kind
  • nullable source user, task, and session identifiers
  • manifest_json
  • declared_permissions_json
  • artifact_path
  • validation_status
  • nullable safe validation error
  • created_at

Release rows and extracted artifacts are immutable. The artifact path is under the Kandev data directory. It is not an agent workspace path.

plugin_instance_grants

  • plugin_instance_id
  • permission_kind
  • resource
  • optional exact HTTPS origin
  • trusted scope ceiling
  • approving user and time

The service normalizes grants before persistence. It rejects a grant that the active or pending release does not declare.

Package and release contract

A local web application package contains the manifest, source, built static files, and optional metadata. Kandev serves only files under the declared web root. It stores the source so an edit agent can create the next draft.

The agent builds any TypeScript, React, or other source in its task executor. Kandev does not install dependencies or run package scripts during validation. Simple canvases can use direct HTML, CSS, and JavaScript without a build step.

Initial package limits are:

  • 10 MiB compressed package
  • 25 MiB expanded package
  • 512 files
  • 5 MiB for one file
  • 64 KiB manifest
  • 240 bytes for one normalized path

Retained artifacts use these initial storage limits:

  • 2 GiB for one workspace
  • 10 GiB for one Kandev installation
  • one active valid release, one prior valid release, and one pending release for each plugin instance

The admission transaction reserves the compressed artifact bytes before it moves an artifact into immutable storage. A concurrent publish cannot exceed a workspace or installation limit. A rejection does not create a release row or change the active release.

The validator accepts HTML, CSS, JavaScript, JSON, common web images, fonts, and source-map files. It uses content sniffing and file extensions. It rejects device files, links, duplicate normalized paths, traversal, absolute paths, and files outside the package root.

The capability path is rooted at the declared entry directory for ordinary relative asset requests. For example, ui/index.html can load ./app.js and ./app.css from ui/. The reserved _kandev/v1 path remains rooted at the capability URL and is dispatched before ordinary asset mapping.

pkgtar provides the extraction pattern. Web packages use a separate validator because they can omit a managed backend executable.

Release activation

Release activation uses these steps:

  1. Stream the bounded package into a temporary directory.
  2. Validate all package paths and sizes before extraction.
  3. Parse and validate the complete manifest.
  4. Validate each declared web application and its entry document.
  5. Compute the package digest and declared permission set.
  6. Move the validated artifact into immutable release storage.
  7. Create the release row.
  8. Compare the permission set with the current instance grants.
  9. Activate the release in one transaction when no new grant is required.
  10. Publish one instance lifecycle event after commit.

If the release needs a new grant, the service records it as pending. The active release stays unchanged. A user can approve the permissions and activate the pending release in one transaction.

The canvas service can supply recorded initial creation authority through its creation transaction. The instance store validates and inserts the exact grants in that transaction. The generic plugin publication and import paths cannot infer this exception from a package manifest, source-user field, or instance ownership.

The initial retention rule keeps the active release and one prior valid release for each local canvas instance. A pending release is also retained until approval, replacement, or removal.

Instance grants

The effective permission calculation is:

manifest declaration
  intersect instance grant
  intersect trusted instance scope
  intersect current caller authorization

The permission vocabulary reuses current manifest capabilities:

  • api_read
  • api_write
  • events
  • state
  • declared authenticated actions

Add exact HTTPS origin grants for browser network access. Each web application declares its own network_origins, so one app cannot inherit another app's external origins. Remote script origins are not grantable in this version.

Task-scoped instances default to the current task as their scope ceiling. Workspace access requires a separate approved grant. Promotion performs a new review because it changes the scope ceiling.

Browser host

Add one host component for web applications. The component requests a runtime URL for an authorized instance, release, placement, and user. The backend returns a short-lived capability URL.

WebAppFrame and BuildContentSecurityPolicy both declare sandbox="allow-scripts allow-forms allow-same-origin". Both must change in one implementation slice. Either opaque policy would otherwise retain the failure. The runtime keeps its served origin, including top-level capability navigation. Form submissions remain denied by form-action 'none'.

The host injects no native API or credential value. Appearance and startup messages stay bounded. Host chrome remains outside the frame for presentation; trusted frame code can access that chrome through normal same-origin DOM APIs.

The desktop shell adds a narrow frame-src rule for the loopback backend. BuildContentSecurityPolicy always prepends the literal CSP source 'self' to frame-ancestors, then appends normalized explicit launcher and Tauri origins. Keep normalizeFrameAncestors restricted to exact origins; the host adds the keyword separately. Never derive ancestors from inbound Host, Origin, Referer, or forwarded headers, and never use wildcard hosts or ports.

Relative capability URLs keep the browser UI and runtime on the same public origin, including TLS termination at a reverse proxy. Each DNS alias works independently without a configured list. An alias does not authorize framing a different alias. Cross-origin frontend/runtime hosting requires an explicitly trusted origin; this repair adds no new operator configuration for that topology. Preserve the current exact development ports and Tauri exceptions. Framing permission and browser same-origin identity remain separate decisions. Browser coverage must use real runtime responses through two custom HTTPS hosts and an unrelated-parent negative case, not just assert the header text.

Host appearance protocol

The isolated runtime has one host-to-frame presentation message. Version 1 has this shape:

{
  "type": "kandev.web_app.appearance",
  "version": 1,
  "mode": "dark",
  "tokens": {
    "background": "...",
    "foreground": "...",
    "card": "...",
    "cardForeground": "...",
    "muted": "...",
    "mutedForeground": "...",
    "border": "...",
    "primary": "...",
    "primaryForeground": "...",
    "accent": "...",
    "accentForeground": "...",
    "destructive": "...",
    "destructiveForeground": "...",
    "ring": "..."
  }
}

WebAppFrame resolves these values from the current host theme. Each value is a bounded serialized CSS color from a fixed key allowlist. The envelope contains no identity, capability, data, storage, navigation, or action field.

After the iframe load event, the host sends the initial envelope to that iframe's contentWindow. Reveal additionally requires the current startup acknowledgement described below and one animation frame for appearance. The host sends another envelope when the resolved theme changes without reload.

The existing appearance sender targets the exact frame window with targetOrigin: "*" to retain support for web and distinct-origin desktop hosts. The application listener accepts only messages whose source is window.parent and whose type, version, mode, keys, and value bounds match the contract. This source check prevents a sibling frame from setting appearance values. The wildcard does not grant authority because the payload contains public presentation data only.

The bundled scaffold maps the token keys to documented CSS custom properties. It includes safe light and dark fallbacks. An application can ignore this message, but it does not receive another theme API or a privileged reply channel.

Runtime startup protocol

The plugin runtime owns a bounded startup monitor, not authored application code or an optional scaffold. During entry-HTML serving, insert a host-owned bootstrap before authored scripts using the existing Go HTML tokenizer dependency. Preserve the doctype, encoding, entry-relative paths, and original markup bytes around the insertion. Support valid HTML with an omitted head. Bound processing by the entry-file limit; fail safely if insertion cannot be performed. Non-entry assets stay byte-identical. Stored files, digests, and edit source never change. Compute Content-Length from the served representation.

Serve the bootstrap from a reserved capability-relative _kandev/host-runtime.js route before versioned protocol dispatch. Validate the capability and apply normal runtime headers. Package files cannot shadow it. The bootstrap installs capture-phase script/asset error and unhandled-rejection listeners before authored code. It waits for document load, then fetches ./_kandev/v1/context using the existing capability path and credentials: "same-origin". Never use omit for this host-owned request. It reports only safe startup result codes. A handled application data error can still render inside a successfully started frame; Ready is not proof of application correctness or business-operation success.

WebAppFrame creates a fresh bounded attempt nonce for every mount/runtime URL. After load it sends kandev.web_app.startup_probe version 1 with that nonce to the exact frame window. The bootstrap accepts only window.parent and echoes the nonce with kandev.web_app.startup_result, version 1, and result ready or failed after the startup outcome is known. It retains one pending probe and one outcome; no repeating message or polling loop is required. Failure codes are a closed vocabulary (document_error, context_unavailable). Arbitrary error text and request URLs are never forwarded.

The parent checks exact message shape, version, nonce, and event.source === iframe.contentWindow. Origin alone is not sufficient authority. Same-origin code is trusted and can access host state directly. Replies from a previous mount, sibling window, wrong nonce, or unsupported version are ignored. The reply changes presentation only. No domain APIs, grants, navigation, or credentials cross this channel. Top-level capability navigation has no host probe and remains sandboxed.

The 15-second startup deadline begins when the iframe is mounted. It is separate from the 15-minute capability lifetime. No acknowledgement, including CSP-blocked navigation, causes unavailable status and frame teardown. Retry obtains a fresh descriptor and nonce through existing host actions. Late replies cannot clear failure; listeners and timers are cancelled on teardown. Authority changes still unmount the frame immediately. Renewal mounts a new attempt; live theme changes do not restart startup. Existing retained releases receive the bootstrap at serve time and need no author edits or republish.

Runtime token

The runtime URL contains a random capability segment. The backend stores only a digest of this value. The capability expires after 15 minutes and can use a bounded sliding renewal while the host page remains authorized.

The capability binds these values:

  • user identity
  • plugin instance
  • active release
  • web-application key
  • placement
  • scope kind and trusted resource identifiers
  • grant generation

Each request loads the current instance and compares these bindings. A scope, grant, release, status, or access change revokes the old capability on its next request.

Approved external origins are a deliberate direct-browser exception to this per-request runtime check. Kandev cannot inspect a request after the browser has sent it to a third-party origin. The WebSocket lifecycle broadcaster sends an authority-change notification to the host, and the host unmounts the matching iframe immediately. The replacement iframe is mounted only after a fresh HTTP metadata and runtime-binding load. This is the revocation boundary for direct external requests. Kandev runtime and protocol requests still run the binding validator on every request.

The runtime route accepts the capability independently of ambient session cookies. A reverse proxy can require its own cookie before forwarding the request. Kandev does not accept a runtime token from a general header or query field. Legacy opaque-origin CORS responses remain restricted; no credentialed null origin or wildcard CORS permission is added.

Browser data protocol

The capability path is the application base. The application uses relative requests. Example paths are:

GET    ./_kandev/v1/context
GET    ./_kandev/v1/data/tasks
GET    ./_kandev/v1/data/tasks/{id}
PATCH  ./_kandev/v1/data/tasks/{id}
POST   ./_kandev/v1/data/tasks/{id}/messages
GET    ./_kandev/v1/data/workflows
GET    ./_kandev/v1/data/workflows/{id}/steps
GET    ./_kandev/v1/state
GET    ./_kandev/v1/state/{key}
PUT    ./_kandev/v1/state/{key}
DELETE ./_kandev/v1/state/{key}
GET    ./_kandev/v1/events
POST   ./_kandev/v1/actions/{key}

context returns only public instance metadata, scope identifiers that the application can already use, active release identity, and effective capabilities. It does not return a user token, Kandev session, filesystem path, secret, or private plugin configuration.

The data routes reuse the DTOs, filters, opaque pagination, and conservative write fields from the Plugin Host data API. The shared task projection adds workflow_step_id. The browser and gRPC adapters return this field with the same nullability and authorization rules.

The task message route reuses the Plugin Host SendMessage service. It accepts a bounded prompt and an optional session ID. The runtime context supplies the plugin instance, user, workspace, and source attribution. A static canvas uses this route for actions such as sending continue to a task.

Extract shared application services from internal/plugins/host_data_* when the current gRPC handler owns logic that both adapters need.

All writes use Kandev domain services. They never write a repository directly. The service publishes the same task and workflow events as first-party writes.

Initial browser request limits are 256 KiB for a request and 1 MiB for a response page. Each request has a 30-second deadline and supports cancellation.

Shared instance state

Add plugin_instance_state or extend plugin_state with a non-null instance identity. The migration must preserve current instance-global plugin state.

Each entry contains:

  • plugin instance
  • state key
  • JSON value
  • monotonic revision
  • update time
  • safe writer kind

The browser uses If-Match with the current revision for an update or removal. A stale request returns 409 plugin_state_conflict with the current revision. The response does not include the current value unless the caller still has read permission.

Canvas agent tools use the same state service. They derive the plugin instance from trusted canvas and session context.

Live event protocol

The browser opens the relative Server-Sent Events endpoint. The event gateway subscribes to declared Kandev event subjects and filters every event through the current instance scope.

The stream sends public, bounded DTOs. It never forwards internal event objects or unfiltered plugin bus payloads.

Each stream has a process generation and monotonic sequence. A bounded in-memory ring retains recent events for active instances. The browser sends Last-Event-ID after reconnect.

The initial stream contract uses these operational limits:

  • one heartbeat comment every 15 seconds
  • immediate header and event flushing
  • at most two streams for one user and instance
  • at most 20 streams for one user
  • at most 1,000 retained events or five minutes of replay for one instance

The response sets Cache-Control: no-cache, no-transform and X-Accel-Buffering: no. The server releases subscriptions and counters when the request context ends. EventSource cannot set an authorization header, so the event endpoint uses the capability path and accepts no query token.

The gateway sends runtime.resync_required when the generation changed or the sequence is no longer available. The application then reloads its authoritative data and state snapshot. Domain data and plugin state remain the sources of truth.

Managed plugin actions

A managed plugin can declare a web application and authenticated actions. The relative action route reuses the authorization, scope verification, deadlines, body limits, status projection, and cancellation contract from POST /api/plugins/:id/actions/:key.

A static web-application-only package has no custom action handler. Its browser code uses the Host data and state routes. A later backend-runtime decision can add another handler type without changing the browser protocol.

Browser security boundary

The runtime document response uses this minimum header policy:

  • Content-Security-Policy includes sandbox allow-scripts allow-forms allow-same-origin, default-src 'none', form-action 'none', base-uri 'none', and object-src 'none'
  • frame-ancestors contains the host-owned 'self' keyword and normalized explicit Kandev launcher and Tauri origins
  • X-Content-Type-Options is nosniff
  • Referrer-Policy is no-referrer
  • Cross-Origin-Resource-Policy remains cross-origin for distinct-origin Tauri hosts
  • capability-bearing HTML and API responses use Cache-Control: no-store

The response uses a restrictive resource policy:

  • packaged and inline application scripts can run
  • dynamic code evaluation is denied
  • remote scripts are denied
  • styles and packaged assets can load
  • data images can load within a bounded policy
  • data connections can reach the runtime API
  • approved HTTPS origins can receive explicit connect-src, image, or font access while the iframe binding is current
  • direct external requests are not server-proxied in this version. Lifecycle authority notifications tear down the iframe before a replacement load
  • framing is limited to the Kandev host

The implementation must build the policy from normalized grants. It must not copy manifest text into a response header.

Runtime headers remain host-owned. They do not grant broader service-worker scope. Same-origin code can access origin storage and the parent document; service-worker unavailability must not be claimed as an isolation guarantee. Authors use instance state for shared values and memory for temporary values. They must not modify host internals or depend on shared browser-storage keys. These are authoring rules, not enforced browser boundaries.

Same-origin transport and trust

This change applies to the shared ui.web_apps runtime, including local, imported, and installed-plugin canvases. It adds no manifest option, profile switch, database migration, or separate iframe transport.

The startup bootstrap uses credentials: "same-origin". Standard relative fetch, XHR, asset loads, and EventSource requests use their same-origin browser behavior. No global fetch shim rewrites authored requests. Retained packages that explicitly omit credentials need an author edit and republish. Cross-origin credential delivery, including separate-origin desktop hosts, is not enabled globally. Capability-only local operation remains supported.

The proxy must preserve the public Host for writes that carry Origin. corsMiddleware already accepts matching-host origins through AllowedOrigin. Do not trust arbitrary forwarded headers or broaden the general CORS policy. The HTTPS test proxy must preserve browser cookies and public Host; it must not supply missing cookies from its Node client or mock successful protocol replies.

The proxy cookie only passes the outer gate. Runtime.Serve still validates its token and binding before assets, data, state, actions, and events. Cookies must not change the bound actor, release, scope, or grant generation. Invalid capabilities and denied writes still fail through existing backend services. Direct first-party API requests use the ordinary user authorization boundary; canvas grants do not constrain them. Revoking a runtime token cannot undo DOM changes, shared storage, or callbacks already installed in the parent page.

Keep script-src, network grants, form policy, and exact frame ancestors. These govern the served document, not trusted code acting through its parent. Proxy HTML injection remains incompatible with the startup monitor and CSP. Operators must exclude runtime responses from Cloudflare RUM and other injected scripts that violate policy. Do not allow remote analytics scripts or suppress document errors to conceal this problem.

See response preservation.

UI behavior stays unchanged. Test Ready, data, a permitted write, and an event through a real local cookie gate. Test missing cookies, reauthentication and Retry, invalid tokens, and unchanged package bytes. Record desktop and phone results separately.

Compatibility

The native plugin frontend remains an in-process trusted extension. A package must declare ui.bundle to use that path. A ui.web_apps declaration does not receive host.api, host.storage, host.context, host.ui, or registry methods.

The managed backend gRPC contract remains unchanged for a static web application. Managed plugins can combine both frontend contribution types.

The plugin list and settings API add instance and web-application projections without removing current installed-plugin fields.

The ./_kandev/v1 browser protocol remains available while any retained release declares version 1. A later protocol version uses a new relative path. Kandev does not rewrite an immutable release. If a future Kandev version cannot serve a retained protocol, startup marks the release unavailable before it can execute and the host shows edit, rollback, or removal actions.

Artifact storage and recovery

Validated artifacts remain immutable files under the Kandev data directory. The database stores their digest, relative path, byte count, and availability. The artifact directory is part of the Kandev-home recovery boundary.

SQLite database snapshots do not contain release artifacts. PostgreSQL backups also require a backup of the Kandev artifact directory. Public operations and configuration documentation state this boundary.

Startup reconciles retained release rows with the artifact inventory before it registers runtime routes. A missing file, digest mismatch, or unsafe path marks the release plugin_release_unavailable. Reconciliation never executes or silently removes the artifact. It records a content-free diagnostic and leaves the release metadata available for recovery.

Workspace and instance removal create durable artifact-cleanup jobs in the same transaction that removes release ownership. A worker removes the files after commit and retries after restart. This order preserves cleanup inventory if the process stops between the database change and file removal.

Failure and recovery

  • An invalid package never enters release storage as a valid release.
  • An activation transaction publishes no event before commit.
  • A stale runtime token returns runtime_token_stale.
  • A revoked grant returns plugin_permission_denied.
  • A stale state write returns plugin_state_conflict.
  • A lost event range returns runtime.resync_required.
  • A missing active release returns plugin_release_unavailable.
  • An unavailable managed backend disables its action routes but does not block static asset and Host data routes.

The browser host maps stable codes to localized messages. It never shows raw backend or package errors.

Observability

Record bounded result counters and durations for validation, activation, runtime access, data/state operations, events, policy denials, and artifact storage. Logs may identify plugin, instance, release, operation, result, and duration. Never log source, bodies, state values, event payloads, tokens, or credentials.

Test strategy

  • Validate manifests, package combinations, traversal, links, duplicates, limits, digests, activation, grant intersection, and SQLite/PostgreSQL migrations.
  • Exercise runtime bindings, stale tokens, CORS, safe errors, shared state, pagination, and browser/gRPC projection parity.
  • Verify both sandbox declarations, cookie delivery, form and remote-script denial, nested assets, exact ancestors, and capability revocation.
  • Verify scoped events, replay, generation changes, and resync.
  • Cover failed documents/assets/context, absent and stale acknowledgements, sibling replies, timeout cleanup, retries, renewal, and unchanged artifacts.
  • Verify initial and live appearance in direct, Dockview, and phone hosts.
  • Execute the cookie-proxy and authentication tests in the same-origin delivery plan.