ADR-031: One domain, and the URL says which surface you are on
| Status | Accepted |
| Date | 2026-08-03 |
| Author | bodurkalukasz |
Context
The ecosystem is not one repository. The core carries the method; a stack repository carries the tooling for a technology, and ADR-025 says a repository adopts the latest of each independently. Each has a landing page and a documentation site, generated by the same generator from its own markdown.
That is four surfaces, and until now nothing decided where they live. The generator carried a base_path for exactly this and no site ever set it, so the question stayed open in a way that only looked settled: the switcher in the header hardcoded "here" on the core entry and sent the stack entry off to GitHub, which is right on the core's pages and wrong on every page the stack would ever build.
The pressure is not aesthetic. A reader who lands on a stack page and wants the method it implements has to get there, and a search engine has to be told these are one product rather than several.
Options considered
- A - A domain per repository (
repositorystandards.com,node.repositorystandards.com). Rejected: it splits the authority a single domain accumulates, doubles the certificate and DNS work per stack, and makes every cross-link an off-site link - including the ones that are conceptually one step, like a stack page pointing at the rule it implements. - B - A path per repository, docs nested under it (
/node/,/node/docs/). Rejected: it puts the surface first and the audience second, so/docs/stops being one place. A reader looking for documentation has to already know which stack they want before they can find any. - C - Surface first, stack second. Chosen:
/and/docs/are the core's,/node/and/docs/node/are the stack's. Documentation is one tree with a stack living inside it, which is also how the reader's question is shaped - the docs, for Node.
Decision
One domain, repositorystandards.com, with the surface first in the path.
| URL | What is there |
|---|---|
/ | the core's landing page |
/docs/ | the core's documentation |
/node/ | the Node stack's landing page |
/docs/node/ | the Node stack's documentation |
Each repository builds its own site and knows only two things about where it will be served: site_root (where its landing sits) and base_path (where its docs sit). Nothing else in the generator is layout-aware, and a second stack is two config lines rather than a change here.
Every internal link is written root-absolute against base_path. This is not a style preference. A relative link is resolved against whatever the browser believes the base to be, so a single visit to /docs without the trailing slash - which is what people type, and what a link in a chat message usually carries - re-based every link on the page one level up and broke the sidebar along with them. Under this layout that stops being a local-server curiosity: /docs/node without the slash would resolve a stack's links into the core's tree, which is worse than a 404 because some of them would work.
The ecosystem switcher decides "here" by comparing against the site's own base, not by being told. The same generator builds every site in the ecosystem, so any hardcoded answer is correct on exactly one of them.
Consequences
- Cross-surface links are same-origin: a stack page can point at a core rule and stay on the site. The switcher becomes navigation rather than an exit.
- One certificate, one analytics property, one robots and sitemap story, and search-engine authority accrues to one domain instead of being divided per stack.
- Adding a stack costs a path and two config lines. It does not cost DNS.
- The cost: the surfaces are joined at deploy rather than at build. Nothing in either repository can verify the whole tree, so a link from one surface into another is checked only against the layout this record describes - which makes this file, and not a script, the thing that keeps them consistent.
- The redirect from
/docsto/docs/is now load-bearing and belongs to the host. It was already needed; under this layout its absence is a correctness bug rather than an annoyance.
Confirmation
site-check fails any generated page carrying a relative internal link, so the root-absolute rule is mechanical inside a repository. The switcher's "here" is derived rather than written, so it cannot disagree with the site it is on.
Publishing belongs to this repository, and it needs no second one to do it. A project site with a custom domain is served at the domain root, not under its repository name - the /<repo>/ prefix applies only to the default <org>.github.io address. So the constraint that seemed to force a separate assembling repository does not exist: one repository can publish every path in the table above.
The workflow builds this repository's surfaces, then walks stacks.json - the registry this repository already publishes - and builds each registered stack into its own prefix. One list, not two, so a stack cannot be registered and unpublished by disagreement between files. A stack whose repository is not reachable yet is warned about, not skipped silently: a missing surface nobody mentions reads as a surface that was published.
Nothing is committed. The site is uploaded as an artifact, so no repository in the ecosystem carries build output, and no repository needs write access to another.
What is not mechanical, and is worth stating rather than implying: nothing checks that /docs/node/ actually serves the stack's build, because no repository can see the deployed tree. That is verified by opening it after a deploy.
Revisit when
A stack needs its own brand or its own domain for a reason that is not technical - a partnership, a separate product identity. That is a positioning change and it reopens this. Wanting a shorter URL does not.
