The Build Bible

The reasoning behind every architectural decision in the Skeleton.

The reasoning behind every architectural decision in the Skeleton. This is the teaching, given away free.


Why this exists

Most people who want to own their platform face two doors. Rent a platform you do not control. Or hire someone to build one you do not understand. Both leave you dependent.

This Skeleton is the third door: a working, sovereign publishing platform with every decision explained so you understand what you own and why it works.


1. Why Astro

Astro is a static site framework. It takes your content (markdown files with structured data), runs it through templates at build time, and produces plain HTML files. No server. No database. No runtime. The output is a folder of files you can host anywhere.

This matters for sovereignty because:

Astro was chosen over other static generators because it supports typed content collections with Zod validation, which gives you build-time safety without a runtime cost.


2. Why typed content collections

Every post in your platform has a schema: a contract that defines what fields are required, what types they must be, and what values are valid.

title: z.string().min(1)          // must exist, must not be empty
slug: slugSchema                   // must be kebab-case
status: publishStateSchema         // must be "draft", "published", or "archived"
publishedAt: z.coerce.date()       // must be a valid date

When you build the site, Astro validates every content file against this schema. A post with a missing title, a bad slug, or an invalid date will fail the build with a clear error message. Bad data never reaches production.

This prevents a class of problems that grow silently in untyped systems: missing fields, inconsistent formats, broken references. A schema catches them at the earliest possible point.

The schema lives at src/content/schemas/posts.ts. You can extend it with new fields (add a category enum, a readTime string, a heroImage path) and the validator will enforce the new contract immediately.


3. Why the draft gate

The function getPublished() in src/lib/content.ts is a query filter. Every page that displays content calls it instead of querying the collection directly. It returns only entries where status === "published", with one exception: in dev mode, drafts are also visible so you can preview them.

This single function prevents the most dangerous publishing mistake: accidentally shipping an unfinished draft to the public. It is used in:

Because it gates every surface, a draft post does not generate a page, does not appear in any list, and does not enter the feed. The only way to make it public is to change its status to “published” and rebuild.

The draft gate is the single most valuable safety feature in this architecture.


4. Why owned RSS

Your RSS feed is generated at build time by src/pages/rss.xml.ts. It creates a standard XML feed from your published posts, sorted by date, and serves it from your domain.

This is your audience pipe. Anyone who subscribes to your feed connects directly to you, not through a platform’s algorithm or notification system. That connection is:

If you move hosts, your feed URL stays the same as long as you keep your domain. If a reader switches feed apps, your feed still works. There is no middleman.


5. Why four dependencies

Every dependency is a point of failure, a thing that can be deprecated, and a surface that can change its pricing or terms. The Skeleton ships with four:

DependencyPurpose
astroThe framework: static site generation, content collections, routing
@astrojs/mdxMDX support for rich content (components inside markdown)
@astrojs/rssRSS feed generation
@astrojs/sitemapAutomatic sitemap for search engines

No runtime dependencies. No database. No CMS. No auth layer. Four things to audit, four things to update, four things that can break. Everything else is standard web platform (HTML, CSS, JavaScript).


6. Why this file structure

src/
  content/
    posts/           # Your content lives here, as markdown files
    schemas/         # The validation rules for your content
  layouts/           # The page shell (header, footer, structure)
  lib/               # Shared utilities (the draft gate lives here)
  pages/             # Routes: each file becomes a URL
    posts/           # The posts list and detail pages
    rss.xml.ts       # The RSS feed
  styles/
    tokens.css       # Design tokens (colors, fonts, spacing)
  content.config.ts  # Registers your content collections

Content and code are separated. Your writing lives in content/posts/ as plain markdown files you can read, edit, and move without touching any code. The code that turns those files into a website lives everywhere else.

This separation means your archive is portable. If you ever outgrow Astro or want to move to a different system, your content is just markdown files with YAML frontmatter. Any static site generator can read them.


7. Patterns the Skeleton teaches (not pre-built)

The Skeleton ships with one chamber and one content type. The architecture it is built on supports more. These patterns are explained here so you understand what is possible and what the Forge (done-with-you build service) would construct.

Cross-references

Astro’s reference() function lets one collection point to another. For example, a post could reference a list of tags that are themselves a validated collection, or an author collection, or a sources collection with citation data.

In the Skeleton, the posts schema uses a simple tags array of strings. To upgrade to typed cross-references:

  1. Create a new collection (e.g., topics/) with its own schema.
  2. In the posts schema, change tags from z.array(z.string()) to z.array(reference("topics")).
  3. Each post’s tags field now must reference valid entries in the topics collection. The build validates this.

This pattern scales to a full knowledge graph: posts reference topics, topics reference related topics, posts reference sources. The validation ensures every reference points to something real.

Multiple chambers (the multi-world system)

The Skeleton has one visual identity. The architecture supports multiple visually distinct editorial spaces on a single codebase. Each chamber can have its own color palette, typography, layout patterns, and editorial voice, while sharing the same content system and build pipeline.

To add a second chamber:

  1. Add a new content collection with its own schema.
  2. Add new pages under a new route (e.g., src/pages/stories/).
  3. Add a chamber prop to the layout and style each chamber differently using CSS custom properties.

The multi-world system is the core architectural difference between a blog and an editorial platform. It is taught here, not pre-built, because wiring and theming chambers to a specific person’s brand is labor (the Forge).


8. The sovereignty test

When evaluating any platform, ask four questions:

  1. Do you own the code? Not a login. The actual files, on your machine.
  2. Do you own the audience? Through pipes you control, in formats you can export.
  3. Do you own the archive? In plain, portable files that outlast any platform.
  4. Do you own the terms? No algorithm, no pricing change, no policy can erase you.

This Skeleton passes all four. If yours does not, you are renting.