fix: authenticate ActivityPub follows behind TLS proxies #286

Merged
erik merged 1 commit from fix/task-b1bc9dcb-activitypub-follows into main 2026-10-02 22:14:51 -05:00
Owner

Summary

Fix the reproduced upstream template ActivityPub Follow authentication/proxy failures without changing actor identity, keys, follower data, or automatic acceptance.

Task

Task: task-b1bc9dcb — Fix ActivityPub follow handling in SlugKit example site.

Changes

  • Route both actor/shared inbox verification and queued processing through the existing compatible acct:<actor>@<domain> signer, mapping only the primary actor's Fedify-synthesized identity and preserving loader safety options.
  • Reconstruct signature-verification URLs from the configured public origin, preserving method/path/query/headers/body. Ignore arbitrary forwarding headers and reject conflicting/malformed signed Host authorities.
  • Advertise automatic approval explicitly and retain compatible signed Accept delivery with existing keys.
  • Add allowlisted, sanitized inbox/fetch/processing/delivery diagnostics; never emit raw Fedify records, payloads, sensitive headers or exceptions.
  • Add controlled authenticated HTTP fixtures, both RFC 9421/Cavage signature paths, cryptographic Accept verification, negative safety/spoofing checks, body-limit compatibility, database reopen/worker recreation, and real template-artifact coverage.
  • Document example-specific baseline reproductions, proxy configuration, generated-site upgrades, and verification. Shared federation helpers and archive generation need no implementation change.

Evidence

Baseline main 791c239: both authenticated-fixture inbox routes returned 401. With available shared-inbox documents, Cavage passed behind HTTP while RFC 9421 failed. Production-loader comparisons reject unsigned/default synthesized-key GETs and accept the existing compatible signer. Automatic acceptance and outbound signing were already present, not a manual-approval defect.

Testing

  • ./scripts/pre-pr.sh: generated CSS, formatting, lint, all workspace typechecks, and 859 tests passed (494 template-site tests).
  • Both real production-loader inboxes verify authenticated actor/key GETs, process queued Follow, and deliver cryptographically verified Accepts with private-address restrictions enabled.
  • RFC 9421 and Cavage, tampering, actor mismatch, unsafe actor/key/inbox/shared-inbox URLs, spoofed forwarded headers, conflicting Host authorities, preserved query/body, and earlier body-limit rejection covered.
  • Existing follower, actor identity, signing keys and profile metadata survive database reopen/worker recreation.
  • Actual packaged template contains corrected source, dependency, docs and regressions.
  • No unrelated changes; existing untracked docs/advertising-options.md excluded and preserved.

No external deployment or live Follow testing was performed. The existing in-process queue remains non-durable; downstream/generated sites require an ordinary approved source upgrade/release or a corrected published template artifact. CI and independent review are pending; merge requires explicit human approval.

