feat: add canvas marketplace distribution and sharing (#3603)

This commit is contained in:
Carlos Florêncio
2026-09-14 09:31:20 +01:00
committed by GitHub
parent 5c6434d7d8
commit ba960f9732
121 changed files with 10732 additions and 121 deletions
@@ -0,0 +1,30 @@
---
created: 2026-09-14
status: done
requirements:
- REQ-PLUGINS-MARKETPLACE-002
system_design:
- ../../specs/plugins/system-design/marketplace.md
legacy_specs: []
---
# Implementation plan: Plugin marketplace previews
Extend the existing native-plugin marketplace with optional registry-owned
preview galleries while preserving legacy entries and the current installation
path. The canvas marketplace plan consumes the same registry and gallery
primitives, but the plugin requirement remains owned by this package.
## Scope
- Ordered optional preview metadata for official and custom plugin listings.
- Cover and details gallery behavior with keyboard, touch, and image failures.
- Existing no-preview rows and native install actions.
- Plugin marketplace, manifest, authoring, and registry README guidance.
## Work orders
- [ ] [Task 01: Extend plugin marketplace previews](task-01-plugin-previews.md)
Dependencies: 01. The single work order keeps the plugin requirement and its
system design in one lookup scope for documentation validation.
@@ -0,0 +1,115 @@
---
id: "01-plugin-previews"
title: "Extend plugin marketplace previews"
status: done
wave: 1
depends_on: []
plan: "plan.md"
requirements:
- REQ-PLUGINS-MARKETPLACE-002
acceptance_criteria:
- AC-PLUGINS-MARKETPLACE-002.1
- AC-PLUGINS-MARKETPLACE-002.2
- AC-PLUGINS-MARKETPLACE-002.3
- AC-PLUGINS-MARKETPLACE-002.4
system_design:
- ../../specs/plugins/system-design/marketplace.md
---
# Task 01: Extend plugin marketplace previews
## Summary
Complete the optional native-plugin preview extension that shares the registry
metadata and gallery primitives used by canvas entries. Keep preview metadata
outside plugin packages and preserve the existing native installation path.
## In scope
- Ordered optional `previews: [{url, alt}]` fields for official and custom
plugin registry entries and generated catalog records.
- First-image cover presentation, details gallery, keyboard and touch controls,
image-position descriptions, one-image mode, and contained image failures.
- Existing plugin rows and install actions when a listing has no previews.
- Registry-owned preview metadata, so URLs, descriptions, and order can change
without releasing a plugin version.
- Shared gallery tests and native-plugin marketplace regression coverage.
- Public plugin marketplace and manifest guidance, the linked authoring guide,
and plugin registry README examples.
## Out of scope
Canvas package validation, canvas installation review, screenshot requirements
for bundle creation, native plugin package changes, and a new installation path.
## Acceptance
- Official and custom plugin entries accept an ordered HTTPS preview list while
existing entries without previews remain valid.
- Listings with previews show the first image as the cover and expose the full
gallery; listings without previews keep their current row and install action.
- Gallery navigation works with keyboard and touch, retains installation when an
image fails, and never executes package code in the preview.
- Preview metadata remains registry-owned and can change independently of plugin
package releases.
## Verification
```bash
rtk bash -lc 'KANDEV_CANVAS_PACKAGE_INSPECTOR=apps/backend/bin/canvas-package node --test plugin-registry/build-index.test.mjs'
(cd apps/web && rtk pnpm exec vitest run components/settings/plugins/marketplace-preview-gallery.test.tsx components/settings/plugins/plugin-row.test.tsx)
(cd apps/web && rtk pnpm e2e:run --project chromium tests/plugins/plugins.spec.ts -- --retries=0)
(cd apps/web && rtk pnpm e2e:run --project mobile-chrome tests/plugins/mobile-plugin-settings-row.spec.ts -- --retries=0)
rtk proxy node --test scripts/validate-public-docs.test.mjs
rtk git diff --check -- docs/public/plugins-marketplace.md docs/public/plugins-manifest.md docs/public/plugins-authoring.md plugin-registry/README.md
```
Use local fixtures and the built inspector. Do not publish a registry entry or
execute a plugin while testing preview details.
## Files likely touched
- `plugin-registry/schema.json`, `build-index.mjs`, and `build-index.test.mjs`
- `apps/web/components/settings/plugins/marketplace-preview-gallery.tsx`
- Existing plugin row/details components and their focused tests
- `apps/web/components/settings/plugins/plugins-settings.tsx`
- `apps/web/lib/api/domains/marketplace-api.ts` and marketplace hooks/tests
- `apps/web/src/locales/{en,pt-pt,zh-cn,zh-hk,zh-tw,pseudo}/`
- `docs/public/plugins-marketplace.md`
- `docs/public/plugins-manifest.md`
- `docs/public/plugins-authoring.md`
- `plugin-registry/README.md`
## Dependencies
The canvas marketplace plan provides the shared registry and gallery primitives;
the plugin contract remains independently traceable to the plugin design.
## Risks
Do not make previews part of a plugin manifest or release archive. Remote image
failures must not block native installation, and the details surface must never
load a runtime URL or execute package code.
## Parallelism
`sequential`
## Inputs
- Plugin marketplace requirements and the registry preview system design.
- Existing `buildEntry`, `build-index.test.mjs`, plugin rows, and native install
dialog.
- Shared marketplace gallery behavior and the plugin documentation validation
guide.
## Results
Implemented optional ordered plugin previews in registry and catalog records,
shared gallery details with keyboard and touch controls, image-failure fallback,
and unchanged native installation for entries without previews. Updated the
plugin marketplace, manifest, authoring, and registry README guidance.
Verification: the existing plugin marketplace regression passed 16 tests; the
registry build-index suite and shared gallery coverage passed with the canvas
marketplace validation; public documentation validation and diff checks passed.
+446
View File
@@ -0,0 +1,446 @@
---
created: 2026-09-10
status: done
requirements:
- REQ-CANVASES-MARKETPLACE-001
- REQ-CANVASES-MARKETPLACE-002
- REQ-CANVASES-MARKETPLACE-003
- REQ-CANVASES-MARKETPLACE-004
- REQ-CANVASES-MARKETPLACE-005
- REQ-CANVASES-MARKETPLACE-006
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
legacy_specs: []
---
# Implementation plan: Canvas marketplace
## Overview
Add canvas distribution to the existing Plugins marketplace. Authors download
an installable bundle and source project, then follow manual repository and
registry instructions. Recipients inspect screenshots and permissions before
installing an independent workspace canvas.
The [requirements](../../specs/canvases/requirements/marketplace-sharing.md) and
[design](../../specs/canvases/system-design/marketplace-sharing.md) are the
authoritative pair. The selected owner is Canvases because it owns reusable
canvas identity, source lineage, workspace instances, and discovery.
Implementation is complete across the seven sequential work orders: portable
package contract, export preparation, reviewed installation, registry
enrichment, marketplace UI, sharing UI, and public instructions. The optional
native-plugin preview extension is tracked in the companion plugin plan. No native
implementation subagents were authorized by this plan. Each work order's
Results section records its implementation and validation evidence.
## Scope
### In scope
- One existing marketplace with a dedicated Canvases tab in Plugins settings.
- `.tar.gz` upload, direct HTTPS bundle link, and catalog installation.
- Required registry cover URL and up to seven additional previews for canvases.
- Registry cover and preview URLs for canvas entries.
- Screenshot-free bundle creation, source downloads, and upload/link installs.
- Package metadata, source mode, license, compatibility, and permission review.
- Download bundle and source ZIP from the same prepared release snapshot.
- Manual repository/release/sharing instructions and PR-based registry listing.
- Independent workspace copies, durable provenance, retry-safe confirmation.
- Static runtime isolation, workspace authorization, quotas, and disabled paths.
- Desktop and phone UI, localization, targeted E2E, author/public documentation.
### Out of scope
- Repository-URL installation, cloning, builds during install, or new hosting.
- Automated code-host publishing, repository creation, or registry PR submission.
- Canvas updates/replacement, state export, credentials, or public live instances.
- Screenshot capture, ratings, payments, telemetry, and a standalone website.
- Runtime toggle promotion, new flags, or native binary canvas execution.
## Baseline and companion plans
The `plugin-backed-canvases` package is marked done. The UX follow-up records
Tasks 01-06 complete and Task 07's external ACP evaluation outstanding.
This plan extends their source-retention and discovery boundaries; it does not
reopen their completed work or change their historical verification results.
Implementation must preserve the one-core-read authoring contract. The external
ACP evaluation is not an additional delivery gate for this feature.
## Technical approach
### Portable package boundary
Extend `internal/plugins/manifest` with optional typed distribution metadata.
Extend `internal/plugins/webapp` with strict distribution validation, checksum
coverage, source-only file retention, and safe
export writers. `Runtime.Serve` denies `distribution/` files. The native
`pkgtar` installer stays unchanged. Add `cmd/canvas-package` for offline
inspection through the production validator; the registry builder uses this
command instead of implementing a second parser.
No-build source remains the current application files. Build-based source is
retained under `distribution/source/` in the same immutable release. Extend
authoring collection and Quick Chat materialization to preserve that subtree.
### Export and installation boundary
Add a small canvas distribution service with expiring, bounded, user-bound
preparations. Export reads the immutable active artifact and derives both
downloads without touching the executor or changing the active release.
Installation stages one inspected package and approves those exact bytes.
Add `InstallPrepared` through the canvas/instance transaction boundary and one
`canvas_install_receipts` table. The receipt preserves provenance and prevents
duplicate confirmation after timeout/restart. Reuse artifact cleanup,
workspace admission, grants, and lifecycle invalidations. Native plugin
registry records and its auto-update poller do not contain canvas instances.
New browser routes live beside `backendapp/canvas_routes.go` in focused files.
Authorize human workspace access at service boundaries; server-side URL fetch
uses bounded, public-destination-only transport. Tests inject a local transport.
### Registry and catalog boundary
Add optional `kind: canvas` to the existing pointer schema. The builder fetches
the exact versioned bundle, inspects it, and requires its archive digest.
Preview URLs come from the registry and pass unchanged into the generated index.
Use the proposed [registry schema](registry-entry.schema.json) for canvas entries;
there is no image extraction, mirroring, or Pages media staging. Use additive catalog
fields and kind filtering. Instance annotations are workspace-authorized and
must not enter the shared source cache. Old native entries keep their behavior.
### UI boundary
`PluginsSettings` adds Canvases alongside Installed and Browse. Add domain hooks
and a typed canvas-distribution API client; reuse existing gallery/dialog,
permission summary, upload, and settings primitives where they fit. Details
and review derive from one shared model across viewports. Share is host-owned,
available from the task/workspace canvas and workspace rows. All copy uses the
five real locales and generated Traditional Chinese/pseudo catalogs.
## ASCII UI preview
All views below are proposed. Structure, action order, permissions before
confirmation, explicit image controls, and separate phone composition are
requirements. Names, image proportions, and spacing are illustrative.
### UI-01: Canvas catalog
Entry: Settings > Plugins > Canvases, or Browse shared canvases from a workspace.
Maps to AC-CANVASES-MARKETPLACE-004.1, 005.2, 005.4, 006.1, and 006.2.
```text
Desktop
+-------------------------------------------------------------------+
| Plugins |
| Installed | Browse | CANVASES |
| Workspace [Project A v] Search [____________] Sort [Recent v] |
| [Install canvas] |
| +----------------------+ +----------------------+ |
| | [cover screenshot] | | [cover screenshot] | |
| | Sprint board 1.0.0 | | Release viewer | |
| | by example-author | | by contributor | |
| | Description... | | Description... | |
| | [View details] | | 2 copies [Open v] | |
| +----------------------+ +----------------------+ |
+-------------------------------------------------------------------+
Phone
+------------------------------+
| Plugins |
| Installed | Browse | Canvases |
| Workspace [Project A v] |
| Search [_____________] |
| Sort [Recent v] |
| [Install canvas] |
| +--------------------------+ |
| | [cover screenshot] | |
| | Sprint board / author | |
| | Description... | |
| | [View details] | |
| +--------------------------+ |
+------------------------------+
```
Cards open details by pointer, keyboard, or tap. One page scroll owns the list.
Empty: explain no matching canvases, retain search and Install canvas. Failed
source: inline source warning plus Retry; healthy results remain visible.
Loading: skeleton covers with a status announcement. Gate-off: no Canvases tab.
### UI-02: Canvas details and install review
Use "Preview images" as the gallery label for canvas details.
Entry: catalog card or inspected upload/link. Maps to
AC-CANVASES-MARKETPLACE-002.3, 004.2-004.6, 005.3, and 006.1-006.3.
```text
Desktop details
+-------------------------------------------------------------------+
| [Back to canvases] Sprint board 1.0.0 [Close] |
| +--------------------------------+ by example-author |
| | | Description |
| | [selected image] | License: MIT [Repository] |
| | | Requires Kandev ... |
| +--------------------------------+ |
| [Previous] 1 / 3 [Next] [thumb 1] [thumb 2] [thumb 3] |
| Permissions |
| Read: tasks, workflows Write: none |
| Events: none State: none Network: none |
| Workspace [Project A v] [Review & install] |
+-------------------------------------------------------------------+
Phone details / final review
+------------------------------+
| [Back] Sprint board | fixed
| 1.0.0 / by example-author |
| [selected image] |
| [Previous] 1 / 3 [Next] |
| Description, license, repo | one scrolling body
| Permissions |
| Read: tasks, workflows |
| Write: none |
| Events / State / Network |
| Workspace [Project A v] |
| Package verified / version |
|------------------------------|
| [Install in Project A] | fixed, safe-area footer
+------------------------------+
Upload/direct-link entry (shared order)
+-------------------------------------+
| Install canvas |
| Upload bundle | Direct link |
| [Choose .tar.gz file] |
| Workspace [Project A v] |
| [Cancel] [Review bundle] |
+-------------------------------------+
```
The desktop review uses the same detail surface with authoritative inspected
metadata. Catalog Review & install stages bytes first; the final Install action
explicitly approves the displayed permissions. No runtime iframe appears here.
Phone inputs use a focused full-height form. No two-column detail remains
mounted on phones. Upload/link reviews omit the gallery; registry reviews label
images as listing previews, separate from verified permissions. Image enlargement replaces the gallery region with Back to
details; it does not nest another dialog. One image hides previous/next.
States: inspecting shows progress; failed inspection retains input; expired or
changed review offers Review again; incompatible package disables confirmation
with the required version; failed image shows a placeholder and Retry; success
shows Installed in Project A and Open canvas. Add another copy always starts a
new review; retrying a successful request returns its original copy.
### UI-03: Share canvas
Entry: host Share action or workspace canvas row. Maps to
AC-CANVASES-MARKETPLACE-001.2-001.3, 002.1, 003.1-003.2, 003.4, and 006.1-006.3.
Screenshots are supplied later in the registry entry, never in this form.
```text
Desktop
+-------------------------------------------------------------------+
| Share canvas [Close] |
| Active release: 1.0.0 |
| Package ID [sprint-board] Version [1.0.0] |
| Name [Sprint board] Author [________] License [________] |
| Description [____________________________________________] |
| Source: [Static application v] |
| [How to share] [Prepare downloads] |
|-------------------------------------------------------------------|
| Ready: review included files [Expand] |
| Check exported files for private content. |
| Bundle: ... MiB Source: ... MiB |
| [Download bundle] [Download source] |
+-------------------------------------------------------------------+
Phone
+------------------------------+
| [Back] Share canvas | fixed
| Active release: 1.0.0 |
| Package ID [___________] |
| Version [____] |
| Name / author / license | one scrolling body
| Description [__________] |
| Source [Static v] |
| [How to share] |
| Ready: included files, sizes |
| Check for private content. |
| [Download bundle] |
| [Download source] |
|------------------------------|
| [Prepare downloads] | fixed until ready
+------------------------------+
```
Download controls appear only for the current prepared snapshot. After ready,
the footer can show Close while both downloads stay reachable in the body;
neither download dismisses the form. Form edits invalidate prepared output.
Missing source shows Edit and republish guidance. New active release requires
preparation again. Closing discards temporary share inputs, not canvas data.
### UI-04: Sharing instructions
Entry: How to share in UI-03. Maps to AC-CANVASES-MARKETPLACE-003.3-003.4 and 006.1.
```text
Desktop information dialog Phone inset bottom drawer
+--------------------------------+ +------------------------------+
| How to share [X] | | How to share [X] |
| 1. Download source + bundle | | Download source + bundle |
| 2. Create a repository | | Create/upload repository |
| 3. Upload source | | Publish bundle; copy link |
| 4. Publish bundle; copy link | | Send link or file |
| 5. Send the link or file | | Add to registry (optional) |
| Optional: list in Kandev | | [Copy registry example] |
| [Copy registry example] | | [Done] |
| [Done]| +------------------------------+
+--------------------------------+
```
Keep instruction steps in a bounded scroll body; fixed title/dismiss and bottom
safe-area padding. Closing returns focus to How to share and preserves UI-03.
The copy action copies text only. No remote creation action is offered.
Registry instructions include the required canvas screenshot URLs and alt text,
cover ordering, and the same fields for custom marketplace registries.
## Tests
All test files/methods below are proposed unless explicitly named as existing.
Work-order frontmatter lists each full AC identity, including those abbreviated
in this table. Use nearby `@covers` annotations when method names are insufficient.
| AC suffixes under AC-CANVASES-MARKETPLACE | Planned evidence |
| --- | --- |
| 001.1, 001.2, 001.4 | `webapp/distribution_test.go`: `TestDistributionPackageRoundTrip`, `TestDistributionSourceModes`, `TestDistributionRejectsNonCanvas`; `backendapp/canvas_distribution_source_test.go`: `TestCanvasProjectSourceSurvivesEditAndExecutorCleanup` |
| 001.3, 003.1, 003.2, 003.4 | `canvas/distribution_export_test.go`: `TestCanvasExportSnapshot`, `TestCanvasExportExclusions`, `TestCanvasExportStaleRelease`, `TestCanvasExportNoLifecycleMutation` |
| 002.1, 002.2, 002.4 | `plugin-registry/canvas-index.test.mjs`: registry URL/list shape and cover order; export/install tests prove screenshots are not required |
| 002.3, 004.2 | `canvas-marketplace-detail.test.tsx`: gallery ordering, one-image mode, broken image, permission groups; `backendapp/canvas_distribution_routes_test.go`: `TestCanvasInstallReviewUsesPackagePermissions` |
| 004.1, 004.3, 004.4 | `canvas/distribution_install_test.go`: `TestCanvasInstallSources`, `TestCanvasInstallDigestReview`, `TestCanvasInstallAtomicFailure`, `TestCanvasInstallRestart` |
| 004.5, 004.6 | `canvas/distribution_install_test.go`: `TestCanvasInstallConcurrentRetry`, `TestCanvasInstallIndependentCopy`; `backendapp/canvas_distribution_routes_test.go`: `TestCanvasDistributionWorkspaceAuthorization` |
| 005.1, 005.3 | `plugin-registry/canvas-index.test.mjs`: exact release asset, actual manifest, required registry URLs, source, ID/version, checksum, image metadata independent of package, invalid PR rejection |
| 005.2, 005.4 | `marketplace/canvas_catalog_test.go`: `TestCanvasCatalogWorkspaceProjection`, `TestCanvasCatalogDegradedSource`, `TestCanvasCatalogLegacyEntries`; existing marketplace client/hook tests |
| 003.3 | `canvas-share-help.test.tsx`: complete manual instructions and exact registry example; public-doc validators |
| 006.1, 006.2, 006.3 | Desktop/phone E2E below; API/hook stale-response tests; i18n checks and pseudo-locale rendered pass |
| 006.4 | `backendapp/canvas_distribution_routes_test.go`: `TestCanvasDistributionFeatureOff`; frontend tab/hook tests; existing native plugin tests; `TestDistributionLegacyLocalPackage` |
Additional package tests cover encoded source-subtree access, archive traversal,
links, expansion bombs, duplicate normalized paths, binary contributions
disguised by category, and current file budgets. Registry tests cover URL schemes,
credentials/fragments, list bounds, alt text, and custom-source equivalence.
Receipt tests cover lost response, restart, concurrent confirm, quota races,
workspace deletion, fresh schema, replay, and gated PostgreSQL behavior.
## E2E tests
Use the existing `e2e/tests/canvas/canvas-fixture.ts` pattern with disposable
workspaces, a known mock-agent canvas, and a locally served catalog/package.
Enable the existing feature explicitly and restore it in cleanup. The new
tests must fail, rather than skip, if their required fixture is unavailable.
| File and project | UI flows and AC mapping |
| --- | --- |
| `tests/canvas/canvas-marketplace.spec.ts`, chromium | UI-01/UI-02: search, three images, permission review, upload and link, registry install, reload/Open, additional copy, expired review, bad digest, source outage; 002.3, 004.1-004.6, 005.2-005.4 |
| `tests/canvas/mobile-canvas-marketplace.spec.ts`, mobile-chrome | UI-01/UI-02 by tap: browse, gallery controls, workspace selection, upload/link/catalog review, install/Open, one-column layout, viewport containment and focus; 006.1-006.2 plus same install/gallery criteria |
| `tests/canvas/canvas-sharing.spec.ts`, chromium | UI-03/UI-04: no screenshot input, metadata, prepare, both downloads, help with registry URLs, import screenshot-free bundle and open resulting canvas; 001.1-001.3, 002.1, 003.1-003.4 |
| `tests/canvas/mobile-canvas-sharing.spec.ts`, mobile-chrome | UI-03/UI-04: screenshot-free preparation, both downloads, help drawer/back, input retention, pseudo-locale, actual touch targets, keyboard/safe-area containment; 006.1-006.3 plus sharing criteria |
Use real backend inspection/confirmation and local fixture HTTP; do not intercept
the install response into a fake success. Preview details must not issue a
runtime URL request. Capture downloads and inspect archive contents. Use causal
HTTP/WS waits, no arbitrary sleeps, and no display server or real provider account.
## Work orders
- [ ] [Task 01: Define portable canvas packages](task-01-portable-packages.md)
- [ ] [Task 02: Prepare canvas exports](task-02-export-preparation.md)
- [ ] [Task 03: Install reviewed canvas bundles](task-03-reviewed-installation.md)
- [ ] [Task 04: Publish canvas catalog entries](task-04-registry-catalog.md)
- [ ] [Task 05: Build canvas marketplace browsing](task-05-marketplace-ui.md)
- [ ] [Task 06: Add canvas sharing controls](task-06-sharing-ui.md)
- [ ] [Task 07: Document canvas distribution](task-07-distribution-docs.md)
Dependencies: 01 -> 02 -> 03 -> 04 -> 05 -> 06 -> 07. This deliberately uses
sequential delivery because schema, shared UI/API models, and fixtures overlap.
The optional native-plugin preview extension is delivered by the companion
[plugin marketplace previews plan](../canvas-marketplace-plugin/plan.md).
## Verification commands
Run each work order's exact block from the repository root. Work orders use
isolated subshells for working directories. Install workspace dependencies once
before the first pnpm command: `(cd apps && rtk pnpm install --frozen-lockfile)`.
No product commands below have been run during this design turn.
The work orders own Go/package tests, registry Node tests, focused Vitest,
typecheck, affected-file lint, i18n, and managed desktop/phone E2E commands.
Managed E2E builds fresh production artifacts. No all-worker overrides or
overlapping full suites are allowed. Persistence work includes SQL guard and
store-conformance checks; PostgreSQL needs `KANDEV_TEST_POSTGRES_DSN`.
Design-package checks:
```bash
rtk proxy python3 scripts/lint-spec-files.test.py
rtk proxy python3 scripts/lint-spec-files.py --all
rtk git diff --check -- docs/specs docs/plans/canvas-marketplace
rtk git status --short -- docs/specs docs/plans/canvas-marketplace
```
## Verification results
Initial design validation on 2026-09-10 (before the registry-image revision):
- `python3 scripts/lint-spec-files.test.py`: passed, 36 tests.
- `python3 scripts/lint-spec-files.py --all`: passed.
- Requirement/work-order/link audit: six requirements, 26 acceptance criteria,
seven work orders, and ten new documents checked. Every criterion has a work
order; all relative document links and system-design paths resolve.
- `git diff --check -- docs/specs docs/plans/canvas-marketplace`: passed.
- New-document whitespace audit: passed, including untracked work orders.
- `git status --short -- docs/specs docs/plans/canvas-marketplace`: inspected;
the package and index/cross-reference edits are present and uncommitted.
Registry-image revision validation on 2026-09-10:
- Specification linter tests: passed, 36 tests; full specification lint passed.
- Traceability/link audit: seven requirements, 30 criteria, seven work orders;
all criteria assigned and document/design links resolved.
- Proposed JSON Schema parses and contains the preview definition and
conditional canvas requirement. Full JSON Schema engine validation was not
run in this environment; implementation owns semantic schema test cases.
- Tracked diff and new-document whitespace checks passed.
The revision adds the Plugins-owned preview requirement and the proposed schema
artifact; it does not change the production registry schema yet.
Implementation checks were not run; all seven work orders remain pending.
Record actual command results here and in each task during delivery.
## Risks
- The current source allowlist excludes TS/TSX and YAML; widening must be
limited to an inert subtree that runtime URLs cannot serve.
- Registry image URLs can change or fail independently of package releases;
gallery failure must not block valid installation or imply package corruption.
- Registry metadata currently uses a shallow parser and nullable hash. Canvas
entries need production inspection without breaking native plugin entries.
- Receipt insertion, grants, count admission, and artifact references must be
atomic across retries and supported database dialects.
- Host URL fetch must not gain private-network access through DNS or redirects.
- Shared catalog caching must never retain a user's workspace instance annotations.
- Old clients must fail safely on static canvas bundles; set a real minimum
compatible release before shipping the format.
- Private data embedded in exported files cannot be reliably removed
automatically. Show the exact inventory and author review reminder.
- Official registry discovery remains GitHub-only. Other hosts can share public
direct bundle links or files; the helper must explain that distinction.
## Design handoff
The next phase is explicit implementation of these work orders with TDD.
This package does not authorize commits, pushes, registry submissions, or
external publishing. Public docs are updated with implementation, not presented
as shipped behavior during this planning turn.
@@ -0,0 +1,116 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/kdlbs/kandev/blob/main/plugin-registry/schema.json",
"title": "Kandev marketplace registry",
"description": "Proposed registry schema for plugin and canvas repositories. Preview image URLs belong to registry entries; release metadata remains package-owned.",
"type": "object",
"additionalProperties": false,
"required": [
"plugins"
],
"properties": {
"plugins": {
"type": "array",
"description": "The curated list of plugin repositories, one entry per plugin.",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"repo"
],
"properties": {
"id": {
"type": "string",
"description": "Stable plugin identifier. MUST equal the plugin manifest `id` of the repo's latest release. Unique across the file.",
"pattern": "^[a-z0-9][a-z0-9-]*$"
},
"repo": {
"type": "string",
"description": "GitHub repository in owner/name form; the release source for this plugin.",
"pattern": "^[^/]+/[^/]+$"
},
"featured": {
"type": "boolean",
"description": "When true, a maintainer has pinned this entry above star order in the catalog.",
"default": false
},
"categories": {
"type": "array",
"description": "Curation-only category tags used for filtering in the catalog.",
"items": {
"type": "string"
}
},
"kind": {
"type": "string",
"enum": [
"plugin",
"canvas"
],
"default": "plugin",
"description": "Omitted kind means plugin for backward compatibility; canvas kind must match the validated package."
},
"previews": {
"type": "array",
"maxItems": 8,
"items": {
"$ref": "#/$defs/preview"
},
"description": "Ordered preview images. First image is the cover. Optional for plugins; required for canvas registry listings."
}
},
"allOf": [
{
"if": {
"required": [
"kind"
],
"properties": {
"kind": {
"const": "canvas"
}
}
},
"then": {
"required": [
"previews"
],
"properties": {
"previews": {
"minItems": 1
}
}
}
}
]
}
}
},
"$defs": {
"preview": {
"type": "object",
"additionalProperties": false,
"required": [
"url",
"alt"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"pattern": "^https://",
"maxLength": 2048,
"description": "Direct HTTPS image URL. Credentials and fragments are rejected by registry validation."
},
"alt": {
"type": "string",
"minLength": 1,
"maxLength": 300,
"pattern": "\\S",
"description": "Description of the preview image for accessibility."
}
}
}
}
}
@@ -0,0 +1,118 @@
---
id: "01-portable-packages"
title: "Define portable canvas packages"
status: done
wave: 1
depends_on: []
plan: "plan.md"
requirements:
- REQ-CANVASES-MARKETPLACE-001
- REQ-CANVASES-MARKETPLACE-002
- REQ-CANVASES-MARKETPLACE-006
acceptance_criteria:
- AC-CANVASES-MARKETPLACE-001.1
- AC-CANVASES-MARKETPLACE-001.2
- AC-CANVASES-MARKETPLACE-001.3
- AC-CANVASES-MARKETPLACE-001.4
- AC-CANVASES-MARKETPLACE-002.1
- AC-CANVASES-MARKETPLACE-006.4
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
---
# Task 01: Define portable canvas packages
## Summary
Define the canvas distribution profile in the existing plugin manifest and
static validator. Preserve editable source with immutable releases and expose
a reusable offline inspector for registry validation.
## In scope
- Typed distribution metadata, source modes, checksum validation, compatibility,
exactly one canvas app, and rejection of other plugin contributions.
- Screenshot-free bundle/source validation; registry previews are not manifest fields.
- Source-only project subtree, bounded authoring transfer, immutable retention,
Quick Chat materialization, and runtime denial of distribution paths.
- Deterministic bundle/source archive writers and production offline inspector.
- Minimal embedded authoring reference changes needed to keep source modes valid;
Task 07 owns the full sharing instructions.
## Out of scope
HTTP downloads, registry fetching, canvas installation, UI, and native installer changes.
## Acceptance
- Distribution round-trip fixtures retain matching static/project source and
application assets while malformed, incomplete, disguised native, and oversized
bundles fail. No screenshot or preview URL is required.
- Runtime requests cannot read the inert source subtree through plain,
encoded, or normalized aliases; editing retains original build inputs after
the author executor is unavailable.
- The offline inspector and host use the same validator and a shared descriptor
fixture. Native managed-plugin installation tests remain unchanged and pass.
## Verification
Run from repository root. Start with focused RED cases, then run these blocks
after implementation. The new test files/methods are named in the plan.
```bash
(cd apps/backend && rtk go test -race ./internal/plugins/manifest ./internal/plugins/webapp ./internal/plugins/pkgtar ./cmd/canvas-package -count=1)
(cd apps/backend && rtk go test -race ./internal/backendapp -run 'TestCanvas.*(Source|Scaffold|Edit)' -count=1)
(cd apps/backend && rtk go test -race ./internal/agentctl/server/api -run Canvas -count=1)
(cd apps/backend && rtk go test ./internal/mcp/canvasskill -count=1)
rtk git diff --check
```
## Files likely touched
Existing:
- `apps/backend/internal/plugins/manifest/manifest.go`, `validate.go`
- `apps/backend/internal/plugins/webapp/package.go`, `runtime.go`
- `apps/backend/internal/backendapp/canvas_authoring.go`, `canvas_routes.go`
- `apps/backend/internal/agentctl/server/api/workspace_canvas_source.go`
- `apps/backend/internal/mcp/canvasskill/files/references/manifest.md`
New:
- `apps/backend/internal/plugins/manifest/distribution.go`
- `apps/backend/internal/plugins/webapp/distribution.go`, `distribution_test.go`
- `apps/backend/internal/backendapp/canvas_distribution_source_test.go`
- `apps/backend/cmd/canvas-package/main.go`, `main_test.go`
- Adjacent focused archive-writer tests and shared static/project fixture files.
## Dependencies
None. Follow the existing plugin-backed canvas runtime contract.
## Risks
A file-extension exception must not broaden runtime serving. Preserve the
current package byte/file budgets, source transfer fencing, and authoring core
inventory. Build-source declarations cannot prove that arbitrary builds succeed;
fixtures must prove both supported source modes and their documented commands.
## Parallelism
`sequential`
## Inputs
- Design: Package profile, Source retention, Validation.
- Existing `webapp/package_test.go`, `runtime_test.go`, authoring/edit tests.
- `apps/backend/AGENTS.md` and both scoped agentctl AGENTS files before changes.
- Existing canvas authoring guide and one-core-read inventory contract.
## Results
Implemented the typed canvas distribution profile, strict manifest/package and
source validation, checksum coverage, deterministic bundle/source archives,
runtime source isolation, bounded source retention, and the offline inspector.
The authoring reference and fixture preserve static/project source behavior.
Verification: the package/manifest/webapp/pkgtar/inspector race suite passed 210
tests, the agentctl canvas-source race suite passed 6 tests, the backend canvas
source/scaffold/edit race suite passed 7 tests, the authoring skill suite passed
7 tests, and the relevant backend suite passed 1,587 tests across 8 packages.
@@ -0,0 +1,103 @@
---
id: "02-export-preparation"
title: "Prepare canvas exports"
status: done
wave: 2
depends_on:
- "01-portable-packages"
plan: "plan.md"
requirements:
- REQ-CANVASES-MARKETPLACE-001
- REQ-CANVASES-MARKETPLACE-003
- REQ-CANVASES-MARKETPLACE-006
acceptance_criteria:
- AC-CANVASES-MARKETPLACE-001.2
- AC-CANVASES-MARKETPLACE-001.3
- AC-CANVASES-MARKETPLACE-003.1
- AC-CANVASES-MARKETPLACE-003.2
- AC-CANVASES-MARKETPLACE-003.4
- AC-CANVASES-MARKETPLACE-006.4
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
---
# Task 02: Prepare canvas exports
## Summary
Prepare both downloadable formats from one active immutable release. Introduce
the bounded temporary preparation store that the install review will also use.
## In scope
- User/workspace-bound preparations, 15-minute expiry, admission accounting,
request leases, cancel, startup cleanup, and safe failure cleanup.
- Export preparation with metadata, inventory and size preview. No screenshot input.
- Authenticated bundle/source downloads with safe headers.
- Reauthorization and active-release checks before prepare/download.
- Stable error codes, content-free diagnostics, and no executor dependence.
## Out of scope
Permanent share-profile settings, external publication, installation mutations,
frontend forms, and changes to canvas runtime state.
## Acceptance
- Export preparation and both downloads refer to the same validated snapshot;
form/release changes invalidate it and unauthorized requests disclose nothing.
- Source/state exclusion, screenshot-free preparation, expiry, cancellation, interrupted
streaming, quotas, and restart cleanup leave the active release unchanged.
- Preparation routes are absent when canvases are disabled; download access
requires current human workspace authority even if an ID is known.
## Verification
```bash
(cd apps/backend && rtk go test -race ./internal/canvas -run 'TestCanvasExport|TestCanvasPreparation' -count=1)
(cd apps/backend && rtk go test -race ./internal/backendapp -run 'TestCanvasExport|TestCanvasPreparation|TestCanvasDistributionFeatureOff' -count=1)
rtk git diff --check
```
## Files likely touched
New:
- `apps/backend/internal/canvas/distribution.go`, `distribution_preparation.go`
- `apps/backend/internal/canvas/distribution_export.go`, `distribution_export_test.go`
- `apps/backend/internal/canvas/distribution_preparation_test.go`
- `apps/backend/internal/backendapp/canvas_distribution_routes.go`, `canvas_distribution_routes_test.go`
Existing integration points:
- `apps/backend/internal/backendapp/canvas_routes.go` and service composition
- `apps/backend/internal/plugins/webapp/package.go` artifact read interface
## Dependencies
Task 01's validated package and archive writers.
## Risks
Do not mistake opaque IDs for authorization. Cleanup must not race a live
download or delete a shared immutable artifact. Bound compressed outputs and
multipart input before memory allocation. Avoid collecting task workspace files.
## Parallelism
`sequential`
## Inputs
- Design: Validation and bounded preparation, Export preparation, HTTP contracts.
- `canvasEditService.loadEditableCanvas` for immutable artifact authorization.
- Existing canvas route/feature-off tests and safe attachment response patterns.
## Results
Implemented user-bound, expiring export preparations with quota and cleanup
handling, immutable active-release snapshots, metadata/inventory review, and
authenticated bundle/source downloads. The export path is screenshot-free and
does not depend on the executor.
Verification: the canvas export/preparation race tests passed 3 tests, the
desktop sharing E2E passed, and the mobile sharing E2E passed. The full relevant
backend suite and SQL guard also passed.
@@ -0,0 +1,121 @@
---
id: "03-reviewed-installation"
title: "Install reviewed canvas bundles"
status: done
wave: 3
depends_on:
- "02-export-preparation"
plan: "plan.md"
requirements:
- REQ-CANVASES-MARKETPLACE-001
- REQ-CANVASES-MARKETPLACE-004
- REQ-CANVASES-MARKETPLACE-006
acceptance_criteria:
- AC-CANVASES-MARKETPLACE-001.1
- AC-CANVASES-MARKETPLACE-001.4
- AC-CANVASES-MARKETPLACE-004.1
- AC-CANVASES-MARKETPLACE-004.2
- AC-CANVASES-MARKETPLACE-004.3
- AC-CANVASES-MARKETPLACE-004.4
- AC-CANVASES-MARKETPLACE-004.5
- AC-CANVASES-MARKETPLACE-004.6
- AC-CANVASES-MARKETPLACE-006.4
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
---
# Task 03: Install reviewed canvas bundles
## Summary
Install an inspected static canvas as an independent workspace instance after
explicit permission approval. Preserve reviewed bytes and retry identity across
the database transaction.
## In scope
- Upload and direct-link preparation, authoritative package/permission review without required images,
expected digest, bounded HTTPS fetch, redirect/DNS destination validation.
- Catalog-reference resolver interface; Task 04 wires actual catalog entries.
- `InstallPrepared`, workspace admission, atomic instance/release/grant creation,
receipt/provenance persistence, and existing lifecycle invalidation.
- Idempotent retry, concurrent confirmation, restart replay, deletion handling,
safe artifact compensation, and native-install authority isolation.
- Additive receipt schema, migration/replay fixtures, persistence descriptor
updates where the existing canvas store's conformance contract requires them.
## Out of scope
Repository cloning, provider credentials, replacing/updating existing canvases,
frontend review controls, and native plugin auto-update changes.
## Acceptance
- Upload/link installs execute only inspected bytes after current workspace
authorization and full permission approval, with no instance before confirm.
- Concurrent or retried confirmation creates one instance; a new review creates
an independent copy. Receipt, provenance, grants, and release survive restart.
- Invalid URL/digest/package, revoked workspace, quota/DB failure, and disabled
routes leave existing data unchanged; supported schema replay checks pass.
## Verification
```bash
(cd apps/backend && rtk go test -race ./internal/canvas ./internal/plugins/instances -count=1)
(cd apps/backend && rtk go test -race ./internal/backendapp -run 'TestCanvasInstall|TestCanvasDistribution' -count=1)
(cd apps/backend && rtk go run ./cmd/sqlguard ./internal)
(cd apps/backend && rtk go test -race ./internal/persistence/storeconformance -count=1)
rtk git diff --check
```
Run the same targeted canvas/instance tests with `KANDEV_TEST_POSTGRES_DSN`
set when the test database is available. Record skipped PostgreSQL evidence
explicitly; do not report SQLite replay as cross-dialect evidence.
## Files likely touched
- `apps/backend/internal/canvas/distribution_install.go`, `distribution_install_test.go` (new)
- `apps/backend/internal/canvas/distribution_download.go`, `distribution_download_test.go` (new)
- `apps/backend/internal/canvas/repository.go`, `service.go`, `types.go`
- `apps/backend/internal/plugins/instances/store.go` and focused transaction tests
- `apps/backend/internal/backendapp/canvas_distribution_routes.go`, route tests
- Existing canvas schema registration, storage cleanup, and conformance fixtures
## Dependencies
Tasks 01-02. Use a narrow catalog resolver interface with fixture data until Task 04.
## Risks
A sequence of independently committed create/publish/approve calls is not atomic.
Use the existing store transaction boundary and receipt uniqueness. PostgreSQL
locking must cover admission and concurrent retries. DNS checks must bind to the
actual connection destination, not only an earlier lookup.
## Parallelism
`sequential`
## Inputs
- Design: Install preparation, Commit and persistence, Authorization.
- Existing `canvas.PublishPackage`, `instances.Store` transaction/cleanup tests.
- `canvasHTTPHandler.authorizeWorkspace`, authentication scoping guidance.
- Existing native-plugin install tests as a compatibility boundary.
## Results
Implemented upload, direct HTTPS link, and catalog-reference preparation;
server-side digest/package/permission review; bounded public-destination
fetching; atomic workspace install integration; provenance and receipt
persistence; and retry-safe confirmation.
Verification: the canvas installation/export race selection passed 3 tests, the
full relevant backend suite passed 1,587 tests across 8 packages, and SQL guard
passed. Existing native plugin installation paths remained covered by the
16-test plugin E2E regression.
Review remediation: production confirmation now uses the canvas service's
single transaction for instance, release, grants, and receipt ownership. It
also reauthorizes retry paths, fails closed on receipt-store errors, and
compensates a newly-created artifact when the transaction cannot commit.
@@ -0,0 +1,127 @@
---
id: "04-registry-catalog"
title: "Publish canvas catalog entries"
status: done
wave: 4
depends_on:
- "03-reviewed-installation"
plan: "plan.md"
requirements:
- REQ-CANVASES-MARKETPLACE-002
- REQ-CANVASES-MARKETPLACE-004
- REQ-CANVASES-MARKETPLACE-005
- REQ-CANVASES-MARKETPLACE-006
acceptance_criteria:
- AC-CANVASES-MARKETPLACE-002.1
- AC-CANVASES-MARKETPLACE-002.2
- AC-CANVASES-MARKETPLACE-002.3
- AC-CANVASES-MARKETPLACE-002.4
- AC-CANVASES-MARKETPLACE-004.1
- AC-CANVASES-MARKETPLACE-005.1
- AC-CANVASES-MARKETPLACE-005.2
- AC-CANVASES-MARKETPLACE-005.3
- AC-CANVASES-MARKETPLACE-005.4
- AC-CANVASES-MARKETPLACE-006.4
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
---
# Task 04: Publish canvas catalog entries
## Summary
Extend the existing repository registry and catalog with validated canvas entries
and registry-owned preview URLs. Wire registry selection into the same reviewed
installation service.
## In scope
- Optional pointer kind, exact release asset selection, production inspector,
matching package/repository/version and required hash/source.
- Shared `previews: [{url, alt}]` on official/custom canvas entries; preserve URL
order into the generated index.
- Additive catalog descriptor, kind filter, safe degraded-source behavior.
- No package screenshot extraction, image fetching, or Pages media staging.
- Per-workspace instance annotations after authorization and source-cache lookup.
- Catalog source/ID/version/digest resolution for Task 03's prepare endpoint.
- PR validation with read-only job permissions and no package code execution.
## Out of scope
Submitting a real canvas registry entry, publishing this branch, changing native
plugin package rules, a new catalog service, or automated repository creation.
## Acceptance
- Registry tests reject canvas listings without image URLs and packages without
source, mismatched identity/version, ambiguous assets, or invalid archives.
Package metadata/hash comes from the archive; images come from the registry.
- Catalog preserves preview URL order independently of package versions; failed
sources and old native entries remain
compatible. Workspace annotations cannot leak through shared cache entries.
- Catalog installation checks selected source/version/hash server-side and
enters the existing review path. No production canvas executes during indexing.
## Verification
```bash
(cd apps/backend && rtk go build -o bin/canvas-package ./cmd/canvas-package)
rtk bash -lc 'KANDEV_CANVAS_PACKAGE_INSPECTOR=apps/backend/bin/canvas-package node --test plugin-registry/build-index.test.mjs plugin-registry/canvas-index.test.mjs'
(cd apps/backend && rtk go test -race ./internal/plugins/marketplace ./cmd/canvas-package -count=1)
(cd apps/backend && rtk go test -race ./internal/plugins -run Marketplace -count=1)
(cd apps/backend && rtk go test -race ./internal/backendapp -run 'TestCanvasInstall|TestCanvasDistribution' -count=1)
rtk git diff --check
```
The new Node test file uses local fixture responses and the built inspector.
Do not run a publishing workflow or contact external repositories for test data.
## Files likely touched
- `plugin-registry/schema.json`, `build-index.mjs`, `build-index.test.mjs`
- `plugin-registry/canvas-index.mjs`, `canvas-index.test.mjs` (new)
- `.github/workflows/plugin-registry-index.yml`
- `apps/backend/internal/plugins/marketplace/types.go`, `catalog.go`
- `apps/backend/internal/plugins/marketplace/canvas_catalog_test.go` (new)
- `apps/backend/internal/plugins/marketplace_handlers.go`, handler tests
- `apps/backend/internal/backendapp/canvas_distribution_routes.go` resolver wiring
- Canvas receipt projection in `apps/backend/internal/canvas/repository.go`
## Dependencies
Tasks 01-03. The inspector must be available on the trusted registry base before
accepting the first contributor canvas pointer; the feature PR itself only adds
local test fixtures and runs candidate-code tests without publishing privileges.
## Risks
Do not parse nested canvas YAML with the current shallow regex helper. Never
choose a source archive because it is the first tarball. Preserve native catalog
semantics and ensure the built inspector is from trusted registry code when
processing contributor pointers. Media/index deployment must stay coherent.
## Parallelism
`sequential`
## Inputs
- Design: Registry publication, Catalog projection, Install preparation.
- Proposed complete schema: `registry-entry.schema.json` beside this work order.
- Existing `buildEntry`, `parseManifestFields`, `build-index.test.mjs`.
- Existing marketplace catalog/source-store tests and Pages workflow.
## Results
Implemented canvas registry entries, ordered shared previews, exact release
asset inspection, archive digest enforcement, kind-aware catalog projection,
degraded-source handling, and catalog installation resolution.
Verification: the registry build-index suite passed 11 tests, the proposed
schema parsed successfully, the relevant marketplace/backend suite passed, and
desktop/mobile marketplace E2E passed 2 tests total.
Review remediation: canvas catalog presentation now comes from the inspected
archive descriptor, repository identity is checked against the registry
pointer, asset inspection is streamed and time-bounded, and pull requests
fail when a canvas entry is invalid instead of silently omitting it.
@@ -0,0 +1,182 @@
---
id: "05-marketplace-ui"
title: "Build canvas marketplace browsing"
status: done
wave: 5
depends_on:
- "04-registry-catalog"
plan: "plan.md"
requirements:
- REQ-CANVASES-MARKETPLACE-002
- REQ-CANVASES-MARKETPLACE-004
- REQ-CANVASES-MARKETPLACE-005
- REQ-CANVASES-MARKETPLACE-006
acceptance_criteria:
- AC-CANVASES-MARKETPLACE-002.3
- AC-CANVASES-MARKETPLACE-004.1
- AC-CANVASES-MARKETPLACE-004.2
- AC-CANVASES-MARKETPLACE-004.3
- AC-CANVASES-MARKETPLACE-004.4
- AC-CANVASES-MARKETPLACE-004.5
- AC-CANVASES-MARKETPLACE-004.6
- AC-CANVASES-MARKETPLACE-005.2
- AC-CANVASES-MARKETPLACE-005.3
- AC-CANVASES-MARKETPLACE-005.4
- AC-CANVASES-MARKETPLACE-006.1
- AC-CANVASES-MARKETPLACE-006.2
- AC-CANVASES-MARKETPLACE-006.3
- AC-CANVASES-MARKETPLACE-006.4
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
---
# Task 05: Build canvas marketplace browsing
## Summary
Add the Canvases tab, visual details, and upload/link/catalog installation to
Plugins settings. Use one review model across desktop and focused phone views.
## In scope
- Workspace selection, catalog cards with required registry cover, search/sort, instance
actions, and Browse shared canvases from workspace settings.
- Shared canvas gallery with explicit controls, descriptions, one-image mode, image errors,
enlargement within details, and keyboard/focus handling.
- Upload and direct-link forms, authoritative package/permission review,
confirmation, progress/errors, retry, and installed Open canvas action.
- Typed API client and domain hooks with stale-response invalidation.
- Workspace-owner actions independent of native-plugin administrator gating.
- Localized labels/statuses across the canvas catalog; desktop/mobile E2E.
## Out of scope
Share/export author forms, canvas updates, and provider UI. Native plugin preview
extension is covered by Task 08.
## Acceptance
- UI-01/UI-02 support all three input paths through real inspection and explicit
workspace permission approval, then show the installed canvas after reload.
- Gallery, failure states, per-workspace copies, and late-response handling work
without executing the canvas in details or leaking another workspace's data.
- Phone composition, keyboard flow, actual touch targets, source outage,
localization, and feature-off behavior pass the assigned rendered checks.
## ASCII UI preview
Excerpt of [UI-01/UI-02 in the plan](plan.md#ui-01-canvas-catalog).
AC-CANVASES-MARKETPLACE-002.3, 004.1, 004.2, 005.2, 006.1, and 006.2.
```text
Desktop
Plugins: Installed | Browse | CANVASES
Workspace [Project A v] Search [____] [Install canvas]
[cover + card] [cover + card] -> View details
+------------------------------------------------------+
| [Back] Name / version / author |
| [selected image] Description / license |
| [Prev] 1/3 [Next] [thumbs] Required permissions |
| Workspace [Project A v] [Review & install] |
+------------------------------------------------------+
Phone details
+------------------------------+
| [Back] Name | fixed
| [selected image] |
| [Previous] 1/3 [Next] |
| Description / author | single scroll
| License / required version |
| Read / Write / Events |
| State / exact network hosts |
| Workspace [Project A v] |
| Package inspected |
|------------------------------|
| [Install in Project A] | fixed, safe-area
+------------------------------+
```
Upload/link opens final review without a gallery or screenshot requirement.
Registry canvas listings require preview URLs; broken remote preview shows Retry
without blocking installation. Inspecting is
announced; stale/expired review requires review again; incompatibility disables
confirm with its reason; success shows Open. Empty/degraded catalogs retain
Install canvas. Phone catalog uses one-column cards. Copy/layout details are
illustrative, action hierarchy and containment are required.
## Verification
Install dependencies once from `apps/` if not already installed. E2E fixtures
must not skip on missing canvas readiness. Record screenshots/geometry results
from the managed test artifacts and compare with UI-01/UI-02.
```bash
(cd apps/web && rtk pnpm exec vitest run lib/api/domains/canvas-distribution-api.test.ts lib/api/domains/marketplace-api.test.ts hooks/domains/canvas/use-canvas-install.test.ts hooks/domains/plugins/use-marketplace.test.ts components/settings/plugins/canvas-marketplace.test.tsx components/settings/plugins/canvas-marketplace-detail.test.tsx components/settings/plugins/marketplace-preview-gallery.test.tsx components/settings/plugins/plugin-row.test.tsx components/settings/plugins/install-plugin-dialog.test.tsx)
(cd apps/web && rtk pnpm run typecheck)
(cd apps/web && rtk pnpm exec eslint components/settings/plugins lib/api/domains/canvas-distribution-api.ts lib/api/domains/marketplace-api.ts hooks/domains/canvas hooks/domains/plugins/use-marketplace.ts components/settings/workspace-canvases-page.tsx)
(cd apps/web && rtk pnpm run i18n:check)
(cd apps/web && rtk pnpm run i18n:ratchet)
(cd apps/web && rtk pnpm e2e:run --project chromium tests/canvas/canvas-marketplace.spec.ts -- --retries=0)
(cd apps/web && rtk pnpm e2e:run --project mobile-chrome tests/canvas/mobile-canvas-marketplace.spec.ts -- --retries=0)
(cd apps/web && rtk pnpm e2e:run --project chromium tests/plugins/plugins.spec.ts -- --retries=0)
(cd apps/web && rtk pnpm e2e:run --project mobile-chrome tests/plugins/mobile-plugin-settings-row.spec.ts -- --retries=0)
rtk git diff --check
```
Create `install-plugin-dialog.test.tsx` only for the native compatibility cases
affected by shared entry controls; do not manufacture unrelated tests.
## Files likely touched
- `apps/web/components/settings/plugins/plugins-settings.tsx`
- `apps/web/components/settings/plugins/marketplace-browser.tsx`
- `apps/web/components/settings/plugins/canvas-marketplace.tsx`, `canvas-marketplace-detail.tsx` (new)
- `apps/web/components/settings/plugins/canvas-install-dialog.tsx` (new)
- Shared `marketplace-preview-gallery.tsx` (new), its focused unit tests, and
canvas detail components
- `apps/web/components/settings/workspace-canvases-page.tsx`
- `apps/web/lib/api/domains/canvas-distribution-api.ts` and tests (new)
- `apps/web/lib/api/domains/marketplace-api.ts` and tests
- `apps/web/hooks/domains/canvas/use-canvas-install.ts` and tests (new)
- `apps/web/hooks/domains/plugins/use-marketplace.ts` and tests
- `apps/web/src/locales/{en,pt-pt,zh-cn,zh-hk,zh-tw,pseudo}/`
- `apps/web/e2e/tests/canvas/canvas-marketplace.spec.ts`,
`mobile-canvas-marketplace.spec.ts`, shared distribution fixtures (new)
## Dependencies
Tasks 01-04 provide real package/catalog/installation routes. Reuse existing
canvas permission projections and the current native-plugin install dialog.
## Risks
Current plugin management controls are admin-gated; static workspace installs
must not inherit that restriction or weaken native controls. Late package and
workspace responses must not overwrite the review the user is confirming.
## Parallelism
`sequential`
## Inputs
- Design: UI composition, Mobile contract, Catalog projection, Authorization.
- Existing `PluginsSettings`, `InstallPluginDialog`, `MarketplaceBrowser`.
- Existing canvas lifecycle review, `canvas-host-route.tsx`, `useResponsiveBreakpoint`.
- Canvas registry preview contract and current canvas installation controls.
- `apps/web/AGENTS.md`, /mobile-parity, /e2e, and existing canvas fixture tests.
## Results
Implemented the Canvases marketplace tab, workspace selection, search/category/
sort controls, registry cover cards, canvas preview gallery, detail/review
surfaces, upload/direct-link/catalog installation entry points, and localized
desktop/phone layouts with touch-sized controls. The optional native-plugin
preview extension is recorded in Task 08.
Verification: 8 focused Vitest files passed 21 tests; web typecheck, full lint,
i18n checks, and the new-code ratchet passed; desktop and mobile marketplace E2E
passed.
Review remediation: every responsive install-dialog dismissal now cancels the
staged review, including drawer and dialog outside-click or Escape handling.
@@ -0,0 +1,173 @@
---
id: "06-sharing-ui"
title: "Add canvas sharing controls"
status: done
wave: 6
depends_on:
- "05-marketplace-ui"
plan: "plan.md"
requirements:
- REQ-CANVASES-MARKETPLACE-001
- REQ-CANVASES-MARKETPLACE-002
- REQ-CANVASES-MARKETPLACE-003
- REQ-CANVASES-MARKETPLACE-006
acceptance_criteria:
- AC-CANVASES-MARKETPLACE-001.2
- AC-CANVASES-MARKETPLACE-001.3
- AC-CANVASES-MARKETPLACE-002.1
- AC-CANVASES-MARKETPLACE-003.1
- AC-CANVASES-MARKETPLACE-003.2
- AC-CANVASES-MARKETPLACE-003.3
- AC-CANVASES-MARKETPLACE-003.4
- AC-CANVASES-MARKETPLACE-006.1
- AC-CANVASES-MARKETPLACE-006.2
- AC-CANVASES-MARKETPLACE-006.3
- AC-CANVASES-MARKETPLACE-006.4
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
---
# Task 06: Add canvas sharing controls
## Summary
Expose bundle/source downloads and manual sharing instructions from published
canvas host actions. Let authors prepare distribution metadata without changing the running canvas.
Screenshots are provided later through registry URLs, never required for downloads.
## In scope
- Share entry in task/workspace host controls and workspace canvas rows.
- Release-bound metadata/source form, validation, and temporary draft retention.
No screenshot upload, reorder, required cover, or preview URL field.
- Preparation, inventory, two downloads, expiry/stale-release recovery, cleanup.
- Information dialog/drawer with manual repository/release/direct-link and
registry-PR instructions, copyable safe examples, and no external mutations.
- Shared domain hook, localized host copy, accessible desktop/phone E2E.
- Export-to-import browser round trip with the real installer from Task 05.
## Out of scope
Automatic screenshots, drag-only reordering, source editor, permanent publishing
profiles, external account connection, remote repository/release/PR writes.
## Acceptance
- UI-03 prepares matching bundle/source from the active release, without any
screenshot requirement; invalid/stale/expired results retain inputs
and cannot download silently substituted content.
- UI-04 gives complete manual instructions and copyable registry examples
without any external mutation; downloads remain usable without code-host auth.
- Desktop/phone tests download both files, inspect their contents, import the
bundle, and open the resulting independent canvas; help/back and gallery
controls satisfy keyboard, localization, and touch geometry requirements.
## ASCII UI preview
Excerpt of [UI-03/UI-04 in the plan](plan.md#ui-03-share-canvas).
AC-CANVASES-MARKETPLACE-001.3, 002.1, 003.1-003.4, and 006.1-006.3.
```text
Desktop
+-------------------------------------------------------------+
| Share canvas [Close] |
| Release / package ID / version / author / license |
| Description / source mode |
| [How to share] [Prepare downloads] |
| Ready: file inventory, sizes, private-content reminder |
| [Download bundle] [Download source] |
+-------------------------------------------------------------+
Phone
+------------------------------+
| [Back] Share canvas | fixed
| Metadata / source fields |
| [How to share] | opens instruction drawer
| File inventory / sizes |
| Check for private content. |
| [Download bundle] |
| [Download source] |
|------------------------------|
| [Prepare downloads] | safe-area footer until ready
+------------------------------+
Help drawer/dialog
1. Download source and bundle
2. Create repository; upload source
3. Publish bundle; copy direct link
4. Send link/file to another user
Optional: add image URLs to the registry entry; request listing
[Copy registry example] [Done]
```
Screenshots are not an export input. Missing project source points
to Edit and republish. One-column phone form has one scroll owner. The help
drawer has its own bounded scroll only while it is active; closing restores
focus/input state. Both downloads remain reachable after either completes.
Current source/version changes invalidate prepared output. These structures
are required; sample labels and spacing are illustrative.
## Verification
```bash
(cd apps/web && rtk pnpm exec vitest run hooks/domains/canvas/use-canvas-share.test.ts components/settings/canvas-share-dialog.test.tsx components/settings/canvas-share-help.test.tsx components/settings/canvas-host-components.test.tsx components/settings/workspace-canvases-page.test.tsx lib/api/domains/canvas-distribution-api.test.ts)
(cd apps/web && rtk pnpm run typecheck)
(cd apps/web && rtk pnpm exec eslint components/settings/canvas-share-dialog.tsx components/settings/canvas-share-help.tsx components/settings/canvas-host-components.tsx components/settings/workspace-canvases-page.tsx hooks/domains/canvas lib/api/domains/canvas-distribution-api.ts)
(cd apps/web && rtk pnpm run i18n:check)
(cd apps/web && rtk pnpm run i18n:ratchet)
(cd apps/web && rtk pnpm e2e:run --project chromium tests/canvas/canvas-sharing.spec.ts -- --retries=0)
(cd apps/web && rtk pnpm e2e:run --project mobile-chrome tests/canvas/mobile-canvas-sharing.spec.ts -- --retries=0)
rtk git diff --check
```
Use file/download fixtures and browser geometry; no external hosting credentials.
Generate Traditional Chinese/pseudo catalogs through the repository generators
during implementation, then run the checks above.
## Files likely touched
- `apps/web/components/settings/canvas-share-dialog.tsx`, tests (new)
- `apps/web/components/settings/canvas-share-help.tsx`, tests (new)
- `apps/web/hooks/domains/canvas/use-canvas-share.ts`, tests (new)
- `apps/web/components/settings/canvas-host-components.tsx`, host route/wrappers
- `apps/web/components/settings/workspace-canvases-page.tsx`
- `apps/web/lib/api/domains/canvas-distribution-api.ts`, tests
- All real locales and generated pseudo catalog
- `apps/web/e2e/tests/canvas/canvas-sharing.spec.ts`,
`mobile-canvas-sharing.spec.ts`, shared distribution fixtures (new)
## Dependencies
Tasks 01-05, especially export endpoints and the real bundle import UI.
## Risks
Exported application files can contain private data. The UI must
show the inventory/review notice without claiming automated sanitization.
Do not auto-invalidate source downloads by dismissing the form after the first
download. Download and clipboard browser fallbacks must work in current host
environments. Keep instructions about GitHub registry versus other-host links clear.
## Parallelism
`sequential`
## Inputs
- Design: Export preparation, HTTP contracts, UI composition, Mobile contract.
- Existing host lifecycle action drawer and shared copy/download helpers.
- Plan UI-03/UI-04 and the /e2e fixture/cleanup guidance.
## Results
Implemented host and workspace-row Share actions, responsive share dialog/drawer,
release-bound bundle/source preparation and downloads, private-content review
notice, and manual repository/release/registry instructions. Sharing accepts no
screenshot input and keeps previews separate from package permissions.
Verification: the focused frontend suite passed 21 tests, desktop sharing E2E
passed, mobile sharing E2E passed, and the export/preparation backend race tests
passed 3 tests.
Review remediation: share-dialog dismissal now cancels prepared exports, and
stale asynchronous downloads cannot overwrite the current dialog state.
@@ -0,0 +1,117 @@
---
id: "07-distribution-docs"
title: "Document canvas distribution"
status: done
wave: 7
depends_on:
- "06-sharing-ui"
plan: "plan.md"
requirements:
- REQ-CANVASES-MARKETPLACE-002
- REQ-CANVASES-MARKETPLACE-005
- REQ-CANVASES-MARKETPLACE-006
acceptance_criteria:
- AC-CANVASES-MARKETPLACE-002.1
- AC-CANVASES-MARKETPLACE-002.4
- AC-CANVASES-MARKETPLACE-005.1
- AC-CANVASES-MARKETPLACE-005.3
- AC-CANVASES-MARKETPLACE-006.4
system_design:
- ../../specs/canvases/system-design/marketplace-sharing.md
---
# Task 07: Document canvas distribution
## Summary
Document the implemented canvas package, sharing, installation, and registry
workflow. Keep public guides, registry instructions, and the embedded authoring
reference consistent with the shipped contract and UI helper.
## In scope
- Public canvas how-to: screenshot-free downloads, file/link/registry
installation, permissions, independent copies, source modes, failure recovery.
- Canvas registry README examples. Previews belong to registry entries, not
canvas package manifests.
- Manual GitHub repository/release/listing instructions; generic GitLab/Bitbucket
bundle-hosting instructions that do not imply official registry support.
- Embedded authoring guide for retained source, screenshot-free distribution
compatibility, and bounded files; preserve core inventory/read count.
- Scoped engineering-guide updates only where implementation changes conventions.
- Synchronize the new package's results/status and link related designs after
each assigned check passes. Preserve older packages' recorded evidence.
## Out of scope
Publishing docs to another repository, submitting a registry entry, creating
a remote repository, adding generated promotional images, or a generic QA audit.
## Acceptance
- A reader can follow one complete manual export-to-repository-to-registry path,
and can alternatively send a bundle file/direct URL without registry admission.
- Package examples match the production inspector, source and permissions.
Registry examples show required canvas image URLs, including custom sources;
docs never require images for bundle creation.
- Public/embedded references, UI helper steps, and implemented contract agree;
documentation validators and authoring inventory tests pass.
## Verification
```bash
rtk proxy node --test scripts/validate-public-docs.test.mjs
rtk proxy node scripts/validate-public-docs.mjs
(cd apps/backend && rtk go test ./internal/mcp/canvasskill ./cmd/canvas-package -count=1)
(cd apps/web && rtk pnpm exec vitest run components/settings/canvas-share-help.test.tsx)
rtk proxy python3 scripts/lint-spec-files.test.py
rtk proxy python3 scripts/lint-spec-files.py --all
rtk git diff --check -- docs plugin-registry/README.md apps/backend/internal/mcp/canvasskill
```
Validate documented package examples through inspector fixtures; do not publish
anything to prove documentation. Re-run UI E2E only if helper behavior or other
rendered UI changed in this task.
## Files likely touched
- `docs/public/canvases.md` (how-to)
- Canvas sections of `plugin-registry/README.md`
- `apps/backend/internal/mcp/canvasskill/files/SKILL.md`,
`references/manifest.md`, `references/security.md`, inventory tests as needed
- `apps/backend/AGENTS.md`, `apps/web/AGENTS.md` if new conventions need recording
- `docs/specs/canvases/`, this plan/work orders
- Audit root `README.md` and `docs/screenshots.md`; change only stale descriptions.
## Dependencies
Tasks 01-06 and their exact recorded results.
## Risks
Do not document proposed behavior as shipped before implementation. Existing
source download means the retained project, not a reconstruction of missing
build inputs. The official registry uses GitHub releases; host-neutral manual
sharing does not broaden that registry contract.
## Parallelism
`sequential`
## Inputs
- All six requirements and the final system design.
- /docs-maintainer and the public-document validation guide.
- Current registry README, public canvas page, and UI-04 instructions.
- Existing canvas authoring core/scaffold inventory tests.
## Results
Updated the public canvas and authoring guides, the canvas portions of the
registry README, and the embedded authoring references. The documentation keeps
screenshots as registry metadata and documents screenshot-free sharing, source
modes, permissions, manual publication, and direct installation.
Verification: public-doc tests passed 62 tests and validated 46 published docs;
specification lint passed; the embedded authoring skill suite passed 7 tests;
and diff/whitespace checks passed.
+39
View File
@@ -176,6 +176,45 @@ Kandev allows up to 100 workspace canvas instances across scopes. Archived canva
On desktop, workspace canvases use the workspace Canvases area. On phones, Kandev opens a full-height canvas route and keeps canvas controls in an inset bottom drawer.
## Share and install a canvas
Canvas sharing is manual and release-bound. It does not capture screenshots or
publish a repository. Screenshots belong to a marketplace registry entry, not
to the canvas package.
1. Open the canvas host or the workspace canvas list.
2. Choose **Share canvas**.
3. Review the active release, package identity, file inventory, and archive
sizes.
4. Choose **Prepare downloads**, then download the bundle or source archive.
5. Check the downloaded files for private content before sharing them.
The bundle is an installable `.tar.gz`. The source download is a ZIP of the
retained project when the release uses project source mode. The preparation is
temporary and expires after 15 minutes. A release change, lost authorization,
expiry, or cancellation requires a new preparation. Kandev does not change the
running canvas while it prepares these files.
Recipients can install a bundle from **Settings > Plugins > Canvases** by
uploading the file or entering an HTTPS direct link. A registry entry provides
an additional catalog path. Kandev fetches and inspects the exact package,
shows its manifest and permissions, and requires an explicit confirmation
before it creates an independent workspace canvas. A registry preview is only
listing metadata. It does not grant permissions and it is not executed during
review.
Canvas registry entries use the same ordered `previews` field as plugin
entries. A canvas entry must contain one to eight objects with an HTTPS `url`
and non-empty `alt` text. The first object is the cover image. Plugin entries
may omit `previews` or include up to eight images. Use **Preview images** in the
catalog to move between images and retry a failed image.
The official registry uses a manually reviewed pull request. Authors publish a
versioned bundle as a release asset, add the repository and preview URLs to
`plugin-registry/plugins.yaml`, and wait for the registry workflow to inspect
the exact asset. Team registries can host an `index.json` with the same shape.
Direct file and direct-link sharing does not require registry admission.
## Related guides
- [Plugin manifest reference](plugins-manifest.md#isolated-web-applications) defines the `ui.web_apps` manifest fields.
+18
View File
@@ -172,6 +172,24 @@ Build and test the archive outside Kandev. Kandev validates the archive before
it stores or runs a release. Use [Agent-authored Canvases](canvases.md) for
creation, permission review, promotion, Quick Chat editing, and recovery.
### Share a portable canvas
For a canvas that another workspace can install, add the `distribution` block
described in the [manifest reference](plugins-manifest.md#portable-canvas-distribution-metadata).
Choose `static` when the packaged application is the only source you want to
share. Choose `project` when you retain a bounded editable project under
`distribution/source/`. Do not put screenshots in the package.
After a valid release is active, use **Share canvas** in the host or workspace
canvas list. Prepare and download the bundle and source archive, inspect them
for private content, and share them as files or HTTPS links. This action does
not create a repository, release, registry entry, or pull request.
To list the canvas, publish the exact bundle as a versioned release asset and
add a `kind: canvas` entry with one to eight ordered `previews` objects to a
trusted registry. The first preview is the cover. Preview URLs and alt text
are registry metadata and are not part of the package manifest.
There is no separate HTTP server to launch. pluginsdk.Serve owns the
go-plugin/gRPC handshake and Host injection. The backend implements
pluginsdk.Plugin (OnEvent and/or HandleWebhook) and embeds
+25
View File
@@ -274,6 +274,31 @@ from the browser. The host tears down the app iframe immediately after a
release, grant, scope, archive, disable, or removal authority change, then
requires a fresh runtime binding before mounting it again.
### Portable canvas distribution metadata
A portable canvas adds a `distribution` block to the manifest. This block is
package metadata. It does not contain registry screenshots, repository
publication settings, credentials, or a live application URL.
```yaml
distribution:
schema_version: 1
kind: canvas
license: MIT
source_mode: static # static or project
```
The distribution validator requires one static web app with a
`workspace-canvas` placement, a compatible `min_kandev_version`, a license,
`README.md`, and generated `checksums.txt`. It rejects native UI, managed
backend, actions, webhooks, tools, repository providers, and other plugin
contributions in the same distribution. `static` source mode keeps only the
application package. `project` source mode also retains the bounded editable
project below `distribution/source/`, including its own manifest and README.
Registry preview images belong to `plugin-registry/plugins.yaml` or another
source's `index.json`. Do not add a `previews` field to `manifest.yaml`.
## Managed vs. legacy manifests
Setting `runtime.type: binary` makes a manifest **runtime-managed**: kandev
+25
View File
@@ -41,6 +41,12 @@ sidebar.
Uninstall and an **Update** button when a newer version is available.
- **Browse**: the merged catalog across all enabled sources.
When canvases are enabled, a **Canvases** tab appears beside these tabs. It
uses the same configured sources but keeps canvas installation separate from
native plugin installation. Canvas installation is workspace-scoped and is
available to an authorized workspace user. Native plugin management remains
administrator-scoped.
### Browse and install
Open **Settings > Plugins > Browse**. Each plugin shows as a card with its
@@ -70,6 +76,25 @@ Plugin-provided icons render on the cards. A plugin that ships an icon (via the
manifest's `icon` field) shows it; otherwise the card falls back to a neutral
letter tile.
### Browse and install canvases
Open **Settings > Plugins > Canvases** after the canvas feature is enabled.
Choose the target workspace, then search, filter, and sort the catalog. The
first registry preview is the cover. Open a card to review its ordered preview
gallery, author, license, compatibility, repository, and declared permissions.
Use **Review and install** to inspect the exact package before confirmation.
The install dialog also accepts a local `.tar.gz` upload or an HTTPS direct
link. These paths do not need a registry listing or preview images. All three
paths inspect the package and show permissions before creating a new workspace
canvas. A successful install shows **Open canvas**. A failed or expired review
must be reviewed again and cannot silently install different bytes.
Registry previews are remote listing metadata. Kandev does not execute an image
or canvas application in the detail view, and preview URLs are not package
permissions. A broken image has a placeholder and **Retry**; it does not block
package inspection or installation.
### Keep plugins updated
Every row on the **Installed** tab shows the plugin's latest known marketplace
+14 -1
View File
@@ -11,12 +11,24 @@ last_updated: 2026-09-10
Canvases own agent-authored task applications and their promotion to workspace
applications. The system owns canvas scope, source lineage, release selection,
editing sessions, and discovery.
editing sessions, discovery, and portable canvas distribution.
The Plugins system owns the isolated web-application runtime, permissions,
Kandev data access, state, events, and package validation. The task system
remains authoritative for task data and permissions.
## Requirements
- [Agent-authored web-app canvases](requirements/agent-authored-web-apps.md)
- [Canvas marketplace and sharing](requirements/marketplace-sharing.md)
- [Deprecated collaborative canvases](requirements/collaborative-canvases.md)
## System design
- [Agent-authored web-app canvases](system-design/agent-authored-web-apps.md)
- [Canvas marketplace and sharing](system-design/marketplace-sharing.md)
- [Superseded collaborative canvases](system-design/collaborative-canvases.md)
## Related context
- [Runtime and permission fix plan](../../plans/canvas-runtime-permission-fixes/plan.md)
@@ -26,4 +38,5 @@ remains authoritative for task data and permissions.
- [Plugin-backed web-app canvases decision](../../decisions/2026-08-26-plugin-backed-web-app-canvases.md)
- [Plugin-backed canvases implementation plan](../../plans/plugin-backed-canvases/plan.md)
- [Canvas UX follow-up implementation plan](../../plans/plugin-backed-canvases-ux-follow-up/plan.md)
- [Canvas marketplace implementation plan](../../plans/canvas-marketplace/plan.md)
- [Historical declarative canvas plan](../../plans/collaborative-canvases/plan.md)
@@ -25,8 +25,9 @@ web-application runtime and its data contract.
- **Task canvas:** A canvas that belongs to one task and appears only in that
task.
- **Workspace canvas:** A promoted canvas that belongs to one workspace and
appears in workspace navigation.
- **Workspace canvas:** A canvas that belongs to one workspace and appears in
workspace navigation. Agent-authored canvases reach this scope by promotion;
[distribution](marketplace-sharing.md) also defines reviewed package installs.
- **Draft:** Editable canvas source in an authorized agent workspace.
- **Release:** An immutable package that passed validation.
- **Promotion:** A user action that changes a task canvas to workspace scope.
@@ -321,7 +322,9 @@ including missing references, lookup failures, and passthrough exclusions.
- A direct source-code editor in Kandev.
- Canvas invitations, collaborator roles, or multi-user live editing.
- Demotion from workspace scope to task scope.
- A marketplace or cross-instance package import flow.
- Automatic permission increases after owner-authorized first publication.
- Marketplace and cross-instance distribution are owned by the separate
[marketplace and sharing requirements](marketplace-sharing.md).
- Automatic permission increases after owner-authorized first publication or
automatic publication of a release that requests new permissions.
- General top-bar, sidebar-widget, or arbitrary-slot plugin contributions.
- A custom server-side runtime for agent-generated backend code.
@@ -0,0 +1,198 @@
---
status: draft
system: canvases
created: 2026-09-10
owners:
- canvases
---
# Canvas marketplace and sharing requirements
## Overview
Users distribute a reusable canvas, discover it in Settings > Plugins, and
install a private instance in their workspace. Canvases owns this vertical
contract because it owns canvas identity, source lineage, scope, and discovery.
Plugins supplies the existing catalog, manifest, validation, and isolated runtime.
This is the first distribution version. Sharing uses downloads and instructions;
Kandev does not create repositories or publish external releases for the user.
## Terminology
- **Bundle:** A versioned, installable archive containing a canvas application,
editable source and distribution metadata. Registry previews are separate.
- **Source download:** A project archive for inspecting, editing, and putting
the same application in a repository. It includes build inputs when needed.
- **Instance:** One installed canvas with its own workspace, grants, and state.
- **Cover:** The first screenshot in the author's ordered image list.
- **Registry:** The curated repository list in the Kandev repository that feeds
the existing marketplace.
## Requirements
### REQ-CANVASES-MARKETPLACE-001: Portable canvas distribution
**Intent:** A recipient can run and edit a shared canvas without its creator's
task, executor, or workspace.
#### Acceptance criteria
- **AC-CANVASES-MARKETPLACE-001.1:** A valid bundle shall contain the application,
required local assets, editable source, identity, version, author, description,
license information, and declared permissions. Installation shall require no
build, package manager, agent, or author-side service.
- **AC-CANVASES-MARKETPLACE-001.2:** Downloaded source shall match the selected
published release. For a build-based application, it shall include build
inputs, dependency declarations, lockfiles, and documented build instructions.
For a no-build application, its HTML, CSS, and JavaScript are its source.
- **AC-CANVASES-MARKETPLACE-001.3:** Exports shall exclude Kandev instance state,
grants, runtime tokens, workspace bindings, task history, dependency caches,
and repository history. The author shall see the file inventory and a notice
to inspect exported files for embedded private content.
- **AC-CANVASES-MARKETPLACE-001.4:** Invalid, oversized, incompatible, incomplete,
or unsafe packages shall be rejected before execution. Category labels shall
not allow a managed binary or native frontend contribution to run as a canvas.
### REQ-CANVASES-MARKETPLACE-002: Screenshot presentation
**Intent:** Recipients can inspect the canvas interface before installing it.
#### Acceptance criteria
- **AC-CANVASES-MARKETPLACE-002.1:** Canvas listings in the official registry
or a custom marketplace registry shall require one to eight preview image
URLs. Bundle creation, source downloads, upload/link installation, private
canvases, and agent drafts shall not require screenshots.
- **AC-CANVASES-MARKETPLACE-002.2:** Registry maintainers shall be able to supply,
reorder, remove, and describe preview URLs in the listing. The first image
shall be its cover. Changes shall not require rebuilding the canvas bundle.
- **AC-CANVASES-MARKETPLACE-002.3:** Canvas registry cards shall show the cover;
details shall show all listed images in order with descriptions, position,
and explicit previous/next controls. A single image shall hide unnecessary
navigation. Failed images shall not hide permissions or block installation.
- **AC-CANVASES-MARKETPLACE-002.4:** Preview URLs shall use absolute HTTPS
addresses without credentials or fragments, with nonempty alternative text.
Invalid URL/list metadata shall fail registry validation. Images shall render
without executing package code. Unlisted bundle reviews shall work without
any gallery.
### REQ-CANVASES-MARKETPLACE-003: Share downloads and guidance
**Intent:** Authors can share a canvas without connecting a code-host account.
#### Acceptance criteria
- **AC-CANVASES-MARKETPLACE-003.1:** An authorized user shall be able to open
Share from a task or workspace canvas with an available active release,
review its metadata and files, and download both its bundle and source.
Both downloads shall describe the same prepared version.
- **AC-CANVASES-MARKETPLACE-003.2:** Preparation shall report missing source,
invalid metadata, or unavailable release files with recovery
guidance. A new active release or edited share input shall require preparation
again before download. No export shall silently substitute another release.
- **AC-CANVASES-MARKETPLACE-003.3:** Share shall include an information dialog
explaining how to create a repository, upload the source, publish the bundle,
share its direct download URL or file, and request registry inclusion through
a pull request to Kandev. Instructions shall distinguish publishing a package
from acceptance into the curated marketplace.
- **AC-CANVASES-MARKETPLACE-003.4:** Downloading or reading instructions shall not
create a repository, push code, publish a release, submit a pull request, or
share the author's live canvas instance. Closing or retrying shall preserve
the canvas and its active release.
### REQ-CANVASES-MARKETPLACE-004: Reviewed workspace installation
**Intent:** Users know what they install and which workspace it can access.
#### Acceptance criteria
- **AC-CANVASES-MARKETPLACE-004.1:** Settings > Plugins shall expose a dedicated
Canvases section with installation by bundle upload and direct bundle link.
Registry entries shall enter the same installation review from their details.
- **AC-CANVASES-MARKETPLACE-004.2:** Before confirmation, users shall see package
identity, author, version, description, license, compatibility,
destination workspace, and all requested read, write, event, state, and exact
network-origin permissions. Registry installs shall also show listing previews.
Empty permission groups shall be stated clearly.
- **AC-CANVASES-MARKETPLACE-004.3:** Confirmation shall install the exact reviewed
bytes and approved declaration in an authorized workspace. A changed package,
expired review, revoked access, quota failure, or invalid workspace shall
prevent installation and leave existing canvases and grants unchanged.
- **AC-CANVASES-MARKETPLACE-004.4:** Successful installation shall create a new
workspace canvas with empty instance state and new grants, show Open canvas,
and appear in workspace canvas navigation after reload. It shall preserve
package version and provenance across backend restart.
- **AC-CANVASES-MARKETPLACE-004.5:** Installing the same package intentionally
again shall create an independent copy. Retrying the same confirmation shall
not create duplicates. Existing copies shall never be overwritten or updated
automatically, including after local edits.
- **AC-CANVASES-MARKETPLACE-004.6:** Workspace owners shall be able to install
static canvases in their own workspaces without native-plugin administrator
authority. Native plugin installation and registry-source administration shall
retain their current authorization requirements.
### REQ-CANVASES-MARKETPLACE-005: Curated marketplace discovery
**Intent:** Authors list their repositories through the existing Kandev registry.
#### Acceptance criteria
- **AC-CANVASES-MARKETPLACE-005.1:** An author shall be able to submit a canvas
repository to the existing registry list in Kandev. Admission shall require
a valid versioned bundle, matching repository/package identity, required
registry preview URLs, source, and permission metadata. Listing shall require maintainer
acceptance and successful catalog publication.
- **AC-CANVASES-MARKETPLACE-005.2:** Users shall find listed canvases through
search and existing catalog sorting, open details, inspect images and
permissions, and install without copying a URL. Native plugin entries shall
retain their current install behavior.
- **AC-CANVASES-MARKETPLACE-005.3:** Package details shall identify the selected
version; previews shall come from that registry listing and shall not be used
as permission or integrity evidence. Installation shall verify the catalog's
expected package digest. A mismatch shall fail before a canvas is created.
- **AC-CANVASES-MARKETPLACE-005.4:** A failed catalog source shall show a degraded
state without blocking healthy sources or upload/link installation. Installed
canvas badges and Open actions shall disclose only the selected authorized
workspace's instances.
### REQ-CANVASES-MARKETPLACE-006: Accessible delivery and compatibility
**Intent:** Desktop and phone users can complete the same sharing and discovery
flows without weakening existing canvas isolation or feature controls.
#### Acceptance criteria
- **AC-CANVASES-MARKETPLACE-006.1:** On desktop and phones, users shall complete
upload/link installation, registry discovery, permission review, image
navigation, both downloads, and repository guidance. Required actions shall
be visible and accessible without hover, drag-and-drop, or swipe alone.
- **AC-CANVASES-MARKETPLACE-006.2:** Phone details and sharing shall use focused
full-height surfaces with one content scroll owner, safe-area clearance,
touch targets of at least 44 CSS pixels, and no document horizontal overflow.
Keyboard users shall have predictable focus, dismissal, and image navigation.
- **AC-CANVASES-MARKETPLACE-006.3:** Host copy and errors shall use the selected
locale. Author descriptions, image descriptions, and identifiers shall remain
author data. Loading, preparation, failure, and success shall be announced.
- **AC-CANVASES-MARKETPLACE-006.4:** With canvases disabled, new canvas routes,
actions, catalog entries, preparation, downloads, and installation shall be
unavailable. Ordinary plugin behavior shall remain available according to its
existing gate. Existing private canvas releases shall need no migration or
screenshot backfill to run.
## Out of scope
- Repository-URL installation, Git cloning, or install-time builds.
- Automated GitHub, GitLab, or Bitbucket repository/release/PR creation.
- New code-host integrations, private-host authentication, or hosted sharing.
- Public live-instance links, state export, credentials, or data migration.
- Automatic screenshots, image editing, ratings, payments, or usage telemetry.
- Automatic canvas updates, replacement installs, or upstream merge tooling.
- A new plugin runtime, native binary canvases, or a standalone marketplace site.
- Changing existing runtime flag identities or shipped defaults.
## Related records
- [System design](../system-design/marketplace-sharing.md)
- [Implementation plan](../../../plans/canvas-marketplace/plan.md)
- [Existing canvas lifecycle](agent-authored-web-apps.md)
@@ -32,8 +32,8 @@ The Plugins system owns package validation, iframe isolation, data access,
state, events, grants, and runtime tokens. This design uses
[the isolated plugin web-application contract](../../plugins/system-design/isolated-web-app-contributions.md).
This design supersedes
[the declarative canvas design](collaborative-canvases.md).
See [declarative canvas](collaborative-canvases.md) and
[marketplace sharing](marketplace-sharing.md).
## Requirement mapping
@@ -0,0 +1,487 @@
---
status: draft
system: canvases
created: 2026-09-10
owners:
- canvases
requirements:
- REQ-CANVASES-MARKETPLACE-001
- REQ-CANVASES-MARKETPLACE-002
- REQ-CANVASES-MARKETPLACE-003
- REQ-CANVASES-MARKETPLACE-004
- REQ-CANVASES-MARKETPLACE-005
- REQ-CANVASES-MARKETPLACE-006
---
# Canvas marketplace and sharing system design
## Purpose and boundaries
Canvases owns distribution of reusable canvas applications and creation of
workspace instances. This is one vertical contract, including Settings >
Plugins presentation. Plugins continues to own the manifest, static validator,
runtime, release artifacts, grants, and catalog machinery.
Reuse the [plugin-backed canvas decision](../../../decisions/2026-08-26-plugin-backed-web-app-canvases.md).
No new runtime or independent marketplace is introduced. The rationale for
this additive distribution profile fits this design; a separate ADR is not
needed. The [existing lifecycle](agent-authored-web-apps.md) still owns authoring,
promotion, editing, rollback, and deletion. The
[plugin web-app contract](../../plugins/system-design/isolated-web-app-contributions.md)
owns runtime isolation. This document adds portable distribution to both.
## Requirement mapping
| Requirement | Design sections |
| --- | --- |
| REQ-CANVASES-MARKETPLACE-001 | Package profile; Source retention; Validation |
| REQ-CANVASES-MARKETPLACE-002 | Registry previews; UI |
| REQ-CANVASES-MARKETPLACE-003 | Export preparation; HTTP contracts; Guidance |
| REQ-CANVASES-MARKETPLACE-004 | Install preparation; Commit; Authorization |
| REQ-CANVASES-MARKETPLACE-005 | Registry; Catalog projection; Failure handling |
| REQ-CANVASES-MARKETPLACE-006 | UI; Mobile contract; Feature gates; Verification |
## Existing implementation and additive changes
- `internal/plugins/manifest.Manifest` already supports isolated `ui.web_apps`.
`IsStaticWebAppOnly` identifies the static runtime form, but the distribution
validator must also reject every non-canvas contribution and managed field.
- `internal/plugins/pkgtar.validateInstallManifest` requires a managed binary.
Leave its native installation path intact. Canvas installs use
`internal/plugins/webapp.ValidatePackage` with an additional distribution
profile; a category is never permission to call the native installer.
- `internal/plugins/webapp.ArtifactStore.ReadFiles` reads immutable releases.
`backendapp.canvasEditService.loadEditableCanvas` already uses it for editing.
- `backendapp.canvasAuthoringService.PublishCanvas` collects one bounded
agentctl tar stream and stores a `webapp.Package`. Extend this path to retain
inert build source; do not read an author's live executor during export.
- `internal/canvas.Service`, `PublishPackage`, and `internal/plugins/instances.Store`
own durable instance/release changes. Add a transactional import entry point
here, rather than composing browser create/publish/approve requests.
- `plugin-registry/build-index.mjs` currently reads limited flat YAML metadata
from a GitHub tag, chooses a release tarball, and emits an advisory null hash.
Canvas entries need package inspection and a required digest. Preserve the
native-plugin path and do not extend its regex parser to nested canvas data.
## Package profile
Use the existing gzip-compressed tar container and root `manifest.yaml`.
Add an optional typed `distribution` block to the plugin manifest:
```yaml
id: sprint-board
api_version: 2
version: 1.0.0
display_name: Sprint board
description: A workspace board for task progress.
author: example-author
categories: [canvas]
min_kandev_version: <first-version-with-canvas-distribution>
distribution:
schema_version: 1
kind: canvas
license: MIT
source_mode: static
ui:
web_apps:
- key: main
title: Sprint board
entry: index.html
placements: [workspace-canvas]
capabilities:
api_read: [tasks, workflows]
```
The minimum-version placeholder is illustrative. Implementation records the
first compatible release using the repository's version metadata; exports must
never emit this placeholder. Preserve an author's higher minimum version.
The distribution profile requires exactly one static web app, workspace-canvas
placement, and optionally task-canvas placement. Reject binary runtimes, remote
backend endpoints, native UI bundles, tools, and all other contributions.
`distribution.kind` is an asserted profile, verified against the complete
manifest. Categories are presentation tags. Legacy native entries without this
block retain their behavior. Local canvases without it remain valid locally.
Required share metadata: valid stable package ID, SemVer, display name,
description, author, nonempty license identifier or custom-license label plus
`LICENSE.txt`, `source_mode`, and compatibility. Screenshots and preview URLs
are not part of this manifest profile.
Do not assign a license on the author's behalf. Preserve declared permissions
exactly; editing share metadata cannot add or remove capabilities. Package ID
and version are editable for export without renaming the live canvas.
`<id>-<version>.tar.gz` is the bundle. It contains runtime files, editable source,
`README.md`, license information, and a `checksums.txt` covering every other
file. `<id>-<version>-source.zip` contains the same project files, with paths
preserved, and is explicitly not accepted by the installer. It includes built
assets so authors can republish the downloaded version without a build.
Build source uses the reserved `distribution/source/` subtree. The plugin
runtime denies the entire `distribution/` subtree before file lookup, including
encoded/traversal forms. Entries and executable runtime asset references cannot
target that subtree. Registry images are external listing metadata.
## Source retention
Two explicit source modes avoid claiming a built bundle is a complete project:
- `static`: the retained HTML, CSS, JavaScript, and local assets are the editable
application. This is the current no-build scaffold and the legacy default.
- `project`: the author places build inputs under `distribution/source/`,
including the package manifest, lockfile, build configuration, and README
with the command that writes runtime output into the package root. This
source is retained in the same immutable artifact as its built output.
Extend the package file policy only inside that reserved source subtree to
allow bounded regular project files, including TS/TSX and YAML lock/config
files. Reject executable binaries, links, devices, nested archives, `.git`,
`node_modules`, credential files, and `.env` files; a placeholder-only
`.env.example` is permitted. Apply exclusions during source collection and
again during package validation. Reject an unsafe file rather than silently
producing an incomplete project. Existing `.canvas-root` remains internal and
is never exported. Authored source or images can still embed private content;
the export UI exposes an inventory and a review notice, not a claim of complete
secret detection.
Quick Chat materializes the complete artifact, including the source subtree,
and preserves source mode on republish. Update the embedded authoring guide and
golden inventory tests with the same contract. No new executor access method is
needed: local, Docker, and SSH continue to use the authenticated source stream.
A legacy retained release can export in static mode with visible wording that
these are the retained application files. If its author needs original build
inputs that were not retained, project export reports `source_unavailable` and
directs them to Edit and republish with source. Never reconstruct missing source
from a deleted task workspace or silently label minified output as project source.
## Registry previews
Canvas entries in both official and custom marketplace sources require one to
eight ordered `previews: [{url, alt}]` objects. The same optional field is
available for plugins. The shared shape and gallery behavior belong to the
[plugin registry preview contract](../../plugins/system-design/marketplace.md#registry-preview-images).
Each URL is an absolute HTTPS address, at most 2048 characters, without
credentials or fragments. Each alt description has 1-300 non-whitespace
characters after trimming. The URL should directly return a PNG, JPEG, or WebP
image; content-type and availability failures use the browser's image fallback.
Prefer commit/release-pinned URLs for accuracy, but changing listing images
does not require a new canvas version.
Image URLs belong to registry pointers and generated source documents, not to
the manifest or bundle. Bundle export, source download, upload/link inspection,
and confirmation have no screenshot requirement or screenshot upload step.
There is no image collection, decoding, re-encoding, package extraction, or
Pages media mirroring in this delivery. Ordinary application image assets
remain ordinary bundle files.
The first listing image is the cover. Preserve registry order through the
generated index and UI. Validate list/URL shape during registry admission and
custom-source parsing. Do not reject a valid install because a remote preview
is temporarily unavailable. A listing image is informational and must never
supply permissions, executable content, or evidence of package authenticity.
## Validation and bounded preparation
Keep current web-app limits: 10 MiB compressed package, 25 MiB expanded data,
512 files, 5 MiB per file, 64 KiB manifest, and 240-byte paths. Authoring transfer
retains its existing 30 MiB wire bound. The source ZIP has a separate 30 MiB
output ceiling; it is generated, never nested in the installable tarball.
Distribution validation requires checksum coverage, normalized unique paths,
all declared files, source-mode structure, compatibility, and
the static canvas profile. An archive hash verifies identity, not publisher
trust. No package hooks, scripts, or application code run in inspection.
Add one small preparation store owned by the canvas distribution service.
It stages files under a dedicated Kandev-home temporary directory, with opaque
random IDs bound to the authenticated user, target workspace, operation,
reviewed digest, and expiration. No token grants access without authentication.
Allow at most two preparations per user and 256 MiB of staged data per install;
reserve before receiving bytes and release on every failure. Preparations expire
after 15 minutes. Remove on cancellation/expiry and clean abandoned files on
startup; a restart invalidates uncommitted preparations. Use request-lifetime
leases during download/commit so cleanup cannot remove in-use files.
This is temporary review/download state, not another installed-package database.
The persistent installation receipt described below handles commit retries.
## Export preparation
1. Authorize access to the canvas workspace and confirm an active, valid,
available release. Bind the request to `expected_release_id`.
2. Read that immutable artifact using `ReadFiles`; verify its digest.
3. Accept bounded author metadata; screenshots are not an export input.
Seed the form from the manifest. Retain the current session's inputs after
validation errors; do not create a permanent share-profile settings system.
4. Build a temporary distribution copy, generate README/checksums, and validate
it with the same distribution validator used by imports and registry CI.
5. Return metadata, inventory, sizes, digest, and the two
authenticated download endpoints. Both formats derive from this one copy.
Preparing or downloading does not publish a canvas release or mutate runtime
state. Before serving either download, reauthorize and compare the active
release to the captured one. Stale release or changed form input requires
preparation again. Deletion or revoked access prevents subsequent downloads.
The dialog preserves unsaved values while open; closing discards this temporary
form and best-effort deletes its preparation. Imported packages already retain
their distribution metadata, so subsequent sharing starts from those values.
## Install preparation
The Canvases section offers two inputs: file upload and a direct HTTPS bundle
URL. It does not accept a repository URL as an alternate source form. Public
download redirects are supported with a maximum of five validated redirects.
No credential-bearing URL, forwarded Kandev cookie, or ambient provider token
is sent. Bound connect/read/total time and bytes. Reject non-public destinations
including loopback, private, link-local, and metadata networks at DNS resolution
and connection time, also after redirects. E2E injects a local test transport;
production has no environment switch that disables destination validation.
Catalog installation sends `{source_id, package_id, expected_version,
expected_sha256}`. The server resolves the configured catalog entry itself and
rejects stale version/digest selection. It never trusts a browser-supplied
catalog package URL or permission list. Download and inspect the selected
tarball; the staged bytes are then used for preview and confirmation. Direct
URL/file installs compute their own digest and identify the source as unlisted.
Inspection returns actual manifest permissions via `canvas.ManifestPermissions`,
compatibility, license, provenance, and the requested workspace. Registry
previews stay attached as separately labeled listing metadata.
The catalog details page may show a published summary first, but the final
review always uses inspected package bytes. Opening details never executes a
preview iframe or grants permissions.
## Commit and persistence
`InstallPrepared` is a new canvas service method. It verifies the preparation's
user, workspace, expiry, digest, and explicit confirmation. Reauthorize the
workspace and enforce canvas/storage limits inside the mutation boundary.
Create canvas metadata, a workspace plugin instance, immutable release,
workspace grants, and an installation receipt in one database transaction.
Use the existing instance-store transaction patterns and artifact reservations.
Write/reconcile the validated artifact before committing references; a failed
transaction schedules or performs safe unreferenced-artifact cleanup without
removing a digest still referenced by another instance.
The instance keeps `plugin_id = kandev.canvas`; the package's stable identity
belongs to release metadata and import provenance. Do not add static canvases
to the native plugin registry or auto-update poller. A workspace install has
no synthetic origin task or creator session. Empty state is created by absence
of state rows. Existing runtime issuance enforces current workspace authority.
Add one canvas-owned `canvas_install_receipts` table with unique preparation
ID, user ID, canvas ID, workspace ID, package ID/version, archive digest,
optional source ID/repository URL, origin kind (`registry`, `url`, `upload`),
and timestamp. Persist only sanitized public provenance; discard signed URL
query strings and fragments. The receipt is inserted in the same transaction
as the instance and also serves as durable import provenance.
Retrying a committed preparation returns its original canvas after authorization,
including after restart. Concurrent confirmations serialize on that receipt ID.
A deliberate second installation uses a new preparation and creates a copy.
Canvas removal removes its receipt; an old request with neither receipt nor
live preparation cannot resurrect the canvas. Add fresh/migrated/replayed
SQLite schema tests and the repository's gated PostgreSQL coverage where supported.
After commit, emit the existing workspace-scoped canvas lifecycle invalidation
and return a canvas route. Keep event payloads content-free. Existing editing
may replace a local release, but preserves original import provenance. Display
the original version as provenance, not as a claim that an edited copy still
matches upstream. V1 has no update/replacement endpoint or new update badges.
## HTTP contracts
These proposed routes use the current `/api/v1/canvases` browser API. All require
authenticated human workspace authority, not canvas runtime tokens or agent MCP.
| Method and route | Input and result |
| --- | --- |
| POST `/:canvasID/exports` | JSON metadata and `expected_release_id`; returns prepared metadata and two download links |
| GET `/exports/:id/bundle` | Reauthorized `.tar.gz` attachment |
| GET `/exports/:id/source` | Reauthorized `.zip` attachment |
| POST `/install-preparations` | Multipart file or JSON URL/catalog reference, plus `workspace_id`; returns immutable review |
| GET `/install-preparations/:id` | Reauthorized review metadata |
| POST `/install-preparations/:id/confirm` | Reviewed digest and explicit approval; returns installed canvas or prior receipt |
| DELETE `/preparations/:id` | Idempotently release an owned temporary preparation |
Use typed DTOs and stable error codes: `canvas_not_found`, `invalid_package`,
`source_unavailable`,
`incompatible_version`, `package_digest_mismatch`, `review_expired`,
`review_stale`, `invalid_download_url`, and existing quota codes. Foreign and
missing preparations return the same not-found result. Attachments use safe
filenames, `nosniff`, and private/no-store cache policy. JSON never contains
package source, host paths, credentials, or executable HTML.
## Registry publication
Extend `plugin-registry/plugins.yaml` entries with optional `kind: canvas`;
absence means the current native-plugin route. Keep `id` and `repo` (GitHub
owner/name) as the curation source. Canvas authors submit this entry by PR.
The repository must have a public release with the exact
`<id>-<version>.tar.gz` asset. Do not use the existing first-tarball fallback
for canvases, because a source archive can otherwise be chosen accidentally.
Add a small Go command, `cmd/canvas-package`, exposing bounded offline inspection
through the production validator. The Node index builder downloads the exact
release asset and invokes this trusted inspector with argument arrays. Do not
execute anything from the contributor repository or package. The inspector
outputs a safe JSON package descriptor. It does not fetch listing images.
The index workflow builds the inspector from trusted
base code for untrusted registry PR validation; no deployment credential is
available to this validation path.
Validate registry ID against manifest ID, release tag against manifest version,
and declared repository URL (when present) against the listed repository. All
package metadata, permission summaries, and hashes come from the validated
archive, not a separate branch manifest. Preview URLs and order come from the
registry entry. Strict PR validation rejects
invalid added/changed canvas entries. Scheduled publication reports invalid
entries as skipped/degraded according to the existing builder behavior and
continues with valid entries; it never publishes an invalid canvas descriptor.
Validate `previews` on canvas registry pointers and copy the list directly
into the generated catalog. Keep image hosting under the listing maintainer's
control. Do not add media output to the Pages workflow. Maintainers can revise
previews without rebuilding the package. The proposed complete pointer schema is
[`registry-entry.schema.json`](../../../plans/canvas-marketplace/registry-entry.schema.json).
## Catalog projection
Keep the current index document and add optional per-entry `kind`, `previews`
(`url`, `alt`), `permissions`, `license`, and `source_mode`.
For canvas entries, `package_sha256` and these presentation fields are required.
Legacy entries with missing kind decode as native plugins. Unknown kinds or
incomplete canvas entries cannot be installed. Add a `kind` query without
removing category filtering. Reserve `canvas` as the displayed category for
validated canvas entries; relabeling a native package cannot change its kind.
The catalog endpoint accepts an optional authorized `workspace_id` when the
requested kind is canvas. Source fetching/cache remains shared, but instance
annotations are computed per request after authorization. Query receipts joined
to live canvas metadata for that workspace only. Return instance count and
authorized routes; never globally join static canvases by package ID into
`InstalledForMarketplace`. Label existing copies with Open (or a copy picker)
and offer Add another copy. Preserve native installed/update behavior.
Catalog images use validated HTTPS URLs from the configured index, without a
referrer, in contained raster image elements. Preserve previews for registry
review, but label them as listing previews, separate from inspected package
permissions. Upload/direct-link review omits the gallery completely. Do not
find a registry by package ID to attach potentially unrelated images. Failed
remote images show a fallback and leave valid installation available.
The same shared gallery serves native plugin details. Plugins without images
keep their existing row and direct install action. A plugin with images offers
View details alongside its current install action. This is covered by the
Plugins-owned preview requirement; the canvas design does not redefine native
plugin permissions or installation.
## Authorization and feature gates
Workspace owners can export their canvases and install static canvases into
their own workspaces. Apply `task.Service.AuthorizeWorkspaceAccess` at new
service boundaries and again on confirmation/download. Installation never
inherits grants from the author or another local instance. Reuse synthetic
identity behavior when auth is disabled.
Keep native plugin install and marketplace-source mutation admin-only. Split
the frontend's current `useIsAdmin` action gate so it does not hide workspace
canvas actions. A details preview grants no permission. An install action
explicitly approves the entire displayed declaration; later permission growth
uses the existing release approval flow.
All new canvas entry points require the existing `features.canvases` gate.
Settings > Plugins and its catalog also retain the current plugin-page gate.
When canvases are off, the catalog filters out canvas entries before returning
results, no preparation/receipt lookup is performed, and the frontend mounts no
canvas hooks or routes. Share from an existing canvas uses the canvas gate and
does not require native-plugin admin authority. No new flag or profile change.
## UI composition
Settings > Plugins keeps Installed and Browse and adds a Canvases tab. It has
an explicit workspace selector, search/sort, and Install canvas action with
upload/direct-link inputs. Cards show cover, name, author, version, and concise
description. Card activation opens details, never an immediate installation.
Use the existing settings route with a query-selected canvas detail, retaining
search/workspace when Back closes it. Add Browse shared canvases on workspace
Canvases settings, carrying that workspace into the tab.
Details shows title, author, version, description, license, repository link,
gallery, and grouped requested permissions. The selected image is contained,
not cropped, with next/previous and thumbnail selection. Enlarging an image
stays within the current details surface. Review & install downloads/inspects
the package, then replaces the body with authoritative package review. The
primary confirmation names the target workspace. Success offers Open canvas.
Share sits in host-owned canvas actions and workspace canvas rows. It shows
active release, identity/version/author/license fields, source mode, and How
to share. Prepare downloads validates the inputs and reveals file inventory,
final sizes, and the two download buttons. There is no screenshot field.
Form edits invalidate prepared output. Validation failures retain inputs.
Read-only inventory and the private-content reminder remain visible beside
download actions.
The helper is an information dialog, not an automation wizard. It explains:
download source and bundle; create a repository on the chosen host; upload the
source; publish/upload the bundle; share the direct downloadable file URL or
send the file; submit the registry pointer with at least one preview image URL for public
GitHub-hosted listings. Explain how custom registries use the same `previews`
field and how plugins may also supply optional previews.
Provide a copyable `plugins.yaml` entry only once repository owner/name is
known, with visible placeholders otherwise. Never put credentials in commands.
GitLab/Bitbucket instructions describe manual hosting for direct links; their
repositories are not accepted by the GitHub-only official registry in v1.
## Mobile contract
- Entry points are the Plugins settings page and existing workspace/canvas
host actions. The closest shipped domain exemplar is
`components/settings/canvas-host-route.tsx` with its host action drawer;
`components/kanban-with-preview.tsx` supplies direct detail navigation.
- On a phone, show one-column cards and a focused full-height details surface.
Keep Back/title fixed, one scrolling body, and the install action in a
safe-area-aware footer. Do not mount a desktop split view under the phone UI.
- Share uses a full-height form because metadata, inventory, and validation need
depth. The short How to share action opens an inset bottom drawer with
bounded internal scrolling. Returning preserves the share draft.
- Use `useResponsiveBreakpoint`, shared Dialog/Drawer primitives, and existing
28px desktop / at-least-44px touch control sizing. No hover-only instructions,
swipe-only gallery, or download-only desktop path.
- Domain hooks own input state, review generation, selected workspace, image
selection, and requests across both presentations. Ignore late responses
after input, workspace, detail, or preparation identity changes.
- Use dynamic viewport height, keyboard/safe-area clearance, focus return, and
one scroll owner per active surface. All interface copy uses i18next; author
text remains data. Show localized status/error regions for asynchronous work.
## Failure handling and observability
An invalid export leaves the active release untouched. An invalid import creates
no instance. On DB failure, compensate unreferenced artifact writes; on lost
confirmation response, replay the receipt. After restart, pending previews expire
visibly while installed canvases remain usable. Workspace deletion/canvas
removal invalidates preparation access and triggers normal artifact cleanup.
No failure silently falls back to native install, mutable latest code, or a
different release. Catalog outages preserve direct-file/link entry points.
Log operation, actor/workspace/canvas IDs where authorized, result code, byte
counts, digest, and duration. Omit source, screenshot content, titles, tokens,
and URL queries. Use content-free diagnostics; add no usage telemetry service.
## Verification and delivery
The [implementation plan](../../../plans/canvas-marketplace/plan.md) maps each
criterion to proposed unit, integration, registry, and desktop/mobile tests.
Tests must prove export-to-import round trips, project-source recovery after
executor cleanup, digest-bound review, concurrent retry receipts, isolation,
registry URL validation, screenshot-free export/import, plugin galleries,
gate-off behavior, and native installer compatibility.
This package extends completed `plugin-backed-canvases` work and the shipped
parts of `plugin-backed-canvases-ux-follow-up`. It does not change their recorded
test results or mark the outstanding external ACP evaluation complete. New
source-guide changes must preserve that package's one-core-read contract.
+3
View File
@@ -36,4 +36,7 @@ canonical requirement and system-design documents. Use the catalog command to fi
- [UI](../ui/README.md): renders plugin contributions.
- [Canvases](../canvases/README.md): binds isolated web applications to task
and workspace canvas lifecycles.
- [Canvas distribution](../canvases/system-design/marketplace-sharing.md): owns
canvas export/import and the canvas-specific marketplace flow; reuses plugin
manifests, static validation, catalog sources, and runtime isolation.
- [Integrations](../integrations/README.md): supplies external connections.
@@ -41,6 +41,29 @@ marketplace — while keeping install-by-URL and sideloading as escape hatches.
- **AC-PLUGINS-MARKETPLACE-001.7:** A catalog entry for a plugin that is already installed SHALL show an **Installed** state; when the catalog's latest version is newer than the installed version, it SHALL show an **Update available** affordance (which reinstalls the newer tarball).
- **AC-PLUGINS-MARKETPLACE-001.8:** The catalog SHALL be assembled from **one or more marketplace sources**. kandev ships with the **official kandev source** enabled by default; operators MAY add **additional sources** (a team or corporate registry) and the catalog merges them.
### REQ-PLUGINS-MARKETPLACE-002: Registry preview images
**Intent:** Registry maintainers can add a visual preview gallery to plugin
listings without changing the plugin package.
#### Acceptance criteria
- **AC-PLUGINS-MARKETPLACE-002.1:** Official and custom marketplace entries
shall accept an ordered `previews` list of HTTPS image URLs and alternative
text. Plugin entries can omit the list; existing entries shall remain valid.
- **AC-PLUGINS-MARKETPLACE-002.2:** A plugin listing with screenshots shall show
its first image as the preview cover and expose all images in a details
gallery. Entries without screenshots shall keep their current presentation.
- **AC-PLUGINS-MARKETPLACE-002.3:** Gallery navigation shall support keyboard
and touch, display descriptions and image position, and keep install actions
available when an image fails. Package code shall not run in the preview.
- **AC-PLUGINS-MARKETPLACE-002.4:** Preview metadata shall belong to the registry,
not the plugin manifest or release bundle. Maintainers shall be able to change
URLs, descriptions, or image order without releasing a new plugin version.
Canvas listing image requirements are owned by
[canvas marketplace and sharing](../../canvases/requirements/marketplace-sharing.md).
## System design
The migrated technical source is split into [part 1](../system-design/marketplace.md).
@@ -34,6 +34,11 @@ 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](../../canvases/system-design/marketplace-sharing.md)
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](../../../decisions/2026-08-26-plugin-backed-web-app-canvases.md).
It extends the data boundary from
@@ -3,6 +3,7 @@ status: draft
system: plugins
requirements:
- REQ-PLUGINS-MARKETPLACE-001
- REQ-PLUGINS-MARKETPLACE-002
created: 2026-07-18
owners:
- jcfs
@@ -13,11 +14,52 @@ owners:
This design preserves the technical source detail for `REQ-PLUGINS-MARKETPLACE-001` during migration.
The [canvas distribution design](../../canvases/system-design/marketplace-sharing.md)
extends this catalog with validated static canvas packages, registry preview URLs,
and workspace installation. Its canvas path does not use the managed-binary
installer described here. Native plugin behavior remains owned by this document.
## Requirement mapping
| Requirement | Design section |
| --- | --- |
| `REQ-PLUGINS-MARKETPLACE-001` | [Migrated source detail](#migrated-source-detail) |
| `REQ-PLUGINS-MARKETPLACE-002` | [Registry preview images](#registry-preview-images) |
## Registry preview images
Extend registry pointers and generated catalog entries with an optional ordered
`previews` list of `{url, alt}` objects. This is registry-owned presentation
metadata for both `kind: plugin` and `kind: canvas`. Omitted kind means plugin.
Plugin screenshots are optional; canvas listing admission requires at least one.
The same contract applies to official and custom source documents.
Allow up to eight entries. Each URL must be an absolute HTTPS URL without
credentials or a fragment, at most 2048 characters; each alternative description
has 1-300 characters after trimming. Reject malformed URLs and executable or
inline-data schemes. URLs should return directly displayable PNG, JPEG, or WebP
images; a pinned commit/release URL is recommended, not required. Preview
availability and content are not evidence of package authenticity or permissions.
The index builder copies validated registry URLs and their order to `index.json`.
Do not download/re-encode media, extract screenshots from packages, or mirror
assets into Pages. A registry can change images without changing package version.
Custom source readers validate the same fields; invalid canvas listings are
omitted with a source diagnostic while valid entries remain usable.
Use one gallery component for plugin and canvas details: first image as cover,
explicit previous/next and thumbnail controls, alt text, position, one-image
mode, and a contained image-failure fallback. Load images without referrers;
never embed HTML/iframes or run package code. A remote image failure does not
block an otherwise valid package install. Plugins without screenshots keep
their existing row and install action; screenshots add a View details action
without adding a mandatory native-plugin installation step. Phone details use
the same focused surface and touch controls as canvas details.
The proposed complete registry schema is a design artifact at
[`registry-entry.schema.json`](../../../plans/canvas-marketplace/registry-entry.schema.json).
The [canvas distribution plan](../../../plans/canvas-marketplace/plan.md)
Tasks 04-05 implement shared registry validation and gallery rendering.
## Migrated source detail