Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

plinth.toml

Plinth reads configuration from plinth.toml (or the path in PLINTH_CONFIG). All fields have defaults — an empty file produces a working configuration.

[site]

KeyTypeDefaultDescription
namestring"Plinth"Site name in header and page titles
taglinestring"Welcome to my website"Short tagline on the home page
descriptionstring"A personal website"Default meta description
langstring"en"HTML lang attribute
default_themestring"dark"Default colour theme ("dark" or "light")
animated_backgroundstring"flow-field"Home page background preset

Accepted animated_background values:

  • "none" disables the animated canvas and uses a static page background.
  • "flow-field" renders an Odysseus-style procedural particle flow field.
  • "constellation" renders drifting nodes with proximity lines.
  • "aurora-ribbons" renders translucent wave ribbons.
  • "orbital-trails" renders orbiting particles with fading trails.
  • "digital-rain" renders sparse terminal-style falling glyphs.
  • "topographic-waves" renders animated contour lines.

[site.author]

KeyTypeDefaultDescription
namestring"Admin"Default author name for articles
emailstring""Email shown in footer (empty = hidden)

[site.social]

KeyTypeDefaultDescription
githubstring""GitHub profile URL (empty = hidden)
gitlabstring""GitLab profile URL
codebergstring""Codeberg profile URL
mastodonstring""Mastodon profile URL
blueskystring""Bluesky profile URL
KeyTypeDefaultDescription
project_namestring"Plinth"Project name in footer attribution
project_urlstring"https://codeberg.org/caniko/plinth"Project URL in footer

Navigation items (order matters). Each entry has:

KeyTypeDescription
labelstringLink text
pathstringURL path

Default navigation:

[[site.nav]]
label = "Posts"
path = "/posts"

[[site.nav]]
label = "Projects"
path = "/projects"

[[site.nav]]
label = "About"
path = "/about"

[pages.home]

KeyTypeDefaultDescription
titlestring""Home page title (empty = use site name)
descriptionstring""Home page meta description

[pages.blog]

KeyTypeDefaultDescription
titlestring"Posts"Blog listing page title
subtitlestring""Subtitle below the title
descriptionstring""Meta description

[pages.portfolio]

KeyTypeDefaultDescription
titlestring"Projects"Portfolio page title
subtitlestring""Subtitle below the title
descriptionstring""Meta description

[pages.about]

KeyTypeDefaultDescription
titlestring"About Me"About page title
descriptionstring""Meta description

[server]

KeyTypeDefaultDescription
hoststring"127.0.0.1"Bind address
portu163000Bind port

[database]

KeyTypeDefaultDescription
database_urlstring"postgres://plinth:plinth@localhost:5432/plinth"Postgres connection URL

[observability]

KeyTypeDefaultDescription
service_namestring"plinth"OTLP service name
log_levelstring"info"Rust log level (RUST_LOG format)
otlp_endpointstring""OTLP endpoint URL (empty = disabled)
otlp_headersstring""OTLP auth headers (comma-separated key=value)
KeyTypeDefaultDescription
default_limitusize10Default search result count
related_limitusize5Default related articles count
min_similarityf320.5Minimum cosine similarity for opinion tracking

[content]

KeyTypeDefaultDescription
words_per_minuteusize200WPM for reading time calculation
vector_truncationusize5000Max characters before generating embeddings

[feeds]

KeyTypeDefaultDescription
blog_limitusize50Max entries in /feeds/blog.xml
projects_limitusize50Max entries in /feeds/projects.xml
activity_limitusize50Max entries in /feeds/activity.xml

[ranking]

Controls how activity entries are scored for the ranked /activity page, the home strip, and the public API. Score is computed at read time, so changes take effect on the next request.

KeyTypeDefaultDescription
strategystring"exponential""exponential", "linear", or "pure"
half_life_daysfloat365.0Exponential ranking: impact * 0.5 ^ (age_days / half_life_days)
window_daysfloat730.0Linear ranking: impact * max(0, 1 - age_days / window_days)

age uses the reference date coalesce(merged_at, closed_at, created_at). pure uses impact alone, with the most recent reference date as a tiebreaker. Results are ordered by score descending, then reference date descending.

[forge]

