Skip to content

Split-domain WebFinger handle#484

Merged
dahlia merged 14 commits into
fedify-dev:mainfrom
dahlia:feature/split-domain
May 14, 2026
Merged

Split-domain WebFinger handle#484
dahlia merged 14 commits into
fedify-dev:mainfrom
dahlia:feature/split-domain

Conversation

@dahlia
Copy link
Copy Markdown
Member

@dahlia dahlia commented May 14, 2026

Closes #161.

Issue #161 asks for a way to surface Fedify's split-domain WebFinger capability through Hollo's configuration, so that operators can address fediverse accounts under one domain (@alice@example.com) while running the ActivityPub server itself under another (https://ap.example.com). The usual motivation is an apex domain that's already taken by a homepage or another service, so Hollo has to run on a subdomain even though handles should look short.

Configuration

Two new optional environment variables, both required when either is set. HANDLE_HOST is the bare hostname used in handles (e.g. example.com). WEB_ORIGIN is the scheme and host where Hollo actually runs (e.g. https://ap.example.com). When both are present, src/federation/federation.ts passes them through as origin: { handleHost, webOrigin } to createFederation. If only one is set, Hollo refuses to start.

Operators are responsible for redirecting /.well-known/webfinger (and, as a courtesy, /.well-known/nodeinfo and /.well-known/host-meta) from the handle domain to the web origin at the reverse proxy layer. Hollo itself only listens on the web origin.

What Fedify handles

In Fedify 2.x, ctx.host returns the request host, while ctx.canonicalOrigin (and therefore ctx.getActorUri() and friends) returns the configured webOrigin. Fedify builds actor IRIs and inbox URLs under WEB_ORIGIN; it builds WebFinger acct subjects under handleHost. Hollo only needs to choose the right host when it constructs @user@host for storage or display.

Where the canonical host is used

A small helper in src/instance-host.ts returns the configured HANDLE_HOST when set, falling back to the request host otherwise. It replaces new URL(c.req.url).host and fedCtx.host where Hollo composes or displays the canonical instance domain.

The Mastodon-compatible src/api/v1/instance.ts and src/api/v2/instance.ts endpoints report it as uri, domain, and title. Account creation in src/pages/accounts.tsx stores @user@HANDLE_HOST in accounts.handle and writes HANDLE_HOST into accounts.instanceHost and instances.host. Local-username lookups in src/api/v1/accounts.ts expand bare acct=username against it. The local-account acct shortening in src/entities/account.ts and the local-emoji suffix stripping in src/api/v1/statuses.ts both anchor on it. The dashboard's host= props in src/pages/accounts.tsx and the home page title in src/pages/home/index.tsx display it to humans.

Immutability and the startup warning

HANDLE_HOST should be configured before the first account is created. Hollo stores the full @user@host handle in accounts.handle when a local account is set up, and other ActivityPub implementations cache that string at first contact. Changing the handle domain later breaks every existing remote follow, and there's no migration that keeps existing relationships intact.

The check in src/handle-host-check.ts runs from bin/server.ts on every boot, for both web and worker nodes. If HANDLE_HOST disagrees with the host portion of any existing local account's stored handle, Hollo emits a warning log under the hollo·config logger and continues. It deliberately does not refuse to start, because there are legitimate migration paths (with manual stored-handle rewriting) and refusing to boot would be worse than warning.

Documentation

The install docs add docs/src/content/docs/install/split-domain.mdx, with parallel translations under docs/src/content/docs/ja/install/, docs/src/content/docs/ko/install/, and docs/src/content/docs/zh-cn/install/. It includes nginx and Caddy reverse-proxy snippets, curl-based verification steps, a WebFinger Canary interop note, and an immutability warning. The two variables are also documented inline in each install/env.mdx. CHANGES.md gets a detailed entry under 0.9.0, AGENTS.md picks up matching rows in its Optional variables table, and docs/astro.config.mjs gains a translated sidebar entry.

Out of scope

Migrating an existing instance's handle domain by rewriting stored accounts.handle rows is not supported and not attempted here. A setup-wizard UI knob for the handle domain is also out of scope, because exposing it in the UI invites exactly the footgun the issue warns about. GoToSocial's restriction that the handle domain must share a root with the server host is not enforced, following the issue author's preference and Fedify's own behavior.

OAuth metadata, pagination Link headers, and dashboard URL construction continue to derive from the request origin: those endpoints are served from WEB_ORIGIN in a correct deployment, so the request URL is already the right value.

Tests

pnpm check (typecheck, oxlint, oxfmt) is clean. pnpm test:ci runs 435 passing tests with one skip, no fixture changes.

Still to verify before merge: boot on a fresh database with HANDLE_HOST=example.com and WEB_ORIGIN=http://localhost:3000, confirm WebFinger for acct:alice@example.com returns a JRD whose self points at http://localhost:3000/@alice, /api/v2/instance reports "domain": "example.com", and accounts.handle reads @alice@example.com. Boot with only HANDLE_HOST set and confirm Hollo refuses to start. Boot with a mismatched HANDLE_HOST and confirm the warning log fires.

When the new HANDLE_HOST and WEB_ORIGIN environment variables are set,
Hollo wires Fedify's origin configuration so that fediverse handles
(e.g. @alice@example.com) and ActivityPub actor URIs
(e.g. https://ap.example.com/@alice) can live on different domains.

The Mastodon-compatible /api/v1/instance and /api/v2/instance endpoints
now report HANDLE_HOST as the canonical instance domain, the dashboard
and home page surface it in their UIs, and account creation stores the
local owner's handle and instanceHost under HANDLE_HOST rather than the
request host.  Local-account lookups (acct=username on /api/v1/accounts)
and the local-emoji stripping logic in /api/v1/statuses are anchored to
the same canonical host.

Both variables must be set together; setting only one is a startup
error.  They must be configured before the first account is created,
since changing the handle domain after federation has begun breaks
existing follow relationships.  At startup, Hollo logs a warning when
HANDLE_HOST disagrees with the stored handle of an existing account.

Operators are responsible for redirecting /.well-known/webfinger on the
handle domain to WEB_ORIGIN at the reverse proxy layer.  A dedicated
documentation page covers the setup with nginx and Caddy snippets in
all four supported languages.

Resolves fedify-dev#161

Assisted-by: Claude Code:claude-opus-4-7
Assisted-by: Codex:gpt-5.5
@dahlia dahlia added this to the Hollo 0.9 milestone May 14, 2026
@dahlia dahlia self-assigned this May 14, 2026
@dahlia dahlia added the enhancement New feature or request label May 14, 2026
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 427ea00890

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/api/v1/accounts.ts Outdated
Comment thread src/api/v1/statuses.ts Outdated
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements split-domain WebFinger support, enabling fediverse handles to use a different domain than the ActivityPub server origin via new HANDLE_HOST and WEB_ORIGIN environment variables. The changes include updating API endpoints, account serialization, and Fedify configuration to respect these settings, alongside a new startup consistency check and extensive documentation. Feedback was provided regarding the handle parsing logic in the consistency check, suggesting a refactor into a shared helper to better handle edge cases and maintain a single source of truth for handle parsing.

Comment thread src/handle-host-check.ts
dahlia added 2 commits May 15, 2026 00:12
In split-domain mode, local accounts and custom emojis are stored
under HANDLE_HOST, but Fedify also recognizes the WEB_ORIGIN host as
an equivalent alias for the same account.  The Mastodon API
endpoints, however, only stripped or normalized the HANDLE_HOST
suffix, so a client supplying alice@ap.example.com would 404 against
@alice@example.com on /api/v1/accounts/lookup, and a local emoji
reaction sent as myemoji@ap.example.com would fall through to the
remote-emoji branch and fail.

Add an isLocalHost(host, requestUrl) helper in src/instance-host.ts
that recognizes the request host, HANDLE_HOST, and the host part of
WEB_ORIGIN as local.  Use it in /api/v1/accounts/lookup to
canonicalize the lookup handle to the stored HANDLE_HOST form, and
in the two emoji reaction handlers in /api/v1/statuses.ts to strip a
local suffix before deciding whether the emoji is remote.

fedify-dev#484 (comment)
fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
The startup consistency check in src/handle-host-check.ts extracted
the host portion of each stored handle with lastIndexOf("@") and
slice(at + 1), which produced a wrong existingHost on a malformed
handle that doesn't follow the canonical "@user@host" form: a bare
"@alice" gives at = 0 and existingHost = "alice", which would then
never match HANDLE_HOST and trigger a spurious warning.

In practice Hollo always writes canonical handles, so no real
warning fires today.  Tighten the parser defensively: require the
leading "@", require lastIndexOf("@") > 0 so the second "@" actually
exists and sits past the leading one, and skip empty host segments.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements optional split-domain WebFinger support, allowing fediverse handles to reside on a different domain than the ActivityPub server. It introduces the HANDLE_HOST and WEB_ORIGIN environment variables, updates the Mastodon-compatible instance and account APIs to utilize the canonical handle host, and adds a startup check to verify handle consistency with existing database records. Comprehensive documentation was also provided in English, Japanese, Korean, and Simplified Chinese. The reviewer recommended refactoring repeated handle and emoji normalization logic into a shared helper function to ensure consistent parameter processing across the API endpoints.

Comment thread src/api/v1/accounts.ts Outdated
Comment thread src/api/v1/statuses.ts Outdated
Comment thread src/api/v1/statuses.ts Outdated
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Gemini Code Assist flagged that /api/v1/accounts/lookup (the acct
param) and the two /api/v1/statuses/:id/emoji_reactions/:emoji
handlers don't strip a leading @ from user-supplied input.

The lookup case is a real bug.  If a client passes the user-typed
form, e.g. acct=@alice@example.com, every existing branch prepends
its own @ when building the DB lookup, so the handle becomes
@@alice@example.com and matches no stored row.  Many fediverse
clients pass through whatever the user typed, so this is reachable
in practice.  The two emoji cases are theoretical (Mastodon
clients don't @-prefix shortcodes), but the same one-line fix
applies.

Add normalizeHandle(handle) to src/patterns.ts next to
HANDLE_PATTERN.  It strips one leading @.  The lookup endpoint and
both emoji handlers call it on the user input before any further
parsing.

While in src/api/v1/accounts.ts, the same helper also replaces the
three existing inline query.q.replace(/^@/, "") calls in /search,
in line with the reviewer's single-source-of-truth point.  Other
.replace(/^@/, "") occurrences across the codebase operate on
already-canonical stored handles with a guaranteed leading @, not
on user input, so they're a different concern and stay as they
are.

fedify-dev#484 (comment)
fedify-dev#484 (comment)
fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces optional split-domain WebFinger support, allowing ActivityPub handles (e.g., @user@example.com) to reside on a different domain than the server origin (e.g., https://ap.example.com). Key changes include the addition of HANDLE_HOST and WEB_ORIGIN environment variables, integration with Fedify's origin configuration, and updates to API endpoints and account serialization to ensure consistent handle display. A startup check was also implemented to warn if the configured HANDLE_HOST conflicts with existing account data. Feedback suggests adding explicit URL validation for WEB_ORIGIN in src/env.ts to provide clearer error messages and documenting the limitations of this check in accordance with repository guidelines.

Comment thread src/env.ts
If WEB_ORIGIN is set to a string that isn't a parseable URL, for
example the operator forgets the scheme and writes
WEB_ORIGIN=example.com, Hollo currently fails with a generic
TypeError thrown from new URL(WEB_ORIGIN) at the top of
src/instance-host.ts when that module loads.  That's a poor signal
for a misconfiguration.

Add a URL.canParse(rawWebOrigin) check after the "both set
together" guard in src/env.ts, and throw a friendly
"WEB_ORIGIN must be a valid URL (e.g. https://ap.example.com)."
when the value doesn't parse.  A short comment notes that this is
a syntax check only; Fedify still enforces the stricter shape
(http/https scheme, no path/query/fragment) when the origin is
wired into createFederation.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6be3f7cc4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/api/v1/accounts.ts Outdated
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements optional split-domain WebFinger support, allowing fediverse handles and ActivityPub actor URIs to reside on different domains via new HANDLE_HOST and WEB_ORIGIN environment variables. Key changes include updating instance metadata endpoints, account lookup/search logic, and emoji reaction handling to correctly resolve handles across both domains, alongside a startup check for configuration consistency. Reviewer feedback suggests centralizing handle normalization logic into a shared helper function to ensure consistency across the lookup and search endpoints and to avoid unnecessary remote lookups when local aliases are used.

Comment thread src/instance-host.ts
Comment thread src/api/v1/accounts.ts Outdated
Comment thread src/api/v1/accounts.ts Outdated
Comment thread src/api/v1/accounts.ts Outdated
Comment thread src/api/v1/accounts.ts Outdated
PR fedify-dev#484 already canonicalized WEB_ORIGIN-host aliases on
/api/v1/accounts/lookup, but the same logic wasn't applied to
/api/v1/accounts/search.  Searching for @alice@ap.example.com
misses the local account stored as @alice@example.com and skips
the resolve-side refresh path entirely.

Extract normalizeHandleForLookup(handle, requestUrl) into
src/instance-host.ts.  It composes the existing helpers
(normalizeHandle to strip a leading @, isLocalHost to recognize
the request host / HANDLE_HOST / WEB_ORIGIN host as local, and
getInstanceHost for the canonical handle host) into one
canonicalizer that yields the @user@host form local accounts are
stored under, or returns a remote handle with a leading @
otherwise.

/lookup in src/api/v1/accounts.ts reduces to a single call to
that helper.  /search builds handleLookup once and threads it
through both the resolve-side exact-match query and the orderBy
clauses, so searching by WEB_ORIGIN-host alias prioritizes the
matching local account.  The remote-WebFinger fallback in /lookup
keeps working: the dropped acct local becomes an inline
normalizeHandle(query.acct) at the lookupObject call site.

fedify-dev#484 (comment)
fedify-dev#484 (comment)
fedify-dev#484 (comment)
fedify-dev#484 (comment)
fedify-dev#484 (comment)
fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces optional split-domain WebFinger support, allowing fediverse handles to be hosted on a different domain than the ActivityPub server. Key changes include the addition of HANDLE_HOST and WEB_ORIGIN environment variables, startup consistency checks, and updates to API endpoints and account serialization to respect the configured handle host. Comprehensive documentation was added in several languages. Review feedback suggests implementing stricter validation for WEB_ORIGIN to ensure it only contains a scheme and host, as required by Fedify, and explicitly documenting that this is a syntax-level check.

Comment thread src/env.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

ilike(accounts.handle, `%${query.q}%`),

P1 Badge Include canonicalized handle in account search filtering

When split-domain mode is enabled, handleLookup correctly rewrites local aliases like @alice@ap.example.com to the canonical stored handle (@alice@example.com), but the main findMany filter still searches with raw query.q. In that case, where: ilike(accounts.handle, %${query.q}%) excludes the local account before ordering runs, so /api/v1/accounts/search?resolve=true&q=@alice@ap.example.com can return no results even though the account exists locally. The canonicalized value needs to participate in the where clause (or be OR’d in) to avoid dropping valid local matches.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The earlier WEB_ORIGIN check in src/env.ts only confirmed that
URL.canParse accepted the string, so values like
https://ap.example.com/path, ftp://ap.example.com, or
https://ap.example.com?x=1 sailed past env.ts and only failed
later from a generic TypeError inside src/instance-host.ts or
Fedify's createFederation.

Keep the URL.canParse check and add two more.  The protocol must
be http: or https:, otherwise throw "WEB_ORIGIN must use the http
or https scheme".  The parsed URL's pathname must be empty or /,
its search must be empty, and its hash must be empty, otherwise
throw "WEB_ORIGIN must be a bare origin (scheme and host only)
with no path, query string, or fragment".

Rewrite the leading comment to say this is syntax-only (no DNS
resolution) and explain why we duplicate Fedify's own validation:
catching it at the env-variable layer points the operator at the
configuration mistake instead of a downstream TypeError.

Smoke-tested with WEB_ORIGIN values "not a url", "ftp://...",
"https://example.com/path", and "https://example.com?x=1"; each
produces the matching message.  The canonical
https://ap.example.com still loads.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces optional split-domain WebFinger support, allowing ActivityPub actor URIs and fediverse handles to reside on different domains. Key changes include the addition of HANDLE_HOST and WEB_ORIGIN environment variables, updated account lookup logic, and comprehensive documentation across multiple languages. A startup check was also implemented to ensure consistency between the configured handle host and existing database records. The reviewer recommended moving Markdown reference links to the end of their respective sections to comply with the style guide and suggested normalizing the WEB_ORIGIN variable to lowercase to prevent potential case-sensitivity issues.

Comment thread CHANGES.md
Comment thread src/env.ts
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces optional split-domain WebFinger support, allowing fediverse handles to be hosted on a different domain than the ActivityPub server. It implements two new environment variables, HANDLE_HOST and WEB_ORIGIN, and updates the Mastodon-compatible API, account lookup logic, and UI components to respect these settings. Additionally, a startup check was added to warn of inconsistencies between the configured host and existing account data, accompanied by extensive documentation updates across multiple languages. I have no feedback to provide.

Hostnames are case-insensitive, but if an operator writes
WEB_ORIGIN=https://AP.Example.COM the env.ts module previously
exported that string verbatim, forcing every downstream consumer
to do its own .toLowerCase() if it wanted to compare against
another host.

After the canParse, scheme, and origin-shape checks pass, store
webOriginUrl.origin in a local normalizedWebOrigin and export
that as WEB_ORIGIN.  URL.origin yields the canonical form
(lowercased host, no trailing slash), so consumers can rely on a
normalized value.  A short comment names what URL.origin
guarantees.

Smoke-tested with WEB_ORIGIN=https://AP.Example.COM: the exported
value comes out as https://ap.example.com.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements optional split-domain WebFinger support, allowing fediverse handles to exist on a different domain than the ActivityPub server. Key changes include the introduction of HANDLE_HOST and WEB_ORIGIN environment variables, updates to account lookup and serialization logic to handle domain aliases, and the addition of a startup consistency check to warn about domain mismatches. Comprehensive documentation and multi-language guides for this setup were also added. Feedback suggests adding stricter validation for the HANDLE_HOST variable to ensure it is a bare hostname without a scheme or port.

Comment thread src/env.ts
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The earlier env.ts validation only trimmed and lowercased
HANDLE_HOST without checking its shape, so values like
HANDLE_HOST=https://example.com or HANDLE_HOST=example.com:3000
slipped past env.ts and only failed downstream with a generic
Fedify TypeError.  HANDLE_HOST is the bare hostname used in
fediverse handles (the part after the second @), so it must not
carry a scheme, port, or path.

After the "both set together" guard, reject the value if the
trimmed and lowercased rawHandleHost contains either / or :.
The error message is "HANDLE_HOST must be a bare hostname
(e.g. example.com) with no scheme, port, or path."  A short
comment notes that this is a syntax-level check only (no DNS
resolution) and explains why a scheme, port, or path makes no
sense for a WebFinger handle host.

The check is slightly stricter than Fedify's own (Fedify rejects
slashes but tolerates ports), which is intentional: WebFinger
handles never carry ports in practice, and rejecting them in
env.ts catches a common footgun where operators conflate
WEB_ORIGIN's port with HANDLE_HOST.

Smoke-tested HANDLE_HOST=example.com:3000, https://example.com,
and example.com/path: each produces the new friendly error; the
canonical example.com still loads.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds optional split-domain WebFinger support, allowing fediverse handles to reside on a different domain than the ActivityPub server. It introduces the HANDLE_HOST and WEB_ORIGIN environment variables, updates documentation in multiple languages, and implements a consistency check for existing accounts. The internal logic across API endpoints and entity serialization is updated to handle these split domains. Review feedback correctly identifies that host comparisons should use hostname to exclude port numbers and suggests more robust regex validation for the HANDLE_HOST variable to prevent misconfigurations.

Comment thread src/instance-host.ts Outdated
Comment thread src/instance-host.ts
Comment thread src/env.ts Outdated
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

dahlia added 2 commits May 15, 2026 02:04
Both new URL(WEB_ORIGIN).host.toLowerCase() (the cached
WEB_ORIGIN_HOST) and requestUrl.host.toLowerCase() (the inline
comparison in isLocalHost) used the URL .host property, which
includes a non-default port.  The value those compare against
comes from the host segment of a fediverse handle (the part
after the second @), which never carries a port.

So when WEB_ORIGIN is http://localhost:3000, WEB_ORIGIN_HOST was
set to localhost:3000 and requestUrl.host was localhost:3000,
and a handle like @alice@localhost wasn't recognized as local
because localhost !== localhost:3000.

Switch both .host accesses to .hostname, which strips the port,
and add a short comment at each site naming why fediverse
handles can't carry ports.

fedify-dev#484 (comment)
fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
The earlier HANDLE_HOST shape check rejected only / and :,
which catches full URLs and host-with-port but lets other
malformed values through: HANDLE_HOST="exa mple.com" (with
whitespace) for example sneaks past env.ts and only fails
downstream with a generic Fedify TypeError.

After the slash/colon check, also run URL.canParse on the
synthesized URL https://${rawHandleHost}/.  Anything that
URL.canParse can't accept as a hostname (whitespace, control
characters, empty labels) throws "HANDLE_HOST must be a valid
hostname (e.g. example.com)."  This mirrors how Fedify itself
validates handleHost downstream, but produces the friendlier
error at the env-variable layer.

The Unicode-aware URL parser leaves IDN domains alone.
Smoke-tested: "exa mple.com" produces the new error, while
"한국.kr", "café.fr", and "example.com" all load cleanly.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements optional split-domain WebFinger support, allowing fediverse handles to reside on a different domain than the ActivityPub server origin. Key changes include the introduction of HANDLE_HOST and WEB_ORIGIN environment variables, a startup consistency check for existing accounts, and updated account lookup and serialization logic to support domain aliases. Documentation has been added in multiple languages to guide operators through the setup. Review feedback identifies a critical potential TypeError during environment variable initialization and an inconsistency in the search logic where local handle normalization is bypassed for certain queries.

Comment thread src/env.ts
Comment thread src/api/v1/accounts.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a9cea99fa8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/instance-host.ts
The previous commit (77ed649, "Use hostname not host for
local-handle comparison") fixed Gemini's port-in-WEB_ORIGIN
concern but introduced a regression for non-split-domain
deployments.  Without HANDLE_HOST/WEB_ORIGIN set, getInstanceHost
falls back to requestUrl.host (including any non-default port),
so stored handles for a local dev instance at localhost:3000 are
written as @alice@localhost:3000.  Stripping the port out of
the comparison in isLocalHost then made those handles fail to
match: the comparison input is localhost:3000 (the stored host
segment) but the check value was localhost (the hostname).
Emoji reactions sent as myemoji@localhost:3000 got misclassified
as remote and returned 404 when no prior remote reaction existed.

Keep the .hostname check (which still addresses the port-in-
WEB_ORIGIN concern) and add the .host comparison back as a
second clause.  Both forms are accepted, so split-domain
deployments (where stored handles use HANDLE_HOST without a
port) and non-split-domain dev (where stored handles include
the port from request.host) both work.  The comment is rewritten
to spell out why both forms are checked.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces optional split-domain WebFinger support, allowing fediverse handles to exist on a different domain than the ActivityPub server. It adds two new environment variables, HANDLE_HOST and WEB_ORIGIN, along with comprehensive documentation in multiple languages and a startup consistency check to warn if the configured handle host conflicts with existing accounts. Feedback includes a suggestion to use the URL constructor for more robust origin normalization in src/env.ts, a performance concern regarding the enumeration of all account handles at startup in src/handle-host-check.ts, and a recommendation to document the limitations of the isLocalHost security check. Additionally, the search logic in src/api/v1/accounts.ts could be refined to avoid redundant ilike conditions.

Comment thread src/env.ts
Comment thread src/handle-host-check.ts
Comment thread src/instance-host.ts
Comment thread src/api/v1/accounts.ts
Add a short block comment above isLocalHost spelling out what
the function does and does not do: it's a string-equality check
between hostnames, with no DNS resolution and no authority over
the request itself.  This mirrors the AGENTS.md convention of
documenting the limits of security-adjacent checks so a future
reader doesn't mistake it for a gatekeeper.

No behavior change.

fedify-dev#484 (comment)

Assisted-by: Claude Code:claude-opus-4-7
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 14, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces optional split-domain WebFinger support, allowing fediverse handles and ActivityPub actor URIs to reside on different domains. It adds HANDLE_HOST and WEB_ORIGIN environment variables, updates the server to validate these settings on startup, and includes comprehensive documentation in multiple languages. The implementation also includes logic to normalize handles for lookups and ensures Mastodon-compatible instance endpoints report the correct domain. Feedback was provided regarding the placement of reference-style links in the new documentation files, which currently violate the repository's Markdown style guide requiring them to be at the end of each section.

Comment thread docs/src/content/docs/install/split-domain.mdx
Comment thread docs/src/content/docs/ja/install/split-domain.mdx
Comment thread docs/src/content/docs/ko/install/split-domain.mdx
Comment thread docs/src/content/docs/zh-cn/install/split-domain.mdx
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@dahlia dahlia merged commit ffffc50 into fedify-dev:main May 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add configuration support for Fedify's split-domain WebFinger capability

1 participant