# Boards
A **board** is a custom HTML page written by an agent (or you) and stored as
an artifact: a results report, a demo gallery with audio/images, a
comparison dashboard, a notebook-style writeup — anything a page can be.
Boards are not screenshots: a board can **fetch the read-only API at view
time**, so it renders current data whenever it's opened.
## Creating one
No special command — a board is an HTML artifact on a run or an experiment:
```python
h.log_artifact("report.html", media_type="text/html", note="Ablation report")
# over MCP: run_log_artifact(run, path, media_type="text/html", note=...)
# cross-run views hang on the experiment instead:
# experiment_log_artifact(experiment, path, media_type="text/html", note=...)
```
Run boards are one-run views; experiment boards are the home for cross-run
comparisons and summaries that outlive any single run. Either way the web UI
renders the board full-page at `#/board/` (bookmarkable) and lists
it in the **board gallery** — the Boards page in the sidebar, the Boards
section of the owning experiment, and the boards group of the run's
Artifacts tab. Boards are content-addressed and immutable — updating one
means logging a new version; old versions stay addressable forever.
**Give it a title.** The gallery names a board by its `note`, falling back
to the HTML ``, then the filename — pass `note=""` when
logging. Boards are also searchable: the title/filename is indexed into the
owning node's search text, so `graph_query("ablation board")` surfaces the
run or experiment that carries it.
## The board contract
A board renders inside a sandboxed iframe with a strict CSP: it gets its
own opaque origin, read-only API access, and **no network beyond this
host**. Write within these rules:
1. **Self-contained.** Inline all CSS and JS. External hosts (CDN scripts,
fonts, trackers) are CSP-blocked — a reference to one simply won't load.
This is also the durability rule: a board must render unchanged years
later. The one loadable script is the host's own optional helper:
```html
```
which defines `window.mlparty` — `node(ref)`, `metrics(runId, {name})`,
`query(q, {type, limit})`, `boards({experiment_id})`,
`artifactUrl(sha, mediaType)`, and `stream(runId, onRecord, onEnd)` for
SSE live-tailing (returns a stop function). It is served by the same
host (never a CDN), so it passes the CSP and keeps boards durable.
2. **Reference store content by address.** Other artifacts embed via the
inline endpoint:
```html