Skip to content

Plate — Product Requirements

Set the table. Your way.

Status: Stable Owner: Cozy Badger Last updated: April 2026


1. Overview

Plate is a presentation frontend that fetches content from Snackbox, renders Markdown to HTML, and serves the result to visitors through a Handlebars theme.

Plate is agnostic. It does not know which theme will be active, and it does not know anything about Pantry or any other ecosystem tool. It only knows two things: the Snackbox API contract, and the theme contract it owns.

2. Responsibilities

  • Fetch content from the Snackbox REST API (/api/v1/)
  • Render raw Markdown to sanitized HTML via the Remark pipeline
  • Load a Handlebars theme from a configured directory at startup
  • Pass well-defined template context objects to theme templates
  • Serve rendered pages to visitors
  • Fall back to Picnic when no external theme is configured or available
  • Define and publish the theme contract (JSON Schema)

3. Non-Responsibilities

  • Plate does not manage content — that is Snackbox's job
  • Plate does not provide an admin UI — that is Pantry's job
  • Plate does not authenticate users or manage sessions
  • Plate does not write to Snackbox

4. Upstream Dependency: Snackbox

Plate is a read-only consumer of the Snackbox API. It uses the following public endpoints (no authentication required):

Endpoint Purpose
GET /api/v1/settings Site title, description, logo, language, theme name
GET /api/v1/navigation Main and secondary navigation menus
GET /api/v1/posts Paginated post listings, optional tag filter
GET /api/v1/posts/{id} Single post by ID or slug
GET /api/v1/pages/{id} Single page by ID or slug
GET /api/v1/tags All tags
GET /api/v1/tags/{id} Single tag by ID or slug
GET /api/v1/settings/social-accounts Social profile links for footer/header
GET /health Snackbox health check

Content fields (content in posts and pages) are returned as raw Markdown. Plate is responsible for rendering them to HTML.

5. Theme System

Plate owns the theme contract. A theme is a directory containing:

  • A manifest.json (validated against the theme contract JSON Schema)
  • Handlebars templates for each page type
  • Handlebars partials
  • Static assets (CSS, fonts, images)

Plate loads one active theme at startup. The theme name is resolved from configuration (environment variable). If no theme is found or configured, Plate falls back to Picnic.

Plate serves theme static assets at a well-known path so templates can reference them without hardcoding URLs.

5.1 Picnic — Built-in Fallback Theme

Picnic is bundled directly into Plate. It is intentionally minimal — the floor, not the ceiling. It exists to:

  • Ensure Plate always renders something reasonable out of the box
  • Act as the reference implementation of the theme contract
  • Show theme developers the minimum required structure

Picnic may use Crumbs as its CSS foundation, but this is not required by the theme contract.

5.2 External Themes

External themes are distributed as uploadable packages and stored in a configured themes directory on disk. Plate discovers them at startup by reading the directory and validating each manifest. Plate has no knowledge of any specific external theme by name.

6. Template Context

Plate constructs a context object for each request and passes it to the appropriate Handlebars template. The context always includes a global section (settings, navigation, site metadata) and a page-specific section.

Page types and their templates

Page type Template Context
Home home.hbs Featured and recent posts
Post list list.hbs Paginated posts, optional tag filter
Single post post.hbs Post content (rendered HTML), tags, authors
Single page page.hbs Page content (rendered HTML)
Tag tag.hbs Tag metadata, paginated posts for tag
404 404.hbs Error info
500 500.hbs Error info

7. Markdown Rendering

Plate uses the Remark/Rehype pipeline to render Markdown:

  • remark-parse — parse Markdown AST
  • remark-gfm — GitHub Flavoured Markdown support (tables, strikethrough, etc.)
  • remark-rehype — convert Markdown AST to HTML AST
  • rehype-sanitize — sanitize HTML output
  • rehype-stringify — serialize to HTML string

Additional transformations applied during rendering:

  • Lazy loading and async decoding on images
  • target="_blank" and rel="noopener noreferrer" on external links

8. Caching

Plate caches Snackbox API responses in memory to reduce upstream load. Cache TTL and maximum entry count are configurable via environment variables. The cache is a simple LRU store with per-entry TTL. There is no shared or distributed cache — each Plate instance manages its own.

9. Configuration

Plate is configured entirely via environment variables. No runtime config file is written or read. All values have sensible defaults so Plate can start without any configuration for local development.

Key variables:

Variable Default Description
PORT 3000 HTTP port
HOST localhost Bind address
NODE_ENV development Environment (development, production)
SNACKBOX_API_URL http://localhost:8080 Snackbox base URL
SNACKBOX_TIMEOUT 5000 Request timeout in ms
THEME_NAME picnic Active theme name
THEMES_DIR themes Directory for external themes
CACHE_ENABLED true Enable API response cache
CACHE_TTL_MULTIPLIER 1 Multiplies base content cache TTLs
CACHE_MAX_ENTRIES 100 Maximum cache entries
POSTS_PER_PAGE 10 Default pagination size

10. Contracts

Plate owns and publishes the theme contract:

Contract Format Location
Theme manifest JSON Schema contracts/theme-manifest.schema.json

Plate consumes but does not own:

Contract Owner Format
Snackbox API Snackbox OpenAPI YAML