Skip to content
Browse documentation

Upgrading runtime permissions

This release changes what existing canvas viewers can do. It preserves stored canvases, versions, KV and files. Prepare affected canvases before deploying it. The server migrations do not adapt deployed canvas code or infer record authors. Operators must prepare and apply those changes explicitly.

There is one permission model. Upgrade each affected canvas to preserve its intended interactions; do not grant participants editor access to work around denied requests. Prepare the updated apps and data migration before the server cutover, then apply their policies and validate them as part of the same rollout.

Existing pattern New behavior Preparation
Viewers write shared KV or increment a vote counter Raw shared mutations require owner/editor; viewers receive PERMISSION_DENIED. Configure authored collections with Shared contributions or Private submissions. Keep preferences in kv.user; the one-response submissions convenience API remains available.
Viewers upload shared files or delete any file Raw shared mutations require owner/editor. Bind attachments using {collection, recordId} or configure a file group. Record authorship controls deletion. Existing file rows remain shared with their original bytes.
Viewers call AI aiAudience defaults to editors. Deliberately set aiAudience: "viewers" where participation requires AI. Legacy guest opt-in/caps still apply.
Viewers call Connections connectionsAudience defaults to editors. Prefer a per-profile policy permitting the required audience and methods. Existing admin grants remain required.
Viewers publish on ordinary realtime channels Without explicit policy, only owner/editor can publish there. Configure existing activity channels for participant publishing. Keep authoritative channels editor-published while everyone receives; never broadcast private answers.
Canvas code hides editing buttons using local state UI state grants no authority. Render controls from me().canvasRole / permissions and handle PERMISSION_DENIED.

Preserve application intent

For shared comments, use a Shared contributions collection: participants can read and create; the author and owners/editors can update status or delete. Export reads the same collection. Keep review-round settings in managed storage. Create a comment record before uploading its attachments so each file can inherit the parent record's rights. A policy alone cannot enforce author-only changes inside an existing shared KV blob.

When migrating existing comments, preserve identifiers and references used by review rounds, exports and attachments. Verify each original author's identity against a trustworthy source; a display name or last updater is not proof of authorship. The runtime create API always attributes records to its caller, so an owner replaying other people's comments would assign the wrong author. Use a controlled server-side migration with a reviewed mapping, preserve timestamps and attachment associations, and verify counts and content against a backup. Unresolved authorship must be reviewed before cutover rather than guessed.

For live activity, allow everyone with backend access to receive the relevant channel. Decide publishing and presence independently: an activity channel may allow participant publishing, while a presentation-control channel can reserve publishing for owners/editors. Existing channel names can remain when explicitly configured. Channel policies match exact names, not wildcard patterns. For dynamic participant channels, use the participants: namespace or redesign them around a fixed configured channel. Do not trust a local “presenter” switch as authorization to control other viewers.

Version pruning is unrelated to the permission cutover. It only runs when explicitly requested; upgrading does not remove history or reclaim storage.

Data and rollout

The additive migrations add canvases.ai_audience, canvases.connections_audience (both default editors) and files.scope (default shared) on SQLite and PostgreSQL. Submissions use a reserved scope in the existing KV table. The policy extension adds nullable canvases.runtime_policy, kv_entries.author_id and files.record_id. No legacy author is inferred from the last person to update a value. Authored records use new reserved records: scopes; existing data requires a deliberate migration with trustworthy authorship. Deploying the server runs its normal pending migrations; no backfill of existing answer data is automatic. Existing shared response data remains shared until an owner deliberately changes the app and migrates/removes that data. Restoring old version files does not restore old permission behavior or roll back live backend data.

The SDK URL remains /sdk/v1.js and is normally cached for one hour. During rollout purge its CDN cache if present and reload clients with a fresh SDK before publishing canvases that call kv.collection, use bound attachments, or inspect resource permissions. Give affected script URLs a new release query, for example /sdk/v1.js?v=permissions-20260908, so a normal reload avoids an old browser cache. A CDN purge alone cannot invalidate a copy already cached in a browser. An already open page keeps its loaded code until reload; changing server permissions takes effect immediately.

Before deployment, inventory enabled-backend canvases and inspect their authored code for the patterns above. Include team/direct viewers, whole-org viewers and retained guest sessions. A canvas author chooses which audience policies to opt in; a capability switch alone does not do it. Public-link viewers remain static-only. Verify a viewer and editor account against each affected app before publishing its updated version.

After an approved deployment, check the access and application error signals for PERMISSION_DENIED, STATIC_ONLY, CAPABILITY_DISABLED, failed form saves and failed upstream calls. Confirm an owner/editor can edit shared content, a viewer can create contributions and only update/delete their own, and private submissions and personal data are hidden according to their presets. Check exports and direct attachment URLs. Open a socket, demote an editor, and verify managed publishing stops while allowed receiving continues. Test per-channel changes on existing sockets. Check version cleanup against actual remaining history; its space number is an estimate, not measured recovery. Monitor for the first hour and the next normal usage day; the deploying operator owns the checks.

If a required workflow breaks, first correct the app or its explicit audience policy. Pause rollout for unexplained access or privacy failures. Restoring the old server can reintroduce broad viewer mutation permissions; do not treat that as a privacy-preserving rollback. Back up before rollout and use the normal deployment and backup procedures. No destructive schema rollback is required for these additive columns.