makr.dk

Domains, UIDs and addresses

For administrators, with an advanced reference section for anyone who needs the exact URL catalogue. How a visitor's address is formed, how to choose and rename a UID safely, and how Domains and their navigation are configured. See Welcome to Kartotek for the vocabulary this doc assumes.

How a visitor's address is formed

Every Post and Category is addressed directly at the site root: domain/<uid>. There's no /posts/ or /categories/ prefix — a Post and a Category share one flat, short address space, so domain/my-trip-report and domain/photos are equally valid, equally permanent addresses.

Choosing a UID: lowercase letters, numbers, and hyphens only (e.g. my-trip-report, q4-2026) — no spaces, capitals, underscores, or punctuation. Pick something short and memorable; it's what appears in the address bar and in anything shared.

Renaming a Post is safe. Its old UID keeps working — silently serving the current content at whatever address was actually visited, not a visible redirect. This makes renaming safe to do even after a UID has been shared or linked to elsewhere.

Renaming or deleting a Category is different — its UID frees immediately, with no alias left behind. See "The rules," below, for why.

Domains

A Domain is a hostname pointed at this deployment — several Domains can share one deployment, with content fully shared (every UID resolves identically no matter which Domain it's reached through) but each Domain getting its own navigation, landing target, and site-bar title. Configured in the admin sidebar's Domains section:

  • Hostname — the domain that should point here, e.g. hoejriis.dk or makr.dk.
  • Landing target — the Post or Category this Domain's bare root redirects to. Optional; a Domain can have none configured, in which case its root falls back to the site-wide default described under "Domain-specific navigation" in Organizing and presenting content.
  • Top navigation — an ordered list mixing Posts, Categories, Tags, and/or arbitrary URLs, each with an optional Domain-specific label. See Organizing and presenting content's "Domain-specific navigation" section for the full model, including how a Hidden/PrePub Post entry behaves and what happens if a placed Category or Tag is later deleted.
  • Category sidebar — an optional collapsible left sidebar rooted at one selected Category, off by default. Also covered in Organizing and presenting content.
  • Default language — the language a visitor sees a Post rendered in when a complete translation into it exists — see Posts and publishing's "Translation" section. Required; defaults to English for a newly created Domain, independent of anything copied from a Primary Domain below.
  • Custom title — shown top-left in place of the default site title; if unset, the raw hostname is shown instead.
  • Show domain selector — whether the public Domain selector (see "The public Domain selector," below) appears at all while viewing this Domain. Defaults to Yes.
  • Include in domain selector — whether this Domain itself is offered as a destination in another Domain's own selector. Defaults to No — most Domains on a shared installation aren't meant to be cross-linked from every other one.
  • Presentation Post — an optional Post supplying the header's "About {this Domain's title}" link. Leave unset to omit that line from the header entirely.
  • Custom favicon URL — an optional explicit favicon for this Domain. Leave unset to use a generated icon based on the Domain's own title's first letter instead.

A Domain not yet added to this list falls back to the default behavior described below, so adding Domains is purely additive and never changes how an unconfigured hostname behaves until it's actually configured (or a Primary Domain is set, below).

One configured Domain may additionally be marked Primary: any hostname that doesn't match a configured Domain (e.g. this app's own Render subdomain) uses the Primary Domain's own landing target, top navigation, and Category sidebar, instead of the site-wide default — a real, editable fallback, not just an "unconfigured" state. A newly created Domain also starts by copying the Primary Domain's navigation configuration wholesale, then becomes independently editable from that point on. At most one Domain is ever Primary; a localhost, IP-address, or Render's own *.onrender.com hostname can't be marked Primary. Since content is fully shared across every configured Domain (a UID resolves identically no matter which one it's reached through — see above), Wayback Machine archival submits a Post's URL under every configured Domain, not only Primary — see Posts and publishing's Archival section. With no Domain configured at all, an unmatched hostname uses the site-wide default and Wayback archival is simply disabled.

Previewing a Domain while logged in

Since every configured Domain shares the same underlying deployment (and, for this site, the same Render hostname before a real custom domain is pointed at it), there's no ordinary way to browse the site as a particular Domain without actually visiting it through its own hostname. A logged-in admin gets a small Domain preview dropdown in the site bar, alongside the language selector and Admin link (see Explore a Kartotek site's "Header controls"), for exactly this: pick a configured Domain and the whole site behaves as if you'd arrived through it — its own landing target, navigation, and Category sidebar — while the address bar still shows this deployment's real hostname with ?domainoverride=<hostname> appended. Picking the Primary Domain (or reloading without the parameter) returns to normal. This is a personal preview only — it's never shown to a logged-out visitor, never changes what anyone else sees, and isn't remembered beyond the current browser tab.

