stalwart Documentation

All-in-one Mail & Collaboration server. Secure, scalable and fluent in every protocol (IMAP, JMAP, SMTP, CalDAV, CardDAV, WebDAV).

13,857 stars Rust Rust 1 file Β· ~4,731 tokens #caldav#carddav#imap#jmap#mail#pop3#rust#server
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy stalwart on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name stalwart -p 5432:5432 --restart always stalwart-pebble-challtestsrv:local
Configured Services & Ports
βš™οΈ cert-init
image: alpine/openssl:latest
βš™οΈ postgres
image: postgres:16-alpine
πŸ”Œ Ports: 127.0.0.1:5432:5432
βš™οΈ mysql
image: mysql:8.0
πŸ”Œ Ports: 127.0.0.1:3306:3306
βš™οΈ foundationdb
image: foundationdb/foundationdb:7.4.6
πŸ”Œ Ports: 127.0.0.1:4500:4500
βš™οΈ fdb-init
image: foundationdb/foundationdb:7.4.6
βš™οΈ redis
image: redis:7-alpine
πŸ”Œ Ports: 127.0.0.1:6379:6379
βš™οΈ opensearch
image: opensearchproject/opensearch:2
πŸ”Œ Ports: 127.0.0.1:9200:9200
βš™οΈ meilisearch
image: getmeili/meilisearch:latest
πŸ”Œ Ports: 127.0.0.1:7700:7700
βš™οΈ minio
image: minio/minio:latest
πŸ”Œ Ports: 127.0.0.1:9000:9000, 127.0.0.1:9001:9001
βš™οΈ minio-init
image: minio/mc:latest
βš™οΈ keycloak
image: quay.io/keycloak/keycloak:latest
πŸ”Œ Ports: 127.0.0.1:9080:9080
βš™οΈ openldap
image: osixia/openldap:1.5.0
πŸ”Œ Ports: 127.0.0.1:389:389, 127.0.0.1:636:636
βš™οΈ pebble-challtestsrv
image: stalwart-pebble-challtestsrv:local
πŸ”Œ Ports: 127.0.0.1:8055:8055
βš™οΈ pebble
image: ghcr.io/letsencrypt/pebble:latest
πŸ”Œ Ports: 127.0.0.1:14000:14000, 127.0.0.1:15000:15000
βš™οΈ powerdns
image: powerdns/pdns-auth-49:latest
πŸ”Œ Ports: 127.0.0.1:5300:53/tcp, 127.0.0.1:5300:53/udp, 127.0.0.1:8081:8081
βš™οΈ powerdns-init
image: powerdns/pdns-auth-49:latest
βš™οΈ nats
image: nats:latest
πŸ”Œ Ports: 127.0.0.1:4222:4222, 127.0.0.1:8222:8222
docker-compose.yml
yaml (docker-compose.yml)
###############################################################################
# Stalwart – Test Infrastructure
# All services are ephemeral (no volumes = fresh on every restart)
# Ports exposed on localhost; 80/443/8080 are remapped
###############################################################################

