Expand description
Static Site Generator (SSG)
AI-augmented, accessibility-first static site generator built in Rust.
§Contents
Getting started
- Install — Cargo, Homebrew, apt, AUR, container, one-liner,
make install - Quick Start — scaffold, build and serve a site in five commands
Reference
- Overview — what SSG compiles, and from what
- Why this approach? — design rationale, and what it costs
- Architecture — the build pipeline, stage by stage
- Benchmarks — headline figures; methodology and the latest nightly run in
BENCHMARKS.md - Features — capability matrix
- The CLI — subcommands, build flags, legacy form
- Library Usage — plugins, schemas, the AI pipeline
- Examples — eight runnable examples and the edge adapters
- Themes — build on a published theme by name
Operational
- Stability guarantees — what a version bump promises
- Minimum supported Rust version — the policy, not just the number
- When not to use SSG — limitations, stated plainly
- Development — make targets, CI workflows, fuzzing; full guide in
DEVELOPMENT.md - Security — guarantees, supply chain, reporting
- Documentation — every reference document
- License
§Install
[dependencies]
ssg = "0.0.63"§Prebuilt binaries
# macOS / Linux -- one command
curl -fsSL https://raw.githubusercontent.com/sebastienrousseau/static-site-generator/main/scripts/install.sh | sh
# Homebrew
brew install --formula https://raw.githubusercontent.com/sebastienrousseau/static-site-generator/main/packaging/homebrew/ssg.rb
# Cargo
cargo install ssg
# Debian / Ubuntu (amd64 or arm64)
sudo dpkg -i ssg_0.0.63_amd64.deb
# Arch Linux (AUR)
yay -S ssg
# Container
docker pull ghcr.io/sebastienrousseau/static-site-generator:latest§Build from source
git clone https://github.com/sebastienrousseau/static-site-generator.git
cd static-site-generator
make # check + clippy + testRequires Rust 1.88.0+. Tested on Linux, macOS, and Windows.
§System install (packagers)
make install # to /usr/local
make PREFIX=/usr install # to /usr
make DESTDIR=/tmp/stage install # staged, for packaging
make uninstall # exact inverse of installInstalls the binary, the ssg.1 man page and bash, zsh and fish
completions to their FHS locations. The man page and completions are
generated from the CLI definition at build time, so man ssg cannot
drift from ssg --help; CI asserts that on every push. PowerShell
completions are generated too, and installed when PWSHCOMPDIR is set.
§Quick Start
# 1 -- Install
cargo install ssg
# 2 -- Scaffold a new site
ssg -n mysite -c content -o build -t templates
# 3 -- Build
ssg -c content -o public -t templates
# 4 -- Development server with live reload
ssg -c content -o public -t templates -s public -w
# 5 -- AI-powered readability fix (requires Ollama)
ssg -c content -o public -t templates --ai-fix§Overview
SSG generates static websites from Markdown content, YAML frontmatter, and MiniJinja templates. It compiles everything into production-ready HTML with built-in SEO metadata, WCAG 2.2 AA accessibility compliance (including the new 2.5.8, 2.4.13, 3.2.6 criteria where automatable), multilingual readability scoring, and feed generation. The 32-plugin pipeline handles the rest.
- 32-plugin pipeline – SEO, a11y, i18n, search, images, AI, CSP, JSON-LD, RSS, sitemaps
- Agentic AI pipeline – audit, diagnose, fix, and verify content readability via local LLM
- Multilingual readability – Flesch-Kincaid (EN), Kandel-Moles (FR), Wiener Sachtextformel (DE), Gulpease (IT), LIX (SV), Fernandez Huerta (ES)
- Incremental builds – content fingerprinting via FNV-1a hashing and dependency graph
- Bounded-memory batch compilation – configurable memory budgets for 100K+ page sites
- WCAG 2.2 AA – accessibility checked on every build (non-blocking by default; reports written to
accessibility-report.json+wcag-compliance.json) and gated in CI by axe-core. Build-failure on a11y violations is opt-in via theSTRICT_A11Yenv var (implemented in v0.0.40) - Zero unsafe code –
#![forbid(unsafe_code)]across the entire codebase
§Why this approach?
SSG occupies the niche Hugo, Zola and Eleventy occupy — Markdown and templates in, static HTML out — and differs in one respect: the checks most generators leave to a deployment pipeline run inside the compiler, on every build.
Three choices follow from that, and each has a cost worth stating:
-
Accessibility and security are build stages, not CI steps. WCAG checks, CSP extraction with SRI hashing, and the 15-gate audit runner execute during
ssg build. A page that fails is reported with its file and line, not discovered after deployment. The cost is build time: a fully-gated build does more work than a generator that only renders.STRICT_A11Ydecides whether a violation warns or fails. -
No async runtime. The crate is deliberately tokio-free; parallelism is Rayon over a work-stealing pool, and the LLM transport is synchronous
ureq. This keeps the dependency tree small and the binary self-contained. The cost is that genuinely concurrent I/O — the dev server, the HMR socket — uses threads rather than tasks, which is heavier per connection than an async runtime would be. The reasoning is recorded indocs/adr/. -
#![forbid(unsafe_code)]workspace-wide. Nounsafeblock exists in the tree. Memory-safety review is therefore a property of the compiler rather than of code review, and the Miri job (.github/workflows/miri.yml) checks the dependency surface rather than our own. The cost is that a few optimisations available through raw pointers are simply unavailable.
§Compared with other generators
The table below is a capability comparison, not a benchmark; for build times see Benchmarks.
| Capability | SSG | Hugo | Zola | Astro |
|---|---|---|---|---|
| Agentic AI pipeline | Yes | No | No | No |
| Multilingual readability | Yes | No | No | No |
| Auto-generated OG images | Yes | No | No | Plugin |
| Built-in WCAG validation | Yes | No | No | No |
| CSP/SRI auto-extraction | Yes | No | No | Plugin |
| axe-core CI gate | Yes | No | No | No |
| WebAssembly target | Yes | No | No | N/A |
| 98% CI coverage floors | Yes | No | No | No |
| Zero unsafe code | Yes | Yes | Yes | N/A |
§Architecture
graph TD
A[Content: Markdown + YAML] --> B{SSG CLI}
B --> V[Content Schema Validation]
V --> C[Incremental Cache + `DepGraph`]
C --> D[Compile: staticdatagen]
D --> E[Post-Processing Fixes]
E --> F[Fused Transform Pipeline: 32 plugins]
F --> G[Output: HTML + RSS + Atom + Sitemap + JSON-LD]
B --> H[File Watcher + CSS HMR]
H -->|changed files| C
B -->|--serve| S[Dev Server + Live Reload + Error Overlay]
B -->|--ai-fix| AI[Agentic LLM Pipeline]
AI -->|audit + fix| A§Benchmarks
| Metric | Value |
|---|---|
| Source | 143,000+ lines across 10 workspace crates (ssg, ssg-core, ssg-a11y, ssg-i18n, ssg-search, ssg-mcp, ssg-rpc, ssg-rpc-macro, ssg-wasm, and the unpublished ssg-heap-probe) |
| Test suite | 3,668 unit tests (cargo test --lib) + 61 integration test targets |
| Coverage | 98% region, 98% line, 98% function (CI-gated); measured 99.31 / 99.29 / 99.24 |
| Plugin pipeline | 32 plugins, Rayon-parallelised |
| Audit gates | 15 (WCAG 2.2 AAA, JSON-LD, hreflang, lang consistency, CSP+SRI, PQC TLS, HTML5, broken links, OG, markdown lint, perf budget, AI discovery, RSS/Atom, image opt, search index integrity) |
| Examples | 8 branded sites + 2 edge-runtime adapters (Cloudflare Workers, Vercel Edge) |
| Edge runtimes | Cloudflare Workers + Vercel Edge with ISR (ssg-wasm) |
| Search | Browser-native int8 vector embeddings via ssg-search (WASM, ≤2 MB gzipped budget) |
| MSRV | Rust 1.88.0 |
§Build performance
| Pages | Time | Memory |
|---|---|---|
| 100 | < 5s (CI-gated) | < 100 MB |
| 1,000 | < 10s | < 200 MB |
| 10,000 | Bounded-memory batches | 512 MB budget |
| 100,000+ | Bounded-memory batches | Configurable via --max-memory |
Reproduce: cargo bench --bench bench -- scalability.
§Features
| Performance | Parallel file operations with Rayon, fused single-pass HTML transforms, content-addressed caching (FNV-1a), dependency graph for incremental rebuilds, bounded-memory batch compilation for 100K+ pages, --jobs N thread control, --max-memory MB budget |
| AI Pipeline | Agentic LLM pipeline (--ai-fix): audit content readability, diagnose failing files, generate fixes via local LLM (Ollama), verify improvement, produce JSON report. Dry-run mode (--ai-fix-dry-run). Auto-generate alt text, meta descriptions, and JSON-LD via LLM |
| Readability | Multilingual scoring: Flesch-Kincaid (EN), Kandel-Moles (FR), Wiener Sachtextformel (DE), Gulpease (IT), LIX (SV/NO/DA), Fernandez Huerta (ES). BCP 47 language detection from frontmatter. CI readability gate |
| Content | Markdown with GFM extensions (tables, strikethrough, task lists), YAML/TOML/JSON frontmatter, typed content schemas with compile-time validation, shortcodes (youtube, gist, figure, admonition), compile-time word count + estimated reading time injected into .meta.json sidecars |
| SEO | Meta description, Open Graph (title, description, type, url, image, locale), auto-generated OG social cards (SVG), Twitter Cards, canonical URLs, robots.txt, sitemaps with per-page lastmod, first-class topic-cluster taxonomy (hub + pillar indexes), related-post discovery via Jaccard tag/category overlap |
| Structured Data | JSON-LD Article/WebPage with datePublished, dateModified, author, image, inLanguage, BreadcrumbList |
| Syndication | RSS 2.0 with enclosures and categories, Atom 1.0, Google News sitemap |
| Accessibility | WCAG 2.2 AA validation on every build (1.1.1, 1.3.1, 2.3.1, 2.4.4, 2.4.13, 2.5.8, 3.1.1, 3.2.6), axe-core Playwright CI, decorative image detection, heading hierarchy, ARIA landmarks; emits wcag-compliance.json matrix (WCAG 2.2 + EAA guide) |
| i18n | Hreflang injection, x-default support, per-locale sitemaps, language switcher HTML. Translated slugs — pages pair across locales by a translation_key front-matter field rather than by identical path, so /about/ and /fr/a-propos/ are reciprocal alternates; pages without a key keep pairing by path. The default locale may serve from the site root (/about/ alongside /fr/a-propos/), matching Hugo, Astro and Next.js (i18n guide) |
| Images | Responsive <picture> with WebP sources, srcset at 320/640/1024/1440, lazy loading, CLS prevention, optional cdn_prefix for serving local image assets from a CDN host |
| Templates | MiniJinja engine with inheritance, loops, conditionals, custom filters |
| Search | Client-side full-text search with modal UI, 28 locale translations, Ctrl+K / Cmd+K |
| Security | CSP build-time extraction (zero unsafe-inline), SRI hash generation, asset fingerprinting, path traversal prevention, structured SsgError type-safe error hierarchy |
| Minification | Native HTML / JS / CSS minification, always on and written in-tree — no minify-html, oxc_minifier or lightningcss. A minifier rewrites every byte the generator emits, so it is kept where this crate’s own tests cover it. <pre>, <textarea>, <script> and <style> content survives byte for byte, attribute values keep their spacing, and comments are preserved. Recursive walk processes every .html, .css and .js file under site_dir regardless of depth. |
| Supply Chain | Automated CycloneDX 1.5 SBOM (sbom.cdx.json) generated on every build via SbomPlugin, listing compiler version, dependency tree, and license metadata |
| DX | CSS hot reload, browser error overlay via WebSocket, file watching with change classification |
| WebAssembly | ssg-core + ssg-wasm + ssg-search compile to wasm32-unknown-unknown with wasm-bindgen; ssg-wasm ships ISR + RPC entry points for Edge runtimes (CI-enforced ≤ 2 MB gzipped) |
| Vector search | ssg-search — browser-native int8-quantised hashed-n-gram (or opt-in model2vec-rs) embeddings, Float32Array boundary, no division / sqrt at runtime, p99 < 100 ms on 1000-doc corpus (CI-gated) |
| Edge runtimes | Cloudflare Workers + Vercel Edge adapters with KV / Edge Config content provider, SHA-256-keyed ISR manifest, invalidation webhook, optional View Transitions client (transitions = true) |
| Edge RPC | #[ssg_rpc] proc-macro, JSON-over-POST dispatch, schemars 1.2 + custom JSON-Schema → TypeScript emitter, golden .d.ts test |
| Edge headers | Per-host emitters for Cloudflare _headers, Netlify _headers, Vercel vercel.json with PQC posture guidance — emits current best-practice TLS/PQC configuration guidance for your CDN (X25519+ML-KEM-768 hybrid notes); ML-DSA content-provenance signing is roadmap (#579) |
| Audit CLI | ssg audit runs 15 gates (WCAG 2.2 AAA, JSON-LD, hreflang, lang consistency, CSP+SRI, PQC TLS, HTML5, broken links, OG, markdown lint, perf budget, AI discovery, RSS/Atom, image opt, search index integrity); JSON / JUnit / SARIF v2.1.0 / text outputs (v0.0.45 #562, GitHub Code Scanning ingestible) |
| Architecture Decision Records | Six baseline ADRs under docs/adr/ in Nygard format documenting the tokio-free architecture, Rayon orchestration, lol_html selection, sync tungstenite HMR, ureq LLM transport, and CycloneDX-over-SPDX SBOM choice. CI-enforced adr: ADR-NNNN citation graph (v0.0.45 #557) |
| Supply-chain attestation | cargo-vet (v0.0.45 #561) layers per-crate audit attestation over cargo deny’s license + CVE checks. Imports Mozilla Firefox, Bytecode Alliance, and Google trust sets; exemption-reduction policy in supply-chain/README.md |
| Concurrency proofs | Miri job (.github/workflows/miri.yml, v0.0.45 #560) runs cargo miri test --lib on a nightly schedule + run-miri-labelled PRs. Loom + Kani follow in v0.0.46 (#564 / #565) |
| Feature-matrix CI | cargo hack check --feature-powerset --depth 2 (v0.0.45 #584) exercises every reachable subset of {ai, benchmark, cli, image-optimization, otel, templates, test-fault-injection} on every PR — catches cfg-gating gaps before they merge |
| Agentic discovery | Opt-in /agents.txt (robots-style AI agent allow/deny), /.well-known/ai-plugin.json (OpenAI plugin manifest), /.well-known/mcp.json (Model Context Protocol registry with auto-populated resources) |
| ISO 20022 JSON-LD | Schema.org descriptors for regulated financial sites: BankAccount, FinancialProduct, MonetaryAmount, PaymentInstrument, RegulatedFinancialInstitution. Built-in IBAN + BIC validators |
| View Transitions | Opt-in (transitions = true) View Transitions API client + lazy hydration; persistent <header> / <footer> get view-transition-name so they don’t animate across boundaries; falls back to plain reload in non-supporting browsers |
| Islands | Web Components with lazy hydration (visible, idle, interaction) |
§The CLI
ssg ships with both a unified subcommand surface (introduced in v0.0.43) and the legacy bare-flag pipeline (preserved with a deprecation warning, removal in 1.0).
§Subcommands (recommended)
Usage: ssg [COMMAND]
Commands:
build Produce a static site under the configured output directory
dev Start the dev server with file watching and HMR
check Run all build-time validators without writing output
plugins Inspect the plugin pipeline
audit Run the 15 native audit gates against the built site
deploy Build the site and ship to a pluggable target
help Print this message or the help of the given subcommand(s)§Build flags
--incremental Skip recompile when DepGraph diff is empty (issue #524)
--isr Emit an SHA-256-keyed ISR manifest for Edge adapters (issue #549)
--no-llm-cache Bypass the deterministic LLM inference cache (issue #528)§Legacy flags (still supported)
-f, --config <FILE> Configuration file path
-n, --new <NAME> Create new project
-c, --content <DIR> Content directory
-o, --output <DIR> Output directory
-t, --template <DIR> Template directory
-s, --serve <DIR> Start development server (HMR + livereload)
-w, --watch Watch for changes and rebuild
-j, --jobs <N> Rayon thread count (default: num_cpus)
--max-memory <MB> Peak memory budget for streaming (default: 512)
--ai-fix Run agentic AI pipeline to fix content readability
--ai-fix-dry-run Preview AI fixes without writing changes
--validate Validate content schemas and exit
--drafts Include draft pages in the build
--deploy <TARGET> Generate deployment config (netlify, vercel, cloudflare, github)
-q, --quiet Suppress non-error output
--verbose Show detailed build information
-h, --help Print help
-V, --version Print version§Library Usage
Minimal pipeline
fn main() -> Result<(), ssg::error::SsgError> {
ssg::run()
}Custom plugin
use ssg::plugin::{Plugin, PluginContext, PluginManager};
use ssg::error::SsgError;
use std::path::Path;
#[derive(Debug)]
struct LogPlugin;
impl Plugin for LogPlugin {
fn name(&self) -> &str { "logger" }
fn after_compile(&self, ctx: &PluginContext) -> Result<(), SsgError> {
println!("Site compiled to {:?}", ctx.site_dir);
Ok(())
}
}
fn main() -> Result<(), SsgError> {
let mut pm = PluginManager::new();
pm.register(LogPlugin);
pm.register(ssg::plugins::MinifyPlugin);
let ctx = PluginContext::new(
Path::new("content"),
Path::new("build"),
Path::new("public"),
Path::new("templates"),
);
pm.run_after_compile(&ctx)?;
Ok(())
}Content schema validation
Create content/content.schema.toml:
[[schemas]]
name = "post"
[[schemas.fields]]
name = "title"
type = "string"
required = true
[[schemas.fields]]
name = "date"
type = "date"
required = true
[[schemas.fields]]
name = "draft"
type = "bool"
default = "false"Pages with schema = "post" in their frontmatter are validated at compile time. Run ssg --validate for schema-only checks.
Readability audit
use ssg::llm::{LlmPlugin, LlmConfig};
use std::path::Path;
let report = LlmPlugin::audit_all(Path::new("content"), 8.0).unwrap();
println!("{}/{} files pass grade 8.0", report.passing, report.total_files);
// Multilingual: French content uses Kandel-Moles automatically
// when frontmatter contains `language: fr`OG image generation
use ssg::og_image::generate_og_svg;
let svg = generate_og_svg("My Page Title", "My Site", "#1a1a2e", "#ffffff");
std::fs::write("og-card.svg", svg).unwrap();
// 1200x630 SVG social card ready for og:imageVector search index (issue #545)
use ssg_search::artifacts::{Artifacts, InputDoc};
use ssg_search::VectorEngine;
// Build side: turn a doc corpus into the 4-file artifact blob.
let docs = vec![
InputDoc {
url: "/post-1".into(),
title: "Rust WebAssembly".into(),
body: "rust compiles to wasm".into(),
excerpt: "rust wasm".into(),
},
InputDoc {
url: "/post-2".into(),
title: "Sourdough".into(),
body: "starter flour water salt".into(),
excerpt: "bread".into(),
},
];
let arts = Artifacts::from_docs(&docs);
std::fs::write("site/search/embeddings.bin", &arts.embeddings).unwrap();
std::fs::write("site/search/model.bin", &arts.model).unwrap();
std::fs::write("site/search/tokenizer.bin", &arts.tokenizer).unwrap();
// Runtime (native or WASM): load and query.
let engine = VectorEngine::new(
&arts.model, &arts.tokenizer, &arts.embeddings, arts.count(),
).unwrap();
let top = engine.search("rust webassembly", 3);
println!("{:?}", top);Edge RPC method (issue #548)
use ssg_rpc::ssg_rpc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, JsonSchema)]
struct EchoIn { msg: String }
#[derive(Serialize, JsonSchema)]
struct EchoOut { msg: String }
#[ssg_rpc]
fn echo(input: EchoIn) -> Result<EchoOut, ssg_rpc::DispatchError> {
Ok(EchoOut { msg: input.msg })
}
// The build emits `site/rpc.d.ts` automatically; clients call:
// const r = await ssgRpc("echo", { msg: "hi" });Audit a built site (issue #551)
ssg audit site/ --out junit > audit.xml
# 15 gates run: WCAG 2.2 AAA, JSON-LD, hreflang, lang consistency, CSP+SRI, PQC TLS,
# HTML5, broken links, OG, markdown lint, perf budget, AI discovery,
# RSS/Atom, image opt, search index integrity.§Examples
Run any example:
cargo run --example basic
cargo run --example blog| Example | Purpose |
|---|---|
basic | Minimal site with SEO, search, and fused transforms |
quickstart | Scaffold and build in 10 lines |
multilingual | Multi-locale site with hreflang and per-locale sitemaps |
multilingual_full | Nested content/<lang>/ layout, plus translated slugs paired by translation_key (/en/about/ ↔ /fr/a-propos/ ↔ /de/ueber-uns/). Asserts reciprocity, not just that pages rendered |
plugins | Full plugin pipeline demo with DepGraph API |
blog | EAA-compliant accessibility-first blog with dual feeds |
docs | Documentation portal with schema validation and syntax highlighting |
landing | Zero-JS landing page with CSP hardening |
portfolio | Developer portfolio with JSON-LD and Atom feed |
§Edge-runtime adapters
examples/edge-cloudflare/ and examples/edge-vercel/ are runnable
reference implementations of the ISR + RPC pipeline for the two target
edge runtimes. Each contains a wrangler.toml / vercel.json, the
TypeScript glue (worker.ts, api/[...path].ts), an upload script
for content provisioning (KV / Edge Config), and a smoke-test
harness.
# Cloudflare Workers
cd examples/edge-cloudflare
npm install
npm run upload:kv # uploads site/ to KV namespace
npx wrangler deploy
# Vercel Edge
cd examples/edge-vercel
npm install
npm run upload:edge-config # uploads site/ to Edge Config
vercel deploy§Themes
A theme is a directory of layouts and the assets they reference. Name one and SSG resolves it:
# ssg.toml
theme = "quill"ssg build --theme quillEither form sets the template directory for you, so a project does not have to hand-write a path into someone else’s tree — a path that breaks when the theme moves and says nothing about which theme it is.
Where themes are looked for, in order:
themes/beside thessg.tomlthat names the theme, so a build does not depend on the directory it was invoked fromthemes/in the working directory- each entry of
SSG_THEME_PATH
Inside a theme, either _layouts/ or templates/ is accepted.
Which convention a theme follows is its author’s business, not its
consumer’s.
Precedence is --template > --theme > theme = in the config.
Naming a theme and a template directory together is how you override one
layout without forking the theme.
A name that does not resolve reports every directory searched and the themes that do exist. A directory that exists but holds no layouts says that instead, and says what it expected to find.
The nine published themes — apex, atlas, kaishi, kinetic,
lucid, quill, stablo, velocity, voxt — live at
ssg-themes.
§Stability guarantees
§What a version bump promises
This project is 0.0.x until 0.0.999
(ADR-0009).
Under Cargo’s semantic-versioning rules every 0.0.z release is its own
compatibility island, so any release may contain a breaking change.
That is stated plainly rather than implied: pin an exact version if you
need stability, and read the CHANGELOG before upgrading.
What the project does instead of a promise it cannot yet keep:
cargo-semver-checksruns on every push against the published baseline, so an accidental API break is caught and has to be deliberate.- Every breaking change is called out in
CHANGELOG.mdunder the release that made it, with the migration.
§The output-stability rule
For a generator the public API is not only the Rust signatures — it is what the tool emits. A change to generated HTML, a meta tag, a sitemap, a feed or a JSON-LD block is a breaking change even when no function signature moves, because it lands in someone’s deployed site and their diff.
So output changes are treated as breaking:
- They are listed in the CHANGELOG like any other break.
tests/golden_files.rspins generated artefacts against checked-in snapshots, so an unintended change to output fails CI rather than shipping.- Refresh a snapshot deliberately with
UPDATE_GOLDEN=1; the diff is then part of the review.
§Deprecation window
A deprecated flag, config key or API keeps working for two releases after the one that deprecates it, and warns when used, naming its replacement. Removal happens no earlier than the third release and is listed in the CHANGELOG.
The legacy top-level CLI flags are the current example: still accepted,
still tested, and warning via LEGACY_DEPRECATION_WARNING.
§Minimum supported Rust version
Rust 1.88.0.
§The policy
The MSRV is a floor set by dependencies, not a target chosen
independently. It currently comes from time-macros, staticdatagen
and the oxc_* crates.
- It may rise in any release, because a
0.0.zrelease carries no compatibility promise (see above). - It rises only when a dependency this project already needs requires it, or when a language feature removes real complexity — never incidentally.
- Each rise is recorded in the CHANGELOG for that release, with the reason and the crate that forced it.
- The floor is CI-enforced:
scheduled.ymlbuilds against[stable, 1.88], so a change that quietly needs a newer compiler fails rather than being discovered by a user.
§On distro compatibility
This project does not claim compatibility with any distribution’s
packaged Rust. That claim would need a table mapping current distro
toolchains to this floor, kept current — and an unverified claim there
is worse than none, because it is exactly what a packager would rely on.
If you need SSG on a distro toolchain, check rust-version in
Cargo.toml against your rustc --version, then build from source or
use a prebuilt binary.
§When not to use SSG
Stated plainly, so the answer is not discovered halfway through a migration:
- You need server-rendered, per-request pages. SSG compiles ahead of
time. Islands hydrate on the client and
ssg-rpcreaches an edge function, but the page itself is a file. An application whose HTML depends on the requesting user is the wrong shape for it. - You want a large theme ecosystem. The theme suite ships nine first-party themes. Hugo has thousands. If picking a ready-made theme matters more than the build-time gates, Hugo is the better answer.
- Your content lives in a CMS you cannot export.
ContentProviderabstracts the source and is used for Cloudflare KV and Vercel Edge Config, but there is no turnkey adapter for a hosted CMS today; you would be writing it. - You need a stable plugin API right now. The
Plugintrait is usable and documented, but it is pre-1.0 and can change between releases. Third-party plugins are compiled in, so a plugin author builds their own binary until the WASM runtime lands. - Build time matters more than build-time checking. Accessibility, CSP extraction and the audit gates are work a renderer-only generator does not do. That is the trade the design makes; if it is the wrong trade for you, it is the wrong tool.
§Development
make # check + clippy + test
make test # run all tests
make bench # run Criterion benchmarks
make lint # lint with Clippy
make format # format with rustfmt
make deny # supply-chain audit
make doc # build API documentation
make a11y # run axe-core accessibility audit
make clean # remove build artifacts§CI
| Workflow | Trigger | Purpose |
|---|---|---|
ci.yml | push, PR | fmt, clippy, test (3 OS), docs lint, install contract, coverage (98% floor), cargo-deny, cargo-vet |
document.yml | push to main | Build and deploy API docs to GitHub Pages |
release.yml | tag v* | Cross-platform binaries, GHCR container, crates.io, AUR |
scheduled.yml | weekly, tag | Multi-OS portability, axe-core a11y, CycloneDX SBOM, benchmarks |
visual.yml | PR | Playwright screenshots (3 viewports) + axe-core WCAG 2.2 AA |
wasm.yml | push, PR | Build and test ssg-core + ssg-wasm for wasm32 |
readability-gate.yml | PR | Flesch-Kincaid audit on docs and content |
See DEVELOPMENT.md for the full developer guide — notably the table mapping every CI job to the exact command that reproduces it locally, which is CI-checked against the workflow. See CONTRIBUTING.md for signed commits and PR guidelines.
§Security
Safety guarantees and compliance
#![forbid(unsafe_code)]across the entire codebase- CSP build-time extraction: all inline styles and scripts moved to external files with SRI hashes. Zero
unsafe-inline - Path traversal prevention with
..detection and symlink rejection - File size limits and directory depth bounds
cargo auditwith zero advisoriescargo deny– license, advisory, ban, and source checkscargo vet– per-crate audit attestation; imports Mozilla Firefox, Bytecode Alliance, and Google trust sets. Seesupply-chain/README.mdCycloneDXSBOM generated as release artifact with Sigstore attestation- SPDX license headers on all source files
- Signed commits enforced via SSH ED25519
See docs/whitepaper/csp-without-compromise.md for the full CSP security architecture.
All 69 modules
| Module | Purpose |
|---|---|
accessibility | WCAG 2.2 AA checker, ARIA validation, decorative image detection, target-size + focus-appearance checks, compliance matrix |
ai | AI-readiness hooks, alt-text validation, llms.txt / llms-full.txt generation |
assets | Asset fingerprinting and SRI hash generation |
cache | Content fingerprinting (FNV-1a) for incremental builds |
cmd | CLI argument parsing, SsgConfig, input validation |
content | Content schemas, ContentValidationPlugin, --validate |
csp | CSP build-time extraction of inline styles/scripts to external files |
depgraph | Page-to-template dependency graph for incremental rebuilds |
deploy | Netlify, Vercel, Cloudflare Pages, GitHub Pages adapters |
drafts | Draft content filtering |
frontmatter | Frontmatter extraction and .meta.json sidecars |
fs_ops | Safe file operations with traversal prevention |
highlight | Syntax highlighting for code blocks |
i18n | Hreflang injection, per-locale sitemaps, language switcher |
image_plugin | Responsive <picture> with WebP, srcset generation |
islands | Web Components with lazy hydration |
livereload | WebSocket live-reload, CSS HMR, browser error overlay |
llm | Local LLM pipeline (Ollama), multilingual readability scoring, agentic fix |
logging | Structured logging configuration |
markdown_ext | GFM tables, strikethrough, task lists |
og_image | Auto-generated OG social card SVGs |
pagination | Pagination plugin for listing pages |
pipeline | Build orchestration, plugin registration, RunOptions |
plugin | Plugin trait with lifecycle hooks, PluginManager, PluginCache |
plugins | MinifyPlugin, ImageOptiPlugin, DeployPlugin |
postprocess | Sitemap, RSS, Atom, manifest, HTML post-processing fixes |
process | Directory creation and site processing |
scaffold | Project scaffolding (ssg --new) |
schema | JSON Schema generator for configuration |
search | Full-text search index, modal UI, 28 locale translations |
seo | SeoPlugin, JsonLdPlugin, CanonicalPlugin, RobotsPlugin |
server | Development HTTP server |
shortcodes | youtube, gist, figure, admonition expansion |
stream | High-performance streaming file processor |
streaming | Bounded-memory batch compiler for large sites (--max-memory budget) |
taxonomy | Tag and category index generation |
listings | Named, filtered, paginated listings |
topic_clusters | Curated pillar-page metadata for topics |
template_engine | MiniJinja templating engine integration |
template_plugin | MiniJinja template rendering plugin |
theme | Resolves a theme name to its layout directory, for theme = / --theme |
walk | Shared bounded directory walkers |
watch | Polling-based file watcher with change classification |
event_watch | Event-driven file watcher (notify::recommended_watcher, 100 ms debounce) — issue #526 |
hmr | Component-level Hot Module Replacement over sync WebSocket (tungstenite) with discriminated hmr-css / hmr-html / reload frames — issue #526 |
llm_cache | Deterministic content-hash-keyed LLM inference cache at target/ssg-cache/llm/ (atomic writes, --no-llm-cache to bypass) — issue #528 |
isr_manifest | Per-page SHA-256 manifest for Edge ISR adapters (--isr flag) — issue #549 |
view_transitions | Opt-in View Transitions API client + lazy-hydration emitter (transitions = true) — issue #547 |
rpc_schema | #[ssg_rpc] JSON-Schema → TypeScript .d.ts emitter — issue #548 |
search_index | Build-side emitter for ssg-search artifacts (embeddings.bin, model.bin, tokenizer.bin, manifest.json) — issue #545 |
postprocess::edge_headers | Per-host header emitters (Cloudflare _headers, Netlify _headers, Vercel vercel.json) with PQC posture guidance — issue #550 |
postprocess::agentic_discovery | /agents.txt + /.well-known/{ai-plugin.json,mcp.json} emitters — issue #552 |
seo::jsonld::iso20022 | ISO 20022 schema.org descriptors for regulated financial sites (IBAN/BIC validators) — issue #553 |
audit | 15-gate audit runner (ssg audit) with JSON / JUnit / text output — issue #551 |
bench_corpus | Deterministic seeded corpora for benchmarking; byte-identical across machines and releases |
agent_api | Agent JSON API emitter — issue #586 |
collections | Typed content collection API — issue #456 |
content_stager | Content staging; isolates residual upstream compiler gaps |
dates | Dependency-free date parsing shared by feeds and sitemaps |
deploy_adapter | Deploy adapter trait + per-target stubs for ssg deploy |
dev_server | Dev-server glue wiring EventWatcher → DepGraph — issue #526 |
error | Error types and context extension traits |
io_pool | Bounded writer-thread pool decoupling disk writes from rayon |
oembed | oEmbed 1.0 emitter — issue #586 |
otel | OpenTelemetry build-pipeline tracing scaffolding — issue #422 |
sbom | Build-time CycloneDX SBOM generation — issue #457 |
theme_manifest | Theme manifest compatibility check |
urls | Canonical page-URL derivation shared by staging, feeds and SEO |
util | Cross-cutting utilities, including the lol_html head walker |
§Workspace crates
| Crate | Published | Purpose |
|---|---|---|
ssg | ✓ crates.io | Main library + binary. Imports the other crates. |
ssg-core | ✓ crates.io | WASM-compatible core: markdown compile, frontmatter parse, ContentProvider trait, ISR manifest types. Shared by build + Edge runtimes. |
ssg-wasm | Internal | wasm32-unknown-unknown entry points for Cloudflare Workers + Vercel Edge (ISR + RPC dispatch). Built via wasm-pack. |
ssg-search | Internal | Browser-native vector semantic search. Int8-quantised hashed-n-gram embedder by default (model-free, deterministic); opt-in real model2vec-rs encoder. |
ssg-rpc | Internal | JSON-over-POST RPC dispatch + schemars 1.2 + custom TS emitter. Paired with #[ssg_rpc] proc-macro for zero-config method registration. |
ssg-rpc-macro | Internal | Proc-macro for #[ssg_rpc] attribute. Registered into a global inventory at compile time. |
§Documentation
| Document | Contents |
|---|---|
BENCHMARKS.md | Performance methodology, CI budgets, comparison tables, and the latest nightly figures |
DEVELOPMENT.md | Toolchain setup, running every CI gate locally, test layout, release model |
docs/ARCHITECTURE.md | How the build pipeline fits together, for contributors |
docs/packaging.md | For distribution maintainers: licence grant, toolchain floor, offline builds, install layout, signature verification |
SECURITY.md | Reporting policy, supported versions, guarantees |
CONTRIBUTING.md | Development workflow and review expectations |
docs/adr/ | Architecture Decision Records in Nygard format |
docs/guide/ | Configuration, content, i18n, search, SEO, deployment |
supply-chain/README.md | cargo-vet policy and exemption burn-down |
docs/SUMMARY.md | User Manual — every guide, reference and decision, rendered with mdbook serve |
| docs.rs/ssg | Generated API reference |
§License
Dual-licensed under Apache 2.0 or MIT, at your option.
See CHANGELOG.md for release history.
Re-exports§
pub use error::PathErrorExt;pub use error::SsgError;pub use staticdatagen;
Modules§
- accessibility
- Automated WCAG accessibility checker and ARIA validation plugin.
- agent_
api - Agent JSON API emitter (issue #586, port 3 of 5).
- ai
- AI-readiness content hooks.
- assets
- Asset optimization: fingerprinting, SRI hashes, and basic minification.
- audit
- Native CI audit gates (issue #549).
- audit_
plugin - Automated 10-Pillar Quality Gate and Master Compliance Audit Plugin.
- bench_
corpus - Deterministic synthetic corpora for benchmarking.
- cache
- Content fingerprinting for incremental builds.
- cmd
- Command Line Interface Module
- collections
- Typed content collection API (issue #456).
- content
- Typed content collections with frontmatter schema validation
- content_
stager - Content staging — residual upstream-gap workarounds for the
staticdatagen→staticweaver→metadata-genpipeline. - csp
- Content Security Policy hardening plugin.
- dates
- Flexible, dependency-free date parsing shared by the feed and sitemap post-processing plugins.
- depgraph
- Page dependency graph for incremental rebuilds.
- deploy
- Deployment adapter generation.
- deploy_
adapter - Deploy adapter trait + per-target stubs for the
ssg deploysubcommand (issue #527 AC4). - dev_
server - Dev-server glue (issue #526) — wires
EventWatcher→DepGraph→HmrBroadcasterinto one loop. - drafts
- Draft filtering plugin.
- error
- Error handling types and context extension traits for the SSG library.
- event_
watch - Event-driven file watcher (issue #526).
- frontmatter
- Shared frontmatter extraction and
.meta.jsonsidecar support. - fs_ops
- File system operations: directory copying, safety validation, and traversal.
- highlight
- Syntax highlighting plugin.
- hmr
- Hot-module-reload protocol for
ssg dev(issue #526). - i18n
- Internationalisation (i18n) routing primitives
- image_
plugin - Image optimization plugin.
- io_pool
- Bounded writer-thread pool that decouples disk writes from rayon CPU workers (issue #569, phase 1).
- islands
- Resumable hydration — interactive islands (Web Components).
- isr_
manifest - ISR manifest emitter —
dist/.ssg/manifest.json+ raw content KV payloads underdist/.ssg/content/. - listings
- Named, filtered, paginated listings (#587).
- livereload
- Live-reload script injection plugin.
- llm
- Local LLM content plugin.
- llm_
cache - Deterministic content-hash-keyed cache for local LLM inference (issue #528).
- logging
- Logging infrastructure for the static site generator.
- markdown_
ext - GitHub Flavored Markdown (GFM) extensions plugin.
- oembed
- oEmbed 1.0 emitter (issue #586, port 4 of 5).
- og_
image - Auto-generates Open Graph social card images from page metadata.
- otel
- OpenTelemetry build-pipeline tracing scaffolding (issue #422).
- pagination
- Pagination plugin.
- pipeline
- Build pipeline: plugin orchestration and site compilation.
- plugin
- Plugin architecture for SSG
- plugins
- Built-in plugins
- postprocess
- Post-processing plugins that fix staticdatagen output.
- process
- Argument-driven site processing.
- rpc_
schema - Edge RPC schema emitter (issue #548 AC1 + AC4).
- sbom
- Build-time SBOM generation (issue #457).
- scaffold
- Project scaffolding.
- schema
- Configuration Schema Generator
- search
- Client-side search index generator.
- search_
index - Vector-search artifact emitter (issue #545).
- seo
- SEO plugins for the static site generator.
- server
- Dev server infrastructure for the static site generator.
- shortcodes
- Shortcode expansion plugin.
- stream
- High-performance streaming file processor.
- streaming
- Streaming compilation for large sites.
- taxonomy
- Taxonomy generation plugin.
- template_
engine - Template engine integration (
MiniJinja). - template_
plugin - Template rendering plugin.
- theme
- Theme resolution.
- theme_
manifest - Theme manifest compatibility check.
- topic_
clusters - Curated metadata for the
topicstaxonomy (#587). - urls
- Canonical page-URL derivation shared by staging, feeds, and SEO output (spec A2/B1, plan §2 item 1.2, issue #586).
- util
- Cross-cutting utility modules.
- view_
transitions - View Transitions API + lazy navigation (issue #547).
- walk
- Shared bounded directory walkers.
- watch
- File-watching module for the static site generator.
Structs§
- Http
Transport - Production transport: starts an
http_handle::Server. - Paths
- Represents the necessary directory paths for the site generator.
- Paths
Builder - Builder for creating Paths configurations
Constants§
- MAX_
DIR_ DEPTH - Maximum directory nesting depth for all traversal operations. Prevents stack overflow from pathological or circular directory trees. 128 levels accommodates any realistic project structure.
Traits§
- Serve
Transport - Pluggable transport that drives the dev server.
Functions§
- collect_
files_ recursive - Recursively collects all file paths within a directory.
- compile_
site - Compiles the static site from source directories.
- copy_
dir_ all - Recursively copies a directory whilst maintaining structure and attributes.
- copy_
dir_ all_ async - Asynchronously copies an entire directory structure, preserving file attributes and handling nested directories.
- copy_
dir_ with_ progress - Copies directories with a progress bar for feedback.
- create_
directories - Creates and verifies required directories for site generation.
- create_
log_ file - Creates and initialises a log file for the static site generator.
- execute_
build_ pipeline - Runs the build half of the pipeline:
before_compile→ compile →after_compile. Does not start the dev server. - generate_
locale_ redirect - Generates a root index.html that reads the browser’s language preference and redirects to the best matching locale directory.
- handle_
server - Configures and launches the development server.
- is_
path_ within_ root - Checks that
pathresolves to a location insideroot. - is_
safe_ path - Checks if a given path is safe to use.
- log_
arguments - Logs processed command-line arguments for debugging and auditing.
- log_
initialization - Records system initialisation in the logging system.
- now_iso
- Returns the current time as an ISO 8601 UTC string.
- prepare_
serve_ dir - Prepares the serve directory by creating it and copying site files.
- run
- Executes the static site generation process.
- serve_
site - Converts a site directory path to a string and starts an HTTP server.
- serve_
site_ with - Starts the dev server using a caller-supplied transport.
- verify_
and_ copy_ files - Validates and copies files from source to destination.
- verify_
and_ copy_ files_ async - Asynchronously validates and copies files between directories.
- verify_
file_ safety - Verifies the safety of a file for processing.