chronicle.yaml
All site configuration lives in a single chronicle.yaml file in your project root. The config is validated using Zod — invalid fields produce clear errors at startup.
Only site.title and content are required. Everything else has a default or
is optional.
For how the files on disk map to URLs, see Project structure.
Full example
site:
title: My Project Docs
description: Documentation for My Project
url: https://docs.example.com
preset: vercel
logo:
light: ./logo-light.png
dark: ./logo-dark.png
theme:
name: default
content:
- dir: docs
label: Docs
- dir: dev
label: Dev Docs
latest:
label: "3.0"
landing: true
versions:
- dir: v2
label: "2.0"
content:
- dir: docs
label: Docs
- dir: v1
label: "1.0"
landing: true
badge:
label: deprecated
variant: warning
content:
- dir: dev
label: Developer Guide
- dir: docs
label: Docs
api:
- name: REST API (v1)
spec: ./v1-openapi.yaml
basePath: /apis
server:
url: https://api.example.com/v1
navigation:
links:
- label: GitHub
href: https://github.com/myorg/myproject
links:
- label: Support
href: https://support.example.com
search:
enabled: true
placeholder: Search docs...
api:
- name: REST API
spec: ./openapi.yaml
basePath: /apis
server:
url: https://api.example.com
description: Production Server
auth:
type: apiKey
header: Authorization
placeholder: "Bearer token"
redirects:
- from: /old-page
to: /docs/new-page
- from: /legacy/api
to: /apis
permanent: true
analytics:
enabled: true
googleAnalytics:
measurementId: G-XXXXXXXXXX
telemetry:
enabled: true
serviceName: my-docs
port: 9090Reference
site
Required. Site-level metadata.
site:
title: My Documentation
description: Documentation powered by Chronicle| Field | Type | Description |
|---|---|---|
title | string | Site title (navbar, browser tab, canonical metadata). Required. |
description | string | Meta description for SEO and OG. |
url
Optional site URL used for the sitemap and canonical URLs.
url: https://docs.example.comcontent
Required. Content dirs for the latest version. Each entry maps to content/<dir>/ on disk and to /<dir>/... in URLs.
content:
- dir: docs
label: Docs
- dir: dev
label: Dev Docs| Field | Type | Description |
|---|---|---|
dir | string | Folder name under content/. Must be unique. |
label | string | Display label in navigation and landing pages. |
description | string | Optional description shown on landing page cards. |
icon | string | Optional icon identifier for landing page cards. |
latest
Optional metadata for the latest version. Required when versions: is declared.
latest:
label: "3.0"
landing: true| Field | Type | Description | Default |
|---|---|---|---|
label | string | Version label (e.g. 3.0). Shown in the version switcher, or as a static label in the sidebar footer when no versions: are declared. | — |
landing | boolean | true → / renders a landing page listing content dirs. false (default) → / 302s to the first content dir. | false |
versions
Optional list of older versions. Each entry lives under versions/<dir>/ on disk and is reachable at /<dir>/... in URLs.
versions:
- dir: v1
label: "1.0"
landing: true
badge:
label: deprecated
variant: warning
content:
- dir: dev
label: Developer Guide
- dir: docs
label: Docs
api:
- name: REST API (v1)
spec: ./v1-openapi.yaml
basePath: /apis
server:
url: https://api.example.com/v1| Field | Type | Description |
|---|---|---|
dir | string | Folder name under versions/. Doubles as URL prefix. Must be unique. |
label | string | Version label. Shown in the switcher. |
landing | boolean | true → /<dir> renders a landing page; otherwise 302s to the version's first content dir. Default false. |
badge | object | Optional Apsara badge next to the version label. |
badge.label | string | Badge text. |
badge.variant | "accent" | "warning" | "danger" | "success" | "neutral" | "gradient" | Badge colour. Default accent. |
content | {dir, label}[] | Content dirs for this version. Entries may rename, reorder, or omit top-level content dirs. |
api | ApiConfig[] | Version-scoped API specs, rendered at /<dir>/apis/.... Same shape as top-level api:. |
See Versioned documentation for how to cut a new version.
preset
Deploy target. Decides what chronicle build produces and where it writes.
Overridden by --preset.
preset: vercel| Value | Output |
|---|---|
| — (unset) | A Node server in .output/ |
node-server | A Node server in .output/ |
vercel | A Vercel function build in .vercel/output/ |
cloudflare | A Cloudflare Workers build in .output/ |
static | Pre-rendered HTML in .output/public/ |
vercel-static | Pre-rendered HTML in .vercel/output/ |
cloudflare-pages | Pre-rendered HTML in .output/public/ |
github-pages | Pre-rendered HTML in .output/public/ |
The four static presets pre-render every page at build time and need no server to run. See Deploy.
logo
The mark shown in the sidebar, used as the favicon, and drawn on social cards.
logo:
light: /logo.svg
dark: /logo-dark.svg| Field | Type | Description |
|---|---|---|
light | string | Path or URL used on a light background |
dark | string | Path or URL used on a dark background |
Files live in public/, so public/logo.svg is written as /logo.svg. Setting
one is enough — the other falls back to it, which is what you want for a mark
that reads on either background.
Set neither and every theme shows the site's first letter in a tinted box. It is deliberately plain, so a site without a logo looks unfinished rather than borrowed.
The same image becomes the favicon, replacing public/favicon.ico and
public/favicon.svg. Those two are still used when no logo is set, so an
explicit favicon keeps working.
theme
Theme selection and customization.
theme:
name: default| Field | Type | Description | Default |
|---|---|---|---|
name | 'default' | 'paper' | 'fanfold' | Theme to use | default |
colors | Record<string, string> | Colour overrides, keyed by design token | — |
theme:
name: default
colors:
background-base-primary: "#fbfaf8"
foreground-accent-primary: "#0a3793"Each key names an Apsara colour token, and the --rs-color- prefix is added for
you. Write the whole custom property yourself — --paper-ink — to reach one
outside that family.
A value applies in both light and dark, since the config holds one value per token. Leave a token alone if the two themes should differ.
See Choosing a theme for which one to pick and Theme options for what each gives you.
navigation
Top navbar links and social icons.
navigation:
links:
- label: GitHub
href: https://github.com/myorg/myproject
social:
- type: github
href: https://github.com/myorg/myprojectnavigation.links
| Field | Type | Description |
|---|---|---|
label | string | Link text |
href | string | URL (internal or external) |
navigation.social
| Field | Type | Description |
|---|---|---|
type | string | Icon type: github, twitter, discord, or custom |
href | string | URL |
links
Links shown in the sidebar footer, behind a ? menu button next to the version switcher. Available in the default, paper and fanfold themes.
links:
- label: Go to app
href: https://app.example.com
- label: Support
href: https://support.example.com| Field | Type | Description |
|---|---|---|
label | string | Menu item text |
href | string | URL. Absolute URLs open in a new tab; relative paths navigate in-app |
External links are tagged with UTM query params so the destination can attribute the visit, using the standard names every analytics tool recognises:
| Param | Value |
|---|---|
utm_source | Hostname of the docs site the click came from |
utm_medium | site.title, slugified |
utm_content | Path of the page the link was clicked from |
https://support.example.com?utm_source=docs.example.com&utm_medium=my-documentation&utm_content=%2Fdocs%2FguideExternal links open with noopener (not noreferrer), so the destination also receives a Referer header — but under the browser default referrer policy that header carries only the origin, so utm_content is what identifies the specific page.
Any query string already on href is preserved, and a utm_* param you set yourself is never overwritten. Relative paths are not tagged — they stay on the same origin, so the params would only register as a self-referral. Non-web schemes (mailto:, slack:) are left untouched.
search
Full-text search over titles, headings and page bodies. Scoped to the active version.
search:
enabled: true
placeholder: Search documentation...| Field | Type | Description | Default |
|---|---|---|---|
enabled | boolean | Enable/disable search | true |
placeholder | string | Search input placeholder | Search... |
When enabled, search is accessible via the navbar button or keyboard shortcut Cmd+K / Ctrl+K. Active version comes from the URL; switching versions scopes the index.
See Search for what gets indexed.
api
OpenAPI specification configuration at the top level applies to the latest version (served at /apis/...). Version-scoped specs live under each versions[].api.
api:
- name: Petstore API
spec: ./petstore.yaml
basePath: /apis
server:
url: https://petstore.swagger.io/v2
description: Production Server
auth:
type: apiKey
header: api_key
placeholder: Enter your API key| Field | Type | Description |
|---|---|---|
name | string | API display name |
spec | string | Path to OpenAPI spec file (JSON or YAML), relative to chronicle.yaml |
basePath | string | URL path prefix for API pages (e.g., /apis) |
icon | string | Optional icon identifier |
server.url | string | Base URL for the API server |
server.description | string | Server description |
auth.type | string | Authentication type (e.g., apiKey, bearer) |
auth.header | string | Header name for auth token |
auth.placeholder | string | Placeholder text in auth input |
See API reference for what readers get from a spec.
redirects
URL redirects for migrating old routes to new ones. Checked before all other routes.
redirects:
- from: /old-page
to: /docs/getting-started
- from: /legacy/api-docs
to: /apis
permanent: true| Field | Type | Description | Default |
|---|---|---|---|
from | string | Old URL path to redirect from | — |
to | string | New URL path to redirect to | — |
permanent | boolean | true for 308 (permanent), false for 307 (temporary) | false |
Use permanent: true when the old URL should never be used again — search engines and browsers will cache the redirect.
See Links and redirects for when to use each status.
analytics
Analytics integration for tracking page views.
analytics:
enabled: true
googleAnalytics:
measurementId: G-XXXXXXXXXX| Field | Type | Description | Default |
|---|---|---|---|
enabled | boolean | Enable/disable analytics | false |
googleAnalytics.measurementId | string | Google Analytics measurement ID | — |
See Monitoring for what else a running site reports.
authors
Optional registry of the people who write your docs. Pages reference an entry by
its key in their authors frontmatter, so the details live in one place.
authors:
jane:
name: Jane Doe
bio: Writes about distributed systems.
avatar: /team/jane.png
url: https://github.com/jane
email: jane@example.com| Field | Type | Description | Default |
|---|---|---|---|
name | string | Required. Display name | — |
bio | string | Short description shown on the author's page | — |
avatar | string | Image path for the avatar; initials are used without it | — |
url | string | Profile link, shown on the author's page | — |
email | string | Contact address, shown on the author's page with mailto: | — |
# page.mdx frontmatter
authors: [jane]A frontmatter string that matches no key is still valid — it renders as a plain
name, so occasional contributors need no registry entry. See
Frontmatter for the field itself, and browse the people
writing a site at /authors.
See Authors and bylines for how bylines and author pages work.
telemetry
Prometheus metrics export via OpenTelemetry. Served on a separate port.
telemetry:
enabled: true
serviceName: my-docs
port: 9090| Field | Type | Description | Default |
|---|---|---|---|
enabled | boolean | Enable/disable telemetry | false |
serviceName | string | OpenTelemetry service name | chronicle |
port | number | Port for Prometheus metrics endpoint | 9090 |
Metrics are available at http://localhost:<port>/metrics in Prometheus exposition format.
See Monitoring for the health and readiness endpoints.
Defaults
When chronicle.yaml is missing or fields are omitted, these defaults apply:
site:
title: Documentation
content:
- dir: docs
label: Docs
theme:
name: default
search:
enabled: true
placeholder: Search...