services:

  # ---------------------------------------------------------------------------
  # Init: generate shared self-signed TLS certificates
  # ---------------------------------------------------------------------------
  cert-init:
    image: alpine/openssl:latest
    volumes:
      - certs:/certs
      - ./scripts/gen-certs.sh:/gen-certs.sh:ro
    entrypoint: [ "sh", "/gen-certs.sh" ]

  # ---------------------------------------------------------------------------
  # PostgreSQL
  # ---------------------------------------------------------------------------
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: stalwart
      POSTGRES_PASSWORD: stalwart
      POSTGRES_DB: stalwart
    ports:
      - "127.0.0.1:5432:5432"
    tmpfs:
      - /var/lib/postgresql/data

  # ---------------------------------------------------------------------------
  # MySQL
  # ---------------------------------------------------------------------------
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: stalwart
      MYSQL_DATABASE: stalwart
      MYSQL_USER: stalwart
      MYSQL_PASSWORD: stalwart
    ports:
      - "127.0.0.1:3306:3306"
    tmpfs:
      - /var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password

  # ---------------------------------------------------------------------------
  # FoundationDB
  # ---------------------------------------------------------------------------
  foundationdb:
    image: foundationdb/foundationdb:7.4.6
    ports:
      - "127.0.0.1:4500:4500"
    environment:
      FDB_NETWORKING_MODE: container
    volumes:
      - fdb-config:/var/fdb
    tmpfs:
      - /var/fdb/data
      - /var/fdb/logs
    healthcheck:
      test: [ "CMD-SHELL", "fdbcli --exec 'status' --timeout 3 >/dev/null 2>&1" ]
      interval: 2s
      timeout: 5s
      retries: 30
      start_period: 5s

  fdb-init:
    image: foundationdb/foundationdb:7.4.6
    depends_on:
      foundationdb:
        condition: service_healthy
    volumes:
      - fdb-config:/var/fdb
      - ./scripts/init-fdb.sh:/init-fdb.sh:ro
    entrypoint: [ "bash", "/init-fdb.sh" ]

  # ---------------------------------------------------------------------------
  # Redis
  # ---------------------------------------------------------------------------
  redis:
    image: redis:7-alpine
    ports:
      - "127.0.0.1:6379:6379"
    command: redis-server --save "" --appendonly no

  # ---------------------------------------------------------------------------
  # OpenSearch (ElasticSearch-compatible)
  # ---------------------------------------------------------------------------
  opensearch:
    image: opensearchproject/opensearch:2
    environment:
      discovery.type: single-node
      DISABLE_SECURITY_PLUGIN: "true"
      OPENSEARCH_JAVA_OPTS: "-Xms256m -Xmx256m"
      DISABLE_INSTALL_DEMO_CONFIG: "true"
    ports:
      - "127.0.0.1:9200:9200"
    tmpfs:
      - /usr/share/opensearch/data:uid=1000,gid=1000

  # ---------------------------------------------------------------------------
  # Meilisearch
  # ---------------------------------------------------------------------------
  meilisearch:
    image: getmeili/meilisearch:latest
    environment:
      MEILI_ENV: development
      MEILI_NO_ANALYTICS: "true"
      MEILI_MASTER_KEY: stalwart-master-key
    ports:
      - "127.0.0.1:7700:7700"
    tmpfs:
      - /meili_data

  # ---------------------------------------------------------------------------
  # MinIO (S3-compatible)
  # ---------------------------------------------------------------------------
  minio:
    image: minio/minio:latest
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    ports:
      - "127.0.0.1:9000:9000"
      - "127.0.0.1:9001:9001"
    command: server /data --console-address ":9001"
    tmpfs:
      - /data

  minio-init:
    image: minio/mc:latest
    depends_on:
      - minio
    volumes:
      - ./scripts/init-minio.sh:/init-minio.sh:ro
    entrypoint: [ "bash", "/init-minio.sh" ]

  # ---------------------------------------------------------------------------
  # Keycloak (OIDC Provider)  –  port 9080 (moved from 8080)
  # ---------------------------------------------------------------------------
  keycloak:
    image: quay.io/keycloak/keycloak:latest
    depends_on:
      cert-init:
        condition: service_completed_successfully
    environment:
      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: admin
      KC_HTTP_PORT: 9080
      KC_HEALTH_ENABLED: "true"
    ports:
      - "127.0.0.1:9080:9080"
    volumes:
      - ./keycloak/stalwart-realm.json:/opt/keycloak/data/import/stalwart-realm.json:ro
      - certs:/certs:ro
    command: >
      start-dev --import-realm
    tmpfs:
      - /opt/keycloak/data:uid=1000,gid=1000

  # ---------------------------------------------------------------------------
  # OpenLDAP
  # ---------------------------------------------------------------------------
  openldap:
    image: osixia/openldap:1.5.0
    depends_on:
      cert-init:
        condition: service_completed_successfully
    environment:
      LDAP_ORGANISATION: "Stalwart Test"
      LDAP_DOMAIN: "stalwart.test"
      LDAP_BASE_DN: "dc=stalwart,dc=test"
      LDAP_ADMIN_PASSWORD: "admin"
      LDAP_READONLY_USER: "true"
      LDAP_READONLY_USER_USERNAME: "readonly"
      LDAP_READONLY_USER_PASSWORD: "readonly"
      LDAP_TLS: "true"
      LDAP_TLS_CRT_FILENAME: "cert.pem"
      LDAP_TLS_KEY_FILENAME: "key.pem"
      LDAP_TLS_CA_CRT_FILENAME: "cert.pem"
      LDAP_TLS_VERIFY_CLIENT: "never"
    ports:
      - "127.0.0.1:389:389"
      - "127.0.0.1:636:636"
    volumes:
      - ./ldap/50-users.ldif:/seed/50-users.ldif:ro
      - ./ldap/60-groups.ldif:/seed/60-groups.ldif:ro
      - certs:/certs-shared:ro
    entrypoint: [ "/bin/bash", "-c", "mkdir -p /container/service/slapd/assets/config/bootstrap/ldif/custom && cp /seed/*.ldif /container/service/slapd/assets/config/bootstrap/ldif/custom/ && cp /certs-shared/* /container/service/slapd/assets/certs/ 2>/dev/null; exec /container/tool/run" ]

  # ---------------------------------------------------------------------------
  # Pebble Challenge Test Server  –  port 8055 (management API)
  # Provides controllable DNS, HTTP, and TLS-ALPN challenge responders.
  # Use the management API to add/remove challenge responses before requesting
  # certificates from Pebble.
  # ---------------------------------------------------------------------------
  pebble-challtestsrv:
    build:
      context: ./pebble
      dockerfile: Dockerfile.challtestsrv
    image: stalwart-pebble-challtestsrv:local
    ports:
      - "127.0.0.1:8055:8055"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped

  # ---------------------------------------------------------------------------
  # Pebble (ACME server)  –  ports 14000 (directory) + 15000 (management)
  # ---------------------------------------------------------------------------
  pebble:
    image: ghcr.io/letsencrypt/pebble:latest
    depends_on:
      - pebble-challtestsrv
    environment:
      PEBBLE_VA_NOSLEEP: "1"
      PEBBLE_WFE_NONCEREJECT: "0"
    ports:
      - "127.0.0.1:14000:14000"
      - "127.0.0.1:15000:15000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    volumes:
      - ./pebble/pebble-config.json:/test/config/pebble-config.json:ro
    command: -config /test/config/pebble-config.json -dnsserver pebble-challtestsrv:8053

  # ---------------------------------------------------------------------------
  # PowerDNS (DNS with TLSA + RFC2136)  –  port 5300 (moved from 53)
  # ---------------------------------------------------------------------------
  powerdns:
    image: powerdns/pdns-auth-49:latest
    environment:
      PDNS_AUTH_API_KEY: stalwart-api-key
    ports:
      - "127.0.0.1:5300:53/tcp"
      - "127.0.0.1:5300:53/udp"
      - "127.0.0.1:8081:8081"
    volumes:
      - ./powerdns/pdns.conf:/etc/powerdns/pdns.d/stalwart.conf:ro
      - ./powerdns/init-zone.sh:/etc/powerdns/init-zone.sh:ro
      - pdns-data:/var/lib/powerdns

  powerdns-init:
    image: powerdns/pdns-auth-49:latest
    depends_on:
      - powerdns
    volumes:
      - ./powerdns/entrypoint.sh:/init.sh:ro
      - ./powerdns/init-zone.sh:/etc/powerdns/init-zone.sh:ro
      - pdns-data:/var/lib/powerdns
    entrypoint: [ "bash", "/init.sh" ]
    network_mode: "service:powerdns"

  # ---------------------------------------------------------------------------
  # NATS (message queue, core mode)
  # ---------------------------------------------------------------------------
  nats:
    image: nats:latest
    ports:
      - "127.0.0.1:4222:4222"
      - "127.0.0.1:8222:8222"
    command: "--addr 0.0.0.0 --port 4222 --http_port 8222"

