Configuration
mbr uses a layered configuration system:
flowchart BT
DEFAULTS["Compiled-in Defaults"]
ENV["Environment Variables<br/>(MBR_*)"]
CONFIG[".mbr/config.toml"]
CLI["Command-line Flags"]
FINAL["Final Configuration"]
DEFAULTS --> ENV
ENV --> CONFIG
CONFIG --> CLI
CLI --> FINAL
style FINAL fill:#90EE90
Later layers override earlier ones.
Configuration File
Create .mbr/config.toml in your markdown repository:
# .mbr/config.toml
# Server settings
host = "127.0.0.1"
port = 5200
# Markdown settings
markdown_extensions = ["md", "markdown"]
index_file = "index.md"
# Static file folder (relative to repo root)
static_folder = "static"
# Directories to ignore during scanning
ignore_dirs = [
"target",
"node_modules",
".git",
"build",
"dist"
]
# File patterns to ignore
ignore_globs = [
"*.log",
"*.bak",
"*.tmp"
]
# Directories ignored by file watcher (live reload)
watcher_ignore_dirs = [
".direnv",
".git",
"target"
]
# oEmbed/OpenGraph fetch timeout in milliseconds (server/GUI default: 500)
# Note: Build mode defaults to 0 (disabled) for performance. Override with CLI if needed.
oembed_timeout_ms = 500
Configuration Options
Server Settings
| Option | Type | Default | Description |
|---|---|---|---|
host | string | "127.0.0.1" | IP address to bind |
port | number | 5200 | Port number |
Content Settings
| Option | Type | Default | Description |
|---|---|---|---|
markdown_extensions | array | ["md"] | File extensions treated as markdown |
index_file | string | "index.md" | Default file for directories |
static_folder | string | "static" | Folder for static file overlay |
Ignore Settings
| Option | Type | Default | Description |
|---|---|---|---|
ignore_dirs | array | (see below) | Directories to skip |
ignore_globs | array | (see below) | File patterns to ignore |
watcher_ignore_dirs | array | (see below) | Dirs ignored by file watcher |
Default ignored directories:
target, result, build, node_modules, ci, templates, .git, .github, dist, out, coverage
Default ignored globs:
*.log, *.bak, *.lock, *.sh, *.css, *.scss, *.js, *.ts
Behavior Settings
| Option | Type | Default | Description |
|---|---|---|---|
oembed_timeout_ms | number | 500 (server/GUI), 0 (build) | URL metadata fetch timeout (0 to disable) |
oembed_cache_size | number | 2097152 | Cache size in bytes (0 to disable) |
skip_link_checks | bool | false | Skip internal link validation during builds |
link_tracking | bool | true | Enable bidirectional link tracking (backlinks) |
mark_incomplete | bool / unset | mode default (server/GUI on, build off) | Highlight blocks starting with TK/TODO/FIXME/XXX |
incomplete_markers | array | ["TK", "TODO", "FIXME", "XXX"] | Marker strings that flag a block as incomplete |
Navigation Settings
| Option | Type | Default | Description |
|---|---|---|---|
sidebar_style | string | "panel" | Sidebar navigation style: "panel" (modal 3-pane) or "single" (persistent sidebar) |
sidebar_max_items | number | 100 | Maximum items per section in sidebar navigation |
title_prefix | string | "" | Text to prepend to all page titles |
title_suffix | string | "" | Text to append to all page titles |
Sidebar styles:
"panel"(default): Three-pane modal browser accessible via menu. Opens as an overlay with folders, files, and tags in separate columns."single": Persistent single-column sidebar beside content (like picocss.com/docs). Shows folder tree, files, and tags in a scrollable sidebar.
Responsive behavior (single sidebar):
| Viewport | Behavior |
|---|---|
| >= 1024px (desktop) | Sidebar appears beside content via CSS grid |
| < 1024px (mobile) | Hamburger menu triggers slide-in drawer overlay |
CSS variables for customization:
:root {
--mbr-sidebar-width: 280px; /* Sidebar width */
--mbr-hide-nav-bp: 1024px; /* Breakpoint for mobile mode */
--mbr-show-tags: block; /* Set to 'none' to hide tags */
}
Example configuration:
# .mbr/config.toml
# Use persistent sidebar instead of modal
sidebar_style = "single"
# Show more items in large repositories
sidebar_max_items = 200
Title Settings
| Option | Type | Default | Description |
|---|---|---|---|
title_prefix | string | "" | Text prepended to all page <title> tags |
title_suffix | string | "" | Text appended to all page <title> tags |
These options let you brand page titles across the site without modifying individual pages. They apply to markdown pages, directory listings, tag pages, and media viewer pages (not error pages).
Example configuration:
# .mbr/config.toml
# Brand all page titles
title_prefix = "My Notes: "
title_suffix = " | Paul's Wiki"
This turns a page titled “Getting Started” into <title>My Notes: Getting Started | Paul's Wiki</title>.
CLI usage:
mbr -s --title-prefix "My Site: " --title-suffix " | Docs" ~/notes
Tag Settings
| Option | Type | Default | Description |
|---|---|---|---|
tag_sources | array | [{ field = "tags" }] | Frontmatter fields to extract tags from |
build_tag_pages | bool | true | Generate tag pages in static builds |
Tag source configuration:
Each tag source can specify:
field(required): Frontmatter field name (supports dot-notation for nested fields liketaxonomy.tags)label: Singular label for display (e.g., “Tag”, “Performer”)label_plural: Plural label for display (e.g., “Tags”, “Performers”)
Example configuration:
# .mbr/config.toml
tag_sources = [
{ field = "tags" },
{ field = "taxonomy.performers", label = "Performer", label_plural = "Performers" }
]
build_tag_pages = true
See the Tags feature documentation for complete details.
Note: Setting
oembed_timeout_msto0disables OpenGraph fetching entirely, rendering bare URLs as plain links. YouTube and Giphy embeds still work since they don’t require network calls.
Note: The oembed cache stores fetched page metadata to avoid redundant network requests. URLs are fetched in parallel and cached for reuse across files (in build mode) or requests (in server mode). Set
oembed_cache_sizeto0to disable caching.
Build Mode Performance
By default, static builds (-b) disable oembed fetching (oembed_timeout_ms=0). If you want rich link previews in your static site, you can enable it by specifying a timeout:
mbr -b --oembed-timeout-ms 500 ~/notes
Oembed fetching is parallelized and cached, so the overhead is minimal even for large repositories.
Parallel Building
Static builds process markdown files in parallel for maximum speed:
| Setting | Effect |
|---|---|
| Default (auto) | Uses 2x CPU cores, capped at 32 |
--build-concurrency 1 | Sequential processing (useful for debugging) |
--build-concurrency 16 | Explicit concurrency limit |
Memory usage scales with concurrency. Use lower values if running out of memory on very large repositories.
Link Validation
By default, static builds validate all internal links (links to other pages within the site) and report broken ones. To skip this check for faster builds:
mbr -b --skip-link-checks ~/notes
Or in .mbr/config.toml:
skip_link_checks = true
Link Tracking (Backlinks)
mbr automatically tracks bidirectional links between pages. The info panel (Ctrl+g) shows both:
- Links Out: Pages this document links to
- Links In: Pages that link to this document (backlinks)
This feature enables wiki-style backlink navigation without requiring any special syntax.
How it works:
| Mode | Method | Performance |
|---|---|---|
| Server/GUI | On-demand grep search (cached) | First request: ~1-3s, subsequent: instant |
| Build | Eager index during render | Computed in parallel, no runtime cost |
API: Each page has a links.json endpoint:
# Server mode
curl http://localhost:5200/docs/guide/links.json
# Static build
cat build/docs/guide/links.json
Response format:
{
"inbound": [
{"from": "/other/page/", "text": "link text", "anchor": "#section"}
],
"outbound": [
{"to": "/another/page/", "text": "link text", "anchor": "#section", "internal": true}
]
}
Disable link tracking:
mbr -s --no-link-tracking ~/notes
Or in .mbr/config.toml:
link_tracking = false
When disabled, the links.json endpoint returns 404 and no link files are generated during builds.
Incomplete-Block Highlighting
mbr can highlight blocks (paragraphs, headings, list items, table cells) whose
first text starts with an incomplete-marker like TK, TODO, FIXME, or
XXX. Highlighted blocks are wrapped in <span class="mbr-incomplete">…</span>
and styled with a yellow background and dotted orange underline.
Match rule: uppercase only, word-boundary at the end of the marker. So TK,
TK:, TODO foo, and FIXME(name) match; Tk, todo, Tomato, TKTK,
and TODOs do not.
| Mode | Default | Reason |
|---|---|---|
| Server / GUI | on | Helps writers spot drafts at a glance |
| Static build | off | Avoids leaking unfinished markers into published sites |
CLI overrides:
# Force highlighting on (e.g., during a build to preview drafts)
mbr -b --mark-incomplete ~/notes
# Force highlighting off in server mode
mbr -s --no-mark-incomplete ~/notes
Configuration file:
# .mbr/config.toml
# Force a value (overrides the per-mode default; CLI flag still wins)
mark_incomplete = true
# Customize the marker list. Markers are matched uppercase only at the
# start of a block, with a word boundary on the right edge.
incomplete_markers = ["TK", "TODO", "FIXME", "XXX"]
Environment variables:
MBR_MARK_INCOMPLETE=true
MBR_INCOMPLETE_MARKERS='["NOTE","DRAFT"]'
Setting incomplete_markers = [] disables the feature entirely (the pass
becomes a no-op even when mark_incomplete = true).
Per-Page Error Indicator (Server / GUI Only)
When running with -s (server) or -g (GUI), mbr exposes a lightweight
per-page diagnostics endpoint at /{page}/errors.json and a matching
<mbr-page-errors> navbar indicator that lights up with a ⚠ icon when the
current page has problems.
The endpoint detects three issue types in v1:
| Type | Trigger |
|---|---|
broken_internal_link | An outbound internal link whose target does not resolve (via path_resolver). Mirrors the existing build-time link validation, but runs live and non-fatally. |
broken_media_reference | <img>, <video>, <audio>, or <source> whose internal src does not exist on disk or via the static-folder overlay. |
unresolved_wikilink | A literal [[...]] that survived into the rendered HTML (e.g. inside a raw-HTML block). Most wikilinks are caught by broken_internal_link instead. |
The response is JSON with a stable, tagged shape:
{
"page_url": "/docs/guide/",
"errors": [
{ "type": "broken_internal_link", "target": "/nonexistent/", "text": "bad" },
{ "type": "broken_media_reference", "src": "./missing.png", "kind": "image" },
{ "type": "unresolved_wikilink", "raw": "[[never-a-real-page]]" }
]
}
Status codes:
200— page exists and was scanned (even whenerrorsis empty; the client uses the array length to decide whether to show the ⚠ icon).404— the path is not a markdown page, orlink_trackingis disabled.
No new flags. This feature reuses the existing --no-link-tracking /
link_tracking switch: disable link tracking to suppress the endpoint and
hide the indicator.
Zero leakage into static builds. The endpoint is registered only in
server.rs, the <mbr-page-errors> element is gated on
{% if server_mode %} in templates/_nav.html, and the Lit component
self-guards on window.__MBR_CONFIG__.serverMode. cargo run -- -b <repo>
continues to validate links at build time via the existing
--skip-link-checks flow, without emitting errors.json files or
<mbr-page-errors> elements into the output.
Video Metadata Extraction
Note: This feature requires the
media-metadataCargo feature to be enabled at compile time.
mbr can extract video metadata (cover images, chapters, and captions) from video files. This works in two ways:
Server Mode (Dynamic Generation):
When running with -s or -g, mbr automatically generates metadata files on-the-fly when they don’t exist on disk. Request any of these special paths to trigger generation:
| Pattern | Description |
|---|---|
{video}.cover.jpg | Cover image (frame captured at 5 seconds, or earlier for short videos) |
{video}.chapters.en.vtt | Chapter markers in WebVTT format |
{video}.captions.en.vtt | Subtitles/captions in WebVTT format |
Example: If you have videos/demo.mp4, requesting /videos/demo.mp4.cover.jpg will dynamically extract and return a cover image.
Generated metadata is cached in memory to avoid repeated ffmpeg operations.
CLI Mode (Pre-generation):
Use --extract-video-metadata to extract metadata and save as sidecar files:
# Extract metadata from a single video
mbr --extract-video-metadata ~/videos/demo.mp4
# Output:
# Analyzing video: /Users/you/videos/demo.mp4
# Duration: 120.5s, Chapters: yes, Subtitles: no
# + Created: /Users/you/videos/demo.mp4.cover.jpg
# + Created: /Users/you/videos/demo.mp4.chapters.en.vtt
# - No captions found in video
This is useful for pre-generating metadata for static site builds or when you want the files persisted to disk.
Video Transcoding (EXPERIMENTAL)
Note: This feature requires the
media-metadataCargo feature to be enabled at compile time.
⚠️ EXPERIMENTAL - This feature is new and feedback is welcome! Please report issues or suggestions at the project repository.
mbr can dynamically transcode videos to lower resolutions (720p, 480p) using HLS (HTTP Live Streaming) for bandwidth savings on mobile devices and slow connections. This feature only works in server/GUI mode (-s or -g).
Enable transcoding:
mbr -s --transcode ~/notes
How it works:
- When transcoding is enabled, video embeds include multiple
<source>tags with media queries - Desktop browsers (viewport >= 1280px) load the original MP4 directly
- Tablets (viewport >= 640px) use HLS 720p playlist (Safari only)
- Mobile devices use HLS 480p playlist (Safari only)
- Non-Safari browsers fall back to the original MP4 (HLS requires JavaScript in Chrome/Firefox)
- HLS segments (~10 seconds each) are transcoded on-demand and cached
URL patterns:
| Type | Example |
|---|---|
| Original video | /videos/demo.mp4 |
| 720p HLS playlist | /videos/demo-720p.m3u8 |
| 720p HLS segment | /videos/demo-720p-005.ts |
| 480p HLS playlist | /videos/demo-480p.m3u8 |
Browser compatibility:
| Browser | HLS Support | Behavior |
|---|---|---|
| Safari (macOS/iOS) | Native | Uses HLS variants on mobile/tablet |
| Chrome/Firefox/Edge | None | Falls back to original MP4 |
Hardware acceleration: Transcoding automatically uses hardware encoders when available:
- macOS: VideoToolbox (
h264_videotoolbox) - Linux: NVIDIA (
h264_nvenc), AMD (h264_amf), Intel (h264_qsv), VAAPI - Fallback: Software encoding (
libx264)
Configuration:
| Option | Type | Default | Description |
|---|---|---|---|
transcode | bool | false | Enable dynamic video transcoding via HLS |
Memory usage: HLS segments and playlists are cached in memory (~200MB max by default). Each segment is approximately:
- 720p segment (10s @ 2.5 Mbps): ~3 MB
- 480p segment (10s @ 1.0 Mbps): ~1.25 MB
- Playlist: ~1-2 KB (negligible)
Cache evicts oldest segments when full, prioritizing keeping playlists cached.
When to use:
- Serving videos to Safari users on mobile devices
- Reducing bandwidth usage for remote viewers on iOS/macOS
- Fast video startup (only first segment needs to transcode)
When NOT to use:
- Local browsing on the same machine (default: off)
- Users primarily on Chrome/Firefox (they get original MP4 anyway)
- Static site generation (transcoding is server-only)
PDF Cover Extraction
Note: This feature requires the
media-metadataCargo feature to be enabled at compile time.
mbr can extract cover images (first page) from PDF files. This works in two ways:
Server Mode (Dynamic Generation):
When running with -s or -g, mbr automatically generates cover images on-the-fly when requested. Request {pdf}.cover.jpg to get the cover:
| Pattern | Description |
|---|---|
{pdf}.cover.jpg | Cover image (first page rendered at max 1200px width) |
Example: If you have docs/report.pdf, requesting /docs/report.pdf.cover.jpg will dynamically extract and return the cover image.
Pre-generated covers: If a file {pdf}.cover.jpg already exists on disk (as a sidecar file), it will be served directly without extraction. This is useful for static builds.
CLI Mode (Pre-generation):
Use --extract-pdf-cover to extract covers and save as sidecar files:
# Extract cover from a single PDF
mbr --extract-pdf-cover ~/docs/report.pdf
# Output:
# Extracting cover: /Users/you/docs/report.pdf -> /Users/you/docs/report.pdf.cover.jpg
# ✓ Created 1 cover image
# Extract covers from all PDFs in a directory (recursive)
mbr --extract-pdf-cover ~/docs
# Output:
# Extracting cover: docs/report.pdf -> docs/report.pdf.cover.jpg
# Extracting cover: docs/manual.pdf -> docs/manual.pdf.cover.jpg
# ✓ Created 2 cover images
Exit codes:
| Code | Meaning |
|---|---|
0 | Success (all covers created) |
1 | Partial failure (some PDFs failed, others succeeded) |
2 | Total failure (no covers created) |
Error handling:
- Password-protected PDFs are skipped with an error message
- Corrupt or unreadable PDFs are reported to stderr
- The process continues even when individual PDFs fail
Static builds: For static site generation, pre-generate covers before building:
# 1. Extract all PDF covers
mbr --extract-pdf-cover ~/notes
# 2. Build static site (covers are included as assets)
mbr -b ~/notes
The sidecar .cover.jpg files are automatically included in static builds via asset symlinking.
Environment Variables
There are good reasons to allow this behavior, but in general, please don’t use environment variables for configs. It’s very easy to get unexpected behavior as you forget about the silent inputs from the environment.
Every configuration option can be set via environment variable with the MBR_ prefix:
# Server settings
MBR_HOST=0.0.0.0
MBR_PORT=3000
# Content settings
MBR_STATIC_FOLDER=assets
MBR_INDEX_FILE=README.md
# Behavior
MBR_OEMBED_TIMEOUT_MS=1000
MBR_OEMBED_CACHE_SIZE=4194304 # 4MB
# Video transcoding (requires media-metadata feature)
MBR_TRANSCODE=true
# Incomplete-block highlighting (TK / TODO / FIXME / XXX)
MBR_MARK_INCOMPLETE=true
MBR_INCOMPLETE_MARKERS='["NOTE","DRAFT"]'
Environment variables override config file settings.
Root Directory Detection
mbr automatically finds the repository root by searching upward for common repository markers:
Directory markers (searched first, in order):
| Marker | Description |
|---|---|
.mbr/ | mbr configuration folder (highest priority) |
.git/ | Git repository |
.zk/ | Zettlekasten notes |
.obsidian/ | Obsidian vault |
File markers (searched if no directory markers found):
| Marker | Description |
|---|---|
book.toml | mdBook project |
mkdocs.yml | MkDocs project |
docusaurus.config.js | Docusaurus project |
The search works as follows:
- Start from the specified path
- Search upward for directory markers in priority order
- If no directory markers found, search for file markers
- First marker found determines the root directory
- If no markers found, fall back to current working directory (if ancestor) or specified path
This allows running mbr from any subdirectory:
cd ~/notes/docs/guide
mbr -s . # Still uses ~/notes/.mbr/config.toml
Static Folder
The static_folder setting creates an overlay for serving static files. The default is static, meaning files in a static/ folder at the repo root are served at the root URL path.
notes/
├── static/ # Default static folder
│ └── images/
│ └── logo.png # Available at /images/logo.png
└── docs/
└── guide.md
To use a different folder, configure it in .mbr/config.toml:
static_folder = "assets"
In guide.md:

Path Resolution Order
- Check if path matches a markdown file
- Check if path is a directory with index file
- Check if path matches file in static folder
- Return 404
Template Folder
The --template-folder flag overrides the default template resolution:
mbr -s --template-folder ./my-theme ~/notes
Files are loaded from this folder first, falling back to compiled-in defaults if not found.
Useful for:
- Theme development
- Testing template changes
- Sharing themes across repositories