The public Domain selector

Separately from the admin-only preview above, every visitor — logged in or not — can see a small chevron next to the site title in the header, opening a menu of other Domains on the same installation to switch to. Unlike the preview dropdown, choosing an entry here is a real navigation to that Domain's own hostname (each configured Domain is a genuinely different address, even though they currently share one deployment), not a same-page simulation.

Two independent settings control this, per Domain (see "Domains," above): Show domain selector decides whether the chevron appears at all while viewing that Domain, and Include in domain selector decides whether that Domain is offered as a destination in every other Domain's own selector. A Domain can show the selector without appearing as anyone's destination, and vice versa.

Identity vs. label

A Post or Category's true identity is its internal record, not its UID. The UID is a mutable label pointing at that identity — it can be renamed without the Post or Category "underneath" it changing.

Renaming keeps the old UID working — for Posts

When a Post's UID is changed, the old UID is not freed up — it becomes a historical alias: still resolving to the same Post, just under a new name now. Visiting the old UID still works.

This is not a visible redirect — the address bar stays on whatever UID was visited. What changes is only the content: an aliased UID always serves whatever the current, live content is, silently, at the address already being visited.

A UID can be renamed more than once over its life, forming a chain of aliases. Resolving any UID in that chain — old or new — always follows it through to whichever UID is current today.

Categories are the one exception to this. A Category's UID is freed immediately on rename or deletion, with no alias left behind — see Organizing and presenting content. Alias tracking exists to keep old content links working indefinitely; a Category is structural rather than content, so it doesn't carry that guarantee.

Detaching a Post's alias

An admin can deliberately detach a historical alias from what it used to point to and assign it to a different Post or Category instead. This breaks the old redirect — the alias stops pointing at its previous target and becomes a fresh, live UID for its new owner.

This is a distinct, deliberate action — separate from ordinary renaming — because it destroys a working redirect. Managed from a Post or Category's own admin page.

The rules

  1. Shared namespace. Post UIDs and Category UIDs are the same pool — a Post can't take a UID that's already used by a Category, or by another Post, and vice versa. This includes a Post's historical aliases: a UID that currently exists only as an alias pointing somewhere else isn't "free" for normal reuse. Taking it over requires the explicit detach action above, not just typing it into a new UID field. (Category UIDs, per the exception above, don't carry this restriction once freed — they're immediately available.)
  2. Allowed characters: lowercase letters, numbers, and hyphens only.
  3. The underscore prefix is reserved. Every part of the site that isn't a Post or a Category — the admin area, uploaded media, the internal API — lives at a path starting with _ (e.g. /_admin, /_media). A content UID can never contain an underscore, so it can never collide with a reserved path.

Advanced address reference

