Links and references
Trail links pages by identity, not by URL. Instead of writing a path that breaks the moment a page moves, you write a ~ reference naming the product and enough of the page's slugs to be unambiguous:
Reorganise the tree — move that topic into an anthology, wrap it in a shelf, renumber it — and the link still resolves. Delete the target, and the build fails and tells you every page that pointed at it.
The grammar #
~<product-slug>[/<segment>]…[#<fragment>]
- The first segment is always a product slug. There is no relative form and no site-wide search; every reference says which product it means.
- The remaining segments match the end of a page's path. Trail compares them against each page's slugs — the anthology slugs, the topic slug, any subfolder or chapter slugs, and the page's own — and matches if your segments are the tail of that list.
- Exactly one page must match. Zero or several is an error.
- A fragment may follow, and is appended to the resolved URL:
~pekit/reference/cli#global-flags.
The rule of thumb is: write the shortest thing that is unambiguous, and add segments from the left only when you have to.
A worked example #
Given this page:
/peios/security-fundamentals/identity/tokens/issuing
└ anthology ───────┘ └ topic ┘ └folder┘ └page┘
all of these resolve to it:
~peios/issuing
~peios/tokens/issuing
~peios/identity/tokens/issuing
~peios/security-fundamentals/identity/tokens/issuing
~peios/issuing is the one to write — until a second page in peios is also called issuing, at which point that reference becomes ambiguous, the build fails, and you add a segment.
Note that the deeper segments are for disambiguation only. A chapter or subfolder slug can appear in a reference even though it is not a page you could link to on its own.
What can be a target #
| Targetable | Reference | |
|---|---|---|
| A product | yes | ~pekit |
| An anthology | yes | ~peios/security-fundamentals |
| A book cover | yes | ~peios/pgss |
| An article | yes | ~pekit/reference/cli |
| A topic | no | link to an article in it |
| A topic subfolder | no | (except as a .link target) |
| A book chapter | no | link to an article in it |
| A shelf | no | it has no page at all |
Topics, subfolders and chapters have no page of their own, so there is nothing for a reference to land on. Link to the article you actually mean.
Ordinary links still work #
A ~ at the start of a destination is what makes it a reference. Everything else is left alone:
Root-relative links are the escape hatch for the handful of URLs that are not pages — /print views, /llms.txt, /site.json. They are not checked, so they are also the way to ship a broken link. Prefer ~ wherever the target is a page.
~ is only meaningful in a link destination. In an image destination it is an error, with a message saying so: images are files, addressed relative to the article.
Where else references are used #
The same grammar appears in three other places:
# an article's frontmatter — no leading ~
related:
- pekit/running/invocation
# a .link alias file — with the ~
= "~pekit/running/signing-and-provenance"
# the root trail.toml's nav — with the ~, or an external URL
[[]]
= "Specs"
= "~peios/pgss"
The related: list is the odd one out in dropping the ~; everything else keeps it.
When a link breaks #
Every link in the site is checked on every build, and all the failures are reported together — one build tells you the whole story rather than stopping at the first bad reference.
Error: 3 broken links:
in article '/pekit/recipes/anatomy': link '~pekit/running/invokation' matches no page in product 'pekit'
in article '/peios/identity/sids': link '~pekti/overview' names unknown product 'pekti'
in article '/pekit/reference/cli': link '~pekit/targets' is ambiguous; candidates:
~pekit/running/commands-and-targets/targets
~pekit/reference/recipe-format/targets
There are three failure kinds, and they are not treated alike:
| Means | With --allow-dangling-links | |
|---|---|---|
| Unknown product | the first segment is not a product slug | warning |
| No match | nothing in that product ends with those segments | warning |
| Ambiguous | two or more pages match | still fatal |
Ambiguity is never downgradable. A missing target is a link that does nothing; an ambiguous one is a link that goes somewhere specific and possibly wrong, and only the author knows which page was meant.
--allow-dangling-links is for the middle of a refactor, when you have moved a hundred pages and want to see the site before fixing every reference. It is not a setting to leave on.
References in print views and mirrors #
The single-page and markdown views rewrite references so they still work away from the site:
- In a
/printbundle, a reference to a page inside the same bundle becomes an in-document anchor, so the whole document navigates itself. A reference to a page outside it becomes an absolute URL when the site config has aurl, and stays root-relative otherwise. - In a
.mdmirror, a reference becomes the target's own.mdURL, so a reader following links through the markdown surface stays in it.