131 lines
6.6 KiB
Markdown
131 lines
6.6 KiB
Markdown
---
|
|
id: canvases-local-creation-authority-design
|
|
title: Owner-authorized canvas creation design
|
|
status: draft
|
|
system: canvases
|
|
owners:
|
|
- canvases
|
|
created: 2026-09-10
|
|
last_updated: 2026-09-23
|
|
requirements:
|
|
- REQ-CANVASES-LOCAL-CREATION-001
|
|
---
|
|
|
|
# Owner-authorized canvas creation design
|
|
|
|
## Boundary and mapping
|
|
|
|
Canvases owns the creation workflow and its recorded authorization. Plugins
|
|
owns grant storage, release activation, and runtime enforcement. This design
|
|
implements `REQ-CANVASES-LOCAL-CREATION-001`; the authority, transaction, and
|
|
compatibility sections map to criteria .1-.3, .4-.5, and .6-.7 respectively.
|
|
|
|
See [the creation authority decision](../../../decisions/2026-09-10-canvas-creation-authority.md),
|
|
[its workspace-data amendment](../../../decisions/2026-09-23-task-canvas-workspace-data.md),
|
|
and [plugin grants](../../plugins/system-design/isolated-web-app-contributions.md#instance-grants).
|
|
|
|
## Trusted creation authority
|
|
|
|
`canvasAuthoringService.CreateCanvas` is the only production issuer. Resolve
|
|
the execution and session through existing trusted MCP context. Resolve the
|
|
workspace owner through the task service and retain current access checks.
|
|
Do not use `workspaceOwner`'s lookup-error fallback as authorization. An actual
|
|
auth-disabled workspace can use its persisted default-user owner; an absent
|
|
owner or failed lookup cannot mint authority.
|
|
|
|
Add a canvas-owned `canvas_creation_authority` table through
|
|
`canvas.Repository.SchemaSQL` initialization:
|
|
|
|
| Field | Contract |
|
|
| --- | --- |
|
|
| `canvas_id` | Primary key, references the canvas lifecycle identity |
|
|
| `owner_user_id` | Trusted workspace owner at creation |
|
|
| `creating_session_id` | Trusted source session |
|
|
| `policy_version` | Integer 1 for historical task-data grants; version 2 for new workspace-data grants |
|
|
| `consumed_at` | Empty until the first release transaction consumes it |
|
|
|
|
Create the row in the canvas/instance creation transaction. No agent-facing
|
|
tool input can write these fields. Scope and task remain in the existing
|
|
canvas and instance records. Installation or future import paths do not write
|
|
this table, even when the resulting package belongs to the same user.
|
|
Existing databases acquire the empty table; do not backfill authority.
|
|
|
|
At publication require every predicate: a nonzero captured `PublishAuthority`,
|
|
local-canvas instance source, task scope, matching task and workspace, matching
|
|
creating session, unchanged current owner, pending instance, no active or
|
|
persisted release, no previous grant changes, and an unconsumed supported
|
|
policy version. New authority uses version 2; historical version-1 authority
|
|
keeps its task data ceiling and is never upgraded by publication alone.
|
|
Check the full instance snapshot again inside the transaction. Re-read the
|
|
current task/workspace ownership through the same transaction and serialize
|
|
against ownership changes; the adapter's earlier lookup alone is insufficient.
|
|
A changed
|
|
owner or stale execution fails rather than falling back to inferred ownership.
|
|
The trusted adapter supplies the freshly resolved owner; caller-controlled
|
|
`SourceUserID` and `SourceActorKind` alone are not sufficient.
|
|
|
|
## Grant derivation and transaction
|
|
|
|
Derive grants from `ManifestPermissions` and the existing static web-app
|
|
capability vocabulary. Version-2 authority binds the approved data scope to
|
|
the recorded workspace while the canvas remains task-placed. Historical
|
|
version-1 authority retains task data scope. See the
|
|
[workspace preview design](task-canvas-workspace-preview.md). Normalize each
|
|
declared external origin through `NormalizeNetworkOrigins`; initial external
|
|
grants count as delegated owner approval, with that owner as `ApprovedBy`. A locally
|
|
requested app can contact its declared HTTPS service on its first run.
|
|
Unsupported capabilities and backend/native contributions cannot gain access
|
|
through this path. Do not add a generic manifest-controlled auto-approve flag.
|
|
|
|
Extend the existing `persistAuthorityRelease` transaction. Insert the release
|
|
with the current `CreateReleaseIfAuthorityTx` guard, insert only declared
|
|
initial grants through a narrow transaction helper in `instances.Store`, set
|
|
the plugin identity, activate through `ActivateReleaseTx`, and consume the
|
|
creation record. Reuse grant normalization and declaration validation. Do not
|
|
call the separate public approval transaction after publication.
|
|
|
|
The zero-permission first release consumes authority too. A transaction failure
|
|
rolls back all writes. Invalid source rejected before persistence leaves the
|
|
draft eligible. Grant generation advances through the normal instance writes;
|
|
authority changes still invalidate issued capabilities. Publish one activation
|
|
event after commit. Retention and artifact cleanup retain their existing
|
|
post-commit and failed-publication contracts.
|
|
|
|
Later publications follow existing `permissionsFit` behavior. They cannot use
|
|
creation authority to restore revoked grants. Consumed rows remain until canvas
|
|
removal, so pruning releases cannot restore first-publication eligibility.
|
|
Archive/restore never creates a row. Canvas/task/workspace removal deletes the
|
|
associated row in its existing cleanup transaction. SQLite and PostgreSQL
|
|
schema replay and backup enumeration must include this table.
|
|
|
|
## API and compatibility
|
|
|
|
Return an additive `initial_permission_policy` field in the create response:
|
|
the policy version, eligibility, placement `task`, data scope, supported
|
|
permission kinds, and exact HTTPS-origin constraint. This is descriptive
|
|
output, never a publish input.
|
|
It must not claim a permission ceiling that the backend does not enforce.
|
|
Keep the existing `activated` and `permission_required` publish result fields.
|
|
|
|
Update the versioned bundled authoring guidance during implementation. Local,
|
|
Docker, and remote executors use the same trusted path. Existing releases and
|
|
drafts remain unchanged on upgrade and can still use manual review. Operators
|
|
do not need to republish an already valid release for the separate embedding
|
|
policy correction.
|
|
|
|
## Validation and observability
|
|
|
|
Use `authoring_test.go` and `services_canvas_test.go` for owner, session,
|
|
source-kind, no-identity, initial grant, subsequent increase, and legacy cases.
|
|
Retain `TestPublishPackageFirstReleaseRequiresMatchingGrants` for canvases
|
|
without recorded authority. Add mixed eligible/ineligible instances, concurrent
|
|
publication, owner/scope drift, transaction rollback, and revoke/retry cases.
|
|
Keep runtime denial tests for foreign tasks and undeclared origins.
|
|
|
|
Diagnostics can record the policy version and safe result code. They must not
|
|
record capability URLs, source, network payloads, or grant request bodies.
|
|
|
|
## Implementation plans
|
|
|
|
- [Canvas runtime and permission fixes](../../../plans/canvas-runtime-permission-fixes/plan.md)
|