# =============================================================================
# Shared volumes (ephemeral – docker compose down removes them)
# =============================================================================
volumes:
  certs:
    driver: local
  fdb-config:
    driver: local
  pdns-data:
    driver: local

1. Project Overview & Quickstart (stalwartlabs/stalwart)

Secure, scalable mail & collaboration server with comprehensive protocol support πŸ›‘οΈ (IMAP, JMAP, SMTP, CalDAV, CardDAV, WebDAV)

Features

Stalwart is an open-source mail & collaboration server with JMAP, IMAP4, POP3, SMTP, CalDAV, CardDAV and WebDAV support and a wide range of modern features. It is written in Rust and designed to be secure, fast, robust and scalable.

Key features:

  • Email server with complete protocol support:
  • Collaboration server:
  • Spam and Phishing built-in filter:
    • Comprehensive set of filtering rules on par with popular solutions.
    • LLM-driven spam filtering and message analysis.
    • Statistical spam classifier with collaborative filtering, automatic training capabilities and address book integration.
    • DNS Blocklists (DNSBLs) checking of IP addresses, domains, and hashes.
    • Collaborative digest-based spam filtering with Pyzor.
    • Phishing protection against homographic URL attacks, sender spoofing and other techniques.
    • Trusted reply tracking to recognize and prioritize genuine e-mail replies.
    • Sender reputation monitoring by IP address, ASN, domain and email address.
    • Greylisting to temporarily defer unknown senders.
    • Spam traps to set up decoy email addresses that catch and analyze spam.
  • Flexible:
    • Pluggable storage backends with RocksDB, FoundationDB, PostgreSQL, mySQL, SQLite, S3-Compatible, Azure and Redis support.
    • Full-text search available in 17 languages using the built-in search engine or via Meilisearch, ElasticSearch, OpenSearch, PostgreSQL or mySQL backends.
    • Sieve scripting language with support for all registered extensions.
    • Email aliases, mailing lists, subaddressing and catch-all addresses support.
    • Automated DNS management.
    • Automatic account configuration and discovery with autoconfig and autodiscover.
    • Multi-tenancy support with domain and tenant isolation.
    • Disk quotas per user and tenant.
  • Secure and robust:
    • Encryption at rest with S/MIME or OpenPGP.
    • Automatic TLS certificate provisioning with ACME using TLS-ALPN-01, DNS-01, DNS-PERSIST-01 or HTTP-01 challenges.
    • Automated blocking of IP addresses that attack, abuse or scan the server for exploits.
    • Rate limiting.
    • Security audited (read the report).
    • Memory safe (thanks to Rust).
  • Scalable and fault-tolerant:
    • Designed to handle growth seamlessly, from small setups to large-scale deployments of thousands of nodes.
    • Built with fault tolerance and high availability in mind, recovers from hardware or software failures with minimal operational impact.
    • Peer-to-peer cluster coordination or with Kafka, Redpanda, NATS or Redis.
    • Kubernetes, Apache Mesos and Docker Swarm support for automated scaling and container orchestration.
    • Read replicas, sharded blob storage and in-memory data stores for high performance and low latency.
  • Authentication and Authorization:
    • OpenID Connect authentication.
    • OAuth 2.0 authorization with authorization code and device authorization flows.
    • LDAP, OIDC, SQL or built-in authentication backend support.
    • Two-factor authentication with Time-based One-Time Passwords (2FA-TOTP)
    • Application passwords (App Passwords).
    • Roles and permissions.
    • Access Control Lists (ACLs).
  • Observability:
    • Logging and tracing with OpenTelemetry, journald, log files and console support.
    • Metrics with OpenTelemetry and Prometheus integration.
    • Webhooks for event-driven automation.
    • Alerts with email and webhook notifications.
    • Live tracing and metrics.
  • Web-based administration:
    • Dashboard with real-time statistics and monitoring.
    • Account, domain, group and mailing list management.
    • SMTP queue management for messages and outbound DMARC and TLS reports.
    • Report visualization interface for received DMARC, TLS-RPT and Failure (ARF) reports.
    • Configuration of every aspect of the mail server.
    • Log viewer with search and filtering capabilities.
    • Self-service portal for password reset and encryption-at-rest key management.

