From e7bc37f28238990c580bc337a7005b794ebe7006 Mon Sep 17 00:00:00 2001 From: Sven Date: Thu, 19 Mar 2026 00:49:13 +0100 Subject: [PATCH] =?UTF-8?q?chore:=20merge=20upstream=20AP=20fork=20v2.13?= =?UTF-8?q?=E2=80=93v2.15.4,=20remove=205=20obsolete=20patches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream features merged into svemagie/indiekit-endpoint-activitypub: - v2.13.0: FEP-8fcf/fe34, custom emoji, manual follow approval - v2.14.0: server blocking, Redis caching, key refresh, async inbox - v2.15.0-4: outbox failure handling, reply forwarding, CW property, soft-delete filtering, as:Endpoints stripping Patches removed (now baked into fork): - patch-ap-object-url-trailing-slash - patch-ap-normalize-nested-tags - patch-ap-like-announce-addressing - patch-inbox-skip-view-activity-parse - patch-inbox-ignore-view-activity Patches updated: - patch-ap-allow-private-address: match v2.15 comment style Co-Authored-By: Claude Opus 4.6 --- README.md | 37 ++++++---------------- package-lock.json | 4 +-- package.json | 4 +-- scripts/patch-ap-allow-private-address.mjs | 30 ++++++++++++++++-- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 9a74a5d4..402f9e45 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ Four packages are installed directly from GitHub forks rather than the npm regis | Dependency | Source | Reason | |---|---|---| -| `@rmdes/indiekit-endpoint-activitypub` | [svemagie/indiekit-endpoint-activitypub](https://github.com/svemagie/indiekit-endpoint-activitypub) | Alpine.js fix for reader buttons + private-address document loader for self-hosted Fedify instances | +| `@rmdes/indiekit-endpoint-activitypub` | [svemagie/indiekit-endpoint-activitypub](https://github.com/svemagie/indiekit-endpoint-activitypub) | DM support, Like/Announce addressing, draft/unlisted outbox guards, merged with upstream v2.15.4 | | `@rmdes/indiekit-endpoint-blogroll` | [svemagie/indiekit-endpoint-blogroll#bookmark-import](https://github.com/svemagie/indiekit-endpoint-blogroll/tree/bookmark-import) | Bookmark import feature | | `@rmdes/indiekit-endpoint-microsub` | [svemagie/indiekit-endpoint-microsub#bookmarks-import](https://github.com/svemagie/indiekit-endpoint-microsub/tree/bookmarks-import) | Bookmarks import feature | | `@rmdes/indiekit-endpoint-youtube` | [svemagie/indiekit-endpoint-youtube](https://github.com/svemagie/indiekit-endpoint-youtube) | OAuth 2.0 liked-videos sync as "like" posts | In `package.json` these use the `github:owner/repo[#branch]` syntax so npm fetches them directly from GitHub on install. -> **Lockfile caveat:** The fork dependency is resolved to a specific commit in `package-lock.json`. When fixes are pushed to the fork, run `npm update @rmdes/indiekit-endpoint-activitypub` to pull the latest commit. The current lockfile pins to `eefa46f` (v2.10.1); the fork HEAD is at `d143abf` with Like/Announce addressing and nested tag fixes baked in. +> **Lockfile caveat:** The fork dependency is resolved to a specific commit in `package-lock.json`. When fixes are pushed to the fork, run `npm update @rmdes/indiekit-endpoint-activitypub` to pull the latest commit. The fork HEAD is at `b99f5fb` (merged upstream v2.13.0–v2.15.4 with DM support, Like/Announce addressing, and draft/unlisted guards). --- @@ -130,8 +130,8 @@ Posts are converted from Indiekit's JF2 format to ActivityStreams 2.0 in two mod |---|---|---|---| | note | Create | Note | Plain text/HTML content | | article | Create | Article | Has `name` (title) and optional `summary` | -| like | Like | URL | `to: Public, cc: followers`; outbox serves as Note for Mastodon compat | -| repost | Announce | URL | `to: Public, cc: followers`; outbox serves as Note for Mastodon compat | +| like | Like | URL | `to: Public, cc: followers` (baked into fork); outbox serves as Note for Mastodon compat | +| repost | Announce | URL | `to: Public, cc: followers` (baked into fork); outbox serves as Note for Mastodon compat | | bookmark | Create | Note | Content prefixed with bookmark emoji + URL | | reply | Create | Note | `inReplyTo` set, author CC'd and Mentioned | @@ -164,12 +164,7 @@ These patches are applied to `node_modules` via postinstall and at serve startup | Patch | Target | What it does | |---|---|---| | `patch-ap-allow-private-address` | federation-setup.js | Adds `signatureTimeWindow` and `allowPrivateAddress` to `createFederation()` | -| `patch-ap-object-url-trailing-slash` | federation-setup.js | Object dispatcher uses `$in` query to match URLs with/without trailing slash | | `patch-ap-url-lookup-api` | Adds new route | Public `GET /activitypub/api/ap-url` resolves blog URL → AP object URL | -| `patch-ap-like-announce-addressing` | jf2-to-as2.js | Adds `to: Public, cc: followers` to Like and Announce activities for shared inbox routing | -| `patch-ap-normalize-nested-tags` | jf2-to-as2.js | Strips path prefix from nested hashtags (`on/art/music` → `#music`) | -| `patch-inbox-skip-view-activity-parse` | federation-bridge.js | Buffers body, skips PeerTube View, preserves `_rawBody` for Digest verification | -| `patch-inbox-ignore-view-activity` | inbox-listeners.js | Registers no-op View handler to suppress "Unsupported activity type" errors | | `patch-federation-unlisted-guards` | endpoint-syndicate | Prevents unlisted posts from being re-syndicated (AP fork has this natively) | | `patch-endpoint-activitypub-locales` | locales | Injects German (`de`) translations for the AP endpoint UI | @@ -406,24 +401,9 @@ Patches are Node.js `.mjs` scripts in `scripts/` that surgically modify files in **`patch-ap-allow-private-address.mjs`** Adds `signatureTimeWindow: { hours: 12 }` and `allowPrivateAddress: true` to `createFederation()`. Handles both fresh v2.10.1 and already-patched files. Without this, Fedify rejects Mastodon retry signatures and blocks own-site URL resolution on the private LAN. -**`patch-ap-like-announce-addressing.mjs`** -Adds `to: Public, cc: followers` to Like activities and `cc: followers` to Announce activities in `jf2ToAS2Activity()`. Without this, Mastodon shared inboxes accept the activities (HTTP 202) but silently drop them because they lack the followers collection in their addressing. Create/Note activities were already correctly addressed. - -**`patch-ap-normalize-nested-tags.mjs`** -Strips path prefix from nested hashtags in JF2→AS2 conversion (`on/art/music` → `#music`). Mastodon doesn't support slash-delimited tag paths. - -**`patch-ap-object-url-trailing-slash.mjs`** -Replaces exact-match `findOne()` in the object dispatcher with a `$in` query that tries both `postUrl` and `postUrl + "/"`. Posts in MongoDB have trailing slashes; AP object URLs don't. - **`patch-ap-url-lookup-api.mjs`** Adds a public `GET /activitypub/api/ap-url?url=` endpoint that resolves a blog post URL to its canonical Fedify-served AP object URL. Used by the "Also on fediverse" widget for `authorize_interaction`. -**`patch-inbox-skip-view-activity-parse.mjs`** -Buffers incoming ActivityPub request bodies, short-circuits PeerTube View activities (returns 200), and preserves original bytes in `req._rawBody` for HTTP Signature Digest verification. Without the raw body preservation, `JSON.stringify()` produces different bytes and Fedify rejects all incoming activities. - -**`patch-inbox-ignore-view-activity.mjs`** -Registers a no-op `.on(View, ...)` inbox handler to suppress "Unsupported activity type" error logs from PeerTube watch broadcasts. - **`patch-endpoint-activitypub-locales.mjs`** Injects German (`de`) locale overrides into `@rmdes/indiekit-endpoint-activitypub` (e.g. "Benachrichtigungen", "Mein Profil"). The package ships only an English locale; this copies and customises it. @@ -666,11 +646,12 @@ Environment variables are loaded from `.env` via `dotenv`. See `indiekit.config. ### 2026-03-19 -**fix: add cc:followers addressing to AP Like/Announce activities** (`bee8df2`) -Like and Announce activities were missing the followers collection in their `to`/`cc` addressing. Mastodon shared inboxes silently drop activities without `cc: followers`, so likes and reposts were delivered (HTTP 202) but never appeared on remote instances. Fix applied to both the fork source and as a patch script (`patch-ap-like-announce-addressing.mjs`). +**chore: merge upstream rmdes:main v2.13.0–v2.15.4 into fork** (`b99f5fb`) +Merged 15 upstream commits adding: manual follow approval, custom emoji, FEP-8fcf/fe34 compliance (v2.13.0), server blocking, Redis caching, key refresh, async inbox queue (v2.14.0), outbox failure handling with strike system, reply chain forwarding, reply intelligence in reader (v2.15.0–v2.15.4), CW `content-warning` property, soft-delete filtering, `as:Endpoints` type stripping. Preserved our DM compose path, Like/Announce addressing, draft/unlisted outbox guards. -**chore: update activitypub endpoint fork** (`d143abf`) -Pushed Like/Announce addressing fix and nested tag normalization directly to the `svemagie/indiekit-endpoint-activitypub` fork. +**chore: remove 5 obsolete AP patches** — `patch-ap-object-url-trailing-slash`, `patch-ap-normalize-nested-tags`, `patch-ap-like-announce-addressing`, `patch-inbox-skip-view-activity-parse`, `patch-inbox-ignore-view-activity` are now baked into the fork source. + +**fix: update patch-ap-allow-private-address for v2.15 comment style** — The upstream `createFederation` block changed its comment format; updated the patch to match. ### 2026-03-14 diff --git a/package-lock.json b/package-lock.json index 22488513..1e77c80b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2356,8 +2356,8 @@ } }, "node_modules/@rmdes/indiekit-endpoint-activitypub": { - "version": "2.10.1", - "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-activitypub.git#eefa46f0c17354cf4b557e30bd2597f988ad9d7e", + "version": "2.15.4", + "resolved": "git+ssh://git@github.com/svemagie/indiekit-endpoint-activitypub.git#b99f5fb73eb23e82f28d642aeb0ba7c665903219", "license": "MIT", "dependencies": { "@fedify/debugger": "^2.0.0", diff --git a/package.json b/package.json index b5082307..921905a5 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-object-url-trailing-slash.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-ap-normalize-nested-tags.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-ap-like-announce-addressing.mjs", - "serve": "export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-object-url-trailing-slash.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-ap-normalize-nested-tags.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-ap-like-announce-addressing.mjs && node node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs", + "postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs", + "serve": "export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], diff --git a/scripts/patch-ap-allow-private-address.mjs b/scripts/patch-ap-allow-private-address.mjs index 60f7b0fb..9118b8a4 100644 --- a/scripts/patch-ap-allow-private-address.mjs +++ b/scripts/patch-ap-allow-private-address.mjs @@ -33,9 +33,33 @@ const candidates = [ const MARKER = "// allow private address fix"; const patchSpecs = [ - // Case 1: signatureTimeWindow already present (from old patch or fork) + // Case 1: v2.15+ — signatureTimeWindow present, upstream comment style (no marker suffix) { - name: "with-signature-time-window", + name: "upstream-v2.15-with-signature-time-window", + oldSnippet: ` const federation = createFederation({ + kv, + queue, + // Accept signatures up to 12 h old. + // Mastodon retries failed deliveries with the original signature, which + // can be hours old by the time the delivery succeeds. + signatureTimeWindow: { hours: 12 }, + });`, + newSnippet: ` const federation = createFederation({ + kv, + queue, + // Accept signatures up to 12 h old. + // Mastodon retries failed deliveries with the original signature, which + // can be hours old by the time the delivery succeeds. + signatureTimeWindow: { hours: 12 }, + // Allow fetching own-site URLs that resolve to private IPs. // allow private address fix + // blog.giersig.eu resolves to 10.100.0.10 on the home LAN. Without this, + // Fedify's SSRF guard blocks lookupObject() / WebFinger for own posts. + allowPrivateAddress: true, + });`, + }, + // Case 2: signatureTimeWindow present with old marker comment style + { + name: "with-signature-time-window-marker", oldSnippet: ` const federation = createFederation({ kv, queue, @@ -57,7 +81,7 @@ const patchSpecs = [ allowPrivateAddress: true, });`, }, - // Case 2: fresh v2.10.1 without signatureTimeWindow — add both + // Case 3: fresh install without signatureTimeWindow — add both { name: "fresh-without-signature-time-window", oldSnippet: ` const federation = createFederation({