Build pipeline
When you run make publish, a lot happens inside the container. This is the path from your source to a finished
artifact. It applies to every template — in core you can read every piece of it under .pandoc/.
From make to artifact
make publishruns the engine'spublish.shinside the Keystone image, with yourmanuscript/,assets/,fonts/, and config mounted in.publish.shreadspublish.txt, merges the system and user shortcuts, and resolves metadata.- The lint reads the listed manuscript files, in order, and your
shortcuts.yaml, stopping the build on markup Pandoc would misread — see Lint findings. publish.shinvokes Pandoc on those files with the right options for the requested format.- Pandoc concatenates those files and parses the Markdown into its document model, then runs Keystone's Lua filters over it (below).
- Pandoc's writer for the format emits the result — XeLaTeX to PDF, the EPUB archive, or a DOCX/ODT built from a styled reference document.
- The artifact lands in
artifacts/, named<project>-<target>[-<edition>]-<date>(the edition segment is set byusing=).
The Lua filters
The heart of the engine is a chain of Lua filters, run in this order. Order matters — each builds on the output of the ones before:
hooks.lua— hands each code block a hook claims to that hook and splices back what it returns. Present only when a hook is wired; first, because what comes back is author-level markup that the rest of the chain still has to see.shortcuts.lua— expands shortcut names into handler classes and attributes, so everything downstream sees plain handler elements. The system shortcuts are compiled into the image; before Pandoc runs, yourshortcuts.yamlis compiled over them, stopping on anything it can't take, and this filter expands each use from the result. See The shortcut system.keystone.lua— validates and enriches metadata (required fields per target, date, cover image, draft watermark, code theme) and injects the font and style definitions for the format.divs.lua— the dispatcher. It routes every div, span and heading a shortcut expanded to its handler and emits format-specific output. See Handlers.page-furniture.lua— composes the PDF running headers, footers, and page numbers from your metadata, resolving placeholders like{chapter}, and writes the running-head marks an unnumbered heading's starred sectioning command skips. It runs after the handler filters so any handlers inside header text have already rendered, and it sees each heading as the handlers left it.math-check.lua— the guardrail. Stops the build (naming the equation) when one can't convert to your target format, instead of leaking raw LaTeX into the output. See Mathematics.
The EPUB expand pass
EPUB embeds only the fonts your book actually uses, and Pandoc needs that list before the run that writes the archive. So an EPUB build runs hooks, expands shortcuts and finds the referenced font families in a pass of its own, then hands the main run the expanded document, which embeds exactly those fonts. Every hook is asked once and every diagnostic prints once. Other formats build in one run.
Where it lives
In core, all of this is in your project under .pandoc/: publish.sh and import.sh, the shell libraries they
source under lib/, the filters/ directory (the Lua filters and the per-handler directories under filters/divs/),
and metadata/ (the target bundles). In core-slim the same files live inside the prebuilt image. To change any of it,
use core and see Customizing.