Prompted by Hollo's split-domain setup. Two parts:
- docs/ideas/split-domain-handles.md: offering split handle/server
domains to self-hosters is config-level work — Fedify's origin
option natively accepts { handleHost, webOrigin } — and handles are
permanent identity, so apex handles matter. Single-domain stays the
default (Decision #18); post-launch polish.
- Interop constraint pinned in social-federation task 6.1 and
route-federation's gating decision: the trails-to-trails NodeInfo
check must run against the actor IRI's host after WebFinger
resolution, never the handle's domain — split-domain remote
instances would otherwise be wrongly refused.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.5 KiB
Markdown
55 lines
2.5 KiB
Markdown
# Split-domain handles for self-hosters
|
|
|
|
Pre-spec exploration (2026-06-07), prompted by Hollo's split-domain
|
|
setup (https://docs.hollo.social/install/split-domain/).
|
|
|
|
## What it is
|
|
|
|
Letting the **handle domain** differ from the **server domain**:
|
|
`@bob@example.com` while the journal runs at `trails.example.com`.
|
|
The apex only has to serve (or redirect) `/.well-known/webfinger`;
|
|
actor IRIs, inbox/outbox, and the web UI all live on the server
|
|
domain. Mastodon (`WEB_DOMAIN`/`LOCAL_DOMAIN`), GoToSocial
|
|
(host/account-domain), and Hollo all support this.
|
|
|
|
## Why it matters for trails.cool specifically
|
|
|
|
- Fediverse handles are **permanent identity** — you cannot migrate
|
|
`@bob@trails.example.com` to `@bob@example.com` later. Self-hosters
|
|
who care about their handle want the apex in it from day one.
|
|
- Most apexes are taken (a website, an existing service): the operator
|
|
case in this very project — `social.ullrich.is` exists because
|
|
`ullrich.is` was occupied; split-domain would have allowed
|
|
`@ullrich@ullrich.is`.
|
|
- A self-hosting-first platform that forces "handle = wherever you can
|
|
host a Node app" is leaving its best identity feature on the table.
|
|
|
|
## Why it's cheap for us
|
|
|
|
Architecture Resolved Decision #18 (single domain) was justified as
|
|
"avoids WebFinger complexity" — but Fedify natively supports the split:
|
|
the `origin` option we already pass to `createFederation` accepts
|
|
`{ handleHost, webOrigin }` instead of a string (since Fedify 1.5).
|
|
Our side would be roughly:
|
|
|
|
1. Optional `HANDLE_DOMAIN` env (defaults to `DOMAIN` — Decision #18
|
|
stays the default)
|
|
2. Pass `origin: { handleHost: HANDLE_DOMAIN, webOrigin: ORIGIN }`
|
|
3. Audit the places that build handles vs URLs (`localActorIri` builds
|
|
URLs — already correct; acct: construction, `users.domain` column
|
|
semantics, profile `@user@domain` rendering)
|
|
4. Self-hoster docs: one redirect rule on the apex
|
|
(`/.well-known/webfinger` → server domain), like Hollo's guide
|
|
|
|
## Interop direction (already a constraint, not an idea)
|
|
|
|
Regardless of whether WE offer split domains, remote instances use
|
|
them — the trails-to-trails check must run against the **actor IRI's
|
|
host** after WebFinger resolution, never the handle's domain. Pinned in
|
|
`social-federation` task 6.1 and `route-federation`'s gating decision.
|
|
|
|
## Scope guess
|
|
|
|
Config + audit + docs + a two-domain test in the (future) two-instance
|
|
integration environment. Post-launch self-hosting polish; pairs with
|
|
the broader self-host packaging story.
|