## Summary Fix the reproduced upstream template ActivityPub Follow authentication/proxy failures without changing actor identity, keys, follower data, or automatic acceptance. ## Task Task: `task-b1bc9dcb` — Fix ActivityPub follow handling in SlugKit example site. ## Changes - Route both actor/shared inbox verification and queued processing through the existing compatible `acct:<actor>@<domain>` signer, mapping only the primary actor's Fedify-synthesized identity and preserving loader safety options. - Reconstruct signature-verification URLs from the configured public origin, preserving method/path/query/headers/body. Ignore arbitrary forwarding headers and reject conflicting/malformed signed Host authorities. - Advertise automatic approval explicitly and retain compatible signed Accept delivery with existing keys. - Add allowlisted, sanitized inbox/fetch/processing/delivery diagnostics; never emit raw Fedify records, payloads, sensitive headers or exceptions. - Add controlled authenticated HTTP fixtures, both RFC 9421/Cavage signature paths, cryptographic Accept verification, negative safety/spoofing checks, body-limit compatibility, database reopen/worker recreation, and real template-artifact coverage. - Document example-specific baseline reproductions, proxy configuration, generated-site upgrades, and verification. Shared federation helpers and archive generation need no implementation change. ## Evidence Baseline main `791c239`: both authenticated-fixture inbox routes returned 401. With available shared-inbox documents, Cavage passed behind HTTP while RFC 9421 failed. Production-loader comparisons reject unsigned/default synthesized-key GETs and accept the existing compatible signer. Automatic acceptance and outbound signing were already present, not a manual-approval defect. ## Testing - [x] `./scripts/pre-pr.sh`: generated CSS, formatting, lint, all workspace typechecks, and **859 tests** passed (494 template-site tests). - [x] Both real production-loader inboxes verify authenticated actor/key GETs, process queued Follow, and deliver cryptographically verified Accepts with private-address restrictions enabled. - [x] RFC 9421 and Cavage, tampering, actor mismatch, unsafe actor/key/inbox/shared-inbox URLs, spoofed forwarded headers, conflicting Host authorities, preserved query/body, and earlier body-limit rejection covered. - [x] Existing follower, actor identity, signing keys and profile metadata survive database reopen/worker recreation. - [x] Actual packaged template contains corrected source, dependency, docs and regressions. - [x] No unrelated changes; existing untracked `docs/advertising-options.md` excluded and preserved. No external deployment or live Follow testing was performed. The existing in-process queue remains non-durable; downstream/generated sites require an ordinary approved source upgrade/release or a corrected published template artifact. CI and independent review are pending; merge requires explicit human approval.
fix: authenticate follows consistently behind TLS proxies
All checks were successful
CI / build-lint-test (pull_request) Successful in 2m23s
182e3cdfbe
Task: #task-b1bc9dcb
Author
Owner

Review update

  • PR: #286, open; reviewed commit 182e3cdfbee8536678aae00ef0febdfe1492c629 for task-b1bc9dcb.
  • Final verdict: approved.
  • Scope: independent review of the PR diff, directly relevant source/library behavior, recorded baseline evidence and verification artifacts. This is a template-only fix, not a deployed-site modification. No tracked source edits, deployment, live Follow requests, template release, merge or task closure were performed. Unrelated untracked docs/advertising-options.md was left untouched.

Acceptance criteria

  1. met — Suspected defects reproduced or ruled out. template/site/src/federation/README.md and the PR body record example-specific baseline 791c239d85fd37f23234b20caac3517bcf68d6c2 with Fedify 2.2.4/Node adapter 2.0.3: authenticated-fixture requests failed at both inboxes; available shared-inbox documents isolated RFC 9421 HTTP-backend failure from passing Cavage behavior. Existing automatic acceptance/outbound signing were explicitly ruled out as the cause. These are recorded controlled reproductions, not live-site claims.
  2. met — Both inboxes use compatible authenticated actor/key fetching. loaders.ts remaps only the primary synthesized #main-key identity to the existing acct: signer and preserves factory/per-fetch options. routes.ts supplies a shared key dispatcher and uses the same federation factory for the worker. inbox.integration.test.ts covers both real application endpoints, production authenticated GET signatures verified against persisted advertised keys, rejection of unsigned/incompatible GETs, and authenticated fetching during queued processing.
  3. met — Canonical HTTPS verification and retained signature formats. request.ts reconstructs the URL from operator configuration while preserving method, encoded path, query, headers, body and abort signal. Both-route RFC 9421/Cavage proxy regressions exercise HTTP backend requests signed for the public HTTPS origin; request.test.ts verifies preservation without rewriting signed headers.
  4. met — Invalid/unsafe/spoofed requests remain rejected. Both-route/both-spec tests reject tampered bodies, actor mismatches and cross-origin signatures despite spoofed forwarding headers. Host tests reject conflicting/malformed authorities. Production-loader tests retain private-address/protocol restrictions without calling fetch; queued wrong-target and unsafe inbox/shared-inbox tests create no followers or deliveries. Existing signature verification, actor ownership and shared URL-safety checks are retained; earlier body-limit rejection remains covered.
  5. met — Automatic queued acceptance and signed Accept delivery. follow.ts retains follower validation, automatic persistence and the existing compatible outbound signer; actor-document.ts explicitly advertises manuallyApprovesFollowers: false. Both-route/both-spec integration tests process the queue, inspect Accept contents and cryptographically verify delivered Accepts against the actor's advertised public key, rather than treating HTTP 202 as delivery evidence.
  6. met — Useful sanitized failure diagnostics. request.ts, loaders.ts, routes.ts, follow.ts and the startup-installed logging.ts bridge emit distinct inbox, verification, fetch, processing, queue and delivery outcomes. Only classified reasons, status and hostname are extracted from failures; raw records, exceptions, payloads and sensitive headers are not forwarded. Request/logging/inbox regressions check secret-marker suppression and delivery failure status. The allowlisted verification and delivery message templates match the installed Fedify implementation.
  7. met — Persistence and unrelated behavior preserved. The file-backed database reopen/app-worker recreation regression checks unchanged actor identity, signing keys and profile metadata, retained follower data, and successful subsequent automatic acceptance. Other regressions retain existing follower records. No migration, data deletion or key rotation appears in the diff. This is controlled restart evidence, not a claim that any deployed site was upgraded.
  8. met — Regression checks, template propagation and upgrade documentation. /tmp/slugkit-pre-pr.log records the complete pre-PR suite passing CSS, formatting, lint, workspace types and 859 tests (494 template-site tests). The real-archive regression in cli/src/__tests__/template-package-script.test.ts checks corrected wiring, modules, dependency, docs and new regressions against template source. Existing archive generation includes these paths; shared helpers already provide key import/URL safety. Federation README and both site READMEs document canonical origin/Host configuration, reviewed downstream source upgrades, stable logging dependency, preserved data/keys, restart and reproducible controlled verification.

