Every key of the two config schemas, end to end: .docuriser-site and .docuriser-project.
Everything Docuriser builds is described by a small family of JSON files: a .docuriser-site at a website's root, a .docuriser-project in each project directory the site registers, and a .docuriser manifest at the root of each project's source repository. Builds also write one derived file, the .docuriser-built provenance beside each built version. The GUI's configurators edit the first three; this page documents every key end-to-end, including the ones the panels don't surface (the configurators' Text view covers those).
The format tag. Every Docuriser-owned file carries "format": 1 as its first key. Readers accept files at or below their own format (a file with no format reads as pre-1.0 legacy) and refuse newer files with a message naming the Docuriser upgrade as the remedy, so an old install can never silently misread configs written by a newer one. Save paths stamp the tag automatically, including onto legacy files on their first edit.
Path rules. Relative paths in a site config resolve against the directory containing .docuriser-site; a leading / also reads from that directory (and, for values that end up as links in the emitted pages, from the built site's root). Build-shaped paths (README, topic sources, include directories) live in the manifest and resolve against the repository root that contains it: your checkout for working-tree reads, the tag's snapshot at build time. Keys documented as URL are addresses in the built site (e.g. /images/logo.svg, which is the file images/logo.svg beside the config), not source-tree paths.
.docuriser-site| Key | Type | Meaning |
|---|---|---|
title |
string | Site title; the home page <title> unless pages.home_title overrides it. |
description |
string | Baseline meta description for every page (also og:description). |
url |
string | Canonical site URL, available to templates. |
copyright |
string | Footer copyright line. |
output |
string | Required to build. Where the site emits, resolved against the site directory (e.g. "./build/site"). |
vortex.default_theme |
string | Default theme display id for the emitted pages' theme picker. Defaults to "Dark (huntrsoftwarellc)". |
branding| Key | Type | Meaning |
|---|---|---|
logo_path |
URL | Brand tab logo image. When empty, the tab shows tab_text instead. |
tab_text |
string | Text label for the brand tab (used when there is no logo). |
logo_height |
string | Brand logo height in pixels ("25px" and "25" both parse). Default 25. |
logo_height_mobile |
string | Edited and previewed by the GUI's tab preview; the emitted site currently consumes only logo_height. |
favicon_from_logo |
bool | Generate the favicon set from logo_path, re-derived whenever the logo changes (the panel's Generate from logo). |
favicon_source |
URL | Explicitly picked favicon source image (recorded by the panel's Browse; absent in from-logo mode). |
favicon_background |
string | #rrggbb fill behind the home-screen icon sizes (apple-touch and Android). Unset: the default theme's primary surface color. |
The generated set lives in the site's favicon/ directory and overlays the built site's root (/favicon.ico, /favicon-16x16.png, /favicon-32x32.png, /apple-touch-icon.png, /android-chrome-192x192.png, /android-chrome-512x512.png); a hand-made set dropped there under the same names ships just as well, and a site with no favicon/ ships Docuriser's defaults. site.webmanifest is derived at build time (name and short name from title and branding.tab_text, colors from the default theme's primary), and a favicon/site.webmanifest overrides the derived one.
projects[]Each entry registers one project:
{ "config": "./projects/layers/.docuriser-project" }
The path resolves against the site directory. Any extra keys on the entry override the referenced project config's keys for this site, a per-site inline override. Ordering matters: family members are conventionally listed contiguously after their family root (the GUI keeps them that way). See the projects guide.
socials[]Rendered as cards on the socials page, and a non-empty array adds the Socials entry to the site's navigation.
| Key | Type | Meaning |
|---|---|---|
label |
string | Card title. |
username |
string | Handle shown on the card (optional). |
url |
string | Link target. |
icon |
URL | Card icon (optional). |
external |
bool | Marks the link external (adds the external-link glyph). |
policies[]Each policy renders to /<slug>.html from a Markdown source and gets a titlebar tab on its page.
| Key | Type | Meaning |
|---|---|---|
slug |
string | Required: the output page name. |
file |
string | Required: Markdown source path under the site directory. |
title |
string | Page <title> (falls back to the slug). |
name |
string | Tab label (falls back to title). |
icon |
URL | Tab icon (defaults to the policies glyph). |
A policy is usually paired with a footer_links entry pointing at /<slug>.html; the GUI's Add/Remove Policy flows keep the two in sync.
footer_links[]{ "label": ..., "href": ... } pairs rendered in every page's footer.
homeThe home page is composed from an ordered list of sections (home.sections), rendered top to bottom in the order given. The GUI's Website → Home Page editor manages this list (add, remove, reorder, edit), and you can also author it directly.
"home": {
"sections": [
{ "type": "description" },
{ "type": "projects" },
{ "type": "socials" },
{
"type": "custom",
"id": "sponsor",
"label": "Sponsor",
"subtext": "Sponsor CTA with animated hearts",
"icon": "/images/heart.svg",
"body": "/content-fragments/sponsor_home.html",
"head": "/content-fragments/sponsor_home_head.html"
}
]
}
Each entry has a type:
type |
Renders |
|---|---|
description |
The site description value as the home's lead paragraph. This is the same field that drives the meta description, so the visible tagline and the SEO snippet share one source. |
projects |
The project cards (shown only when projects[] is non-empty). |
socials |
The social cards (shown only when socials[] is non-empty). |
custom |
An author-supplied HTML fragment. |
The three preset types carry only type and appear at most once. A custom section adds:
| Key | Type | Meaning |
|---|---|---|
id |
string | Stable identifier (also names the fragment the GUI seeds). |
body |
path | Required. The section's HTML fragment (a full <section>…</section>) under the site directory. This is the rendered section. |
head |
path | Optional. A <head> fragment (scripts or styles the section needs), folded into the page <head>. |
label / subtext / icon |
string / string / URL | GUI-only: the section's card title, subtitle, and avatar in the Home Page editor. They have no effect on the built site. |
The home layout and the navigation bar are independent: removing the projects or socials section from home.sections does not remove its nav dropdown. Navigation is derived from projects[]/socials[] presence (see nav_menus below).
slotsA map of slot name to HTML fragment path under the site directory. Fragments are pre-rendered and spliced into the built-in pages. The home page's body is composed from the home block (above), not slots. The built-in slot is the optional home_head (extra home-page <head> content, though a custom section's own head is the usual way to add scripts a section needs). Custom names are allowed and become available to page templates.
content_pages[]Free-form extra pages, each emitted at /<slug>.html. A page with a sidebar lays it out like a project page's: a sticky column beside the body on wide screens, stacked above it on narrow ones. Style it through #uv-content-sidebar.
| Key | Type | Meaning |
|---|---|---|
slug |
string | Required: the output page name. |
title |
string | Page <title>. |
body |
path | HTML body fragment under the site directory. |
sidebar |
path | Optional sidebar fragment. |
extra_head |
path | Extra <head> fragment. |
description |
string | Per-page meta description (overrides the site's). |
tab |
object | {name, icon, close_href?}: the page's titlebar tab; close_href defaults to /. |
images/A site's images/ directory is first-class: everything in it is copied to the built site's /images, with nothing to declare. Drop logo.svg in images/ and any config URL can address it as /images/logo.svg (the web path and the path on disk are the same). The GUI's image pickers (site logo, project logo, social/dependency icons) all copy the file you choose into this directory and write the matching URL.
Docuriser's own interface images (the sidebar and nav glyphs) share the /images namespace, so a site can replace one by name: an images/socials.svg overrides the built-in.
css/ and scripts/A site's css/ and scripts/ directories are first-class too: they're copied to the built site's /css and /scripts, with nothing to declare. Each .css and .js file gets the build stamp (the build's date) in its name, the same as Docuriser's own, so a browser fetches the new copy after a later day's build. Reference them from a fragment with the stamp: css/extra.css publishes as /css/extra_{{ build.stamp }}.css. A site file with the same name as one of Docuriser's replaces it.
downloads/A site's downloads/ directory holds the files for the download buttons. The folder needs no static_dirs entry, but a file in it ships only when a row in a project config's downloads[] links to it (see downloads[] below). A row's /downloads/... url points to the file at the same path in the folder: /downloads/app/app-3.0.1-linux-x86_64.AppImage is the file downloads/app/app-3.0.1-linux-x86_64.AppImage beside the site config.
Unlike images/, the folder isn't copied whole. The build copies only the files that the site links to: the downloads that each project serves, and each version's downloads on the project's versions page. So downloads/ can hold a full release archive, old patches included, and the built site still ships only the files it links to. In CI, mount or symlink the archive as downloads/; read-only is fine.
Docuriser owns <output>/downloads/, the same way it owns each project's docs version folders. It marks the folder with a .docuriser-generated file, then clears and refills it on every build, so a file that the site no longer links to drops out of the output. An output downloads/ without that marker file is left alone: the build logs an error and stages no downloads. Delete that folder once, and the next build takes it over.
A static_dirs entry whose dest is exactly downloads, like { "src": "releases", "dest": "downloads" }, names the folder that the files come from, in place of downloads/. Only the files that the site links to are copied from it, and the build logs an Info line suggesting the files move to downloads/ and the entry be removed.
static_files[] and static_dirs[]For verbatim content that isn't images or downloads. static_files lists individual files (paths under the site directory) copied into the output preserving their relative path. static_dirs lists whole-tree overlays, { "src": "framework/api", "dest": "api" } (dest defaults to src): this is how a backend, or any other tree, reaches the built site. Overlays may target /images too; they merge with the site's images/ rather than replacing it. An overlay into /downloads is skipped with an error, except for the entry with dest downloads that names the downloads folder (above).
pagespages.home_title overrides the home page <title>.
nav_menus: derived, don't author itThe site's navigation is derived at build time from what the config declares (a Projects menu when projects[] is non-empty, a Socials menu when socials[] is non-empty). Anything written under nav_menus is replaced.
.docuriser-project| Key | Type | Meaning |
|---|---|---|
slug |
string | Required. The project's id and URL segment: lowercase letters and digits in dash-separated runs. |
name |
string | Display name. |
tagline |
string | Short descriptor; the project page's meta description, and the subtext on family cards and the member switcher. |
logo |
URL | Project logo. |
parent_slug |
string | Names the family root this project belongs to. Absent means standalone. See families. |
family_label |
string | This member's label in the family switcher and grid (falls back to name). |
family_index |
bool | On a root that has members: false opts out of the umbrella index page, making the root an ordinary switchable member again. Default true. |
Which versions publish, and which one fronts the project, is not part of its identity: both are derived at site build from the built output on disk (see versions_excluded[] below). The retired versions[], latest_version, and default_version keys are ignored, with a line in the advanced log.
buildOne key. Everything else build-shaped lives in the source repository's own manifest (next section), because it describes the source tree and should be versioned with it.
| Key | Type | Meaning |
|---|---|---|
source_path |
string | Root of the project's local source checkout, relative to the config's directory (absolute paths work too). This is where tags are read and where the manifest is found. Machine-local, so it stays on the site side. |
facts[]The fact rows in the project page's sidebar.
| Key | Type | Meaning |
|---|---|---|
label |
string | Row label. |
value |
string | Row value. |
href |
string | Optional: makes the value a link. |
note |
string | Optional: rendered in parentheses after the value. |
preset |
string | Optional: makes the row derived instead of authored (below). |
The License fact is special by convention: version builds use its value as the license name on each version's license page ("licensed under MIT"). The license text comes from the manifest's build.license; see versions.
Preset facts. A fact carrying "preset": "stable_release" renders the project's derived latest version (the versions guide): its value is the built tag (e.g. 2.1.3), its note is that version's release date, and it never links. Any authored value/href/note on such a row is overridden, and the row is skipped entirely when the project publishes no version yet. The entry itself is the opt-in, so a typical row is just:
{ "label": "Latest", "preset": "stable_release" }
repoThe repository card in the sidebar: platform (e.g. "Codeberg"), path (e.g. "HuntrSoftware/docuriser"), and url. The card's icon is Docuriser's stock repository glyph, nothing to configure.
requires[]Dependency cards: name, url (site-relative like /vortex.html for internal projects, absolute for external ones), optional icon (URL), and external (bool, adds the external-link glyph).
downloads[]Download rows for the project page sidebar. The config only names URLs; the files themselves live in the site's downloads/ folder (above).
| Key | Type | Meaning |
|---|---|---|
url |
string | Site-relative path to the file. It may contain {version}, which stands for a full X.Y.Z version, so one row covers every release: /downloads/nodeyou/nodeyou-{version}-linux-x86_64.tar.gz. A url without {version} names one fixed file. |
platform_label |
string | Platform name shown on the button (e.g. "Linux"). |
platform_icon |
URL | Optional platform glyph. |
platform_key |
string | Optional os-arch key (e.g. linux-x86_64, windows-x86_64). A row that carries one becomes a per-platform entry of the desktop update manifest (below). |
size |
string | Optional hand-typed display size. Whenever the file is staged, the generator replaces this with the file's real size, so the button never drifts from the binary and you rarely need to set it. |
Rows with {version}. Docuriser fills in {version} itself. It serves the newest X.Y.Z version that has a file for every {version} row, looking only at versions at or below the project's latest tag, within a published minor. For example, with these files in downloads/nodeyou/:
nodeyou-2.0.1-linux-x86_64.tar.gz
nodeyou-2.0.1-windows-x86_64-setup.exe
nodeyou-2.0.2-linux-x86_64.tar.gz
and the latest tag 2.0.2, the site serves 2.0.1, because 2.0.2 has no Windows file yet. The build logs one Info line that names what the newer version lacks:
[Site] Project 'nodeyou': serving downloads for 2.0.1; 2.0.2 missing nodeyou-2.0.2-windows-x86_64-setup.exe
Once the Windows file is in place, the next build serves 2.0.2. The same fallback covers a patch tag that only changed docs (it ships no binaries) and a CI build that runs before every platform's file has landed. When no version has a full set, the {version} rows show no buttons, no latest.json is written, and the build logs an error that names the newest version's missing files.
The download button's title names the served version: "Download node.you 2.0.1". While a platform lags, that's older than the docs version, which is exactly when a reader needs to see it.
Rows without {version} link to one fixed file. They take no part in picking the served version and don't appear on the versions page. When every row is fixed, the button's title names the latest minor ("Download node.you 2.0").
A /downloads/... row whose file isn't there is dropped and the build logs an error, so the site never shows a button that leads to a 404. An absolute (off-site) url is used as typed, with the row's hand-typed size.
The desktop update manifest. A project that serves at least one platform_key download also emits /downloads/<dir>/latest.json, in the folder its platform binaries were staged into. Installed desktop apps (Vortex's update channel) poll that file to learn a newer build exists. Its schema:
{
"version": "2.0.1",
"pub_date": "2026-07-20T00:00:00Z",
"notes_url": "https://huntrsoftware.com/nodeyou/docs/2.0/changelog.html",
"download_page": "https://huntrsoftware.com/nodeyou.html",
"platforms": {
"linux-x86_64": { "url": "https://huntrsoftware.com/downloads/nodeyou/nodeyou-2.0.1-linux-x86_64.tar.gz", "size": 31457280, "sha256": "..." },
"windows-x86_64": { "url": "https://huntrsoftware.com/downloads/nodeyou/nodeyou-2.0.1-windows-x86_64-setup.exe", "size": 29360128, "sha256": "..." }
}
}
The manifest is derived, never hand-authored: each platform entry's url is absolutized against the site url, and its size (bytes) and sha256 are computed from the staged file. For a project with {version} rows, version is the served version, so the manifest never advertises a version that some platform can't download yet. When every row is fixed, it's the derived latest version's full built tag (from its .docuriser-built provenance). Either way, patch releases reach the update channel too. The remaining top-level fields come from these project keys:
| Key | Type | Meaning |
|---|---|---|
download_page |
URL | Optional. Defaults to the project's own landing page. |
notes_url |
URL | Optional release-notes/changelog link (site-relative is made absolute). |
pub_date |
string | Optional RFC 3339 timestamp, passed through verbatim. |
Because the sidebar buttons and the manifest both come from the same served files, adding a release's files and rebuilding the site updates the project page and every installed app's update check together, and the two can never disagree.
versions_excluded[]An array of minor labels (["1.0", "0.17", "0.16"]) recording the versions you unchecked: tags that exist but should not publish. Every tag not listed here publishes, so this is where old tags go. The Versions table maintains it; you rarely touch it by hand.
A version's build status (Excluded, Pending, Complete, Preview) is never stored anywhere: it is derived from the repository's tags, this array, and what versions/<label>/ on disk was built from. See the versions guide.
Published versions and the latest are derived. At site build, every versions/<label>/ directory with a .docuriser-built file publishes unless this array lists it. The newest (semver order on labels) fronts the project: its README renders on the landing page, the docs tab defaults to it, and the stable_release preset fact shows it. Its built tag is also the newest version that the downloads can serve (see downloads[]). Building a newer version is all it takes to front it; there is nothing to author.
versions_supported[]An array of minor labels (["2.1"]) naming older versions you still support. At site build, every published version gets one of three support statuses:
With no list, every version but the latest is archived, which suits an app that only supports its newest release. A framework that maintains more than one line adds a label when it announces support for that line, and removes it at the line's end of life. A label that isn't published is ignored. The Versions table's Supported column maintains it.
An archived version stays published, with a banner on each of its doc pages; versions_excluded[] is what keeps a version off the site. See the versions guide for what each status changes.
tabs[] and author fragmentstabs[] adds extra sidebar tabs ({slug, label, href, icon?, subtext?}); a docs tab is always regenerated from the built versions, so don't author one. A project may also name HTML fragments (paths relative to its config directory): body (replaces the default README-rendering body), extra_head, sidebar_top, sidebar_bottom, plus its own static_files[]/static_dirs[], copied under the project's output path.
.docuriser (the in-repo manifest)The build recipe lives in the project's repository, at its root, versioned with the code. That is what lets every tag describe itself: when Docuriser builds version 2.1 from tag 2.1.3, it reads the manifest as it was at that tag, so renamed guides, moved READMEs, and changed include directories never break old versions. A tag whose snapshot has no manifest cannot build (see the versions guide).
{
"format": 1,
"build": {
"includes": ["include/Layers/"],
"external": ["include/"],
"defines": ["LAYERS_EXPORT="],
"readme": "README.md",
"license": "LICENSE",
"topics": [
{ "slug": "build", "label": "Build", "title": "Build Layers",
"intro": "Follow these steps to build this version.",
"source": "guides/build.md" }
]
}
}
All paths are relative to the repository root. The GUI edits the manifest through the README, License, and Topics panels (resolving it through the project's build.source_path), and creates it on the first such edit if the repository doesn't have one yet.
| Key | Type | Meaning |
|---|---|---|
build.includes[] |
string[] | Include directories whose headers are parsed (libclang) into the API reference: a page per class and struct, and a page per header for its functions, aliases, and enums. See the API reference guide. |
build.external[] |
string[] | Additional include directories passed to the parser but not documented. |
build.defines[] |
string[] | Preprocessor macros (NAME=value) for the parse. |
build.readme |
string | The project README's path; it renders on the project landing page. |
build.license |
string | The project's license file (usually just LICENSE; it need not be Markdown). Each version build copies it into the bundle as versions/<label>/LICENSE.md, which is what gives the version a license page, so every version keeps the license it shipped under. |
build.topics[] |
object[] | The project's Markdown guide pages. See the topics guide. |
There is no output setting: a version build always emits to versions/<label>/ beside the project config.
Topic entries:
| Key | Type | Meaning |
|---|---|---|
slug |
string | Required: the topic's page name and sidebar href. Entries without one are skipped. |
source |
string | Required: the Markdown source path (relative to the repository root). |
label |
string | Sidebar label. |
title |
string | Page <title> lead (falls back to label). |
intro |
string | Optional paragraph rendered above the topic. |
.docuriser-built (provenance, derived)Written into versions/<label>/ by every version build; never hand-authored and never published to the site. A tag build records the tag, its commit, and the tag's date:
{ "format": 1, "tag": "2.1.3", "commit": "<sha>", "built": "2026-07-15",
"release_date": "July 15, 2026" }
A --working-tree build records a preview instead:
{ "format": 1, "preview": true, "built": "2026-07-15", "release_date": "Unreleased" }
The Versions table reads it to know a version is Complete (the recorded tag matches the minor's latest patch tag) or Pending again (a newer patch tag has landed, or the output is a preview), and the site build publishes only directories that have one, showing its release_date. Output without a readable .docuriser-built doesn't publish; building the version writes one.
The site Docuriser emits belongs to its author: your content, your license; Docuriser claims nothing over generated sites. The only Docuriser-owned bytes in a build are the runtime assets it copies in (the stamped uv_* CSS/JS and framework images), which remain MIT-licensed wherever they land.