Screenshots

Presentation

Want a deeper dive? Need to explain to your boss why Stalwart is the perfect fit? Whether you're evaluating options, making a case to your team, or simply curious about how it all works under the hood, these slides walk you through the key features, architecture, and benefits of Stalwart. Browse the slides to see what makes it stand out.

Get Started

Install Stalwart on your server by following the instructions for your platform:

All documentation is available at stalw.art/docs.

Support

If you are having problems running Stalwart, found a bug, or just have a question, please head to the Stalwart Support Portal at support.stalw.art.
Additionally, you may purchase an Enterprise License to obtain priority support from Stalwart Labs LLC, including response-time commitments and a private Priority Support area on the portal.

Contributing

We welcome contributions, but to keep the project maintainable there are a few things to know before opening a pull request. Because of the high volume of low-quality, AI-generated submissions, pull requests are limited to a list of vouched contributors; to be added, post at support.stalw.art describing the change you would like to submit, together with a link to the proposed change. At this stage only bug fixes and translations are accepted, and new features are not, unless they involve just a few lines of code.
For the full guidelines, please read CONTRIBUTING.md.

Roadmap

Stalwart has reached an exciting point in its journey, it’s now feature complete. All the core functionality and open standard email and collaboration protocols that we set out to support are in place. In other words, Stalwart already does everything you’d expect from a modern, standards-compliant mail and collaboration platform.

