This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
cargo build # compile (debug)
cargo build --release # compile (release)
cargo run -- --root ./www # run against a local www/ directory
cargo run -- --port 8080 --root ./www
RUST_LOG=debug cargo run -- --root ./www # verbose logging
cargo check # fast type-check without linkingThere are no tests yet. cargo test will compile but find nothing to run.
A single Axum catch-all handler routes every request. All request handling flows through handler::handle → one of three serving functions:
serve_markdown— reads a.mdfile, parses YAML front matter, infers missing fields, renders to HTML via themarkdowncrate (GFM mode), wraps in a Maud template.serve_directory— ifindex.mdexists, delegates toserve_markdown; otherwise reads the directory, collects metadata from each.mdfile, and renders a listing template.serve_static— streams the file viaReaderStreamwithContent-Lengthand a MIME type frommime_guess.
AppState holds two roots: www_root (lexical) and canonical_root (symlink-resolved at startup). Before any file read, validate_path() calls tokio::fs::canonicalize on the resolved filesystem path and checks it stays within canonical_root. This blocks both .. traversal and symlink escapes.
URL percent-decoding uses the percent-encoding crate; invalid UTF-8 after decoding returns 404.
Parses ---\n…\n---\n YAML blocks using serde_yml. The closing delimiter must be --- on its own line (not ---more-text). fill_inferred() fills any missing fields: title from the first # H1, summary from the first paragraph (handles both ATX and Setext headings), date from file ctime/mtime via chrono.
find_css(www_root, file_path) walks up from the served file's directory toward www_root, returning the first style.css found as an absolute URL path (e.g. /blog/style.css). Both arguments must be canonical paths.
Two Maud functions: page() for markdown content (includes og:/article: meta tags from front matter) and directory_index() for listings. HTML responses use axum::response::Html — the maud axum feature is intentionally not used to avoid version coupling.
| Request pattern | Behaviour |
|---|---|
/dir/ or / |
Directory listing (or index.md if present) |
/dir/index.html |
Same as /dir/ |
/dir (no slash, is a directory) |
308 redirect to /dir/ |
/post.md |
Render markdown |
/post (no extension) |
Try www/post.md |
| Static extensions (css/js/png/jpg/gif/svg/webp/pdf/mp4 etc.) | Stream through |
| Flag | Env var | Default |
|---|---|---|
--port |
PORT |
3000 |
--host |
HOST |
0.0.0.0 |
--root |
WWW_ROOT |
www/ next to the binary |
Log level is controlled by RUST_LOG (e.g. RUST_LOG=md_server=debug,tower_http=debug).