Hauchiwa Docs

How It Works

This page explains the internals Hauchiwa relies on. You don't need to understand all of this to use the library, but it helps when debugging builds or designing a large task graph.

Execution model

When you call website.build(), Hauchiwa:

  1. Performs a topological sort of the task graph to find a valid execution order.
  2. Seeds all tasks with no dependencies onto the Rayon thread pool.
  3. As each task completes, its result is placed in a cache and any downstream tasks that now have all their dependencies satisfied are seeded onto the pool.
  4. The main thread waits for all tasks to finish, collecting results and timing data.

This means independent tasks always run in parallel automatically - you get full CPU utilisation without writing any async code.

Diamond dependencies

When two tasks share a common upstream dependency, that upstream task runs exactly once and its result is shared:

    A  (load markdown)
   / \
  B   C  (render HTML, build index)
   \ /
    D  (generate sitemap)

Task A is executed once. B and C run in parallel once A finishes. D runs after both B and C complete. Handles are reference-counted pointers to the cached result, so sharing is zero-copy.

Task granularity

Hauchiwa has two execution modes for tasks:

Use One<T> for aggregators (sitemaps, search indexes, import maps). Use Many<T> for per-file transforms (markdown -> HTML, image optimisation) where surgical invalidation matters.

Content-Addressable Storage

Assets produced by Store::save (CSS bundles, JS bundles, images) are stored using their BLAKE3 content hash as the filename:

.cache/hash/
  a1b2c3d4e5...   (cached source)

dist/hash/
  a1b2c3d4e5.css  (served to browser)

This gives two guarantees:

  1. Deduplication - identical content is stored and served once regardless of how many tasks produce it.
  2. Cache-busting - when content changes, the hash changes, so browsers never serve stale assets from a long-lived cache.

Dist reconciliation

After every build, Hauchiwa assembles a Snapshot - an in-memory record of every file that belongs in dist/, which task produced it, and a BLAKE3 hash of the content.

Two strategies are used depending on context:

A slim version of the snapshot (content hashes only) is persisted to {cache_dir}/snapshot/metadata.cbor (default: .cache/snapshot/metadata.cbor) after each successful build, so the diff path is also taken on the first watch-mode rebuild after a cold build. The cache directory can be changed with Blueprint::set_dir_cache().