Configuration
Config format
The configuration file is in the simple TOML format. Sajt is using version 0.40 of TOML—although likely later versions of TOML will work just as fine, as sajt's use is of TOML's features is limited.
Sajt's config makes use of TOML sections, [<sectionName>]. Sections are used to represent folders or groups of
files on-disk. In each TOML section, we configure site generation by setting a few different
config keys in the section. Which keys are set and how to use them is documented throughout this page. Most config keys are
optional and have well-defined defaults, but two keys are always strictly required:
files- a list of the files that you want to use as content filestemplate- the template that is used to generate the output file by slotting in the contents of an input file somewhere inside the template
[general]
files = ["index.md"]
template = "template.sajt.html"
A template is a bit like a picture frame, whereas you can think of input files from files
like the picture being framed.
files is a list, and can contain many input files. When processing the list of files, each
file is passed through the defined template.
On the section [general]
There is one special section, [general], and we usually refer to all the other types of
sections as groups / a group section, or denoting it as a group placeholder with <groupName>.
The [general] section stores settings that will be applied to all evaluated files unless
explicitly overridden. A group section can override any general settings by providing its own
values for the keys set in [general]:
[general]
template = "sample-template.sajt.html"
[introduction]
# this section group will use the site-wide template `sample-template.sajt.html`
files = ["intro.md"]
[docs]
# override the site-wide template
template = "another-template.sajt.html"
files = ["documentation.md"]
Config keys are defined on either [general] or on [<groupName>] -- for an exhaustive list
of config keys, see section Config keys.
Let's look at how a simple config might be written.
Simple config
Below we have a simple sajt config:
[general]
outroot = "outdir"
extras = ["extras/", "links/"]
[docs]
template = "sample-template.txt"
dirFiles = "./"
files = ["content.md"]
We'll save this in a file called simple-config.toml.
Here is the starting point, before running sajt, as a directory listing:
sample-template.txt
simple-config.toml
extras/
links/
docs/
content.md
The data flow goes like
╔══════════════════╗
║ docs/content.md ║ ──passed to────┐
╚══════════════════╝ │
╔═════════════════════╗ │
║ sample-template.txt ║ ───────────┘
╚═════════════════════╝
│
└───saved to─────┐
╔════════════════════╗
║ outdir/content.md ║
╚════════════════════╝
The config applies template sample-template.txt to the input docs/content.md. Output is saved as outdir/content.md.
Any needed output directories will be created if necessary. Directories and files specified in
extras are also copied to the output directory. Entries in extras are only copied - they
are not applied to the template.
This is the directory listing after running sajt --config simple-config.toml:
sample-template.txt
simple-config.toml
docs/
content.md
extras/
links/
outdir/
extras/
links/
content.md
This example intends to clarify basic usage. There are more config keys that can be used,
allowing us to specify the name of output files, toggle parsing of input as markdown and
converting to HTML or keeping it unchanged, to rewrite input file's suffixes but keeping the
rest of the filename (from .md to .html), and specify all manner of directories depending
on a site's structure.
Setting up RSS
RSS feed generation can be enabled for any section of the config, allowing your site to announce any new files that are added to that section in a way that it is discoverable and compatible with any RSS reader.
To show how to set it up, we'll use the example from above, which described a docs section.
We'll augment docs with the structure needed for the feed generation. We'll take the section
name, append .rss to it, and add at least the RSS-specific config keys canonicalURL and
title:
[general]
outroot = "outdir"
extras = ["extras/", "links/"]
[docs]
template = "sample-template.txt"
dirFiles = "./"
files = ["content.md"]
suffix = "html"
[docs.rss]
canonicalURL = "https://docs-site.com"
relativeURL = "docs/"
title = "sajt documentation"
description = "a feed sharing quarterly updates to sajt"
Note: suffix = "html", which sets the output file's extension to .html and is also picked
up when creating the RSS feed.
Using the info we set above, our feed docs.xml would look like:
<rss version="2.0">
<channel>
<title>sajt documentation</title>
<link>https://docs-site.com</link>
<description>a feed sharing quarterly updates to sajt</description>
<item>
<title><![CDATA[content title]]></title>
<link><![CDATA[https://docs-site.com/docs/content.md.html]]></link>
<description><![CDATA[welcome to the docs site]]></description>
<pubDate><![CDATA[Wed, 14 Oct 2025 19:13:37 +0200]]></pubDate>
</item>
</channel>
</rss>
The RSS generator picks up all the files described by [docs]'s key files, and it even works
with the files = "*.txt" glob syntax.
Let's say we change files by adding installation.md:
files = ["content.md", "installation.md"]
The next time we run sajt.com --config simple-config.toml, our feed would be updated:
<rss version="2.0">
<channel>
<title>sajt documentation</title>
<link>https://docs-site.com</link>
<description>a feed sharing quarterly updates to sajt</description>
<item>
<title><![CDATA[content title]]></title>
<link><![CDATA[https://docs-site.com/docs/content.html]]></link>
<description><![CDATA[welcome to the docs site]]></description>
<pubDate><![CDATA[Wed, 14 Oct 2025 19:13:37 +0200]]></pubDate>
</item>
<item>
<title><![CDATA[installation]]></title>
<link><![CDATA[https://docs-site.com/docs/installation.html]]></link>
<description><![CDATA[here is what to consider when it comes to installation]]></description>
<pubDate><![CDATA[Wed, 15 Oct 2025 09:12:51 +0200]]></pubDate>
</item>
</channel>
</rss>
We can see that installation.md has been picked up and is represented as an html file.
If we're looking carefully, we can also note that the RSS entry for content.md has kept its
publish date pubDate from the first generation. This is good, because if the older entries
had their dates changed it would be a nuisance for anyone following the feed in their reader.
Config keys
Below is an exhaustive list of all the currently defined config keys.
Keys either operate on a sajt-specific section, called [general], or on user-defined
sections, referred to as <groupName>.
Only two keys are required in a sajt config: template and files. All other keys are
optional with their defaults behaviour, if omitted, documented below.
Instead of reading the entire list below, you may want to jump to section Config examples.
To keep this section compact, we use the convention of [general].key to signify setting key on the section
general:
[general]
key = ...
Note: [general] is optional if other user-defined sections exist.
<shared keys>
The section <shared keys> enumerates keys that can be set on any section.
root Optional
The directory that is the root for all the inputs (templates, input files, or extras) and relative paths sajt tries to read and process.
Defaults: the directory hosting the config file is used if [general].root and [<groupName>].root are not provided.
outroot Optional
The directory that will be the root for the output written by sajt after applying template file to input files.
Defaults: value of whatever root is derived to be
extras Optional
A list of paths to additional files or folders that should be copied over to the specified outroot.
Defaults: empty table (list) as default
template Required!
The filename of the template that will be used in combination with an input file, resulting in another file that is saved as the output of the templating process.
This must be set either as [general].template or [<groupName>].template
dirFiles Optional
Used to source files from another part of the file system.
For example, a sajt-specific directory may contain all the HTML-specific files it needs (css,
sajt template, fonts, website image elements). But the actual content files may be stored
elsewhere, such as in a repo directory. By specifying dirFiles, we can pull in the content
files separately from the rest of the project-specific files.
Defaults: uses value of [<groupName>] as directory.
NOTE: If key dirFiles is set in [general] then <groupName> WILL NOT be used
dirTemplate Optional
The parent folder of template files.
Defaults: value of whatever root is derived to be.
dirInput Optional
The parent folder of input files.
Defaults: value of whatever root is derived to be.
_note (2025-05-30): dirInput is used for templating mycelial tech across different group names_
dirOutput Optional
The directory in which to save output files. If dirOutput is an absolute path then that is
used, otherwise dirOutput is relative to outroot as join(outroot, dirOutput).
Defaults: uses value of [<groupName>].
files Required!
A list that specifies the filenames to read as input files.
This list will be processed one at a time. The full path of any input files read is constructed as
path.join(root, dirInput, dirFiles, filename)
NOTE: If any path segment is absolute (e.g. dirFiles) then that overrides
the any preceeding path segments.
This must be set either as [general].files or [<groupName>].files.
NOTE: Even if [general].files is the only one set of files provided, sajt will be able
to read different files in different groups by virtue of setting/using dirInput / dirFiles
to locate the input files to read.
parseInput Optional
Toggles whether input files should be parsed based on the input's file extension (.md for markdown or .gmi for gemini) and converted into HTML.
If parseInput = false the parsing is turned off. This can be useful in circumstances when you
want to create templates of HTML inputs or you want to pass on the input markdown content
unchanged.
Defaults: parseInput = true
suffix Optional
Replace the file extension part from input files and with the specified suffix when writing
output files. For example, with suffix = "html" the input file "readme.md" will be output as
"readme.html"
Defaults: no value and unused if unset
lua_dictionary Optional
Provide a custom and computationally enhanced dictionary (see the documentation for config key dictionary).
The lua file should return an anonymous function of the following format:
function (dict) return newDict end
The returned dictionary will be merged with sajt's dictionary, and it will overwrite any keys present in sajt's dictionary.
Defaults: no value and unused if unset
lua_operators Optional
Register one or more custom sajt operators by providing the path to a lua file. The lua file should return/export a table, setting its keys to the names of the new operators. The value for each key should be an anonymous function of the format:
local fn = function (input, [dict])
-- local matched = input:match(PATTERN)
return newString
end
Defaults: no value and unused if unset
dictionary Optional
A way to specify freeform key-value pairs on a file-by-file basis. If you are familiar with "markdown frontmatter" then think of this like frontmatter that doesn't pollute the markdown file.
Example:
We are setting dictionary for the group docs and its file content.md:
[docs.dictionary."content.md"]
key1 = "<value1>"
key2 = "<value2>"
Example:
We are setting dictionary for all files processed by the config (a custom config-wide dictionary):
[general.dictionary]
globalKey1 = "<value1>"
anotherKey2 = "<value2>"
Just as with other overriding behaviour of sajt, the keys in the config-wide will be overridden if the same key is set for a group. Once that group has been processed, the config-wide dictionary will no longer be overridden.
Defaults: no value and unused if unset
[<groupName>] only
NOTE: [<groupName>]-only is applies if we have more categories than just general. If we
have ONLY [general], then [general] will be able to use these keys if set!
outFilename Optional
Specifies the name of the output file.
Defaults: uses filename of the currently iterated file from files.
rename Optional
A table mapping input filenames to output filenames.
Example: The input file "./" will be saved as "index.html", and "config.html" will be saved as "config.html".
rename = { ./ = "index.html", config.html = "config.html"}
Defaults: uses filename of the currently iterated file from files.
replace Optional
A table mapping search strings to replacement strings. A search and replace
is run for each file in the current section where replace is set (all files, if replace is
set in [general]).
Example:
Each input file will have the string "github.com" replaced with "codeberg.org".
rename = { github%.com = "codeberg.org"}
NOTE: The search strings are Lua pattern matching expressions.
Notably, the following commonly used characters have to be escaped by putting a % in front:
-->%-(hyphens).->%.(periods)?->%?(question marks)
The full set of characters that need to be escaped: ^$()%.[]*+-?
Defaults: If omitted, no replacements are attempted.
RSS specific keys - [<groupName>.rss]
Note: when we use optional and required here, we're referring to the whether the key is required for the RSS functionality -- not sajt at large.
RSS generation works by augmenting an existing section [<groupName>]. It has its own set of
keys which are set in its own section, [<groupName>.rss].
RSS picks up and uses the following general keys set on the parent section [<groupName>]:
suffixfilesdirOutputdirFiles
The rest of the keys in this section have to be set in [<groupName>.rss]
canonicalURL Required!
This is the base URL that will be used. It must be absolute and contain the protocol (http,
https). It will be used in the <channel>'s <link> attribute, as well as the base for all
RSS items.
For example, with canonicalURL = "https://docs-site.com" our feed would look like:
<rss version="2.0">
<channel>
...
<link>https://docs-site.com</link>
...
<item>
...
<link><![CDATA[https://docs-site.com/content.html]]></link>
...
</item>
</channel>
</rss>
title Required!
Sets the channel title property, which names the rss feed. The title is often prominently displayed in RSS readers.
relativeURL Optional
This sets the relative URL that will be used. If set it will be concatenated with
canonicalURL as <canonicalURL>/<relativeURL> -- excess slashes are taken care of
automatically.
If it isn't set, then all RSS entries will be relative to the root of canonicalURL.
description Optional
Sets the channel description property, which describes the rss feed as a whole.
Defaults: the empty string.
feedName Optional
Sets the name of the generated rss file.
Note: the feed file is saved relative to the outroot, which is often the base of directory that
sajt's output is saved to.
Defaults: <groupName>.xml
On directories
Keys root, dirInput, dirFiles and non-[general] categories, [<groupName>], combine to derive the
path for the input files passed through a given template.
rootdefines the root of the current sajt project.- If
rootis not specified, then its value defaults to the absolute path of the directory storing the passed-in config file--config <conf.toml> dirInputdefines the parent directory for any and all input files. If it is not specified, it defaults to the value./.
Directories can be set as either relative to the current sajt project or as absolute paths.
Config examples
Rather than digest the entire set of config keys, it can be easier to understand how to use sajt by reading example config files. Let's go over a few of those.
Example
The config is at /var/www/beetle-site/sajt.toml. root, dirInput, and dirFiles are not
specified. The config has only one category [beetles].
We would read the following input files, demonstrated below by using __ as separators for the different sajt-specific path segments. Since root, dirInput, dirFiles are not specified we show how they are evaluated to their relevant default values:
rootdefaults to/var/www/beetle-sajtsince that directory holds the configdirInputdefaults to./dirFilesdefaults to the name of the category,<groupName>- the category is
beetles, meaningdirFilesdefaults tobeetles
- the category is
root__dirInput__dirFiles
->
/var/www/beetle-sajt__./__<groupName>
->
/var/www/beetle-sajt__./__beetles
->
/var/www/beetle-sajt/beetles # this is where we try to read input files from
Another site might be structured differently, requiring using the key dirInput to read
files.
Example
We have the following sajt.toml:
[general]
dirInput = "markdown-files"
template = "flour-info.html"
[durum]
files = ["index.md"]
[rye]
files = ["index.md"]
[rice]
files = ["index.md"]
The config is at /var/www/flour-site/sajt.toml. Key dirInput is set to the folder name
markdown-files. Keys dirTemplate and dirFiles are not specified and left to their defaults. The
config has the groups [durum], [rye], [rice]. Each group declares a file index.md.
Using this structure, the directory containing input files would be read as:
root__dirInput__dirFiles
->
/var/www/flour-site/__markdown-files__<groupName>
->
/var/www/flour-site/markdown-files/rye
/var/www/flour-site/markdown-files/durum
/var/www/flour-site/markdown-files/rice
Meaning input files for each group would be read as:
/var/www/flour-site/markdown-files/rye/index.md
/var/www/flour-site/markdown-files/durum/index.md
/var/www/flour-site/markdown-files/rice/index.md
Templates
Template variables
As sajt reads and applies a template to a set of content files, there are a set of template
variables that can be used. Template variables have their value determined by the current input. Operate on these
template variables using sajt's commands as you would any other variable.
CONTENT
Perhaps the most important template variable, CONTENT stores the verbatim input of the
currently iterated content file.
${CONTENT} (using sajt's get command) inserts the content into the position of the template file where ${CONTENT} is
written.
PAGE_NAME
The name of the content file stored in CONTENT as determined by its filename.
TEMPLATE_NAME
The name of the template file as determined by its filename.
GROUP_NAME
The config operates on groups, defined by the TOML syntax [group]; ${GROUP_NAME} retrieves the current group name.
ROOT_DIRECTORY
The directory all other directories are relative to; specified by config key root.
BASE_DIRECTORY
The directory storing the currently evaluated template file, as determined by dirname(template_file_fullpath).
CONTENT_DIRECTORY
The directory storing the currently evaluated content file, as determined by dirname(content_file_fullpath).
INPUT_DIRECTORY
The input directory for the current group; specified by config key dirInput.
TEMPLATES_DIRECTORY
The templates directory for the current group; specified by config key dirTemplates.
FILES_DIRECTORY
The files directory for the current group; specified by config key dirFiles.
FILES_LIST
A comma separated list of all the files being iterated on for the current group. Useful for constructing an index listing of multiple files for e.g. cross-linking or constructing a link index.
Advanced config example
Below you can find a rather complex example. It is making use of defaults provided by the
[general] group, as well as per-group overrides.
Thanks to my friend glyph for making a thoroughly interesting site and putting the source code up for view.
[general]
root = "./"
outroot = "./site/"
dirInput = "templates/"
template = "templates/base.html"
extras = ["links/", "static/"]
files = ["index.html"]
[index]
dirFiles = "./"
dirOutput = "./"
[art]
[background]
[bicycles]
[japanese]
[lists]
[meditation]
[projects]
[support]
[bacteria]
files = ["index.html", "sauerkraut-beginnings.html", "sauerkraut-bottled.html"]
[computers]
files = ["index.html", "esp8266-dht11.html", "i2c-adventures.html", "rust-compilation.html"]
[fungi]
files = ["index.html", "design-patterns.html", "glossary.html", "grow-forests.html", "grow-together.html", "lichen-space.html", "mycomaterials_guide.html", "photo-guide.html", "reading-list.html"]
[plants]
files = ["index.html", "aloe-there.html", "blueberry-dance.html", "botanical-deceptions.html", "potato-tech.html"]
Directory listing after running sajt (all templated output resides in folder
site/). Note how static/ and links/ have been copied to the output
directory site/.
site
├── art
│ └── index.html
├── background
│ └── index.html
├── bacteria
│ ├── index.html
│ ├── sauerkraut-beginnings.html
│ └── sauerkraut-bottled.html
├── bicycles
│ └── index.html
├── computers
│ ├── esp8266-dht11.html
│ ├── i2c-adventures.html
│ ├── index.html
│ └── rust-compilation.html
├── fungi
│ ├── design-patterns.html
│ ├── glossary.html
│ ├── grow-forests.html
│ ├── grow-together.html
│ ├── index.html
│ ├── lichen-space.html
│ ├── mycomaterials_guide.html
│ ├── network-resilience.html
│ ├── photo-guide.html
│ └── reading-list.html
├── index.html
├── japanese
│ └── index.html
├── links
│ └── style.css
├── lists
│ └── index.html
├── meditation
│ └── index.html
├── plants
│ ├── aloe-there.html
│ ├── blueberry-dance.html
│ ├── botanical-deceptions.html
│ ├── index.html
│ └── potato-tech.html
├── projects
│ └── index.html
├── static
│ ├── art
│ │ ├── aloe.jpg
│ │ ├── archer.jpg
│ │ ├── beetle.jpg
│ ├── bacteria
│ │ ├── sauerkraut_jar.jpeg
│ │ └── sauerkraut_mountain.jpeg
│ ├── bicycles
│ │ └── bicycle.jpg
│ ├── computers
│ │ ├── debian_pi_pros_cons.png
│ ├── favicon.png
│ ├── feed.rss
│ ├── fungi
│ │ ├── earthstar.jpeg
│ │ ├── earthstar_mycelium.jpeg
│ │ ├── earthstar_roots.jpeg
│ │ ├── photo_guide
│ │ │ ├── bottom_view.jpg
│ │ └── xanthoria_fun_dye.jpeg
│ ├── glyph_laetiporus.jpg
│ ├── glyph.svg
│ ├── meditation
│ │ └── fire_poi.jpg
│ └── plants
│ ├── potato_battery.jpeg
│ ├── potato_sprout.jpeg
│ └── tree_aloe.jpg
└── support
└── index.html