The next major milestone is all about refinement: finalizing the database schema and focusing on performance optimizations to ensure everything runs as efficiently and reliably as possible. Once that’s done, we’ll be ready to roll out version 1.0.

Of course, development doesn’t stop there. The community has contributed hundreds of great ideas for improvements and new features, everything from subtle usability tweaks to entirely new integrations. You can see the full list of proposals over on our GitHub issues. If there’s something you’d like to see prioritized, just give it a thumbs up as we plan to implement enhancements based on the community’s votes.

Sponsorship

Your support is crucial in helping us continue to improve the project, add new features, and maintain the highest level of quality. By becoming a sponsor, you help fund the development and future of Stalwart. As a thank-you, sponsors who contribute $5 per month or more will automatically receive a Enterprise edition license. And, sponsors who contribute $30 per month or more, also have access to Premium Support from Stalwart Labs.

Funding

Part of the development of this project was funded through:

  • NGI0 Entrust Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 101069594.
  • NGI Zero Core, a fund established by NLnet with financial support from the European Commission's programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 101092990.

If you find the project useful you can help by becoming a sponsor. Thank you!

License

This project is dual-licensed under the GNU Affero General Public License v3.0 (AGPL-3.0; as published by the Free Software Foundation) and the Stalwart Enterprise License v2 (SELv2):

  • The GNU Affero General Public License v3.0 is a free software license that ensures your freedom to use, modify, and distribute the software, with the condition that any modified versions of the software must also be distributed under the same license.
  • The Stalwart Enterprise License v2 (SELv2) is a proprietary license designed for commercial use. It offers additional features and greater flexibility for businesses that do not wish to comply with the AGPL-3.0 license requirements.

Each file in this project contains a license notice at the top, indicating the applicable license(s). The license notice follows the REUSE guidelines to ensure clarity and consistency. The full text of each license is available in the LICENSES directory.

Copyright (C) 2020, Stalwart Labs LLC

2. Official Technical Reference & Guides (stalwartlabs/website)

Stalwart Website

The marketing site, documentation, and blog for Stalwart.
Built with Astro 6 and
Starlight; deployed as a static site to
Cloudflare Pages.

