gramr reads three files. In all of them, unknown keys are errors, so a typo never goes unnoticed.

FileWhereWhat for
Global configuration~/.config/gramr/config.tomlDefaults shared by all sites. Optional.
Site configuration~/.config/gramr/sites/<site>.tomlOne per site: where the site repository and the notes are, where to push.
Repository configurationgramr.toml, in the site repositoryHow the site is built. Created by gramr init.

~/.config is $XDG_CONFIG_HOME when it is set.

Global configuration

~/.config/gramr/config.toml. gramr config edit creates and opens it.

[source]
path = "~/Notes"    # default source folder
KeyDefaultRules
source.pathnoneAbsolute, or starting with ~.

Site configuration

~/.config/gramr/sites/<site>.toml. The file name is the site name, used in gramr publish <site> and in the notes' gramr.site: letters, digits, - and _ only. gramr site list lists them.

[repository]
url = "ssh://git@forgejo.example.com/sites/blog.git"
ref = "main"
# path = "~/dev/blog"     # or a local folder

[source]
path = "~/Writing/Blog"   # optional: overrides the global source folder
include = ["**"]          # optional: restrict the folders scanned
default_section = "notes"

[image]
name = "forgejo.example.com/sites/blog"
KeyDefaultRules
repository.url + repository.ref—Remote site repository. ref is a branch, a tag or a commit SHA.
repository.path—Local folder. It does not need to be a Git repository. Absolute, or starting with ~. Set either url + ref, or path, not both.
source.pathglobal source.pathAbsolute, or starting with ~. Required here if the global configuration does not set it.
source.include["**"]Glob patterns relative to source.path. * does not cross /, ** does: Blog/* only matches notes directly in Blog/. Must not be empty. Links and attachments are still resolved against the whole folder.
source.default_sectionrequiredSection of notes without gramr.section.
image.namerequiredImage name without tag or digest, e.g. forgejo.example.com/sites/blog. A registry port (registry:5000/blog) is fine.

How the site repository is used

gramr never modifies the site repository. It copies it into a temporary workspace, writes the notes there, and builds the image from it. The workspace is removed when gramr exits, unless --keep-workspace is set.

Site repositoryWorkspace
url + refCloned without history, submodules included (Zola themes are often submodules). Authentication uses your SSH or credential setup.
pathCopied as is, uncommitted changes included, skipping what the folder's own .gitignore files exclude (public/, node_modules/…). Your global Git excludes are ignored, so the copy is the same on every machine.

.git folders are always removed: the image never contains the history.

With a branch as ref, two builds may use different commits; the git-<sha> tag records the one actually used. Fetching a commit by its SHA depends on the server: if it refuses, use a branch or a tag.

Repository configuration

gramr.toml, at the root of the site repository. Required, but it can be empty: every key has a default. All paths are relative to the repository and must stay inside it.

[build]
dockerfile = "Dockerfile"
context = "."
platforms = ["linux/arm64"]

[content]
generator = "zola"
dir = "content"
assets_dir = "static/assets"
section_index = "_index.md"
KeyDefaultRules
build.dockerfileDockerfile
build.context.Docker build context.
build.platforms[]Target platforms, e.g. linux/arm64. Empty means the platform of the machine running gramr. preview always uses it.
content.generatorzolaOnly zola for now. Zola ≥ 0.23 is required for unlisted pages.
content.dircontentWhere pages are written. For Zola, must be inside content/.
content.assets_dirstatic/assetsWhere attachments are copied. For Zola, must be inside static/ (served at /).
content.section_index_index.mdA section exists only if its folder contains this file. A file name, not a path.