Files
kandev/docs/decisions/2026-08-26-plugin-backed-web-app-canvases.md
T
2026-09-23 20:51:55 +01:00

165 lines
7.8 KiB
Markdown

# ADR-2026-08-26-plugin-backed-web-app-canvases: Use plugin-backed web applications for canvases
**Status:** accepted
**Date:** 2026-08-26
**Updated:** 2026-08-30
**Area:** backend, frontend, protocol, plugins, security
The initial-grant rule is amended by
[owner-authorized canvas creation](2026-09-10-canvas-creation-authority.md).
Task placement and data scope are separated for owner-authorized canvases by
[task canvas workspace data](2026-09-23-task-canvas-workspace-data.md).
The bounded startup acknowledgement is specified in the
[runtime design](../specs/plugins/system-design/isolated-web-app-contributions.md#runtime-startup-protocol);
it adds no privileged host API.
The browser isolation and credential rules below are amended by
[trusted same-origin canvases](2026-09-19-trusted-same-origin-canvases.md).
The original rationale remains historical; the new decision owns that boundary.
## Context
The first canvas design used server-owned Markdown and structured blocks. That
design limited canvas interfaces to types that Kandev implemented in advance.
It did not provide the flexibility of GitHub Copilot App Canvas extensions.
The intended canvas is a custom application that an agent creates for one
task. A user can promote a useful application to its workspace. The application
can then appear in workspace navigation and serve more than one task.
Kandev already has a plugin system for permissions, Kandev data access, events,
state, agent tools, process lifecycle, and UI contributions. A separate canvas
runtime duplicates those contracts and blocks later plugin authoring in
Kandev.
Agent-generated code must not run in the Kandev SPA. The current native plugin
bundle runs with the host React instance and application context. That trust
level is not valid for arbitrary canvas code.
## Decision
Kandev will implement a canvas as a scoped instance of a plugin web-application
contribution.
An agent creates and publishes a canvas from a task session. The first canvas
scope is that task. A user can promote the same canvas instance to workspace
scope after a permission review. Only workspace canvases appear in workspace
navigation.
The web application can contain arbitrary packaged HTML, CSS, and JavaScript.
Kandev will show it in a sandboxed iframe. Kandev will not load this code as a
native plugin bundle or give it the host React, DOM, Zustand store, or cookies.
The runtime document will also use the CSP `sandbox` response directive. This
rule keeps the document opaque when a person opens its URL outside the iframe.
Kandev will not inject a canvas JavaScript object. The application will use
relative HTTP and Server-Sent Events endpoints that the plugin runtime owns.
Those endpoints will expose only the effective manifest capabilities and the
authorized canvas scope.
The host can send a versioned, one-way appearance envelope to the iframe. The
envelope contains only the resolved color mode and bounded semantic colors. It
does not create a host API or grant authority.
The effective permission set is the intersection of these values:
- the package declarations
- the grants that the user approved for the canvas instance
- the current Kandev access decision for the user and resource
Each published release is immutable. A valid release replaces the active
release atomically. A rejected release does not change the active release.
Kandev keeps one prior valid release for rollback and editing.
Release artifacts remain files under the Kandev data directory. The Kandev
home, not a database snapshot alone, is the recovery boundary for these files.
Startup marks a release unavailable before execution when its artifact is
missing or has a different digest.
The first delivery supports static web-application packages. It supports
Kandev data, canvas state, and live Kandev events through the host gateway. It
does not run an agent-generated backend process on the Kandev host.
Installed managed plugins can add web-application contributions through the
same package and placement contract. A later decision can add a sandboxed
custom backend runtime for locally authored plugins.
The user edits a workspace canvas through a Quick Chat agent. Kandev gives the
agent a draft copy of the current source. The agent publishes a new immutable
release through the same validation path.
The canvas-authoring skill uses a canvas-owned embed and a canvas-owned system
skill directory under the Kandev home. It does not use the Office bundled-skill
embed or Office workspace deployment.
Implementation starts from `main`. PR #3061 is the superseded declarative
implementation and is not a prerequisite. The first implementation ships
behind `features.canvases`, with all profile defaults set to `false`.
## Consequences
- Canvas interfaces can use arbitrary packaged HTML, CSS, and JavaScript.
- Canvas data access follows the plugin permission and data contracts.
- Task canvases and workspace canvases use one package and release format.
- Promotion changes scope and discovery. It does not convert the application.
- Future plugin authoring can reuse the package, release, permission, and web
application runtime.
- Kandev must add scoped plugin instances and grants to its instance-global
plugin model.
- Kandev must add an isolated web-application contribution beside trusted
native React contributions.
- The HTTP gateway becomes a public plugin protocol and needs versioned data
shapes, limits, conflict behavior, and event recovery.
- Arbitrary custom backend logic remains unavailable to static-only canvases.
A managed plugin backend can still provide custom plugin actions.
- A canvas must bundle its executable frontend assets. Remote scripts do not
become part of the active release.
- Runtime isolation applies to iframe and direct capability-URL navigation.
- A complete canvas backup includes the database and retained artifacts.
- Database-only recovery can leave a release unavailable but cannot execute a
missing or changed artifact.
- Canvas authoring does not add a bundled skill to Office workspaces.
- Operators can keep every canvas entry point disabled through one backend
feature gate.
- Isolated applications can match live Kandev appearance without sharing host
state or authority.
## Alternatives Considered
### Keep the closed native block model
Rejected. It gives Kandev consistent rendering but cannot provide arbitrary
application interfaces or agent-authored functionality.
### Load canvas JavaScript as a native frontend plugin
Rejected. Native bundles share the Kandev JavaScript process, React context,
DOM, and application origin. Agent-generated code needs a stronger boundary.
### Add a canvas-specific JavaScript SDK
Rejected. It creates another extension contract and couples canvas code to an
injected global object. Standard web requests are sufficient for the isolated
runtime.
### Require an agent-generated Go plugin binary
Rejected for the first delivery. Kandev does not ship a compiler on every
installation. An unsandboxed generated binary also has excessive host access.
### Store only an iframe URL
Rejected. A remote URL does not give Kandev immutable releases, offline use,
permission review, source editing, or dependable rollback.
## Related records
- [Agent-authored web-app canvas requirements](../specs/canvases/requirements/agent-authored-web-apps.md)
- [Agent-authored web-app canvas design](../specs/canvases/system-design/agent-authored-web-apps.md)
- [Isolated plugin web-application requirements](../specs/plugins/requirements/isolated-web-app-contributions.md)
- [Isolated plugin web-application design](../specs/plugins/system-design/isolated-web-app-contributions.md)
- [GitHub Copilot App Canvas reference](../copilot-canvas-reference.md)
- [Plugin Host data API](0043-plugin-host-data-api.md)
- [Plugin agent tools through Kandev MCP](2026-08-11-plugin-tools-through-kandev-mcp.md)
- [Superseded declarative canvas decision](2026-08-25-server-owned-declarative-canvases.md)