How this works#

Three directories, one direction of travel:

Vault, sync script, Hugo site

The vault#

vault/ is the Obsidian vault and the only place you type. It knows nothing about Hugo beyond one front matter key, publish.

The sync script#

bin/vault-sync.py is where the actual translation happens, because Hugo does not understand Obsidian’s markdown. Hugo’s maintainers closed the wikilink feature request as not planned, so [[Note]] reaches Goldmark as literal text and renders as literal text. The script rewrites it into a relref shortcode, which Hugo resolves at build time and fails loudly on if the target disappears.

It also copies each note’s attachments into the page’s own directory, so every published note is a self-contained page bundle and image paths stay correct whatever baseURL you set.

The site#

site/ is a normal Hugo site using the hugo-book theme. content/docs/ is generated output — it is in .gitignore, and make sync deletes and rebuilds it every time. Anything you want to keep by hand goes in content/ outside docs/, in layouts/, or in assets/.

Running it#

make sync     # vault -> site/content/docs
make serve    # sync, then hugo server on :1313
make build    # sync, then a production build into site/public

The word [[wikilink]] inside that code fence is left alone — the converter skips fenced blocks and inline code such as ![[not an embed]].

See also: Syntax cheatsheet and the front page.