About Cooklang Federation
What is This?
Cooklang Federation is a decentralized recipe discovery system that allows anyone to publish and share recipes in the Cooklang format without relying on a central platform.
Think of it as a search engine for recipes that respects your ownership and independence. Publishers host their own recipes, consumers discover them through this federation, and everyone benefits from an open, decentralized ecosystem.
Core Principles
Unlike traditional recipe websites that lock your content into proprietary platforms, Cooklang Federation is built on principles of decentralization and data ownership:
You Own Your Content
Host your recipes wherever you want - GitHub Pages, Netlify, your own server, or any static hosting service.
Simple & Fast
No database required. Plain text recipes and RSS/Atom feeds - the same battle-tested technology that powers podcasts and blogs.
No Vendor Lock-in
Switch hosting providers, change domains, or take your recipes offline anytime. You control the distribution.
Open Standards
Based on Atom/RSS syndication and the open Cooklang specification. Works with existing tools and readers.
How It Works
Cooklang Federation uses a two-tier content model that keeps things lightweight and scalable:
Publishers Create Feeds
Recipe authors create an RSS/Atom feed listing their recipes with metadata (title, tags, cooking time, etc.). The feed contains summaries only, keeping it small and fast to parse.
Federation Crawls & Indexes
This federation server periodically crawls registered feeds, fetches the full .cook files
from the enclosure links, and indexes them with a full-text search engine (Tantivy).
Users Search & Discover
Anyone can search across all indexed recipes using the web UI or API. Results include filtering by tags,
ingredients, cooking time, and difficulty. Click a recipe to see the full content or download the .cook file.
Updates Propagate Automatically
When you update a recipe or add new ones, the crawler detects changes in your feed and re-indexes only what changed. No manual notifications needed - the system stays in sync automatically.
Feed Registration via GitOps
This federation instance uses a GitOps workflow for managing feed registrations. This means feeds are registered through version control, not through API calls.
To add or modify feeds:
- Fork the federation repository on GitHub
- Edit
config/feeds.yamlto add your feed URL and metadata - Submit a pull request with your changes
- Wait for automated CI validation and maintainer approval
- Once merged, the crawler will automatically discover your recipes
Benefits of GitOps:
- Full version control and audit trail
- Peer review process before feeds go live
- Automated validation (feed syntax, accessibility, content checks)
- Easy rollback if issues arise
- Transparent change history
See the repository's config/README.md for detailed instructions and feed format examples.
How to Publish Your Recipes
Publishing your recipes to the federation is a straightforward process. Follow these steps to make your recipes discoverable:
Step 1: Write Recipes in Cooklang Format
Create .cook files using the Cooklang markup language.
Cooklang is a plain-text format that's both human-readable and machine-parseable.
Example recipe file (chocolate-cookies.cook):
---
servings: 24
time: 45 minutes
tags: dessert, cookies, baking
difficulty: easy
---
Preheat #oven to 180°C.
Cream @butter{200%g} and @sugar{150%g} together for ~{5%minutes} until fluffy.
Add @eggs{2} and @vanilla extract{1%tsp}, mix well.
In a separate bowl, combine @flour{300%g}, @baking soda{1%tsp}, and @salt{1/2%tsp}.
Gradually fold dry ingredients into wet mixture.
Stir in @chocolate chips{200%g}.
Drop spoonfuls onto greased #baking sheet.
Bake for ~{12-15%minutes} until golden brown.
Cool on #wire rack before serving.
Key elements: @ingredients{quantity} for ingredients,
#equipment for cookware, ~{time} for timers,
and metadata for recipe details.
Step 2: Generate an Atom/RSS Feed
Create a feed XML file that lists all your recipes with metadata. You can generate this manually or use the CLI tool.
Option A: Use the Federation CLI (Recommended)
# Generate feed from a directory of .cook files
cargo run -- publish --input ./my-recipes --output feed.xml
# This will scan all .cook files and create a properly formatted Atom feed
Option B: Create manually
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:cooklang="https://cooklang.org/feeds/1.0">
<title>My Recipe Collection</title>
<link href="https://example.com/feed.xml" rel="self"/>
<link href="https://example.com/"/>
<updated>2025-10-20T12:00:00Z</updated>
<id>https://example.com/feed.xml</id>
<author>
<name>Your Name</name>
<uri>https://example.com</uri>
</author>
<entry>
<title>Chocolate Chip Cookies</title>
<id>https://example.com/recipes/chocolate-cookies</id>
<link href="https://example.com/recipes/chocolate-cookies" rel="alternate"/>
<link href="https://example.com/recipes/chocolate-cookies.cook" rel="enclosure" type="text/plain"/>
<updated>2025-10-20T12:00:00Z</updated>
<summary>Classic chocolate chip cookies with a crispy edge and chewy center.</summary>
<cooklang:recipe>
<cooklang:servings>24</cooklang:servings>
<cooklang:time total="45" units="minutes"/>
<cooklang:tags>
<cooklang:tag>dessert</cooklang:tag>
<cooklang:tag>cookies</cooklang:tag>
<cooklang:tag>baking</cooklang:tag>
</cooklang:tags>
<cooklang:difficulty>easy</cooklang:difficulty>
</cooklang:recipe>
</entry>
<!-- Add more entries for each recipe -->
</feed>
Important: The rel="enclosure" link must point directly to the raw
.cook file, not an HTML page. This is how the federation fetches your recipe content.
Step 3: Host Your Files
Upload both your feed.xml and all
.cook files to any static web hosting service. All files must be publicly accessible.
GitHub Pages (Free)
Perfect for version-controlled recipes.
git init
git add feed.xml recipes/
git commit -m "Add recipes"
git push origin main
# Enable Pages in repo settings
Netlify/Vercel (Free)
Drag-and-drop deployment with CDN.
# Create a directory structure:
/
feed.xml
recipes/
cookies.cook
pasta.cook
# Deploy via web UI or CLI
Requirements:
- Feed must be served over HTTPS (recommended for security)
- Files must be accessible without authentication
- Set
Content-Type: application/atom+xmlfor feed.xml - Set
Content-Type: text/plain; charset=utf-8for .cook files - Enable CORS headers if you want to allow browser-based clients
Step 4: Register Your Feed with the Federation
Submit a pull request to add your feed to this federation instance. See the GitOps section above for the full process.
Quick summary:
- Fork the federation repository
- Add your feed URL to
config/feeds.yaml:- url: https://example.com/feed.xml name: My Recipe Collection author: Your Name description: A collection of family recipes tags: [desserts, italian, vegetarian] - Commit and push your changes
- Create a pull request with a description of your recipe collection
- Wait for CI validation (checks feed accessibility and format)
- Once approved and merged, the crawler will index your recipes within 24 hours
Step 5: Keep Your Feed Updated
The federation crawler checks feeds periodically (typically daily). To propagate updates:
- Add new recipes: Add entries to your feed and upload the new .cook files
- Update recipes: Modify the .cook file and update the entry's
<updated>timestamp - Remove recipes: Delete the entry from your feed (the .cook file can remain for archival)
- Always update the feed's root
<updated>timestamp when making changes
Tip: The crawler uses conditional HTTP requests (ETags, Last-Modified headers) to efficiently detect changes. Make sure your hosting provider supports these headers for optimal performance.
What is Cooklang?
Cooklang is an open-source markup language specifically designed for writing recipes in plain text. It's the foundation of this federation system.
Why Cooklang?
- Human-readable: Recipes look natural and are easy to write by hand
- Machine-parseable: Structured data for ingredients, quantities, and steps
- Version-control friendly: Perfect for Git - diff, merge, and track changes
- Tool ecosystem: CLI tools, mobile apps, web renderers, and more
- Future-proof: Plain text format will work forever, no proprietary lock-in
Key Syntax Elements
@ingredient{quantity%unit}
Defines an ingredient with optional quantity and unit
#cookware
References equipment needed for the recipe
~{time%unit}
Sets a timer with duration
metadata: value
Adds structured metadata to the recipe
Learn more at cooklang.org or read the full specification.
Technical Architecture
This federation implementation is built with modern, performant technologies designed for reliability and scalability.
Core Stack
Rust
High-performance, memory-safe backend
Axum Web Framework
Fast async web server with Tokio runtime
Tantivy Search Engine
Blazing-fast full-text search, similar to Lucene
SQLite Database
Lightweight storage for feeds and metadata
Key Features
Background Scheduler
Automated periodic feed crawling and indexing
Rate Limiting
API protection with tower-governor middleware
Tailwind CSS UI
Modern, responsive interface with Askama templates
CLI Interface
Command-line tools for searching and publishing
System Architecture
The federation follows a crawler-indexer architecture with three main components:
1. Crawler
Periodically fetches feeds from registered publishers, respects rate limits and robots.txt
2. Indexer
Parses .cook files, extracts content and metadata, builds full-text search index
3. Query Engine
Serves web UI and API, executes searches with filtering, caches results
The source code is open source and available on GitHub. Contributions and self-hosting are welcome.
Federation Specification
This implementation follows the Cooklang Federation Specification v1.0, an open standard that defines how recipes are published, discovered, and indexed across the federated network.
Key Specification Features
- Based on Atom 1.0 (RFC 4287) and RSS 2.0
- Custom Cooklang XML namespace for recipe metadata
- Two-tier content model (lightweight feeds, full recipe files)
- Enclosure links to raw .cook file content
- Structured metadata (servings, time, tags, difficulty)
- Feed pagination for large recipe collections
- Discovery mechanisms (well-known URLs, HTML links)
- Validation rules and security considerations
Cooklang Namespace Extension
The specification defines a custom XML namespace
(https://cooklang.org/feeds/1.0) with elements for:
<cooklang:servings><cooklang:time><cooklang:tags><cooklang:difficulty>
<cooklang:image><cooklang:nutrition>- All elements are optional but recommended
The full specification document is available in the repository at
spec.md. It includes complete examples,
validation rules, implementation guidelines, and security considerations.
API Documentation
The federation exposes a RESTful JSON API for programmatic access to recipe data. All endpoints are publicly accessible and rate-limited to ensure fair usage.
Search Recipes
GET
/api/search
Full-text search with filtering by tags, ingredients, time, and difficulty.
Query Parameters:
| q | Search query (title, ingredients, tags) |
| tags | Comma-separated tag filter (e.g., "dessert,cookies") |
| max_time | Maximum cooking time in minutes |
| difficulty | Filter by difficulty: "easy", "medium", or "hard" |
| limit | Results per page (default: 20, max: 100) |
Example Request:
GET /api/search?q=chocolate+cookies&tags=dessert&max_time=60&limit=10
Example Response:
{
"results": [
{
"id": "123",
"title": "Chocolate Chip Cookies",
"feed_id": "feed-456",
"feed_name": "Jane's Recipes",
"author": "Jane Doe",
"summary": "Classic chocolate chip cookies...",
"tags": ["dessert", "cookies", "baking"],
"servings": 24,
"time_minutes": 45,
"difficulty": "easy",
"updated_at": "2025-10-20T12:00:00Z",
"recipe_url": "https://example.com/cookies.cook"
}
],
"total": 1,
"page": 1,
"limit": 10
}
Get Recipe Details
GET
/api/recipes/:id
Retrieve complete recipe metadata including parsed Cooklang content.
Example Response:
{
"id": "123",
"title": "Chocolate Chip Cookies",
"content": "---\nservings: 24\ntime: 45 minutes\n...",
"parsed": {
"ingredients": [
{"name": "butter", "quantity": "200", "units": "g"},
{"name": "sugar", "quantity": "150", "units": "g"}
],
"steps": ["Preheat oven...", "Cream butter..."]
},
"metadata": {
"servings": 24,
"time_minutes": 45,
"tags": ["dessert", "cookies"],
"difficulty": "easy"
},
"feed": {
"id": "feed-456",
"name": "Jane's Recipes",
"author": "Jane Doe",
"url": "https://example.com/feed.xml"
}
}
Download Recipe File
GET
/api/recipes/:id/download
Download the raw .cook file with proper content-type headers.
List All Feeds
GET
/api/feeds
List all registered feeds with metadata and recipe counts.
System Statistics
GET
/api/stats
System-wide statistics including total recipes, feeds, and indexing status.
Rate Limiting
All API endpoints are rate-limited to 100 requests per second per IP address. Exceed this limit and you'll receive HTTP 429 (Too Many Requests) responses. For higher limits, consider self-hosting your own federation instance.
Command-Line Interface
For developers and power users, the federation includes a CLI tool for searching, downloading, and publishing recipes directly from the terminal.
Search Recipes
# Basic search
cargo run -- search "chocolate cookies"
# With filters
cargo run -- search "pasta" --tags "italian,dinner" --max-time 30 --difficulty easy
# Output JSON for scripting
cargo run -- search "pizza" --format json
Download Recipes
# Download by recipe ID
cargo run -- download 123 --output ./recipes/cookies.cook
# Download multiple recipes
cargo run -- download 123 124 125 --output ./my-recipes/
Publish Your Recipes
# Generate Atom feed from directory of .cook files
cargo run -- publish --input ./my-recipes --output feed.xml --author "Your Name"
# Validate feed before publishing
cargo run -- validate-feed feed.xml
Server Management
# Run database migrations
cargo run -- migrate
# Start the web server
cargo run -- serve
# Manually trigger feed crawl
cargo run -- crawl --all
Deployment & Self-Hosting
The federation is designed to be easy to deploy and self-host. Run your own instance to have full control over which feeds to index and customize crawling behavior.
Docker Deployment (Recommended)
# Clone repository
git clone https://github.com/cooklang/federation.git
cd federation
# Start with Docker Compose
docker-compose up -d
# Access at http://localhost:3000
Production Configuration
Key environment variables to configure:
| DATABASE_URL | SQLite or PostgreSQL connection string |
| PORT | HTTP server port (default: 3000) |
| CRAWLER_INTERVAL | Seconds between feed updates (default: 3600) |
| API_RATE_LIMIT | Requests per second (default: 100) |
| RUST_LOG | Logging verbosity (info, debug, trace) |
Reverse Proxy Setup
For production, deploy behind a reverse proxy (nginx, Caddy, Traefik) to handle:
- TLS/SSL termination
- Additional DDoS protection
- Load balancing across multiple instances
- Proper
X-Forwarded-Forheaders for rate limiting
Community Hosting: Multiple public federation instances can coexist in the ecosystem. Each instance can index different feeds or serve different communities. This distributed approach prevents any single point of failure.
Contributing & Community
Cooklang Federation is an open-source project welcoming contributions from the community.
Ways to Contribute
- Publish your own recipe feeds to grow the ecosystem
- Report bugs and suggest features on GitHub Issues
- Submit pull requests for code improvements
- Improve documentation and tutorials
- Help test new features and beta releases