M6: provenance fixes, stored features, community edits that are real edits
Build / Build (push) Successful in 2m34s
Deploy / privapub.thepra.dev (push) Successful in 2m53s

- A fetched ObjectRecord no longer wears the signature, key, signed headers and @context
  of the activity that caused the fetch, and its ReceivedAt is the fetch's own time. Its
  activity fields now name the trigger. Provenance answers signature null and
  fetchedBy "instance-actor". Migration _008 clears the old fetched records.
- ObjectRecords store their ObjectFeatures extensions and context namespaces (migration
  _009 fills older records in batches), so statistics can count them. Provenance reads the
  stored lists.
- ForeignAvatar.Features records what an actor document uses (ActorFeatures): featured,
  shared inbox, FEP-521a, FEP-8b32, identity proofs, Misskey fields, indexable,
  undiscoverable, locked, key size, and more.
- RemoteEdits.Apply and RemoteDeletes.Remove are shared by the Update, Delete and Announce
  handlers. A community-wrapped Update is now an edit only when newer, refreshes polls
  and media otherwise, revises the ObjectRecord and re-resolves quotes. A
  community-wrapped Delete now tombstones the object, removes its ObjectRecord, reblogs
  and timeline rows, and lowers the reply count. Any deleted quoting post lowers the
  quoted post's quote count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ELjqpznMFMNrJoJUj6K5p2
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-03 11:15:21 +02:00
1 parent d37999970c
commit 90a7e38ce9
19 files changed
+505 -109

No files matched your search

+4 -2
View File
@@ -272,13 +272,15 @@ cd /var/www/privapub.thepra.dev && sudo -u www-data ASPNETCORE_ENVIRONMENT=Produ
activity, inbox, key and signed headers; received time; up to 10 later revisions). Write it right after the post's
save: `CreateHandler` (delivered) and `RemotePosts.StoreContext` (fetched) do, and `UpdateHandler` appends a
revision. Delivery details reach them through `Arrival.Current`, which `InboxProcessor` sets for the handler's
duration. The raw form lives outside `Post` so timelines never load it. Read through
duration. A fetched record has no signature, key or `@context` of the activity that caused the fetch (its activity
fields name that trigger), and every record stores its `Extensions` and `ContextNamespaces` for statistics. The raw form lives outside `Post` so timelines never load it. Read through
`/api/privapub/v1/statuses/:id/provenance` and `/api/privapub/v1/instances/:host`.
- **Remote objects are parsed for every shape in `ObjectShapes`:** `url`/`icon`/`image` as a value, an object or an
array; Markdown `content`; missing `mediaType`s inferred; thumbnails from any of their five places; and the typed
`Link`, `Video`, `Audio` and `Event` details. A Mastodon API card is built from those, never by fetching the linked page
(that fetch is the owner's decision 1, for P6).
- **An `Update` is an edit only when its `updated` is newer than ours** (`UpdateHandler.IsEdit`). Otherwise it refreshes
- **An `Update` is an edit only when its `updated` is newer than ours** (`RemoteEdits.IsEdit`; plain and community-wrapped
Updates both go through `RemoteEdits.Apply`, and deletes through `RemoteDeletes.Remove`). Otherwise it refreshes
the poll, video, audio and event details and nothing else, and leaves no revision: Mastodon and Misskey refresh poll
counts with bare Updates.
- **A poll vote is a `Note` with a `name`, an `inReplyTo` that is a poll we hold, and no content.** `CreateHandler`