Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b34d840
Add @fedify/backfill
sij411 May 22, 2026
1e7bd34
Add backfill API surface
sij411 May 26, 2026
6970c15
Implement context collection backfill
sij411 May 27, 2026
392730b
Document backfill API usage
sij411 May 27, 2026
ff8f75f
Clean up backfill lockfile changes
sij411 May 27, 2026
a9cb0db
Address backfill review feedback
sij411 May 27, 2026
cdcb066
Normalize backfill test entry paths
sij411 May 27, 2026
3f87eaa
Guard string interval without Temporal
sij411 Jun 1, 2026
0ad8d5d
Address backfill review details
sij411 Jun 2, 2026
bdf65df
Polish backfill public API docs
sij411 Jun 2, 2026
5d2a8ed
Use provisional backfill since tags
sij411 Jun 2, 2026
1a0b46d
Merge pull request #779 from sij411/feat/backfill
dahlia Jun 2, 2026
9eb5d60
Add context activity backfill
sij411 Jun 9, 2026
9776f2d
Clarify backfill strategy docs
sij411 Jun 13, 2026
18d44c1
Merge pull request #801 from sij411/feat/backfill
dahlia Jun 15, 2026
1e7a853
Add reply-tree API contract
sij411 Jun 15, 2026
a99e8c6
Refactor backfill strategy orchestration
sij411 Jun 15, 2026
0c75382
Walk reply ancestors in backfill
sij411 Jun 15, 2026
bb4807d
Walk reply descendants in backfill
sij411 Jun 15, 2026
44e3174
Clarify reply-tree API docs
sij411 Jun 15, 2026
29ab8cd
Batch adjacent context strategies
sij411 Jun 15, 2026
cda9f6c
Document reply-tree backfill behavior
sij411 Jun 15, 2026
77a1fe9
Track reply collection visits
sij411 Jun 15, 2026
1e5d422
Cover shared backfill budgets
sij411 Jun 15, 2026
8f70443
Cover ordered backfill strategy dedupe
sij411 Jun 15, 2026
badd88d
Document ordered backfill strategies
sij411 Jun 15, 2026
cc012ab
Preserve strategy order around context auto
sij411 Jun 16, 2026
e6c75de
Skip seen context item URLs
sij411 Jun 16, 2026
e7a2e69
Cache dereferenced backfill documents
sij411 Jun 16, 2026
7d117ee
Cover backfill cache retry behavior
sij411 Jun 16, 2026
8f29cbb
Fix reply-tree sibling traversal
sij411 Jun 16, 2026
925f1d5
Limit reply-tree depth by default
sij411 Jun 18, 2026
90e0abe
Preserve reply-tree traversal semantics
sij411 Jun 18, 2026
fd6f645
Document FEP-f228 support
sij411 Jun 21, 2026
07a6cf1
Merge pull request #807 from sij411/feat/backfill
dahlia Jun 22, 2026
e32f5bb
Add documentation
sij411 Jun 23, 2026
c6ef99d
Add backfill manual page
sij411 Jun 23, 2026
4e9348a
Add pull request 816 to CHANGES.md
sij411 Jun 24, 2026
113dda2
Merge pull request #816 from sij411/feat/backfill-wrap-up
dahlia Jun 24, 2026
d1f446d
Fix pnpm-lock
sij411 Jun 24, 2026
2fb6371
Merge branch 'main' into feat/backfill
sij411 Jun 24, 2026
3d4cc90
Format CHANGES.mdd
sij411 Jun 24, 2026
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
13 changes: 13 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,19 @@ To be released.
[#804]: https://github.com/fedify-dev/fedify/pull/804
[#818]: https://github.com/fedify-dev/fedify/pull/818

### @fedify/backfill

- Added *@fedify/backfill* for reconstructing ActivityPub conversations.
It supports FEP-f228 context collections containing post-like objects or
`Create` activities, optional reply-tree traversal, ordered hybrid
strategies, shared safety budgets, deduplication, and traversal-local
document caching. [[#275], [#779], [#801], [#807], [#816] by Jiwon Kwon]

[#275]: https://github.com/fedify-dev/fedify/issues/275
[#779]: https://github.com/fedify-dev/fedify/pull/779
[#807]: https://github.com/fedify-dev/fedify/pull/807
[#816]: https://github.com/fedify-dev/fedify/pull/816

### @fedify/fixture

- Added `createTestMeterProvider()` and `TestMetricRecorder` helpers for
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ The repository is organized as a monorepo with the following packages:
creating new Fedify projects. Wraps @fedify/init.
- *packages/amqp/*: AMQP/RabbitMQ driver (@fedify/amqp) for Fedify.
- *packages/astro/*: Astro integration (@fedify/astro) for Fedify.
- *packages/backfill/*: ActivityPub conversation backfill support
(@fedify/backfill) for Fedify.
- *packages/cfworkers/*: Cloudflare Workers integration (@fedify/cfworkers)
for Fedify.
- *packages/debugger/*: Embedded ActivityPub debug dashboard
Expand Down
2 changes: 2 additions & 0 deletions FEDERATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Supported FEPs
--------------

- [FEP-67ff][]: FEDERATION.md
- [FEP-f228][]: Backfilling conversations
- [FEP-8fcf][]: Followers collection synchronization across servers
- [FEP-9091][]: Export Actor Service Endpoint
- [FEP-f1d5][]: NodeInfo in Fediverse Software
Expand All @@ -40,6 +41,7 @@ Supported FEPs
- [FEP-ae0c][]: Fediverse Relay Protocols: Mastodon and LitePub

[FEP-67ff]: https://w3id.org/fep/67ff
[FEP-f228]: https://w3id.org/fep/f228
[FEP-8fcf]: https://w3id.org/fep/8fcf
[FEP-9091]: https://w3id.org/fep/9091
[FEP-f1d5]: https://w3id.org/fep/f1d5
Expand Down
1 change: 1 addition & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"workspace": [
"./packages/amqp",
"./packages/astro",
"./packages/backfill",
"./packages/cfworkers",
"./packages/cli",
"./packages/debugger",
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const MANUAL = {
{ text: "Outbox listeners", link: "/manual/outbox.md" },
{ text: "Sending activities", link: "/manual/send.md" },
{ text: "Collections", link: "/manual/collections.md" },
{ text: "Conversation backfill", link: "/manual/backfill.md" },
{ text: "Object dispatcher", link: "/manual/object.md" },
{ text: "Access control", link: "/manual/access-control.md" },
{ text: "WebFinger", link: "/manual/webfinger.md" },
Expand Down
203 changes: 203 additions & 0 deletions docs/manual/backfill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
---
description: >-
Reconstruct ActivityPub conversations from FEP-f228 context collections or
reply relationships using the @fedify/backfill package.
---

Conversation backfill
=====================

*This API is available since Fedify 2.3.0.*

Fedify provides the *@fedify/backfill* package for reconstructing ActivityPub
conversations that may be incomplete on the local server. It can retrieve
post-like objects from [FEP-f228] context collections and optionally crawl
`inReplyTo` ancestors and `replies` descendants.

[FEP-f228]: https://w3id.org/fep/f228


Installation
------------

::: code-group

~~~~ sh [Deno]
deno add jsr:@fedify/backfill
~~~~

~~~~ sh [npm]
npm add @fedify/backfill
~~~~

~~~~ sh [pnpm]
pnpm add @fedify/backfill
~~~~

~~~~ sh [Yarn]
yarn add @fedify/backfill
~~~~

~~~~ sh [Bun]
bun add @fedify/backfill
~~~~

:::


Backfilling a conversation
--------------------------

The `backfill()` function accepts a backfill context, a seed object, and
traversal options. The context supplies a `documentLoader` for dereferencing
context collections, collection items, reply targets, and replies collections:

~~~~ typescript twoslash
import { backfill, type BackfillDocumentLoader } from "@fedify/backfill";
import { lookupObject, Note } from "@fedify/vocab";

declare const note: Note;
// ---cut-before---
const documentLoader: BackfillDocumentLoader = (iri, options) =>
lookupObject(iri, { signal: options?.signal });

for await (
const item of backfill({ documentLoader }, note, {
maxItems: 20,
maxRequests: 50,
})
) {
console.log(item.id?.href);
}
~~~~

The seed object itself is not yielded. If the same object appears in a
discovered collection, it is skipped by ID.

By default, `backfill()` uses the `"context-auto"` strategy. It expects the
seed's `context` to dereference to a `Collection`, `OrderedCollection`,
`CollectionPage`, or `OrderedCollectionPage`. Ordinary post-like items are
yielded directly, while supported `Create` activities are unwrapped and their
objects are yielded.

If the seed has no context, or its context resolves to a non-collection,
context strategies yield nothing.


Strategies
----------

Strategies run in the configured order. They share request and item budgets,
abort state, document caching, and object ID deduplication. If multiple
strategies discover the same object, the first one keeps its `BackfillItem`
metadata.

`"context-auto"`
: Handles both direct post-like objects and supported `Create` activities
from a context collection. This is the default strategy.

`"context-objects"`
: Accepts only post-like objects contained directly in a context collection:

~~~~ typescript twoslash
import { backfill, type BackfillContext } from "@fedify/backfill";
import { Note } from "@fedify/vocab";

declare const context: BackfillContext;
declare const note: Note;
// ---cut-before---
for await (
const item of backfill(context, note, {
strategies: ["context-objects"],
})
) {
console.log(item.object);
}
~~~~

`"context-activities"`
: Accepts supported activities from a context collection. It currently
supports `Create` and yields the activity's object rather than the activity
itself:

~~~~ typescript twoslash
import { backfill, type BackfillContext } from "@fedify/backfill";
import { Note } from "@fedify/vocab";

declare const context: BackfillContext;
declare const note: Note;
// ---cut-before---
for await (
const item of backfill(context, note, {
strategies: ["context-activities"],
})
) {
console.log(item.object);
}
~~~~

`"reply-tree"`
: Walks `inReplyTo` ancestors and `replies` descendants. It yields
post-like objects only and does not unwrap Activity objects. This strategy
is opt-in because it can require substantially more network requests than
a context collection.

For hybrid coverage, run the FEP-f228 path first and use reply-tree traversal
after it:

~~~~ typescript twoslash
import { backfill, type BackfillContext } from "@fedify/backfill";
import { Note } from "@fedify/vocab";

declare const context: BackfillContext;
declare const note: Note;
// ---cut-before---
for await (
const item of backfill(context, note, {
strategies: ["context-auto", "reply-tree"],
maxDepth: 4,
})
) {
console.log(item.origin, item.depth, item.object);
}
~~~~


Traversal controls
------------------

`maxItems`
: Limits the number of yielded objects. Skipped duplicates do not count.

`maxRequests`
: Limits calls to `documentLoader`. Embedded objects and collections do not
count as requests.

`maxDepth`
: Limits reply-tree traversal and defaults to 10. Immediate parents and
direct replies have depth 1; their next-level parents or replies have depth
2, and so on. Context collection items have depth 0 and are not limited by
this option.

`interval`
: Adds a delay between `documentLoader` requests. A callback receives the
zero-based request index. String durations require the global `Temporal`
API or a polyfill; `Temporal.DurationLike` objects work without the global
API.

`signal`
: Cancels traversal before requests and yields. The signal is also passed to
`documentLoader`.


Caching and failures
--------------------

Dereferenced documents are cached in memory for one `backfill()` traversal.
Applications that need persistent or shared caching can implement it in the
provided `documentLoader`.

Failed external dereferences are skipped so other conversation items can still
be discovered. Failed loads are not retained in the traversal cache, allowing
the same IRI to be retried if another traversal path reaches it. Aborting the
provided signal stops traversal instead of skipping the request.
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"@deno/kv": "^0.8.4",
"@fedify/amqp": "workspace:^",
"@fedify/astro": "workspace:^",
"@fedify/backfill": "workspace:^",
"@fedify/cfworkers": "workspace:^",
"@fedify/debugger": "workspace:^",
"@fedify/express": "workspace:^",
Expand Down
Loading
Loading