The 11.4 harness — e2e/federation/: postgres (two DBs) + a Caddy with an internal CA terminating real HTTPS between two complete journal containers (journal-a.test / journal-b.test), driven by an opt-in integration test (FEDERATION_TWO_INSTANCE=1, via run.sh). The driver seeds users straight into each DB, mints a signed session cookie (known SESSION_SECRET), follows across instances through the real /follows/outgoing route, and asserts the full pipeline: Follow → auto-Accept → settle → first outbox poll → bob's public activity rendered in alice's /feed with @bob@journal-b.test attribution. Plus a wire-level 11.3 check: unsigned Create(Note) → 4xx, no DB writes. And the harness immediately earned its keep — it caught a real trails↔trails bug Mastodon interop never could: our Follow ids are fragment URIs on OUR domain, so the Follow embedded in another trails instance's Accept is cross-origin and Fedify rightly distrusts it; re-fetching the id returns the actor document, getObject() yields a Person, and the Accept/Reject/Undo listeners bailed silently — follows stayed Pending forever. The listeners now fall back to the wire objectId (captured BEFORE getObject(), which memoizes the fetched document and changes what objectId reports) validated against our Follow-id shape + the personal inbox's ctx.recipient. Forgery-safe: settleOutgoingFollow only matches a Pending row toward the HTTP-Signature-authenticated sender. Supporting changes: - federation.server.ts: env-gated allowPrivateAddress (FEDERATION_ALLOW_PRIVATE_ADDRESS=true) so the harness's RFC 1918 Docker network is fetchable — testing only, loudly documented. - Root .dockerignore: local builds shipped a 1GB+ context and overlaid host (darwin) node_modules over the image's own install via COPY . . — CI never noticed because it builds from clean checkouts. Context is now ~5MB. - tasks.md: 11.1–11.7 marked with provenance notes; design.md gains the cross-origin embedded-object lesson. Gate: typecheck ✓ lint ✓ unit+integration (FEDERATION_INTEGRATION=1) ✓ e2e 71/72 + known komoot flake green isolated ✓ harness run clean from scratch (2/2, teardown included) ✓ Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
2.2 KiB
Markdown
56 lines
2.2 KiB
Markdown
# Two-instance federation harness
|
|
|
|
Spec: `openspec/changes/social-federation/` task 11.4.
|
|
|
|
Two complete journal instances — `journal-a.test` and `journal-b.test` —
|
|
federate with each other inside a Docker network, with real HTTPS
|
|
between them (a Caddy with `local_certs` terminates TLS; the journals
|
|
trust its internal CA via `NODE_EXTRA_CA_CERTS`). The driver test seeds
|
|
a user on each side, follows across instances through the real UI
|
|
route, and asserts the full pipeline:
|
|
|
|
```
|
|
alice@journal-a ──Follow──▶ bob@journal-b (NodeInfo + WebFinger +
|
|
◀──Accept── actor fetch over TLS)
|
|
──signed outbox poll──▶
|
|
alice's /feed shows bob's public activity with @bob@journal-b.test
|
|
```
|
|
|
|
## Run it
|
|
|
|
```bash
|
|
./e2e/federation/run.sh # build, test, tear down
|
|
KEEP_STACK=1 ./e2e/federation/run.sh # leave the stack up afterwards
|
|
```
|
|
|
|
First run builds the journal image (a few minutes); later runs reuse it.
|
|
Opt-in only — nothing here runs under `pnpm test`, `pnpm test:e2e`, or
|
|
CI. The driver lives at
|
|
`apps/journal/app/lib/federation-two-instance.integration.test.ts`
|
|
(gated by `FEDERATION_TWO_INSTANCE=1`).
|
|
|
|
## How the driver authenticates
|
|
|
|
Sessions are signed cookies (`auth/session.server.ts`). The harness
|
|
journals run with a known `SESSION_SECRET`, so the driver mints a valid
|
|
`__session` cookie for the seeded user instead of driving the WebAuthn
|
|
ceremony. That secret (and everything else in the compose env) is a
|
|
throwaway test value on a loopback-only stack.
|
|
|
|
## Why `FEDERATION_ALLOW_PRIVATE_ADDRESS`
|
|
|
|
Fedify's document loader refuses private network addresses (SSRF
|
|
guard). Both instances here live on RFC 1918 Docker addresses, so the
|
|
harness sets `FEDERATION_ALLOW_PRIVATE_ADDRESS=true` — the env-gated
|
|
opt-out in `federation.server.ts`. Never set it in a real deployment.
|
|
|
|
## Debugging
|
|
|
|
```bash
|
|
docker compose -f e2e/federation/docker-compose.yml logs -f journal-a
|
|
docker compose -f e2e/federation/docker-compose.yml exec postgres \
|
|
psql -U trails trails_a -c 'TABLE journal.follows'
|
|
```
|
|
|
|
`FEDERATION_LOG_LEVEL=debug` is already on — Fedify logs every
|
|
signature verification and delivery attempt.
|