Static sites,
cast in C++.
A pluggable static site generator that fetches from any CMS, compiles templates to bytecode, and writes output that loads before your users click.
Same content. Same CMS. Same hardware.
41 posts, 33 tags, 2 pages, 1 author — fetched from the same Ghost instance.
Gatsby numbers from a cached build (no image processing) on identical hardware against the same Ghost CMS instance. HTML render: Gatsby 300ms for 89 pages vs Guss <1ms for 82 pages.
Connect to any content source.
Pluggable adapters. Eight pagination strategies. Zero glue code.
The ContentAdapter base class provides field mapping, cross-reference
injection, taxonomy synthesis, and prev/next linking out of the box.
Implement two methods to add a new source.
The full pipeline, nothing extraneous.
Any REST CMS or Markdown files
Pluggable adapters pull from Ghost, WordPress, Contentful, or local Markdown. Collections fetched in parallel via OpenMP. API pagination handled automatically across eight strategies.
Archives, pagination, cross-references
Item pages are enriched with computed fields (year, month, permalink, output path). Archive pages — paginated or single — are generated from the collection config. Cross-references stitch collections together without hardcoding.
Bytecode engine, OpenMP parallel, zero heap in the hot path
Templates compile once to flat bytecode. The runtime executes against a stack-allocated Context — no virtual dispatch, no heap allocation, no exceptions inside the loop. Pages rendered concurrently across all CPU cores.
Static HTML, asset copy, sitemap, RSS
Rendered files written sequentially to the output directory. Assets copied from templates/assets/. sitemap.xml and feed.xml generated automatically. Optional HTML minification.
Jinja2-compatible. Compiled to bytecode.
Write familiar template syntax. Guss compiles it once, executes it thousands of times — no re-parsing per page.
{% extends "base.html" %}
{% block content %}
{% set reading = post.html | reading_minutes %}
<article>
<header>
<h1>{{ post.title }}</h1>
<time>{{ post.published_at | date("%B %d, %Y") }}</time>
<span>{{ reading }} min read</span>
</header>
{% if post.tags | length > 0 %}
<ul class="tags">
{% for tag in post.tags %}
<li>
<a href="{{ tag.permalink }}">{{ tag.name }}</a>
{% if not loop.last %} · {% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
<div class="prose">{{ post.html | safe }}</div>
</article>
{% endblock %}
<article>
<header>
<h1>Hello, World!</h1>
<time>January 15, 2024</time>
<span>3 min read</span>
</header>
<ul class="tags">
<li>
<a href="/tags/cpp/">C++</a>
·
</li>
<li>
<a href="/tags/performance/">Performance</a>
</li>
</ul>
<div class="prose">
<p>The actual rendered post body…</p>
</div>
</article>
Why C++?
The render hot path has exactly six rules. All of them are enforced by the compiler or the type system.
No heap allocation in the loop
The value stack (Value stack[64]) and loop stack (LoopFrame loop_stack[16]) are fixed C-arrays declared in execute()'s stack frame. Stack depth is verified at compile time.
No virtual dispatch in the loop
The switch on instr.op is a CPU jump table. Filters are integer IDs resolved at compile time — no string lookup, no virtual calls.
No exceptions in the loop
All error paths use std::expected. The only try/catch boundary is in Runtime::load() — template compilation, not execution.
32 KiB pre-reserved output buffer
WRITE_BUFFER_SIZE = 0x8000 is reserved upfront on every render() call. Typical pages never trigger a reallocation.
8 KiB PMR stack arena for Context
Each render thread stack-allocates an 8 KiB monotonic_buffer_resource. Variable bindings ({% set %}, loop variables) live in the arena — zero heap in the common case.
OpenMP parallel render
All pages render concurrently across every CPU core. The Runtime cache is read-only during the parallel phase — no locks in the hot path.
Every API pagination pattern. Covered.
Eight strategies evaluated in priority order. Mix and match per endpoint.
total_pages_header
HTTP header declares total page count upfront
total_count_header
HTTP header with item count → pages via ceil(n/limit)
link_header
Follow verbatim Link: rel="next" URL (RFC 5988)
json_cursor
Extract opaque cursor token from response body
json_next_url
Body field contains full URL of the next page
json_next
Dot-path non-null sentinel + page counter (Ghost-style)
optimistic_fetching
Blind GET N+1 until 404 or empty response
single page
No pagination — fetch once and stop
Start casting.
Build your first site in under two minutes.