This is the single reference for every visitor-facing address on a Kartotek site — admin routes aren't covered here. As a rule, pretty paths, not query strings, for anything a visitor might see in their browser bar, bookmark, or share.

  • domain/ — the bare root. Renders that Domain's own landing target (a Post or Category) directly at the root address itself, never redirecting there — the root stays the root in the browser bar, in bookmarks, and in search results. If no landing target is set for the hostname being visited, this falls through, in order, to the Primary Domain's own landing target (if one exists and is valid) and finally to the site-wide default — see Organizing and presenting content's "Domain-specific navigation."
  • domain/<uid> — a Post or Category's live page: a Post's latest Version, or a Category's presentation header plus its posts. Optionally followed by ?lang=original (the Post's own authoritative language, regardless of the visiting Domain's default) or ?lang=<language> (a specific configured language) — see Posts and publishing's "Translation" section. This is the one deliberate exception to "pretty paths, not query strings" below: ?lang= is deliberately a plain, normalized query parameter, not a path segment, since it's an optional rendering preference layered on top of the address, not a distinct destination the way /v/<n> is. An explicit ?lang= in the address always wins; absent one, the header's language selector (see Explore a Kartotek site) substitutes for the Domain's own default, if a visitor has set one.
  • domain/<uid>/v/<n> — the nth Version ever published for that Post, 1-indexed from its very first Version. Since Versions are never deleted, this address is permanent. Accepts the same ?lang= query as the live address above.
  • domain/<uid>/history — redirects to domain/<uid>/metadata#versions, the Version table on the Metadata page below.
  • domain/<uid>/metadata — a visitor-facing page of the Post's facts, every past Version in a table, and export links — see Posts and publishing's "Metadata page and JSON-LD export".
  • domain/<uid>/metadata/current — the Post's current Version as raw JSON-LD, no page wrapper.
  • domain/<uid>/metadata/history — every Version the Post has ever had, as JSON-LD.
  • domain/<uid>/metadata/archive — everything /metadata/history has, plus every referenced media file's actual bytes, base64-embedded inline.
  • domain/_docs/ — the public docs index — see Administrator quick start.
  • domain/_docs/<slug> — one doc or Type schema's rendered page.
  • domain/_docs/<slug>.md — the same doc's raw markdown, for a tool that can't run the client's JS.
  • domain/_static/<uid>.pdf — a rendered PDF snapshot of a Published Post's live page, in its own original language — see Posts and publishing's "Static PDF export". Not versioned.
  • domain/_static/<uid>--<language>.pdf — the same snapshot rendered in a configured translation (e.g. ...--da-DK.pdf), present once that translation is genuinely complete — see Posts and publishing's "Translation" section.
  • domain/_static/<uid>.json — a native, lossless JSON export of a Published Post — see Export, import, and external publishing.
  • domain/_privacy — the public, unauthenticated visit-analytics privacy notice — see Privacy and analytics.
  • domain/_link/<post-uid>/<filename>.pdf — a locally-captured PDF snapshot of an external Link's URL — see Posts and publishing's Archival section and its "Link health and archive fallback" section. Reachable only for a Link belonging to a currently Published Post (same Stage rule as any other content); once linked, it's offered directly as that Link's Archived copy whenever the live destination isn't reachable, rather than being an undiscoverable address as it was before that fallback existed.
  • domain/_link/<post-uid>/<filename>.warc.gz — a locally-captured WARC of the same external Link's URL, alongside its PDF — see Posts and publishing's Archival section. Same reachability rule as the PDF above.
  • domain/_warc/<post-uid>/<filename>.warc.gz — a locally-captured WARC of one of a Post's own Published Versions, permanent per Version — see Posts and publishing's Archival section. Reachable only while the owning Post is currently Published and that specific Version isn't Hidden.
  • domain/_element/<kind>/<id> — one Content Element's own standalone page, showing its available public metadata outside any Post's page layout — see Posts and publishing's Content Types section. Gated exactly like visiting its owning Post directly.
  • domain/robots.txt — the crawler instructions for that Domain, as plain text, naming that Domain's own sitemap. See "What search engines are told" below.
  • domain/sitemap.xml — that Domain's list of pages worth offering to a search engine, as XML.

There is deliberately no legacy ?v=<id> or similar query-string form for any of the above — ?lang=, above, is the sole deliberate exception.

What search engines are told

Every configured Domain serves its own robots.txt and sitemap.xml, generated from live content rather than checked in as files, so a Post that gets published, unpublished or hidden is reflected the moment it changes.

The sitemap lists the pages that stand on their own as search results: the Domain's landing page, every Published Post whose current Version isn't Hidden, every Category that actually has such a Post under it, and the Tag pages. It deliberately leaves out an empty Category, and it lists the landing Category once — at the root address — rather than a second time at its own UID, since both addresses show the same page.

The addresses that exist to inspect a Post rather than to be one — its Metadata page, a specific Version, a standalone Content Element, an Archive Media page, the raw .md form of a doc — stay openly reachable, and simply tell a search engine not to list them as separate results. That is deliberate: a crawler has to be able to fetch a page to see that instruction, so hiding these addresses in robots.txt instead would have the opposite of the intended effect. robots.txt therefore mentions only the admin and API paths, which have no such instruction to find.

An address this site doesn't serve now answers with a genuine "not found" status, rather than the previous behaviour of answering "found" and only then displaying a not-found page once the browser had run the site's code. A visitor sees the same page as before; the difference is that a search engine is no longer told that a mistyped or retired address is a real page. A Post that isn't publicly visible behaves the same way for a visitor, while still opening normally for a logged-in admin.

A Post or Category's historical UID aliases resolve at these same address shapes too, transparently serving current content at whatever old address someone visits.