Architecture
This page explains how Pycora Pro works internally. Read this first if you plan to modify or extend the engine.
Overview
Pycora Pro is a static site generator. It reads Markdown, YAML, and templates, and produces static HTML.
The entire process is one command:
python ssg.py
That command runs a pipeline:
content/ ──┐
templates/ ──┤
static/ ──┤──► ssg.py ──► output/
_data/ ──┘
The Build Pipeline
Here is the exact order of operations inside ssg.py:
1. Load Configuration
Reads every YAML file inside _data/ and merges them into a single config dictionary.
_data/site.yaml— global site settings- Any other
.yamlor.ymlfile in_data/— merged under its filename
Legacy support: if config.yaml exists at the project root, it is also merged.
2. Set Up Directories
Ensures these folders exist:
content/templates/templates/layouts/templates/partials/static/output/_data/
3. Scan Content
Walks content/ recursively and parses every .md file:
- Extracts YAML frontmatter
- Renders the body to HTML
- Stores metadata (title, date, tags, etc.)
- Builds collections based on the file path
4. Detect Controllers
A controller is a Markdown file with a collection: key in its frontmatter. Controllers do not become pages — they generate listing pages.
Example: content/journal.md has collection: posts. It lists all posts.
5. Build Collections
For every folder under content/, a collection is created. Nested folders create nested collections with subs.
Example:
content/posts/my-post.md → collection "posts"
content/projects/project-01.md → collection "projects"
6. Generate Pages
For each Markdown file (except controllers), the engine:
- Renders the body to HTML
- Finds the template from the
layout:key - Renders the template with the page's data
- Writes to
output/<slug>/index.html
7. Generate Controllers
For each controller, the engine:
- Loads the linked collection
- Applies filters, sorting, and pagination
- Renders the listing template
- Writes to
output/<output-name>/index.html
8. Generate Tag Pages
For every tag used in posts or projects:
- Creates
output/tags/<tag>/index.html - Also creates
output/tags/index.html
9. Generate Feeds and Sitemap
output/feed.xml— RSS 2.0output/atom.xml— Atomoutput/feed.json— JSON Feed 1.0output/sitemap.xml— XML sitemapoutput/robots.txt— robots fileoutput/404.html— custom 404
10. Copy Static Assets
Copies everything from static/ into output/, preserving structure.
The Template Engine
Pycora Pro uses Jinja2 with a custom loader called PAXLoader.
The PAXLoader does three things:
- Template discovery — looks for
.paxand.htmlfiles intemplates/ - Extension fallback — if
.paxis missing, falls back to.html(and vice versa) - Jinja slice fix — rewrites
[:3]syntax into a| limit(3)filter for backwards compatibility
Example:
{{ posts[:3] }} → {{ posts | limit(3) }}
{{ posts[1:3] }} → {{ posts | slice(1, 3) }}
This lets older templates written for a Liquid-style engine keep working.
Filters Available
| Filter | Description |
|---|---|
limit(n) | Take the first n items |
slice(start, end) | Take a range |
where(key, value) | Filter by a metadata key |
filter_tag(tag) | Filter by tag |
sort_by(field) | Sort by a metadata field |
reverse | Reverse a list |
first | First item |
last | Last item |
slugify | Convert text to a URL slug |
The Data Model
Every piece of content is a PostObj — a Python object with two layers:
metadata— the parsed YAML frontmattercontent— the rendered HTML body
Templates access them like this:
{{ post.title }}
{{ post.metadata.date }}
{{ post.content }}
If a field is missing, the engine returns a ChainableUndefined — a placeholder that never crashes. This means templates do not fail on missing data.
Output Structure
The final output/ folder contains only static files:
output/
├── index.html
├── 404.html
├── robots.txt
├── sitemap.xml
├── feed.xml
├── rss.xml
├── atom.xml
├── feed.json
├── <slug>/
│ └── index.html
├── tags/
│ ├── index.html
│ └── <tag>/
│ └── index.html
├── css/
├── js/
├── img/
└── admin/
└── config.yml
No runtime. No server. No database.
Build Speed
Pycora Pro builds thousands of pages in milliseconds.
On a typical machine:
| Files | Time |
|---|---|
| 10 | < 0.3s |
| 100 | < 1s |
| 1000 | < 3s |