docs
Documentation for teams, the way pages hosts sites: a team creates spaces (a product's docs, an
internal wiki, an API reference), writes pages in a block editor together in real time, publishes them to a
site at <space>.docs.<d> or its own domain, with versions, languages, access rules, comments, an AI
assistant and OpenAPI references. xvii's own documentation is the first space, read-only and built into the
binary. Host: docs.<d>, wildcard *.docs.<d>. Binary: docsd. v1 (stage 11) is the built-in site; v2 is
this spec (ADR 0017).
Concepts
- Space: belongs to a team (and optionally a project). Slug (DNS label, unique per instance: its host is
<slug>.docs.<d>), name, description, icon, languages (the first is the default), access, look,
navigation, custom domains, git source. A space has a draft (what editors work on) and
publications (what readers see).
- Page: belongs to a space; a tree (parent, position), slug (unique among siblings), title, icon,
description, hidden flag, per-language content. A page with children is also a section.
- Content: a Yjs document per page and language (a
Y.XmlFragment holding the editor's blocks). The
server keeps it as an update log compacted into a state snapshot, and derives Markdown from it (the same
dialect as v1: front matter, callouts, tabs, steps, cards, fenced code with titles) for rendering, search,
git, export and AI. The block schema and the Markdown dialect map one to one, so a page can round-trip.
- Publication: an immutable snapshot of the whole space (every page's Markdown, rendered HTML, the
navigation and search index) with a number, author, message and time. The space's live pointer names
one; publishing moves it, rolling back moves it back. Readers only ever see publications.
- Version: a named publication branch for an older product release (
v1, v2). The space has one
current version, edited in the draft; other versions are frozen publications, editable only by
making them current again ("restore as draft"). Readers switch versions in the site header.
- Language: each page may have content in every space language; a missing translation falls back to the
default language with a notice, as in v1. The editor shows per-page translation status.
- Access (reading) per space:
public (anyone, indexed), link (anyone with the secret link or a
password; noindex), instance (signed-in users of the instance), team (members of the owning team).
Single pages can be shared by a secret link even inside a non-public space (read-only, revocable, with
an optional expiry and password).
- Editing: team developers and up edit drafts and publish; team admins manage access, domains,
versions and deletion; viewers read and comment. Guest editors: instance users invited to one space
(editor or commenter) without joining the team.
- Comment thread: anchored to a text range in a page (a Yjs relative position, so it follows edits),
with replies, @mentions (notify event to the mentioned person), resolve and reopen. Visible to editors
and commenters, never on the public site.
- Template: a page or a whole space to start from. Built-in ones: product docs (Get started, Guides,
Reference, Changelog), team wiki, API reference, runbook, changelog page, meeting notes; a team can save
its own from any page or space.
- API reference: a page bound to an OpenAPI 3.x document (uploaded, or a URL fetched on publish, or a
file in the git source). Rendered as an endpoint list with parameters, schemas, examples in curl,
JavaScript, Python and Go, and a "try it" console (calls go straight from the reader's browser).
- Git source (optional, per space): a repository folder of Markdown (GitLab or GitHub, through an id
trust rule or a deploy token). A push (webhook) or
xvii docs publish imports it. Pages that come from
the repository are marked as such and locked in the editor (edit in the repository); other pages of the
space stay editable. Exporting a space writes the same layout, so moving a space into a repository is
export, commit, connect.
Editor
- Tiptap (ProseMirror) with Yjs collaboration; every menu, toolbar, popover and dialog from shadcn/ui and
@xvii. Blocks: paragraph, headings 1–4, lists and task lists, quote, callout (five kinds), code (language,
title, highlighted), tabs, steps, cards, table, image and file (uploaded to the space, resized, alt text),
divider, embed (video and well-known URLs), API reference, page link, math later.
/ opens the block menu; Markdown shortcuts while typing (## , - , ```, > [!NOTE]); a floating
toolbar on selection (marks, link, comment, AI); drag handles to move blocks; paste Markdown or HTML and
get blocks.
- Real time: one WebSocket per open page (
/api/v1/collab/{page}/{lang}), the Yjs sync and awareness
protocols, presence (avatars of who is on the page, their cursors and selections in their colour).
docsd applies updates with a pure-Go Yjs implementation, persists them (log + snapshot every N updates),
and derives Markdown a moment after the last edit. Offline edits merge on reconnect.
- History: every page keeps snapshots (on publish, on idle after edits, at most every few minutes while
editing) with authors; any snapshot can be previewed, diffed and restored (restoring is a new edit).
- The sidebar edits the tree: add, rename, drag to reorder and nest, duplicate, move to another space,
hide, delete (to a trash kept 30 days).
Publishing
- Publish (developers): shows what changed since the live publication, page by page with diffs, takes a
message, builds the snapshot (Markdown → HTML through the v1 content engine, navigation, search index,
broken-link check that warns) and moves
live. Roll back moves live to an earlier publication.
- Versions: "Freeze as version" turns the live publication into a named version; the version menu on
the site lists them, newest first, with the current one marked "latest".
- Scheduled publishing later.
Reading site
https://<slug>.docs.<d>/<lang>/<version>/<path> (the default language and the current version may be
left out), or the space's own domains through domains (docs attachments). The v1 site engine: server
rendering, search, Markdown sources (.md), llms.txt, sitemap, hreflang, per-space MCP tools.
- Look per space: name, logo, favicon, accent, background (glass like the panels), header links, footer
links and text, an optional landing page made of blocks (hero, cards). Defaults to the instance's look.
No xvii service switcher on public sites; signed-in editors get an Edit button.
- Ask the docs (AI, optional per space): a chat that answers from the space's published pages through
the instance's ai service, quoting and linking the pages it used; rate-limited per visitor; off unless the
space has an ai key.
- Feedback: "Was this page helpful?" with an optional comment, kept per page.
- Analytics (optional per space): page views through the analytics service's site for the space, plus
docsd's own report of searches without results and feedback.
docs.<d> itself: the built-in xvii documentation (as in v1) and, for signed-in people, the panel.
Panel
At docs.<d> behind sign-in, built like every panel (app shell, team switcher):
- Spaces: a table with access, languages, version, last publication, views; create from a template or
import.
- Space: the editor (sidebar tree + page), publish bar with draft changes count, history, comments
panel, settings (general, access and sharing, guests, languages, versions, look, navigation, domains,
git source, AI, analytics), publications list, feedback, trash.
- Import: Markdown folder or zip (front matter and the v1 dialect understood), a Notion export (Markdown
& CSV zip), a GitBook export, a Docusaurus/MkDocs folder (front matter and sidebar files), an OpenAPI
document. Export: Markdown zip in the git layout, a single PDF of a version.
- AI in the editor (when the instance has ai): rewrite, shorten, fix grammar, translate into another
space language (into the translation of the page, as a suggestion), continue writing, summarize a page
into its description. Runs with a team key chosen in settings.
API
GET|POST /api/v1/spaces · GET|PATCH|DELETE /spaces/{id}
GET|POST /api/v1/spaces/{id}/pages · GET|PATCH|DELETE /pages/{id} · POST /pages/{id}/move
GET /api/v1/pages/{id}/content?lang=&format=markdown|html · PUT (Markdown in, for scripts)
WS /api/v1/collab/{page}/{lang} Yjs sync + awareness
GET /api/v1/pages/{id}/history · POST /pages/{id}/restore
GET|POST /api/v1/pages/{id}/comments · PATCH /comments/{id} (resolve, edit) · DELETE
POST /api/v1/spaces/{id}/publish · GET /spaces/{id}/publications · POST /publications/{id}/live
POST /api/v1/spaces/{id}/versions · DELETE /versions/{id}
GET|POST /api/v1/spaces/{id}/shares · DELETE /shares/{id} page and space links
GET|POST /api/v1/spaces/{id}/guests · DELETE /guests/{id}
POST /api/v1/spaces/{id}/import · GET /spaces/{id}/export?format=zip|pdf&version=
POST /api/v1/spaces/{id}/assets (uploads) · GET /assets/{id}
POST /api/v1/git/{space}/hook repository webhook
POST /api/v1/spaces/{id}/ask AI answers from the publication
GET /api/v1/templates · POST /templates
Reading endpoints of a site (/api/v1/site/... on the space's host: nav, pages, search) follow v1's shape
and its access rules. MCP at /mcp for editors: list spaces and pages, read, create and update pages from
Markdown, publish; on a public site, search_docs and read_doc as in v1.
CLI
xvii docs spaces spaces of your teams
xvii docs create NAME [--template T] a new space
xvii docs publish [DIR] --space S import a Markdown folder and publish (CI-friendly, trust rules)
xvii docs pull --space S [DIR] export the draft as Markdown
xvii docs open [SPACE] the space's site or editor
xvii docs [QUERY…] search the built-in documentation (v1)
Data and limits
PostgreSQL: spaces, pages, Yjs update logs and snapshots, page snapshots (history), publications (Markdown
and HTML per page, compressed), versions, comments, shares, guests, feedback, templates, git sources. Assets
on disk under DATA_DIR/assets/<space>/ (sha256-addressed). Limits: docs.spaces, docs.pages,
docs.storage (assets and publications), docs.guests. Events: space.published, comment.mention,
comment.created, git.sync_failed, share.created. The manifest gains database, panel and
wildcard.
Build order (v2.0.0 ships when all are done)
- Spaces, pages, access, the panel shell, the site engine serving publications by host; v1's built-in
space moved onto it.
- The editor: blocks ↔ Markdown, Tiptap + Yjs, the Go collaboration server, presence, history.
- Publishing, publications, versions, languages.
- Hosts and custom domains, sharing links and passwords, guests, look per space, analytics and feedback.
- Comments and mentions, templates, import and export, OpenAPI references, AI (ask and assist), git
source and
xvii docs publish.