The datalink specification
A draft. It exists to make one idea checkable: a URL can carry data, and a renderer can turn that URL into both a picture for a person and a spec for a machine. Everything below is either a promise a renderer must keep or a question we have not settled yet.
Status: draft v0.1 · last revised 2026-11-12 · discussion is open on the repository issue.
1 · Definitions
- datalink
- A URL whose parameters carry the complete data required to render something. Not a pointer to data — the data itself.
- data spec
- The canonical, serialised description of that data: what was measured, in what units, over what.
- renderer
- A service that turns a datalink into an output for a human, and — by the invariants below — into the same data spec for a machine.
- context
- What a model is given to reason over. A datalink is context. It does not point at context, and it does not require a fetch-then-describe round trip.
2 · Anatomy
https://datalink.ac/d?type=pie&labels=TS,Py,Go&values=4821,2312,980&v=1
└─ host ──┘└path┘└────────── parameters ──────────────┘ └ver┘
- host — the renderer. A different host may implement the same contract.
- path — the kind of rendering.
/dcovers the flat-parameter renderers;/d/arch2,/d/seq,/d/erd… cover the structural ones. - parameters — flat, named, URL-encoded. They are the data.
- v — pins the renderer version, making the output immutable.
3 · Invariants
These five promises are the specification. A renderer that breaks one is not a datalink renderer.
v=N). A pinned link renders the same bytes forever; an unpinned link tracks the newest renderer and says so.
4 · The data spec
The canonical form is JSON. Two encodings are in use, and they must agree:
- Flat parameters —
labels=A,B,C,values=1,2,3. Readable by eye, trivial for an agent to compose, no escaping puzzles. This is the default. d=— URL-encoded JSON, for anything nested. Same spec, one blob.
// one series
{"title":"","data":[{"label":"A","value":1},{"label":"B","value":2}]}
// several series
{"labels":["A","B"],"series":[{"name":"x","values":[1,2]},{"name":"y","values":[3,4]}]}
// a flow
{"labels":["A","B"],"links":[{"from":"A","to":"B","value":30}]}
A spec says what was measured. It does not say how it should be drawn — that is the renderer's business, and it is why one spec can appear as a chart, a table, a calendar or a slide.
5 · Renderers
The registry is open. These are the kinds that exist today:
| Kind | Path | Parameters |
|---|---|---|
| chart | /d?type=pie, bar, hbar, line, area, radar, gauge, scatter, combo, sankey, butterfly | labels, values, series, title, unit |
| structure | /d/arch2, /d/seq, /d/uc, /d/dataflow, /d/lifecycle, /d/erd, /d/class | n=, e=, b= |
| diagram | /d/mindmap, /d/mermaid, /d/gantt | n=, m= |
| table | /d?type=table | cols=, rows= |
| calendar | /d?type=calendar | start=, ev= |
| deck | /deck | data= (markdown) |
Renderers may add kinds. The invariants are not negotiable: a new kind that cannot satisfy I2 and I5 is not a datalink renderer.
6 · The agent contract
What an agent may rely on, and what it must never do.
May
- Fetch the link to get a rendering — and then not read it as a picture.
- Fetch the data view of the same link to get the spec verbatim.
- Rewrite the parameters to obtain a new link, then fetch that.
- Quote the link it used, so a human opens exactly what the agent saw.
Must not
- Recover values from pixels — no OCR, no eyeballing, no “approximately”.
- Silently round, rescale or re-order the data it was given.
- Mutate anything on the server to make a link work. Links are pure.
7 · HTTP semantics
- GET, cacheable. The renderer decides freshness; unpinned links are short-lived, pinned links are immutable.
- Version pinning —
v=Nmakes the bytes stable, so a link pasted into a document stays correct. - Short links — a renderer may accept the same spec by POST and answer with a short, stable URL. The same data must always yield the same short link; that is what makes them shareable rather than opaque.
- Errors — a 4xx with plain text and a corrected example. A renderer that cannot explain itself is not usable by an agent.
- The data view — the spec must be reachable as JSON from the same link. This site exposes it at
/d.json; content negotiation onAcceptis an open question below.
8 · Privacy
A datalink is public by construction. Anyone holding the string holds the data — and the string travels further than data usually does: chat logs, referrer headers, browser history, CDN caches, screenshots of address bars, issue trackers.
- Never put secrets, tokens, personal data or internal identifiers in a link.
- A short link hides the data, not the existence of the data. It is not a privacy tool.
- Query strings are cached and logged. If a parameter is sensitive, it should not be a parameter.
- Rule of thumb: if it should not appear in a screenshot of somebody's address bar, it does not belong in a datalink.
This section is a starting point, not a finished policy. The wider question — what a public data link means for the people whose data it describes — is tracked in the repository issue and in the blog.
9 · Open questions
- Should the spec be versioned separately from the renderer? A pinned renderer still needs a pinned grammar.
- Should a spec be signed — so an agent can tell a chart from a forgery?
- What happens to data too large for a URL, and what is the privacy cost of storing it server-side?
- Should there be a public registry of renderers, so “a datalink” is a standard rather than a convention?
- Should one link negotiate its output by
Accept— SVG for a browser, JSON for an agent — instead of a second path? - How do we express uncertainty? A chart of estimates is not a chart of facts, and the spec should be able to say so.
Answering these is the point of the draft. The discussion is open in the repository issue.