Simple things should be simple, and difficult things should be possible.
Keystone is a minimalist book publishing template that helps authors not only get to the finish line faster, but think more clearly about structure, tooling, and ownership.
It gives you a reproducible build system, clean separation of layout and content, and a dev-friendly workflow with just enough automation to stay out of your way.
Built with Make, Markdown, Pandoc, LaTeX, and Docker.
- βοΈ Write in plain Markdown
- π Build clean, timestamped artifacts via
make - π Inject metadata via .env β control title, author, layout, keywords, and PDF formatting
- βοΈ Powered by Pandoc, LaTeX, and Docker
- βοΈ Keeps guts (like
.pandoc/,publish.sh) out of your way - β¨οΈ Editor-agnostic: works from any terminal
Why Pandoc? Itβs flexible, scriptable, and supports features like table of contents generation, custom styling, page breaks, heading levels, bibliography, footnotes, and cross-referencing β everything you need to produce a structured, professional-quality document.
Keystone supports advanced formatting through Pandoc's fenced div syntax, using the form ::: div-name and :::.
This lets you apply custom styling or behavior by wrapping sections of content in named blocks β like ::: dialog for character conversations.
For example, you can create a dialog block like this:
::: dialog
- Whoβs there?
- Just the wind.
:::π‘ No special syntax is needed for prose-style dialog; just write your dialog using standard Markdown. The output will format it as prose.
For more examples, take a look at the sample chapter-2.md file.
Keystone supports inline LaTeX inserts when building PDF output. This gives you full access to custom tables, equations, symbols, and layout commands β all embedded directly in your Markdown.
To include raw LaTeX in your document, wrap it in a ::: latex-only block. This ensures itβs rendered only in LaTeX/PDF output, and skipped in other formats like DOCX or EPUB.
For example, hereβs a LaTeX table using \tick symbols:
::: latex-only
\begin{center}
\begin{tabular}{|c|c|}
\hline
\textbf{Feature} & \textbf{Supported?} \\
\hline
Markdown & \tick \\
LaTeX Inserts & \tick \\
Lua Filters & \tick \\
Dockerized Builds & \tick \\
\hline
\end{tabular}
\end{center}
:::1. Use this repo as a template: Click the βUse this templateβ button on GitHub to create your own book project (e.g., my-book), then:
git clone git@github.com:yourname/my-book.git
cd my-book2. Edit your metadata: Set your project name, book's title, author, description, etc. This file is version-controlled and used at build time.
nano .env
nano pandoc.yaml3. Add content: Write your book in Markdown. Keystone uses a simple folder structure to keep things organized:
chapters/ # Your main content, e.g., introduction.md, chapter-1.md
appendix/ # Optional extras, e.g., appendix-a.md
assets/ # Images, cover.png, etc.
The publish.txt file defines the exact order of files to be included in the output. Edit it to rearrange chapters or exclude drafts without renaming source files.
For example, create and add these files to publish.txt:
chapters/introduction.md
chapters/chapter-1.md
appendix/appendix-a.md
π‘ Pandoc numbers all top-level sections automatically.
Because of this, avoid including chapter numbers in your Markdown titles β theyβre applied during export. This keeps the source files clean and makes renumbering painless.
To exclude specific headings (e.g. in a preface or appendix), use the {.unnumbered} attribute on each header in the file:
# Preface {.unnumbered}
## Introduction {.unnumbered}4. Build your book:
make allOutputs will appear in the artifacts/ folder. For example, if you set KEYSTONE_PROJECT=hello-world in .env, the output will be:
artifacts/
βββ hello-world-book-20250405.pdf
βββ hello-world-book-20250405.epub
βββ ...
Keystone supports importing existing documents (such as .docx, .odt, or .html) and converting them to Markdown using Pandoc.
This allows you to bring in drafts or outlines from Word or other editors and start working with them in your versioned Markdown workflow.
Place your document in the artifacts/ folder, for example:
artifacts/my-draft.docx
Run the import command:
make import artifact=my-draft.docxThe converted Markdown will be saved as:
artifacts/my-draft-imported.md
- Move the converted Markdown file to one of the following:
- ./chapters for main content
- ./appendix for supplemental material
- Move media assets to ./assets if needed
- Adjust heading levels and image paths in the Markdown file
- Update publish.txt to include the new file in your book structure
Tip: Structure your content with one file per chapter or appendix. While itβs technically possible to keep everything in a single file, doing so can make your project harder to manage and maintain over time.
All project metadata and publishing options live in two files: .env and pandoc.yaml.
This includes things like:
- Project title, subtitle, author, keywords
- Page size and margin settings for PDF builds
- Build metadata like date and description
Both files are sourced by publish.sh and passed through to Pandoc. You can safely customize them to match your book or document.
For examples and advanced options, see the commented blocks in both files.
You can install example content (chapters, appendix, and publish.txt) by running:
make sample
β οΈ This only works if publish.txt is effectively empty β containing no publishable file entries, only comments or blank lines.
This gives you a complete working example of a Keystone book, useful for experimenting or exploring the system.
This is the core Keystone template β editor-agnostic by design. Other variants are available depending on your needs or desire to customize.
- π οΈ No IDE-specific tooling
- π Works with any text editor
- π§ Built to be run directly from the terminal using make
Keystone focuses on building books, not dictating your workflow. You are free to integrate Keystone into any editor (VS Code, JetBrains, Neovim, etc.) however you like.
If you're using Windows, run this project inside WSL for full compatibility. Keystone requires LF (Unix-style) line endings β this is enforced by .editorconfig. Avoid CRLF endings to prevent formatting and build issues.
This project has the Makefile to simplify the workflow, which requires Docker Desktop.
π‘ On Windows, open this project inside WSL and run
makefrom a WSL terminal for the best results. This ensures consistent paths, permissions, and line endings. See this article for more info.
You can run these commands from your terminal or integrate them into your flow:
| Target | Description |
|---|---|
image |
Builds the Docker image using docker-compose.yaml |
import |
Imports a document (DOCX, ODT, RTF) from the artifacts folder |
publish |
Builds a specific format using publish.sh |
all |
Builds PDF, EPUB and DOCX formats |
clean |
Prunes images and deletes generated PDFs/EPUBs from artifacts |
sample |
Installs sample content (only if publish.txt is empty) |
help |
Displays a list of available Make targets and usage examples |
Example:
make publish
make publish format=epub
make all
make clean
make sample
make help. # Project root
βββ .docker/ # Docker image and Compose config
β βββ Dockerfile
β βββ docker-compose.yaml
βββ .keystone/ # Hidden helpers (sample content, etc.)
β βββ sample/ # Sample chapters, appendix, and publish.txt
β βββ sync.json # Sync metadata
βββ .licenses/ # License documents (Keystone, Pandoc)
βββ .pandoc/ # Pandoc filters and metadata
β βββ filters/
β βββ includes/
β βββ metadata/
β βββ import.sh
β βββ publish.sh
βββ appendix/ # Appendices (e.g., appendix-a.md)
βββ artifacts/ # Output folder for built PDFs and EPUBs
βββ assets/ # Images and cover art
βββ chapters/ # Main content chapters (e.g., introduction.md, chapter-1.md)
βββ drafts/ # Work-in-progress material
βββ research/ # Notes, references, citations
βββ .dockerignore # Docker ignore file
βββ .editorconfig # Editor defaults
βββ .env # Project configuration
βββ .gitattributes # Git attributes
βββ .gitignore # Git ignore file
βββ Makefile # Build commands
βββ NOTICE.md # Project notices and third-party tool acknowledgments
βββ pandoc.yaml # Pandoc metadata (title, author, etc.)
βββ README.md # This file
βββ publish.txt # List of content files to include in order
Keystone stands on the shoulders of giants.
This project would not be possible without the incredible tools and communities that power its every build:
- Pandoc β the universal document converter
- LaTeX β for professional-quality typesetting
- Docker β containerized reproducibility made simple
- GNU Make β declarative builds that just work
- Lua β a lightweight, expressive scripting language used to extend Pandoc
- Markdown β the plain-text format that changed writing forever
Each one of these projects represents years of collective wisdom, generosity, and craft β made available freely, for all.
To their maintainers and contributors: thank you. Keystone is a bridge, but you laid the foundation.
The Keystone template is released under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
You are free to:
- Share and adapt this template for personal and non-commercial use
- Modify it to suit your book projects
However:
- Commercial use (including hosted services or SaaS platforms) is not permitted without express written permission
- Derivative works must be shared under the same license terms
See .licenses/Keystone.md for full details.
βοΈ Keystone builds upon Pandoc, which is licensed under the GNU General Public License (GPL).
For details, see .licenses/Pandoc.md.
Project Keystone is developed and maintained by Knight Owl LLC. If you use this template or build upon it, a link back to this repository is appreciated. Please also retain license and attribution notices in derivative works to help others trace the origin of the system.
Keystone is the foundation. What you build with it is entirely yours.
Ready to write your first book like a dev? Let's go.