From b1add6d8c80c17db9331d4de91501a431fcaf5c8 Mon Sep 17 00:00:00 2001 From: Sylvain Duchesne Date: Fri, 9 Oct 2026 10:26:25 +0200 Subject: [PATCH] Record the registration precedent, and the event-in-event feasibility without new shapes --- .../brief_2026-10-09_activitypods-first-backend.md | 13 ++++++++++--- .project/handoff/refine-provider-abstraction.md | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.project/concepts/data-layer/brief_2026-10-09_activitypods-first-backend.md b/.project/concepts/data-layer/brief_2026-10-09_activitypods-first-backend.md index 72ab381..7c7a607 100644 --- a/.project/concepts/data-layer/brief_2026-10-09_activitypods-first-backend.md +++ b/.project/concepts/data-layer/brief_2026-10-09_activitypods-first-backend.md @@ -12,6 +12,7 @@ Directives recorded at the resumption of the hand-off (`.project/handoff/refine- 1. **ActivityPods is the priority.** The Refine backend over NextGraph plus its polyfill is **suspended, temporarily** — it is not abandoned (the two-rights-models structure still targets both), but no work goes into it while ActivityPods is being wired. 2. **Everything that can must go through `@activitypods/refine-providers`.** The Refine boundary rule from the hand-off applies: what Refine can express is a Refine resource; what it cannot (collection creation, WAC, groups, outbox posting) happens beside Refine, in the seam or in the new backend. Capability differences stay inside the provider layer, never in the app. 3. **A backend must be added for actions "on behalf of a user".** Festipod needs server-side actions executed for the account of a user (the pattern used by the reference apps below). Its mechanism, packages and deployment shape are the first design question of this workstream. +4. **Minimise new shapes — interoperability first (directive 2026-10-09).** A new shape is a last resort, not a default: anything an existing vocabulary already carries must reuse it. A MeetingPoint is therefore modelled as **an event within an event** — an `as/Event` distinguished by an **optional `parentEvent` reference** to its anchor event, not a new `fp:MeetingPoint` type. Whether an existing ActivityStreams predicate (e.g. `as:context`) can carry that link, or the `as/Event` shape must gain an optional property, is part of the survey. For Participation, the existing fediverse vocabulary must be surveyed before anything is created. ## Reference material (user-designated 2026-10-09) @@ -28,9 +29,15 @@ Reading these at this stage is adoption-time reading, explicitly directed — th - `getMany` silently drops failed fetches — it erases the "unreadable reference" state the model needs (a candidate contribution). - `acl:Append` third-party insert-without-remove behaviour is NOT VERIFIED; the structure does not depend on the answer. +## Reconnaissance answers (2026-10-09) + +- **On-behalf mechanism — ANSWERED.** An app backend is an ActivityPub actor (`@activitypods/app` `AppService`, Moleculer); it signs pod requests as the app actor and the pod impersonates the user after checking registration + grants. Frontend calls use the Solid-OIDC dual-identity token instead. No unattended per-user backend token exists. +- **Registration/Participation — production precedent found.** welcometomyplace persists NO registration resource: a join is an `as:Join` activity posted to the user's outbox, the backend service handles it and adds the actor to the event's `apods:attendees` collection (attached via `attachPredicate`, `PodCollectionsHandlerMixin`); a leave is `as:Leave`, same path in reverse. Membership is read via `pod-collections.getItems`. mutual-aid uses none of this (comments, likes, announces only). +- **Event-in-event — feasible without touching any shape.** The `as/Event` and `as/Profile` SHACL shapes are open (no `sh:closed`), and the generic ActivityStreams vocabulary already defines `as:context`, `as:partOf`, `as:inReplyTo`. A meeting point is a plain `as/Event` carrying an `as:context` link to its parent event — the presence of the link is what distinguishes it, nothing else. Shapes are consumed for access needs and container setup, not server-side resource validation, so nothing breaks. + ## Open questions -- The on-behalf mechanism: how a Festipod backend authenticates and acts for a user (tokens? app actor? delegated outbox posting?) — to be mapped from the platform source and the example apps. -- Which shapes from the shapes repo map to Festipod's entities (Event, MeetingPoint, Profile, Participation) as-is, and which need to be created or evolved. -- The backend's deployment shape (service alongside the app? serverless? a platform app?). +- Participation topology — the one real divergence from the 2026-10-03 structure: welcometomyplace keeps the canonical list creator-side (an `apods:attendees` collection on the event, writes backend-mediated), where the two-rights-models structure makes each participation a participant-owned document. Both land on the same application state (a reference that may or may not resolve); the choice is who owns the write path. To arbitrate before implementation. +- `as:Join` has a shape but no shape tree: if participations must be addressable as resources in access needs, adding `shapetrees/as/Join.ttl` for an already-existing AS type is the minimal move — or the collection pattern makes it unnecessary. +- The backend's deployment shape (service alongside the app? a platform app?). - Whether the four pointed-to provider contributions (from the hand-off) are still the right set once the backend exists — some may move from "contribution to the providers" to "our backend's job". diff --git a/.project/handoff/refine-provider-abstraction.md b/.project/handoff/refine-provider-abstraction.md index 86cbcb0..b334da6 100644 --- a/.project/handoff/refine-provider-abstraction.md +++ b/.project/handoff/refine-provider-abstraction.md @@ -24,7 +24,8 @@ Recorded in `data-layer/brief_2026-10-09_activitypods-first-backend`; the essent - **The on-behalf mechanism is mapped.** An app backend IS an ActivityPub actor (`@activitypods/app` `AppService`, Moleculer). It signs pod requests with HTTP signatures as the app actor; the pod provider checks the app's registration and grants, then impersonates the user (`pod-provider/backend/middlewares/app-control.js`). Services call `pod-outbox.post`, `pod-resources.*`, `pod-collections.*`, `pod-wac-groups.*`, `pod-permissions.*` with an `actorUri`. Frontend calls use the Solid-OIDC dual-identity token instead (`webId` = app client_id, `impersonatedUser` = user). Reference implementations: `welcometomyplace/backend/services/registration.service.js`, `attendees-matcher.service.js`; both example apps run the same stack (`@activitypods/app` 2.3 + `@semapps/*` 1.2, Moleculer backend + Vite/React/Refine frontend, Docker-deployed). - **The refine-providers package is NOT in the platform monorepo** — it lives at `/home/sylvain/projects/activitypods/refine-providers` (a `/home/sylvain/projects/nextgraph/activitypodes-refine-providers` checkout also exists; the pinned reconstruction remains the work-time specification). -- **Shapes**: 15 shape trees published at `https://shapes.activitypods.org` from `/home/sylvain/projects/activitypods/shapes` (repo evolvable by us). Mapping proposal: reuse `as/Event` + `as/Profile`; create `fp/MeetingPoint` and `fp/Participation` (the `as:Join` type exists but has no shape tree). No stored-counter participation fields beyond the reference + `active` fit the 2026-10-03 structure. +- **Shapes**: 15 shape trees published at `https://shapes.activitypods.org` from `/home/sylvain/projects/activitypods/shapes` (repo evolvable by us). The 2026-10-09 directives **amended the first mapping proposal — minimise new shapes, interoperability first**: a MeetingPoint is a plain `as/Event` carrying an `as:context` link to its parent event (event within an event; the `as/Event` shape is open — no `sh:closed` — so nothing breaks and no new shape is needed). For Participation the existing vocabulary was surveyed: see the registration precedent in the next bullet. +- **Registration/Participation — production precedent found.** welcometomyplace persists no registration resource: join = `as:Join` activity in the user's outbox, handled by the backend service which adds the actor to the event's `apods:attendees` collection (`PodCollectionsHandlerMixin`, `attachPredicate`); leave = `as:Leave`; reads via `pod-collections.getItems`. The one open design question this raises — collection creator-side (this pattern) vs participant-owned participation documents (the 2026-10-03 structure) — is recorded in the brief, to arbitrate before implementation. - **No unattended per-user backend token exists** on the platform: server-side per-user actions ride the app-actor signature + grants; the platform-idiomatic channel for user-facing reactions is the app outbox → user inbox (notifications style). ## What exists, and what deliberately does not