How It Works

This page gives a high-level view of dodeca's architecture for the curious.

Host + Cells

dodeca runs as a host process that coordinates a set of cell processes. Each cell handles a specialized task — image processing, markdown rendering, SASS compilation, template rendering, font subsetting, etc.

Cells communicate with the host via roam RPC over shared memory. This avoids serialization overhead for large payloads like images and rendered HTML.

graph TD
    Host[ddc host] --- |SHM| img[cell-image]
    Host --- |SHM| md[cell-markdown]
    Host --- |SHM| tpl[cell-gingembre]
    Host --- |SHM| sass[cell-lightningcss]
    Host --- |SHM| font[cell-fontcull]
    Host --- |SHM| etc[...]

Cell binaries are named ddc-cell-* and are discovered on PATH at startup.

Incremental computation

dodeca uses picante — an async incremental query system similar to Salsa. Every transformation is a tracked query:

  • Parse markdown → query
  • Render template → query
  • Process image → query
  • Subset font → query

Queries track their dependencies automatically. When a file changes, picante invalidates only the affected queries and re-runs them. Everything else is served from cache.

The query cache persists across restarts, so even a cold start benefits from previous work.

Content-addressable storage

Large outputs (processed images, subsetted fonts) are stored in a content-addressable blob store (.cache/blobs/). Files are keyed by content hash — if two pages use the same image, it's processed once.

Shared memory layout

Host-cell communication uses 4 size classes of shared memory slots:

SizeCountPurpose
1 KB1024Small RPC arguments
16 KB256Typical payloads
256 KB32Images, CSS
16 MB8Large blobs

This avoids dynamic allocation in the hot path.

Live reload

During ddc serve, dodeca diffs the old and new HTML using hotmeal and sends DOM patches to the browser via a small injected script. Only the changed parts of the page update — no full-page reload, no scroll position reset.