Skip to main content
A canvas is a list of typed blocks. Each block has a stable id, a kind from the closed set below, and a set of kind‑specific fields. A VIEWER MUST render every kind it claims to support and MUST render a fallback for kinds it does not (see Fallback). The source of truth for the field set on every block is the Block interface in apps/hub/src/types.ts.

Common fields

Every block carries:
  • id (string, required) — stable across edits; survives patches; URLs anchor on it.
  • kind (string, required) — one of the kinds below, or a vendor extension shaped vendor.kind (see conformance.mdx#extension-namespaces).
  • title (string, optional)
  • metadata (object, optional) — extension data; viewers MUST ignore unknown keys.

Kinds

KindPurposeNotable fields
headingSection headingtext, level (1–6)
markdownProse / mixed contentmarkdown
imageImage assetassetId OR url, alt, caption
terminalA command and its outputcommand, output, exitCode, language
diffUnified diffdiff (string of the diff body), language
chartData visualizationchart: ChartSpec (type, axes, series)
checklistItems that can be checked offitems: ChecklistItem[]
decisionA prompt for a human to pick from optionsprompt, options
linkExternal or internal URL cardurl, title, caption, thumbnailUrl, thumbnailAssetId
metadataA small grid of key/value rowsmetadata map
videoEmbedded videoassetId OR url, platform, duration, thumbnailUrl, thumbnailAssetId, posterAlt
htmlFree‑form rendered HTMLhtml (inline body, sandboxed) OR screenshotAssetId (rendered fallback image); optional sandbox, height, caption
collectionA list of mixed itemsitems: ChecklistItem[] (used as item rows), pageSize
formA small input formfields: FormField[]
orderable-listA list with drag‑to‑reorderorderableItems: OrderableItem[], optional itemEditor: FormSchema, optional total
splitWeighted split into named slicesslices: SplitSlice[]
rule-builderA typed rule builderruleSchema: RuleSchema, clauses: RuleClause[]
The full per‑kind field set (e.g. ChartSpec.series[].data[], FormField.type) is normative and lives in apps/hub/src/types.ts. A future MINOR bump MAY add new kinds; viewers MUST forward‑compatibly fall back to a labeled placeholder.

Fallback

When a VIEWER encounters a block.kind it does not support, it MUST:
  • Render a visually distinct placeholder that includes the kind value and the block id.
  • Preserve the block in the document model so that round‑trips do not drop it.
  • Not block the rest of the canvas from rendering.
The reference web viewer renders such blocks as a <UnknownBlock> card with the kind label visible.

Edit ops and block kinds

POST /v1/canvases/:id/edits (see lifecycle.mdx#canvas-edits) carries ops that target specific block kinds. The HUB MUST validate kind/op compatibility via opRequiresBlockKind. A consumer that authors edits MUST consult apps/hub/src/validation.ts for the current op→kind mapping; a future MINOR bump SHOULD lift that mapping into this document.

HTML blocks

The html kind lets an agent emit a free‑form rendered view — a small report, a chart, an annotated screenshot, or any HTML fragment a reviewer should see verbatim. Two carriers are supported, and a block MAY include either or both:
  • html — an inline HTML body, capped at 256 KiB. VIEWERS MUST render it inside a sandboxed iframe using srcdoc. The default sandbox is strict (no allowances). sandbox: "relaxed" MAY grant allow-scripts; a VIEWER MUST NOT grant allow-same-origin or allow-top-navigation for either mode.
  • screenshotAssetId — an image asset (typically a PNG/JPEG of the rendered HTML). The HUB MUST validate that the asset exists in the bound canvas’s workspace and SHOULD decorate the block with a public screenshotUrl when an asset CDN is configured. VIEWERS MUST display this image as a fallback when html is absent or when execution is blocked by viewer policy.
Other fields:
  • title, caption — same conventions as image.
  • height — initial iframe height in CSS pixels, 1–1600; VIEWERS MAY clamp.
A block with neither html nor screenshotAssetId is invalid and the HUB MUST reject it with HTTP 400. Agents SHOULD prefer html when reviewers benefit from interactive content (sortable tables, hover details, sparklines) and SHOULD include screenshotAssetId whenever a frozen visual record is also useful — for example, post‑run reports archived after the live session ends.

Assets

Blocks that reference assets do so by assetId. The HUB MUST validate that any viewer session with asset.read can only fetch assets referenced by its bound canvas (see auth.mdx#viewer-links). A canvas MAY embed asset bodies inline by base64 only inside an exported CanvasBundle (bundle.assets[].bodyBase64). Top‑level canvases delivered through GET /v1/canvases/:id MUST reference assets by id, never by inline body.