A Lunet site is a folder containing a config.scriban file. Lunet combines your files with a layered meta-folder named .lunet/ to produce the final output.
mysite/
config.scriban ← site configuration (required)
readme.md ← home page (if readme_as_index = true, the default)
docs/
getting-started.md ← content pages
advanced.md
css/
main.scss ← SCSS (converted to CSS by Dart Sass)
js/
main.js ← JavaScript
img/
logo.svg ← static asset
menu.yml ← navigation definition
.lunet/
includes/ ← reusable template fragments
layouts/ ← page layout templates
data/ ← data files (YAML/JSON/TOML)
extends/ ← local themes/extensions
modules/ ← module assets (search db, menu JS, etc.)
build/
www/ ← generated output (do not edit)
cache/ ← download/build cache
.lunet/ folderThe .lunet/ folder contains everything that supports your site but isn't content itself:
| Subfolder | Purpose | Accessed as |
|---|---|---|
.lunet/includes/ |
Reusable Scriban template fragments | {{ include "partials/nav.sbn-html" }} |
.lunet/layouts/ |
Page layout templates | Resolved automatically by layout name/type (see Layouts & includes) |
.lunet/data/ |
Data files loaded before content processing | site.data.<filename> in templates (see Data modules) |
.lunet/extends/ |
Local themes/extensions | extend "mytheme" in config (see Themes & extensions) |
.lunet/modules/ |
Module runtime assets | Search database, menu JS, API doc assets — managed by plugins |
.lunet/build/ |
Generated output and caches | Not accessed directly |
.lunet/build/| Path | Purpose |
|---|---|
.lunet/build/www/ |
Default output directory. Contains the generated site ready to deploy. |
.lunet/build/cache/ |
Download and build cache. Extensions, npm resources, and API extractor output are cached here. |
.lunet/build/cache/.lunet/ |
Cached meta files (layouts/includes from extensions). |
This is one of Lunet's most powerful features. Instead of a single folder, Lunet sees your site through a layered virtual filesystem that merges multiple sources:
Priority (highest to lowest):
┌───────────────────────────────────────┐
│ 1. Your site files │ ← highest priority
├───────────────────────────────────────┤
│ 2. Theme/extension files │ ← from extend "..."
├───────────────────────────────────────┤
│ 3. Lunet built-in shared files │ ← shipped with Lunet
└───────────────────────────────────────┘
When two layers have a file at the same path, the higher-priority layer wins. This applies to everything: layouts, includes, data files, and content.
The layered filesystem is what makes themes and overrides work seamlessly:
/.lunet/layouts/_default.sbn-html.<your-site>/.lunet/layouts/_default.sbn-html — your file takes priority.css/theme.css? You can override it by placing css/theme.css in your site.You never need to edit theme files. Just create the same path in your site.
.lunet/)The .lunet/ folder has its own layered resolution, composed from two sources:
.lunet/ — the .lunet/ subtree from the main layered filesystem (your site > themes > shared) — highest priority.lunet/ (.lunet/build/cache/.lunet/) — cached meta files — lowest priorityYour site's .lunet/ files always take precedence over cached copies. When a layout or include is requested, Lunet searches through all these layers in priority order.
Lunet ships a set of default layouts, includes, and module assets under a shared/ folder alongside the Lunet binary. These form the lowest-priority layer. Key files include:
| Path | Module | Description |
|---|---|---|
/.lunet/layouts/_default.sbn-html |
Layouts | Minimal HTML document shell |
/.lunet/layouts/_default.rss.xml |
RSS | Default RSS 2.0 feed layout |
/.lunet/layouts/_default.sitemap.xml |
Sitemaps | Default sitemap layout |
/.lunet/includes/_builtins/head.sbn-html |
Core | Default <head> content |
/.lunet/includes/_builtins/bundle.sbn-html |
Bundles | CSS/JS bundle injection |
/.lunet/includes/_builtins/cards.sbn-html |
Cards | OpenGraph/Twitter meta tags |
/.lunet/includes/_builtins/google-analytics.sbn-html |
Tracking | Analytics snippet |
/.lunet/includes/_builtins/livereload.sbn-html |
Server | Live reload WebSocket script |
/.lunet/modules/search/sqlite/* |
Search | SQL.js WASM, search client JS |
/.lunet/modules/menus/* |
Menus | Async menu JS |
Not all files in your site folder are processed. Lunet uses three glob collections to decide:
force_excludes — always excluded, cannot be overridden: **/.lunet/build/**, /config.scribanincludes — overrides excludes: **/.lunet/**excludes — skipped unless matched by includes: **/~*/**, **/.*/**, **/_*/**Files not matching any rule are included. See Configuration for customizing these patterns.
By default, Lunet writes the generated site to:
<site>/.lunet/build/www/
You can override this with the -o / --output-dir CLI option:
lunet build -o ./public
When -o is specified, the path is resolved relative to the current working directory.
.scss) → converted and written (e.g. as .css).After a build, stale output files (from pages that no longer exist in the source) are automatically deleted. Lunet also detects and reports duplicate output paths — if two source files would produce the same output file, an error is logged.
For HTML-like content with folder URLs (the default):
docs/intro.md → output: .lunet/build/www/docs/intro/index.htmlabout.md → output: .lunet/build/www/about/index.htmlreadme.md → output: .lunet/build/www/index.html (when readme_as_index = true)With url_as_file = true:
docs/intro.md → output: .lunet/build/www/docs/intro.htmlThe first directory segment of a content file's path is its section:
docs/intro.md → section = docsblog/2024-01-01-hello.md → section = blogreadme.md → section = "" (root, no section)Sections matter because:
page.layout defaults to the section name (so pages in docs/ look for a docs layout first). See Layouts & includes./.lunet/layouts/docs.sbn-html, /.lunet/layouts/blog.sbn-html).The section is available as page.section in templates, and page.path_in_section gives the remaining path after the section directory (e.g. /intro.md within section docs).
lunet init skeletonWhen you run lunet init mysite, Lunet copies a minimal skeleton from its built-in shared files:
config.scriban — site configuration that extends the default Lunet template with project metadatareadme.md — a quickstart home page with configuration reference and customization guidemenu.yml — top-level navigation (Home + Docs)docs/readme.md — a starter documentation pagedocs/menu.yml — sidebar navigation for the docs sectionThe template provides layouts, includes, CSS/JS assets, a theme switcher, and search — your site is fully functional out of the box. See Getting started for a walkthrough.
config.scriban) — site variables, includes/excludes patterns/.lunet/layouts/lunet init, lunet clean, output directory options