Blocking issues

  • 0 — None identified in the reviewed scope.

Warnings

  • 0 — No non-blocking review findings requiring correction or waiver.

Evidence and operational boundaries

  • Pipeline: green. fj actions tasks reports run #480, commit 182e3cdfbe, build-lint-test success, duration 2m23s. This is the CI evidence source; the reported fj pr status relative-job-URL decode failure is not a failing check.
  • Local verification evidence was inspected, not rerun during this review.
  • Existing generated sites require separately reviewed source upgrades and their normal approved releases. New sites inherit this fix only when the corrected template artifact is published. Neither publication nor downstream deployment is part of this review.
  • The existing in-process queue remains non-durable; HTTP 202 means queued, not delivered. Rejected/lost follows may require sender retries. This documented pre-existing limitation is not a new review finding.
  • Criteria totals: 8 total, 8 met, 0 missing.
  • Next: wait for explicit human merge approval. No merge, release, deployment or task closure is authorized by this verdict.
### Review update - PR: [#286](https://forge.caradoc.com/erik/slugkit/pulls/286), open; reviewed commit `182e3cdfbee8536678aae00ef0febdfe1492c629` for `task-b1bc9dcb`. - Final verdict: **approved**. - Scope: independent review of the PR diff, directly relevant source/library behavior, recorded baseline evidence and verification artifacts. This is a **template-only fix**, not a deployed-site modification. No tracked source edits, deployment, live Follow requests, template release, merge or task closure were performed. Unrelated untracked `docs/advertising-options.md` was left untouched. ### Acceptance criteria 1. **met — Suspected defects reproduced or ruled out.** `template/site/src/federation/README.md` and the PR body record example-specific baseline `791c239d85fd37f23234b20caac3517bcf68d6c2` with Fedify 2.2.4/Node adapter 2.0.3: authenticated-fixture requests failed at both inboxes; available shared-inbox documents isolated RFC 9421 HTTP-backend failure from passing Cavage behavior. Existing automatic acceptance/outbound signing were explicitly ruled out as the cause. These are recorded controlled reproductions, not live-site claims. 2. **met — Both inboxes use compatible authenticated actor/key fetching.** `loaders.ts` remaps only the primary synthesized `#main-key` identity to the existing `acct:` signer and preserves factory/per-fetch options. `routes.ts` supplies a shared key dispatcher and uses the same federation factory for the worker. `inbox.integration.test.ts` covers both real application endpoints, production authenticated GET signatures verified against persisted advertised keys, rejection of unsigned/incompatible GETs, and authenticated fetching during queued processing. 3. **met — Canonical HTTPS verification and retained signature formats.** `request.ts` reconstructs the URL from operator configuration while preserving method, encoded path, query, headers, body and abort signal. Both-route RFC 9421/Cavage proxy regressions exercise HTTP backend requests signed for the public HTTPS origin; `request.test.ts` verifies preservation without rewriting signed headers. 4. **met — Invalid/unsafe/spoofed requests remain rejected.** Both-route/both-spec tests reject tampered bodies, actor mismatches and cross-origin signatures despite spoofed forwarding headers. Host tests reject conflicting/malformed authorities. Production-loader tests retain private-address/protocol restrictions without calling fetch; queued wrong-target and unsafe inbox/shared-inbox tests create no followers or deliveries. Existing signature verification, actor ownership and shared URL-safety checks are retained; earlier body-limit rejection remains covered. 5. **met — Automatic queued acceptance and signed Accept delivery.** `follow.ts` retains follower validation, automatic persistence and the existing compatible outbound signer; `actor-document.ts` explicitly advertises `manuallyApprovesFollowers: false`. Both-route/both-spec integration tests process the queue, inspect Accept contents and cryptographically verify delivered Accepts against the actor's advertised public key, rather than treating HTTP 202 as delivery evidence. 6. **met — Useful sanitized failure diagnostics.** `request.ts`, `loaders.ts`, `routes.ts`, `follow.ts` and the startup-installed `logging.ts` bridge emit distinct inbox, verification, fetch, processing, queue and delivery outcomes. Only classified reasons, status and hostname are extracted from failures; raw records, exceptions, payloads and sensitive headers are not forwarded. Request/logging/inbox regressions check secret-marker suppression and delivery failure status. The allowlisted verification and delivery message templates match the installed Fedify implementation. 7. **met — Persistence and unrelated behavior preserved.** The file-backed database reopen/app-worker recreation regression checks unchanged actor identity, signing keys and profile metadata, retained follower data, and successful subsequent automatic acceptance. Other regressions retain existing follower records. No migration, data deletion or key rotation appears in the diff. This is controlled restart evidence, not a claim that any deployed site was upgraded. 8. **met — Regression checks, template propagation and upgrade documentation.** `/tmp/slugkit-pre-pr.log` records the complete pre-PR suite passing CSS, formatting, lint, workspace types and 859 tests (494 template-site tests). The real-archive regression in `cli/src/__tests__/template-package-script.test.ts` checks corrected wiring, modules, dependency, docs and new regressions against template source. Existing archive generation includes these paths; shared helpers already provide key import/URL safety. Federation README and both site READMEs document canonical origin/Host configuration, reviewed downstream source upgrades, stable logging dependency, preserved data/keys, restart and reproducible controlled verification. ### Blocking issues - **0** — None identified in the reviewed scope. ### Warnings - **0** — No non-blocking review findings requiring correction or waiver. ### Evidence and operational boundaries - **Pipeline: green.** `fj actions tasks` reports run **#480**, commit **182e3cdfbe**, `build-lint-test` **success**, duration **2m23s**. This is the CI evidence source; the reported `fj pr status` relative-job-URL decode failure is not a failing check. - Local verification evidence was inspected, not rerun during this review. - Existing generated sites require separately reviewed source upgrades and their normal approved releases. New sites inherit this fix only when the corrected template artifact is published. Neither publication nor downstream deployment is part of this review. - The existing in-process queue remains non-durable; HTTP 202 means queued, not delivered. Rejected/lost follows may require sender retries. This documented pre-existing limitation is not a new review finding. - Criteria totals: **8 total, 8 met, 0 missing**. - Next: wait for explicit human merge approval. No merge, release, deployment or task closure is authorized by this verdict.
erik merged commit 865630c380 into main 2026-10-02 22:14:51 -05:00
erik deleted branch fix/task-b1bc9dcb-activitypub-follows 2026-10-02 22:14:51 -05:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
erik/slugkit!286
No description provided.