C++23 · OpenMP · Bytecode Compiler

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.

506ms 76 items + archives
C++23 zero runtime
27 template filters
4-phase build pipeline
8 pagination strategies

Same content. Same CMS. Same hardware.

41 posts, 33 tags, 2 pages, 1 author — fetched from the same Ghost instance.

gatsby build (cached)
success load plugins - 0.402s
success source and transform nodes - 0.402s
warn createResolvers passed resolvers for type that doesn't exist in the schema
success building schema - 0.353s
success extract queries from components - 0.928s
warn The GraphQL query in the non-page component will not be run
warn Browserslist: caniuse-lite is outdated
warn `isModuleDeclaration` has been deprecated
success Building production JavaScript and CSS bundles - 4.420s
success Building static HTML for pages - 0.300s - 89/89 297.08/s
info Done building in 9.88 sec
4 warnings · GraphQL · node_modules · webpack
guss build
[2026-03-25 21:46:54.175] [console] [info] 🔥 GUSS BUILD, WITNESS PERFECTION
[2026-03-25 21:46:54.175] [console] [info] Loading configuration from guss.yaml
[2026-03-25 21:46:54.176] [console] [info] Using REST API adapter: https://ghost.michm.de/
[2026-03-25 21:46:54.176] [console] [info] Phase 1: Fetching content from rest_api
[2026-03-25 21:46:54.700] [console] [info] RestCmsAdapter: fetched 4 collections
[2026-03-25 21:46:54.700] [console] [info]   tags: 33 items
[2026-03-25 21:46:54.700] [console] [info]   authors: 1 items
[2026-03-25 21:46:54.700] [console] [info]   pages: 2 items
[2026-03-25 21:46:54.700] [console] [info]   posts: 41 items
[2026-03-25 21:46:54.700] [console] [info] Fetched 4 collections, 77 total items
[2026-03-25 21:46:54.700] [console] [info] Phase 2: Preparing content
[2026-03-25 21:46:54.701] [console] [info] Phase 3: Rendering templates
[2026-03-25 21:46:54.704] [console] [info] Phase 4: Writing 82 files
[==================================================] 100% [00m:00s] Writing files...
[2026-03-25 21:46:54.756] [console] [info] Generated sitemap.xml (82 URLs)
[2026-03-25 21:46:54.756] [console] [info] Generated feed.xml
[==================================================] 100% [00m:00s] Writing files...
[2026-03-25 21:46:54.756] [console] [info] Build complete in 506ms (77 items, 5 archives, 2 extras, 0 minified)

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.

20 faster build
300 faster HTML render
0 warnings
0 node_modules

Connect to any content source.

Pluggable adapters. Eight pagination strategies. Zero glue code.

👻 Ghost
🔷 WordPress
Contentful
📄 Markdown files
🔌 Any JSON API

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.

Fetch Adapters
Prepare Enrich
Render OpenMP
Write HTML + assets
01 / Fetch

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.

02 / Prepare

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.

03 / Render

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.

04 / Write

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.

post.html — template source
{% 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 %}
Rendered HTML — dist/2024/01/hello-world/index.html
<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.

01

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.

02

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.

03

No exceptions in the loop

All error paths use std::expected. The only try/catch boundary is in Runtime::load() — template compilation, not execution.

04

32 KiB pre-reserved output buffer

WRITE_BUFFER_SIZE = 0x8000 is reserved upfront on every render() call. Typical pages never trigger a reallocation.

05

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.

06

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.

1
total_pages_header

HTTP header declares total page count upfront

2
total_count_header

HTTP header with item count → pages via ceil(n/limit)

3
link_header

Follow verbatim Link: rel="next" URL (RFC 5988)

4
json_cursor

Extract opaque cursor token from response body

5
json_next_url

Body field contains full URL of the next page

6
json_next

Dot-path non-null sentinel + page counter (Ghost-style)

7
optimistic_fetching

Blind GET N+1 until 404 or empty response

8
single page

No pagination — fetch once and stop

Start casting.

Build your first site in under two minutes.