Asset Format Contract¶
SynContext stores semantic diagram assets as ordinary context entries with
category="diagram" and a validated JSON envelope. Diagrams are written
through the existing MCP/REST contract, validated server-side at the data
layer, stored encrypted, and rendered only in the authenticated dashboard
— locally, with no external resources.
This page is the cross-AI authoring contract (D#94, DIAGRAMS-P1).
1. The envelope¶
Every diagram entry's content must be exactly this JSON object — three
keys, no more, no less:
{
"schema": "syncontext.diagram.v1",
"format": "mermaid | vega-lite | svg-static",
"payload": "…string… (mermaid, svg-static) or {…object…} (vega-lite)"
}
schemamust be exactlysyncontext.diagram.v1.formatselects the validator and renderer. Tags never select a renderer — the envelope is authoritative.payloadis a non-empty string formermaidandsvg-static, and a JSON object forvega-lite.- Duplicate JSON keys,
NaN/Infinity, depth over 40 and more than 20,000 aggregate JSON nodes are rejected. - Stored content is preserved byte-for-byte — the server never trims, repairs or normalizes a diagram.
2. Choosing a format¶
| Format | Use for | Notes |
|---|---|---|
mermaid |
Semantic diagrams and organigrams: flowcharts, sequence, class, state, ER, gantt, pie, journey, gitGraph, mindmap, timeline | Text-first, ideal for AI authoring |
vega-lite |
Data-readable charts, including finance/candlestick-style series | Data must be inline values |
svg-static |
Lightweight static logos only | Strict element/attribute allowlist |
3. Writing workflow (no new tools)¶
Diagrams reuse the existing 28-tool surface:
hub_store_contextwithcategory="diagram",content=<envelope>.hub_update_contextto change a diagram — the new content must be a complete valid envelope (partial edits are validated against the full effective state).fetch(id, offset, max_chars)to read back; page beyond ~22 KB with offsets.
The server derives exactly one format:<format> tag per diagram. Caller
supplied format:* tags are rejected in any category. Rollback
re-validates the historical envelope under your current tier; an invalid
legacy version is never written.
No new MCP tools, no ImageContent, no raster ingest in P1 (see §8).
4. Limits¶
| Tier | Max payload | Diagrams per project |
|---|---|---|
| Free | 10,000 bytes | 5 |
| Pro | 100,000 bytes | unlimited |
| Team | 256,000 bytes | unlimited |
- Sizes are measured in UTF-8 bytes, not characters.
- The whole envelope is capped at 500,000 bytes (the general content cap).
- Reads beyond ~22 KB use
fetchpagination (offset/max_chars).
5. Security rules (what is rejected)¶
Mermaid — allowed diagram families are listed in §2; graph/flowchart
require a direction (TD, TB, BT, RL, LR). SynContext accepts the
documented edge syntax for the pinned Mermaid 11.16.0 grammar, including
arrows, length-extensible flow links bounded by the service validator,
stateDiagram-v2, and classDiagram-v2. Edge syntax is allowlisted per
diagram family. Rejected: unsupported -v2 declarations, residual angle
brackets, angle brackets inside quoted labels, backtick markdown strings,
click directives, %%{ init/config directives,
http:/https:/data:/javascript:/file:/protocol-relative //
references, image/icon resources, more than 1,000 lines, or more than 500
parsed edges. Plain full-line %% comments are fine.
Safe example:
{"schema":"syncontext.diagram.v1","format":"mermaid","payload":"graph TD\n A[Ingest] --> B[Validate]\n B --> C[Store]"}
Rejected (network reference): A[see https://example] --> B.
Vega-Lite — data must be inline ("data": {"values": [...]}). Rejected
at any depth: url, href, loader, usermeta keys (case-insensitive),
__proto__/prototype/constructor, image marks, named or remote data
loading, non-finite numbers, more than 10,000 inline rows, width/height
outside 1–4,000, and any $schema other than
https://vega.github.io/schema/vega-lite/v6.json.
Safe example:
{"schema":"syncontext.diagram.v1","format":"vega-lite","payload":{"mark":"bar","data":{"values":[{"x":"a","y":3},{"x":"b","y":7}]},"encoding":{"x":{"field":"x","type":"nominal"},"y":{"field":"y","type":"quantitative"}}}}
Rejected: {"data": {"url": "https://example.com/data.csv"}} — remote data
never loads.
SVG (static) — allowed elements: svg, g, defs, symbol, use, path, rect,
circle, ellipse, line, polyline, polygon, text, tspan, textPath, style,
clipPath, mask, pattern, marker, title, desc, linearGradient,
radialGradient, stop. Everything else is rejected — including script,
foreignObject, image, a, animation and filter elements, DTD/entity
declarations and processing instructions (no <?xml …?> prolog).
Attributes are allowlisted; anything starting with on, plus src and
xml:base, is rejected. href/xlink:href only on use/textPath and
only as #fragment. Paint references only as url(#internal-id). CSS may
not contain url(, @import, expression(, javascript:, behavior:,
-moz-binding or @font-face.
6. Privacy¶
- Storage is unchanged: tenant-scoped and encrypted at rest like every other entry.
- Rendering happens locally in your browser in the authenticated dashboard, with libraries bundled into the app — no CDN, no external fonts/configs/schemas, no telemetry, no editor hand-off, no external data loading.
- PNG/SVG export is generated locally; filenames are
diagram-<id>.png/svgand never contain titles, project names or content.
7. Errors and fail-closed rendering¶
Invalid envelopes are rejected on write with a ValueError-style message
(HTTP 400 on REST, a bounded {"error": …} on MCP) that never echoes your
payload. On read, a malformed or legacy category="diagram" entry renders
as escaped plain text with a generic notice — the dashboard never
guesses a renderer from tags or content sniffing.
8. Deferred to D-P2¶
Raster PNG ingest, MCP ImageContent results, and any schema v20 changes
are explicitly out of P1 scope and gated by the D#94 triggers.
9. Accessibility¶
- Mermaid: add a short
title-style first node label or accompanying entry text describing the diagram. - Vega-Lite: use
"description"in the spec so assistive technologies can announce the chart. - SVG logos: include
<title>and<desc>children on the rootsvg.
10. Static logo guidance (size/quality)¶
- Prefer plain paths and basic shapes; keep logos under a few kilobytes — they must fit the Free 10 KB payload cap to be portable across tiers.
- Set a
viewBoxand omit fixed pixelwidth/heightso the logo scales. - Use internal gradients (
url(#id)) instead of raster effects; filters, fonts and images are rejected.