MarqueDown

This website is powered by MarqueDown, a lightweight site engine written in C++17 by Privateer Systems. The name is a play on Markdown (MD) (which is a lightweight plain-text formatting language that uses simple symbols to structure text—such as headings, lists, links, and emphasis - so it can be easily converted into formatted documents like HTML), our version which assembles a whole website out of those simple files, and letters of Marque which were Privateering licences.

How it works

Content is authored in Markdown and stored in a git repository. When a commit lands, MarqueDown renders every page through HTML templates and writes the result as ordinary files on disk — HTML, CSS, images, RSS feed, sitemap and search index. Apache serves that directory. Nothing is rendered while a visitor waits.

Building this site takes 0.18 seconds on the ARM board that hosts it. All three sites we run — 268 pages between them — take 1.4 seconds and 8.5 MB of memory, and then the process exits. Between builds there is no engine running at all.

There is no database, no CMS, and no JavaScript framework.

Features

Performance

To put a page request in context: a typical WordPress request involves PHP execution, multiple database queries and plugin hooks — often 200-500 ms even with caching plugins. MarqueDown does none of that work when you ask for a page, because the page already exists. Apache reads a file and sends it.

A static site generator such as Hugo or Jekyll reaches the same place, and the difference is what happens between writing a sentence and it being live. Those tools give you a build step somebody has to run, and a deployment somebody has to wire up. MarqueDown is that step: push to git and the site rebuilds itself and swaps the new version into place, unattended, in about eight seconds end to end — fetch, render, check every internal link, and publish.

Earlier versions of MarqueDown held the whole site in memory and served it over FastCGI, which was genuinely fast but made the engine part of every request. That was the wrong trade and we changed it. A rebuild could land on an unlucky visitor and make them wait for it. A mistake in a config file could stop a process that three sites depended on at once. Building to disk removes the entire category: whatever the builder does — crash, run long, fail on bad content — the last good version of the site carries on being served.

Publishing is a single atomic operation, so there is no moment when a site is half-updated. We measured this by hammering all three sites with 1200 requests while forcing a full rebuild and swap of every one of them: no request failed. Rolling back to any of the last five builds is the same operation in reverse, and takes effect immediately.

The whole thing runs on a low-power ARM board alongside our other services.

Architecture

MarqueDown architecture diagram

The stack is deliberately minimal, and nothing in the serving path is ours:

Each build is written to its own directory and published by repointing a single symbolic link, which is why publishing is atomic and rollback is instant. Unchanged files are shared between builds, so keeping the last five costs little more than keeping one.

Markdown formatting

MarqueDown uses GitHub Flavoured Markdown (cmark-gfm) with these extensions:

Syntax Result
# Heading to ### Heading Headings (H1 to H3)
**bold** bold
*italic* italic
~~strikethrough~~ strikethrough
[text](url) Link
![alt](image.jpg) Responsive image with srcset
![alt](image.jpg?w=600) Image at specific width
![alt](image.jpg?thumb) Thumbnail (300px)
- item Bullet list
1. item Numbered list
`code` Inline code
``` block ``` Code block
> quote Blockquote
--- Horizontal rule
Pipe tables Tables (as on this page)
text[^1] and [^1]: note Footnotes with return links
Bare URLs Auto-linked

Template and include syntax

Syntax Context Description
<<file.md>> Markdown pages Include shared text block from _includes/
{{extends base.html}} Templates (first line) Inherit from parent template
{{include footer.html}} Templates Include a template fragment
{{variable}} Templates Substitute a front matter field or auto variable
{{blog_list blog/ 20}} Templates Generate date-sorted blog listing
{{category_links}} Templates The current page's categories, as links
{{tag_links}} Templates The current page's tags, as links
{{translation_url}} Templates URL of this article in the other language
{{hreflang_links}} Templates Search-engine language alternates

Front matter

Each page starts with YAML-style metadata:

---
title: Page Title
date: 2026-03-14
template: blog_post
summary: Short description for listings and RSS.
image: thumbnail.jpg
permalink: /an/exact/url/
categories: governance, transparency
tags: planning, review
lang: en
translation_key: shared-key
---

The title, summary, and any custom fields become template variables. template selects which HTML template to render through (defaults to base). date is used for blog sorting. image provides a thumbnail for blog listings.

permalink sets the exact URL a page serves at, overriding the one derived from its filename — this is what lets content imported from another system keep its existing addresses. categories and tags are lists of slugs defined in _includes/categories.def and _includes/tags.def. lang and translation_key group an article with its translations.

Auto-generated variables available in templates: {{content}} (rendered page body), {{year}} (the year the site was built), {{menu}} (navigation HTML), {{performance}} (how long this page took to render during the build).

Categories and tags

Categories nest; tags do not. Both are declared in a plain text file, with two-space indentation for nesting:

- governance                Governance
  - transparency            Transparency
  - code-of-conduct         Code of Conduct
- planning                  Planning

Pages then reference them by slug. The engine generates an archive page for every term — /category/governance/transparency/ and /tag/planning/ — and a page filing itself under a term that no definition declares is a build error rather than a link that quietly 404s.

Search

Search is built at render time into a JSON index and filtered in the browser. There is no search server, no database and no third-party script: the index is a static file like any other page, and it compresses to well under 50 KB for a site of a hundred articles. Accents are folded on both sides of the comparison, so an unaccented query still finds accented text — searching zeleznice finds železnice.

Multiple languages

A multilingual site keeps each language in its own directory and gets a separate navigation menu, blog listing, feed, search index and set of categories per language. Articles are linked to their translations by a shared translation_key, which gives each page a link to its counterpart and generates the reciprocal hreflang metadata search engines expect — including x-default pointing at the site's primary language.

Image pipeline

Source images placed in _images/ are automatically resized to multiple widths (300px, 600px, and full size) and written to static/img/ with responsive srcset attributes. Supported formats: JPEG, PNG, and GIF.

Generated pages

Redirects

A _redirects file maps old URLs to new ones with 301 permanent redirects:

/old-wordpress-url    /new-page
/2024/03/legacy-post  /blog/2024-03-15-new-post

The build writes these out with the pages, exactly as it parsed them, so the web server redirects precisely what MarqueDown understood — and a rollback takes the redirects back with the content they belong to.

Safety

All recursive expansion paths are protected against runaway loops:

If a cycle or excessive depth is detected, the engine returns a clear error identifying the offending file rather than looping indefinitely.

Why not WordPress?

This site was previously a WordPress installation. MarqueDown replaces it with something simpler, faster, and more secure:

This epitomises our approach to engineering. Efficient, secure, simple.