Stack

  • Marketing pages (/, /pricing, /about, /legal/*, etc.) are Astro
    pages driven by YAML/Markdown content collections under
    src/content/pages/ and src/content/legal/.
  • Documentation (/docs/...) is rendered by Starlight from
    src/content/docs/docs/. The previous release line
    lives at src/content/docs/docs/0.15/ and is
    served at /docs/0.15/....
  • Blog (/blog, /blog/) is the
    starlight-blog plugin reading
    posts from src/content/docs/blog/.
  • Search is Pagefind, the offline static-site
    search baked into Starlight. The index is rebuilt on every npm run build.
    No third-party service or API key needed.
  • Per-post OG images are generated by
    astro-og-canvas at
    /og/blog/<slug>.png and referenced from <meta property="og:image"> on
    each blog post.
  • View Transitions (smooth cross-page nav) are enabled site-wide via
    Astro's <ClientRouter />.

Local development

Requirements: Node.js 20+ (set in package.json engines).

sh
npm install            # one-time, installs Astro + Starlight + plugins
npm run dev            # http://127.0.0.1:8787 with hot reload

npm run dev watches every Markdown, Astro and YAML file. Edits reload in the
browser within a second; structural changes (new files, frontmatter schema
changes) may require a manual reload.

Other useful scripts:

sh
npm run build          # production build to dist/, runs link validation
npm run preview        # serve dist/ at http://127.0.0.1:8787 to verify

Authoring docs

Drop new pages into src/content/docs/docs/<section>/.md. Frontmatter
must include at least a title:. Optional helpful fields:

yaml
---
title: My new page
sidebar:
  order: 5            # position within the parent group
  badge: New          # appears in the sidebar
---

To rename a directory's sidebar label or change ordering, drop a _meta.yml
into that directory (starlight-auto-sidebar docs):

yaml
label: Storage settings
order: 5

Admonitions (notes, tips, warnings) use the same triple-colon syntax as
before:

md
:::tip Enterprise feature
This feature ships with the Enterprise edition.
:::

Internal doc links use absolute paths without the .md suffix:
[Storage](/docs/storage/overview).

Authoring blog posts

Add src/content/docs/blog/.mdx (or a folder with index.mdx if
the post has its own assets). Required frontmatter:

yaml
---
title: Post title
date: 2026-05-01
authors: [mdecimus]      # keys are defined in astro.config.mjs starlightBlog config
tags: [release, jmap]
excerpt: |
  One paragraph teaser shown on the index, in tag pages, and in the RSS feed.
---

Reading time is computed automatically.

Deployment (Cloudflare Pages)

The site is a static export. Cloudflare Pages picks up the Astro project
automatically; the only config needed in the dashboard is:

  • Build command: npm run build
  • Build output directory: dist
  • Node version: 20 (or newer)

wrangler.toml declares the same so wrangler pages deploy
works locally or from CI.

Static asset behaviour and redirects are controlled by:

To deploy from your laptop (one-off):

sh
npm run build
npx wrangler pages deploy dist --project-name stalwart-website

Repository layout

text
.
β”œβ”€β”€ astro.config.mjs        # Astro + Starlight integration config
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ content.config.ts   # Astro content collection schemas
β”‚   β”œβ”€β”€ content/
β”‚   β”‚   β”œβ”€β”€ docs/
β”‚   β”‚   β”‚   β”œβ”€β”€ docs/       # current docs   -> /docs/...
β”‚   β”‚   β”‚   β”œβ”€β”€ docs/0.15/  # archived 0.15  -> /docs/0.15/...
β”‚   β”‚   β”‚   └── blog/       # blog posts     -> /blog/...
β”‚   β”‚   β”œβ”€β”€ pages/          # marketing pages (YAML)
β”‚   β”‚   └── legal/          # long-form legal pages (Markdown)
β”‚   β”œβ”€β”€ components/         # Astro components shared by marketing + docs
β”‚   β”‚   β”œβ”€β”€ Header.astro            # marketing nav
β”‚   β”‚   β”œβ”€β”€ Footer.astro            # marketing footer
β”‚   β”‚   β”œβ”€β”€ StarlightHeader.astro   # Starlight Header override -> Header
β”‚   β”‚   β”œβ”€β”€ StarlightFooter.astro   # Starlight Footer override -> Footer
β”‚   β”‚   └── VersionPicker.astro     # docs version dropdown
β”‚   β”œβ”€β”€ layouts/            # marketing layouts (Base, LongForm)
β”‚   β”œβ”€β”€ pages/              # marketing dynamic routes
β”‚   └── styles/             # global tokens and Starlight overrides
β”œβ”€β”€ public/                 # static assets copied as-is to dist/
└── scripts/                # one-off migration scripts (kept for reference)

Things to know

  • The links-validator plugin runs at the end of npm run build and fails
    the build on broken internal links. Pre-existing broken links in
    /docs/0.15/** are excluded (see astro.config.mjs); fix-forward only.
  • Mermaid diagrams (```mermaid) are converted to client-rendered SVG
    via the mermaid npm package; the script is bundled into Starlight pages
    only.
  • The .old/ directory is the archived Docusaurus tree, kept out of git via
    .gitignore until you choose to delete it.