Controls how the server fetches and refreshes activity metadata from code forges. Tokens are provided via environment variables only, never in this file. See Environment Variables.

KeyTypeDefaultDescription
refresh_ttl_secsinteger3600Refresh an entry in the background when its fetched_at is older than this many seconds
refresh_backoff_secsinteger900Minimum seconds between refresh attempts for an entry that errored
github_base_urlstring"https://api.github.com"GitHub API base URL, overrideable for GitHub Enterprise or testing
codeberg_base_urlstring"https://codeberg.org/api/v1"Codeberg/Forgejo API base URL, overrideable for self-hosted Forgejo or testing
[ranking]
strategy = "exponential"
half_life_days = 365.0
window_days = 730.0

[forge]
refresh_ttl_secs = 3600
refresh_backoff_secs = 900
github_base_url = "https://api.github.com"
codeberg_base_url = "https://codeberg.org/api/v1"
# tokens via GITHUB_TOKEN / CODEBERG_TOKEN env vars, not here

[immich]

KeyTypeDefaultDescription
api_urlstring""Immich server URL (empty = image proxy disabled)

[images]

KeyTypeDefaultDescription
cache_max_ageu6431536000Cache-Control max-age for proxied images (seconds)

[analytics]

KeyTypeDefaultDescription
plausible_domainstring""Site domain tracked by Plausible (empty = disabled)
plausible_script_urlstring""URL to your Plausible script (e.g. https://plausible.example.com/js/script.js)

Both fields must be set for the Plausible <script> tag to be injected. This keeps analytics fully opt-in.

[donation]

KeyTypeDefaultDescription
enabledboolfalseEnable donation links across the site
cta_textstring""Custom text for end-of-article CTA (default: “If you found this useful, consider supporting my work.”)

Each entry defines a donation platform link:

KeyTypeDefaultDescription
platformstring(required)Platform identifier: "kofi", "github_sponsors", "liberapay", or "custom"
urlstring(required)URL to your profile on the platform
labelstring""Custom display label (empty = auto-generated from platform name)

When enabled, donation links appear in three places:

  • Header: A “Support” link with heart icon in the navigation bar
  • End of articles: A compact CTA after blog post content
  • Footer: A heart icon alongside social links
  • /support page: A dedicated page showing all configured platforms as cards
[donation]
enabled = true
cta_text = "If you found this useful, consider supporting my work."

[[donation.links]]
platform = "kofi"
url = "https://ko-fi.com/yourusername"

[[donation.links]]
platform = "github_sponsors"
url = "https://github.com/sponsors/yourusername"

[[donation.links]]
platform = "liberapay"
url = "https://liberapay.com/yourusername"

Full example

[site]
name = "My Site"
tagline = "Systems, science, and software"
description = "Personal website and blog"
lang = "en"
default_theme = "dark"
animated_background = "flow-field"

[site.author]
name = "Jane Doe"
email = "jane@example.com"

[site.social]
github = "https://github.com/janedoe"
mastodon = "https://fosstodon.org/@janedoe"

[site.footer]
project_name = "Plinth"
project_url = "https://codeberg.org/caniko/plinth"

[[site.nav]]
label = "Posts"
path = "/posts"

[[site.nav]]
label = "Projects"
path = "/projects"

[[site.nav]]
label = "About"
path = "/about"

[pages.blog]
title = "Blog"
subtitle = "Notes on software and systems"

[server]
host = "127.0.0.1"
port = 3000

[database]
database_url = "postgres://plinth:plinth@localhost:5432/plinth"

[observability]
log_level = "info"

[search]
default_limit = 10

[content]
words_per_minute = 200

[feeds]
blog_limit = 50
projects_limit = 50
activity_limit = 50

[ranking]
strategy = "exponential"
half_life_days = 365.0
window_days = 730.0

[forge]
refresh_ttl_secs = 3600
refresh_backoff_secs = 900
github_base_url = "https://api.github.com"
codeberg_base_url = "https://codeberg.org/api/v1"

[analytics]
plausible_domain = "example.com"
plausible_script_url = "https://plausible.example.com/js/script.js"

[donation]
enabled = true

[[donation.links]]
platform = "kofi"
url = "https://ko-fi.com/janedoe"

[[donation.links]]
platform = "github_sponsors"
url = "https://github.com/sponsors/janedoe"