Changeset 2b4252c in Klonkt for src/services/guardianship


Ignore:
Timestamp:
07/25/2026 09:15:34 AM (7 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
4f15f67
Parents:
af5b79b
Message:

Cross-instance follow-goedkeuring, gemodelleerd op de guardian-offer (§5.3)

Guardians die op een andere instance wonen kunnen nu een gated follow goedkeuren.
Zelfde gedistribueerde patroon als de adoptie-offer: de ward forwardt de follow
naar z'n guardians als een Offer(Follow); elke guardian houdt een kopie en ziet
'm in /guardian2; de guardian antwoordt met Accept/Reject naar de ward; de ward
telt quorum (bestaande follows.decide) en stuurt de gewone Accept(Follow) naar de
volger. Lokale guardians blijven zoals ze waren (push + lokale beslissing, geen
forwarding). Alles achter de shaer:followApproval-marker, dus normaal verkeer
onaangeroerd.

Changed files:
src/services/ActivityPubService.js

  • gate: remote guardian krijgt Offer(Follow) (leg 1); lokale guardian push
  • handleFollowApprovalInbox: Offer(Follow) -> review (leg 2), Accept/Reject -> decide+commit (leg 4)
  • sendFollowDecision: guardian stuurt beslissing naar de ward (leg 3)
  • dispatch-tak op shaer:followApproval vóór de handshake-dispatch

src/services/guardianship/follows.js

  • guardian-side review-store (recordReview/getReview/listReviews/removeReview)

src/config/database.js

  • ap_follow_reviews (PK slug,id): de guardian-kopie van een remote-ward-follow

src/routes/guardian2.js

  • follow-requests toont ook reviews (remote); approve op een review = sendFollowDecision

test/follow-gating.test.js

  • review-store test erbij

remarks: npm test 171/171. Spec-clausule §5.3 toegevoegd (forwarding gemodelleerd
op §3). Lokale/co-located flow ongewijzigd.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/guardianship/follows.js

    raf5b79b r2b4252c  
    7373export function remove(id) { stmts().del.run(id); }
    7474
    75 export default { recordPending, getPending, listForWard, decide, remove };
     75// ── Guardian-side copy (cross-instance, modelled on the guardian offer): a
     76//    gated follow on a REMOTE ward this account guards, forwarded here as an
     77//    Offer(Follow). The decision is Accept/Reject sent back to ward_inbox. ──
     78let _r = null;
     79function rstmts() {
     80  if (!_r) {
     81    _r = {
     82      ins: db.prepare(`INSERT OR IGNORE INTO ap_follow_reviews
     83        (id, guardian_slug, ward_uri, ward_inbox, follower_uri, follower_handle, follower_icon, follow_json, created_at)
     84        VALUES (?,?,?,?,?,?,?,?, CURRENT_TIMESTAMP)`),
     85      get: db.prepare('SELECT * FROM ap_follow_reviews WHERE guardian_slug = ? AND id = ?'),
     86      bySlug: db.prepare("SELECT * FROM ap_follow_reviews WHERE guardian_slug = ? AND status = 'pending' ORDER BY created_at DESC"),
     87      del: db.prepare('DELETE FROM ap_follow_reviews WHERE guardian_slug = ? AND id = ?'),
     88    };
     89  }
     90  return _r;
     91}
     92
     93export function recordReview(guardianSlug, r) {
     94  rstmts().ins.run(r.id, guardianSlug, r.wardUri, r.wardInbox || null, r.follower, r.followerHandle || null, r.followerIcon || null, r.followJson || null);
     95  return rstmts().get.get(guardianSlug, r.id);
     96}
     97export function getReview(guardianSlug, id) { return rstmts().get.get(guardianSlug, id); }
     98export function listReviews(guardianSlug) { return rstmts().bySlug.all(guardianSlug); }
     99export function removeReview(guardianSlug, id) { rstmts().del.run(guardianSlug, id); }
     100
     101export default {
     102  recordPending, getPending, listForWard, decide, remove,
     103  recordReview, getReview, listReviews, removeReview,
     104};
Note: See TracChangeset for help on using the changeset viewer.