Commit Graph

11 Commits

Author SHA1 Message Date
Sylvain Duchesne 88f23b7023 Harden the backend's trust surface: an allow-listed public surface, decisions taken on data re-read from the pod
The backend served its settings dataset (including the app actor's private key) to anonymous callers, through the SPARQL endpoint and through the semapps LDP catch-all route, since WebACL is off. An allow-list mounted on every route now exposes only what the app and the pod need (/app, /as/actor/app and its Client ID Document, /catalog, the public key, access needs, the JSON-LD context, nodeinfo, the app inbox and the webhook endpoint); everything else answers 404.

Every watcher-fed service (public read, catalog, attendees, contacts, registration) now decides on the activity and object re-read from the pod as the app, checks they belong to the outbox owner and that the stored object has the expected type, and never acts on the notification body. Decisions for one object are serialized and taken on a read made when acting, with a 30 s bound and an abort check before every write. The anonymous read follows the event's visibility both ways, attendees collection included; Festipod records the grants it makes and revokes only those, leaving any other public share untouched; an unreadable ACL changes nothing. A participant's attendance follows their latest act by the pod's timestamp, not the order notifications arrive. Known limits (no conditional write on the pod, a write in flight at the timeout) are documented in the code.
2026-10-13 16:30:52 +02:00
Sylvain Duchesne f14cc37f4c List only Festipod-public events in the catalog, and only let an owner change their entry
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.
2026-10-12 17:08:24 +02:00
Sylvain Duchesne a0d2cb8f73 Make events and meeting points readable by anyone only when Festipod marks them public
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.
2026-10-12 16:59:00 +02:00
Sylvain Duchesne f32e166601 Persist the catalog index across backend restarts and de-index deleted events
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.
2026-10-12 16:15:58 +02:00
Sylvain Duchesne a61c8704c9 Close the login loop end-to-end: client_id is the app actor uri, actor path serves the OIDC client document via a boot-safe service, startup heals the access-need-group link, stack aligned on pod provider v2.3.12 2026-10-12 13:12:38 +02:00
Sylvain Duchesne 6b48b24350 Shim the app-registrations verify for the pod provider drift: satisfiesAccessNeed lives on DataGrants 2026-10-12 11:23:45 +02:00
Sylvain Duchesne 3aa9131a01 Serve a Solid-OIDC Client ID Document at /app for the OIDC adapter fetch, keeping the app actor for SAI 2026-10-12 11:01:34 +02:00
Sylvain Duchesne 659d7652c4 Move the Festipod app to :4000 — :5000 belongs to the pod provider frontend in the local stack 2026-10-12 10:33:07 +02:00
Sylvain Duchesne 8dfc574346 Close the adversarial review round: auth hardening, data fidelity against the platform context, backend grants and docker coherence 2026-10-10 09:54:53 +02:00
Sylvain Duchesne e17398d86d T04.f: backend Festipod ActivityPods + dockerisation
- 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).
2026-10-09 13:51:02 +02:00
Sylvain Duchesne 21b1223b84 Add ActivityPods refine-providers dependency and backend package 2026-10-09 11:12:55 +02:00