The catalog indexed every as:Event written to any user's outbox and served private bodies (other apps' events, friend-addressed ones); since the index is persistent, they survived restarts. It now lists only objects addressed to the Public collection in to (cc-only is unlisted: readable, never listed), using the same predicate module as the public-read grant; stored entries that do not qualify are filtered at every read and swept at startup by a conditional delete. A create, update or delete is applied only when the object lies under the pod of the actor whose outbox emitted it (same origin, path prefix with a / boundary): one user could previously remove another's entry with a Delete carrying an embedded Tombstone. Catalog handlers never throw into the webhook path, whose listener does not await them.
Festipod's create path now addresses events and meeting points to the ActivityStreams Public collection (to: as:Public), and the backend grants acl:Read to foaf:Agent on a created as:Event only when it carries that public intent (one predicate, hasPublicIntent, in backend/services/public-intent.js). Before this, every as:Event written to a user's outbox would have been made world-readable, including other apps' events and events addressed to a single friend. The grant is idempotent (it reads the ACL first) and never throws into the webhook path.
The attendees service overrides the collections mixin so that an event posted with apods:attendees already attached no longer reaches new URL(undefined) and takes the whole backend process down. Attendee lists stay world-readable as the pod creates them; whether they should is an open product question.
Each indexed event is one entry in the backend's settings dataset, default graph (an undeclared named graph is kept in memory by fuseki and lost on restart): the item JSON verbatim plus its first-index time, written by one atomic upsert, so concurrent re-adds stay one entry. The catalog now has its own delete matcher, because the platform one compares formerType as:Event to the compacted Event and never fired: an owner's deletion removes the entry, also after a restart. GET /catalog keeps its shape.
- Ajoute la configuration et les services backend (app, actors, registration,
attendees, contacts, catalog, core auth/nodeinfo/proxy/notification).
- Corrige le démarrage : désactive resourcesWithContainerPath pour éviter le
conflit entre l'URI de l'acteur applicatif (/as/actor/app) et la route /app.
- Corrige l'URL du dépôt de shapes : https://shapes.activitypods.org/.
- Ajoute backend/Dockerfile et docker-compose.yml racine (frontend + backend).
- Met à jour .env.example racine avec les variables SEMAPPS_* et VITE_BACKEND_*.
- Gates verts : bunx tsc --noEmit (0 erreur) et bun test (50 pass).