Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ STORAGE_URL_BASE=https://your-bucket.s3.amazonaws.com
| `REMOTE_REPLIES_SCRAPE_COOLDOWN_SECONDS` | 300 | Completed scrape deduplication window |
| `MEDIA_PROXY` | off | Remote media proxy: `off`, `proxy`, `cache` (booleans accepted: `true`→`proxy`, `false`→`off`) |
| `REMOTE_MEDIA_THUMBNAILS` | on | Generate local sharp thumbnails for remote attachments (boolean) |
| `HANDLE_HOST` | - | Split-domain WebFinger handle host (e.g. `example.com`); must be set together with `WEB_ORIGIN` |
| `WEB_ORIGIN` | - | Split-domain ActivityPub server origin (e.g. `https://ap.example.com`); must be set together with `HANDLE_HOST` |


Adding new environment variables
Expand Down
21 changes: 21 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ Version 0.9.0

To be released.

- Added optional split-domain WebFinger support. When the new
`HANDLE_HOST` and `WEB_ORIGIN` environment variables are set,
Hollo uses 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 expose `HANDLE_HOST` as the instance
domain when this is configured, so clients display the correct
`@user@HANDLE_HOST` handle. Both variables must be set together;
setting only one is a startup error. They must be configured
*before* the first account is created — changing the handle
domain after federation has begun breaks remote follow
relationships; on startup Hollo logs a warning when the
configured `HANDLE_HOST` does not match the existing account's
stored handle. Operators must also configure their reverse
proxy on the handle domain to redirect `/.well-known/webfinger`
to `WEB_ORIGIN`. See the [Split-domain WebFinger guide]. [[#161], [#484]]

- Added a media proxy that re-serves remote avatars, headers, post
attachments, custom emojis, and preview-card images from Hollo's own
origin. This sidesteps CORS configurations on remote object stores
Expand Down Expand Up @@ -187,6 +205,8 @@ To be released.
- Upgraded Fedify to 2.2.1.

[FEP-044f]: https://w3id.org/fep/044f
[Split-domain WebFinger guide]: https://docs.hollo.social/install/split-domain/
[#161]: https://github.com/fedify-dev/hollo/issues/161
Comment thread
dahlia marked this conversation as resolved.
[@hollo@hollo.social]: https://hollo.social/@hollo
[#67]: https://github.com/fedify-dev/hollo/issues/67
[#127]: https://github.com/fedify-dev/hollo/issues/127
Expand All @@ -200,6 +220,7 @@ To be released.
[#481]: https://github.com/fedify-dev/hollo/issues/481
[#482]: https://github.com/fedify-dev/hollo/pull/482
[#483]: https://github.com/fedify-dev/hollo/pull/483
[#484]: https://github.com/fedify-dev/hollo/pull/484


Version 0.8.4
Expand Down
4 changes: 4 additions & 0 deletions bin/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { serve } from "@hono/node-server";
import { behindProxy } from "x-forwarded-fetch";

import "../src/logging";
import { checkHandleHostConsistency } from "../src/handle-host-check";
import { configureSentry } from "../src/sentry";

// oxlint-disable-next-line typescript/dot-notation
Expand Down Expand Up @@ -40,6 +41,9 @@ if (!["all", "web", "worker"].includes(NODE_TYPE)) {
process.exit(1);
}

// Warn if the configured HANDLE_HOST disagrees with an existing account.
await checkHandleHostConsistency();

// Start web server if running as web or all node
if (NODE_TYPE === "web" || NODE_TYPE === "all") {
const { default: app } = await import("../src/index");
Expand Down
9 changes: 9 additions & 0 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ export default defineConfig({
},
slug: "install/workers",
},
{
label: "Split-domain WebFinger",
translations: {
ko: "도메인 분리 WebFinger",
ja: "ドメイン分離 WebFinger",
"zh-CN": "分域 WebFinger",
},
slug: "install/split-domain",
},
{
label: "Setting up",
translations: {
Expand Down
28 changes: 28 additions & 0 deletions docs/src/content/docs/install/env.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,34 @@ Turned off by default.
security reasons.
</Aside>

### `HANDLE_HOST` <Badge text="Optional" />

The fediverse handle domain when running a [split-domain WebFinger
setup][split-domain]. When set, fediverse handles take the form
`@user@HANDLE_HOST` even though Hollo itself is served at
[`WEB_ORIGIN`](#web_origin).

Must be set together with `WEB_ORIGIN`; setting only one is a startup
error. Configure both *before* creating the first account — changing
the handle domain after federation has begun breaks remote follow
relationships.

Not set by default.

[split-domain]: /install/split-domain/

### `WEB_ORIGIN` <Badge text="Optional" />

The origin (scheme + host) where Hollo's ActivityPub server actually
runs in a [split-domain WebFinger setup][split-domain], e.g.
`https://ap.example.com`. Actor URIs, inbox URLs, and other federation
endpoints are built under this origin.

Must be set together with [`HANDLE_HOST`](#handle_host); setting only
one is a startup error.

Not set by default.

### `ALLOW_PRIVATE_ADDRESS` <Badge text="Optional" />

Setting this to `true` disables SSRF (Server-Side Request Forgery) protection.
Expand Down
177 changes: 177 additions & 0 deletions docs/src/content/docs/install/split-domain.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
title: Split-domain WebFinger
description: Run Hollo at one domain while addressing accounts under another.
---

import { Aside, Code, Tabs, TabItem } from '@astrojs/starlight/components';

Hollo supports a *split-domain WebFinger* setup: your fediverse handles
live under one domain (e.g. `@alice@example.com`) while the actual
ActivityPub server runs under another (e.g. `https://ap.example.com`).
This is the same pattern Mastodon documents under "[hosting WebFinger
at the root domain][mastodon-webfinger]" and that GoToSocial calls
*host-meta*-based host swapping.

This is implemented on top of [Fedify's `origin` configuration option].

[mastodon-webfinger]: https://docs.joinmastodon.org/admin/config/#web_domain
[Fedify's `origin` configuration option]: https://fedify.dev/manual/federation#separating-webfinger-host-from-the-server-origin
Comment thread
dahlia marked this conversation as resolved.


Why split the domain?
---------------------

The typical reason is that you already own a nice short domain
(`example.com`) and want your handles to look like
`@alice@example.com`, but you don't want to host Hollo at the apex of
that domain — the apex is reserved for a homepage, a different web app,
or an existing service.

In a split-domain setup:

- Users address you as `@alice@example.com` everywhere in the
fediverse.
- Hollo itself is served from `https://ap.example.com`. The web
UI, the Mastodon-compatible API, OAuth, and the actor URIs all
live there.
- The apex domain `example.com` only needs to handle one thing:
redirecting `/.well-known/webfinger` requests over to
`ap.example.com`.

<Aside type="caution">
Pick the handle domain carefully — you can't change it after
federation has begun without breaking every remote follow. See the
warning at the bottom of this page.
</Aside>


Configuration
-------------

Set both of these environment variables on your Hollo instance:

- [`HANDLE_HOST`](/install/env/#handle_host) — the bare hostname
used in handles (no scheme, no path), e.g. `example.com`.
- [`WEB_ORIGIN`](/install/env/#web_origin) — the scheme + host
where Hollo runs, e.g. `https://ap.example.com`.

Both must be set together; setting only one causes Hollo to refuse to
start.

~~~~ env
HANDLE_HOST=example.com
WEB_ORIGIN=https://ap.example.com
~~~~

With this in place, Fedify takes care of the rest:

- WebFinger responses use `acct:alice@example.com` as the subject.
- Actor URIs are built under `https://ap.example.com/@alice`.
- The Mastodon-compatible `/api/v1/instance` and `/api/v2/instance`
endpoints report `example.com` as the instance domain, so clients
display the correct handle.


Reverse proxy redirect
----------------------

Hollo itself only listens on the `WEB_ORIGIN` host. Remote servers
that look up `@alice@example.com` will send their WebFinger query to
`https://example.com/.well-known/webfinger`, so you must configure
that domain's reverse proxy to redirect those requests to Hollo.

A 301 redirect that preserves the query string is enough. Send
`/.well-known/nodeinfo` and `/.well-known/host-meta` along as well,
since some implementations probe those during discovery.

<Tabs>
<TabItem label="nginx">
~~~~ nginx
server {
listen 443 ssl;
server_name example.com;
# … your normal site config …

location /.well-known/webfinger {
return 301 https://ap.example.com$request_uri;
}
location /.well-known/nodeinfo {
return 301 https://ap.example.com$request_uri;
}
location /.well-known/host-meta {
return 301 https://ap.example.com$request_uri;
}
}
~~~~
</TabItem>
<TabItem label="Caddy">
~~~~ caddy
example.com {
# … your normal site config …

redir /.well-known/webfinger* https://ap.example.com{uri} permanent
redir /.well-known/nodeinfo* https://ap.example.com{uri} permanent
redir /.well-known/host-meta* https://ap.example.com{uri} permanent
}
~~~~
</TabItem>
</Tabs>

You do **not** need to redirect the `/@username` path or any actor
URLs — those URLs live on `ap.example.com` and remote servers will go
straight there after resolving the WebFinger response.


Verifying the setup
-------------------

After deploying, three quick checks confirm everything is wired up:

1. WebFinger from the handle domain redirects:

~~~~ sh frame="none"
curl -i "https://example.com/.well-known/webfinger?resource=acct:alice@example.com"
~~~~

should return a 301 to `https://ap.example.com/.well-known/webfinger?...`.

2. WebFinger from the server domain responds:

~~~~ sh frame="none"
curl "https://ap.example.com/.well-known/webfinger?resource=acct:alice@example.com"
~~~~

should return a JRD whose `subject` is `acct:alice@example.com`
and whose `self` link points at `https://ap.example.com/@alice`.

3. The instance endpoint reports the handle domain:

~~~~ sh frame="none"
curl https://ap.example.com/api/v2/instance | jq .domain
~~~~

should print `"example.com"`.

For a more thorough audit, point Julian Fietkau's [WebFinger Canary]
at your handle domain. Mastodon and Fedify-based servers handle
split-domain setups correctly today; Misskey and Pixelfed currently
do not.

[WebFinger Canary]: https://correct.webfinger-canary.fietkau.software/


<Aside type="danger">
**Configure `HANDLE_HOST` and `WEB_ORIGIN` before creating your
first account.**

Hollo stores each local account's full `@user@host` handle in the
database when the account is created. Other ActivityPub
implementations also cache your handle on first contact. Changing
the handle domain later means your existing followers can no longer
resolve you, and there is no migration path that keeps existing
follow relationships intact.

On startup, Hollo logs a warning if `HANDLE_HOST` disagrees with the
stored handle of the existing account. Treat that warning as a
problem to investigate before continuing to run the instance.
</Aside>
28 changes: 28 additions & 0 deletions docs/src/content/docs/ja/install/env.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,34 @@ HolloがL7ロードバランサーの後ろにある場合(通常はそうす
この動作はセキュリティ上注意が必要です。
</Aside>

### `HANDLE_HOST` <Badge text="オプション" />

[ドメイン分離 WebFinger 設定][split-domain]で使用するフェディバースの
ハンドルドメインです。このオプションを設定すると、フェディバースの
ハンドルは`@user@HANDLE_HOST`の形式になりますが、Hollo自体は
[`WEB_ORIGIN`](#web_origin)で動作します。

`WEB_ORIGIN`と一緒に設定する必要があり、片方だけを設定すると
Holloは起動に失敗します。**最初のアカウントを作成する前に**
両方の変数を設定してください。連合が始まった後にハンドルドメインを
変更すると、リモートフォロー関係が壊れます。

デフォルトでは設定されていません。

[split-domain]: /ja/install/split-domain/

### `WEB_ORIGIN` <Badge text="オプション" />

[ドメイン分離 WebFinger 設定][split-domain]でHolloの ActivityPub
サーバーが実際に動作するオリジン(スキーム + ホスト)です。例:
`https://ap.example.com`。アクターURI、インボックスURLなどの
連合エンドポイントは、すべてこのオリジンを基に構築されます。

[`HANDLE_HOST`](#handle_host)と一緒に設定する必要があり、
片方だけを設定するとHolloは起動に失敗します。

デフォルトでは設定されていません。

### `ALLOW_PRIVATE_ADDRESS` <Badge text="オプション" />

このオプションを`true`に設定すると、サーバーサイドリクエストフォージェリ(SSRF)攻撃の防止を解除します。
Expand Down
Loading
Loading