- Timestamp:
- 08/08/2026 08:27:03 PM (4 weeks ago)
- Branches:
- main
- Children:
- ea211b9
- Parents:
- d1075a1
- git-author:
- Robin <roboburr@…> (08/08/2026 08:27:01 PM)
- git-committer:
- roboburr <roboburr@…> (08/08/2026 08:27:03 PM)
- Location:
- src
- Files:
-
- 4 edited
-
routes/posts.js (modified) (2 diffs)
-
services/ActivityPubService.js (modified) (3 diffs)
-
services/guardianship/follows.js (modified) (2 diffs)
-
services/guardianship/outgoing.js (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/posts.js
rd1075a1 r3a0ca0f 891 891 const site = res.locals.site; 892 892 const uri = (req.body.uri || '').toString(); 893 if (site && uri) { 894 ActivityPubService.followActor(site, uri) 895 .catch((e) => console.warn('[AP] remote follow failed:', e.message)); 896 } 897 res.redirect('/authorize_interaction?followed=1&uri=' + encodeURIComponent(uri)); 893 if (!site || !uri) return res.redirect('/authorize_interaction?followed=1&uri=' + encodeURIComponent(uri)); 894 // Afwachten in plaats van wegsturen: ligt het verzoek bij de guardians, dan 895 // moet dat op het scherm staan (shaer-p729). "followed=1" terwijl er niets 896 // gebeurd is, is precies de leugen die de poort waardeloos maakt. 897 ActivityPubService.followActor(site, uri) 898 .then((r) => res.redirect('/authorize_interaction?' + (r && r.held ? 'held=1' : 'followed=1') + '&uri=' + encodeURIComponent(uri))) 899 .catch((e) => { 900 console.warn('[AP] remote follow failed:', e.message); 901 res.redirect('/authorize_interaction?error=1&uri=' + encodeURIComponent(uri)); 902 }); 898 903 }); 899 904 … … 1267 1272 const r = await ActivityPubService.followActor(site, handle, !!req.body.auto_boost); 1268 1273 if (r && r.error) q = 'error=' + encodeURIComponent(r.error === 'not_found' ? 'Account niet gevonden' : (r.error === 'unreachable' ? 'Server onbereikbaar' : 'Volgen mislukt')); 1274 // Een DERDE uitkomst, niet gelukt en niet mislukt (shaer-p729). "Je volgt 1275 // nu X" zeggen terwijl het verzoek bij de guardians ligt is de leugen die 1276 // deze poort waardeloos maakt: het kind denkt dat het gebeurd is. 1277 else if (r && r.held) q = 'success=' + encodeURIComponent(r.status === 'denied' ? 'Je guardians hebben dit geweigerd' : 'Je verzoek ligt bij je guardians'); 1269 1278 else { 1270 1279 q = 'success=' + encodeURIComponent('Je volgt nu ' + ((r && r.name) || handle)); -
src/services/ActivityPubService.js
rd1075a1 r3a0ca0f 5422 5422 5423 5423 // Follow a fediverse account by @handle (WebFinger → actor → signed Follow). 5424 export async function followActor(site, handle, autoBoost = false ) {5424 export async function followActor(site, handle, autoBoost = false, { approved = false } = {}) { 5425 5425 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); 5426 5426 if (!base || !site || !site.slug) return { error: 'config' }; 5427 // DE POORT STAAT HIER, en niet alleen in de C2S-outbox (shaer-p729, Barts 5428 // melding 8-8: de volgverzoeken van Esmee kwamen nooit bij haar guardians 5429 // aan). Hij stond in `case 'Follow'` van de outbox -- dus alleen als je via 5430 // Shaer volgt. Volgde het kind vanuit Klonkts eigen webinterface, dan werd er 5431 // geen verzoek aangemaakt, ging er niets naar de guardians, en was er dus ook 5432 // niets om te beantwoorden. Precies dezelfde deur-naast-de-poort als bij de 5433 // antwoordpoort vanmiddag (shaer-r4c). 5434 // 5435 // Merk op wat het NIET was: niet dat een guardian elders het niet kon 5436 // beantwoorden. Die weg werkt en levert een Offer af bij de externe guardian. 5437 // Er kwam alleen nooit iets aan om af te leveren. 5438 // 5439 // `approved` is de enige doorlaat, voor performApprovedFollow: zonder dat zou 5440 // een goedgekeurd verzoek opnieuw op de poort stuiten en voor eeuwig wachten. 5441 5427 5442 // Accept any of: a profile/actor URL, an @user@host handle (WebFinger), or a 5428 5443 // bare site domain (site.com) — for a single-actor site (Klonkt etc.) the root … … 5435 5450 else actorUrl = null; 5436 5451 if (!actorUrl) return { error: 'not_found' }; 5452 // NA het oplossen, want een kind volgt net zo goed met @naam@server of een 5453 // kaal domein. Zou de poort alleen naar de ruwe invoer kijken, dan is elke 5454 // handle een sluiproute -- en dat is precies de fout die we hier repareren, 5455 // een maat kleiner. 5456 if (!approved) { 5457 const held = await gateOutgoingFollow(site, actorUrl); 5458 if (held) return { held: true, id: held.id, status: held.status || 'pending' }; 5459 } 5437 5460 // SIGNED, as this actor: an authorized-fetch instance refuses an anonymous 5438 5461 // GET of the actor doc, which made following from a boost silently fail … … 5759 5782 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(pending.ward_slug); 5760 5783 if (!site) return { error: 'no_such_ward' }; 5761 const r = await followActor(site, pending.target_uri );5784 const r = await followActor(site, pending.target_uri, false, { approved: true }); 5762 5785 if (r && r.error) return { error: r.error }; 5763 5786 console.log('[AP] outgoing Follow approved', pending.ward_slug, '→', pending.target_uri); -
src/services/guardianship/follows.js
rd1075a1 r3a0ca0f 50 50 * send the Accept/Reject. A single reject denies; approvals meet the quorum. 51 51 */ 52 /** 53 * Hoeveel guardians moeten ja zeggen voor een volgverzoek (Barts besluit, 8-8). 54 * 55 * EENVOUDIGE MEERDERHEID: 1 van 1, 1 van 2, 2 van 3, 2 van 4. Bart: "1/2 is 56 * voldoende." 57 * 58 * BEWUST SOEPELER DAN DE POORTDREMPEL, en dat verschil hoort uitgelegd. Een gate 59 * opent een deur voor alles wat daarna komt; die vraagt om een STRIKTE 60 * meerderheid (thresholdFor in gated.js: 2 van 2, 3 van 4). Een volgverzoek gaat 61 * over een persoon, is met ontvolgen terug te draaien, en stond hier tot vandaag 62 * op 'any' -- een enkele ja, hoeveel guardians er ook waren. Dit is dus geen 63 * versoepeling maar een AANSCHERPING voor iedereen met drie of meer guardians. 64 * 65 * De 'all'-stand die hier stond is weg. Hij werd nergens gezet -- elke schrijver 66 * gaf 'any' mee -- dus het was een keuze die niemand kon maken en die alleen in 67 * de weg stond bij het lezen van deze regel. 68 */ 69 export function followThreshold(setSize) { 70 return Math.max(1, Math.ceil(setSize / 2)); 71 } 72 73 /** 74 * Een race naar de drempel, net als de poorttelling: zodra het aantal gehaald 75 * is, is het besluit gevallen. 76 * 77 * TODO (shaer-8vt): wie antwoordt weet niet dat hij de doorslag geeft. Bij 1 van 78 * 2 is de eerste ja meteen de beslissing, en het scherm zegt dat nergens. Dat is 79 * hetzelfde gat als bij de gate-voorstellen en het hoort daar samen opgelost. 80 */ 52 81 export function decide(id, guardianUri, decision, guardiansOfWard) { 53 82 const follow = stmts().get.get(id); … … 61 90 const approvers = new Set(rows.filter((r) => r.decision === 'approve').map((r) => r.guardian_uri)); 62 91 const guardians = (guardiansOfWard || []).filter(Boolean); 63 const enough = follow.quorum === 'all' 64 ? guardians.length > 0 && guardians.every((g) => approvers.has(g)) 65 : approvers.size >= 1; // 'any' (default) 92 const enough = approvers.size >= followThreshold(guardians.length); 66 93 if (enough) { 67 94 stmts().setStatus.run('accepted', id); -
src/services/guardianship/outgoing.js
rd1075a1 r3a0ca0f 15 15 */ 16 16 import db from '../../config/database.js'; 17 import { followThreshold } from './follows.js'; 17 18 18 19 let _s = null; … … 80 81 const approvers = new Set(rows.filter((r) => r.decision === 'approve').map((r) => r.guardian_uri)); 81 82 const guardians = (guardiansOfWard || []).filter(Boolean); 82 const enough = follow.quorum === 'all' 83 ? guardians.length > 0 && guardians.every((g) => approvers.has(g)) 84 : approvers.size >= 1; // 'any' (default) 83 // Dezelfde eenvoudige meerderheid als bij een inkomend volgverzoek 84 // (followThreshold): het is dezelfde vraag, alleen omgedraaid. Twee 85 // verschillende drempels voor "mag dit kind met deze persoon te maken hebben" 86 // zou een guardian nooit kunnen uitleggen. 87 const enough = approvers.size >= followThreshold(guardians.length); 85 88 if (enough) { 86 89 stmts().setStatus.run('approved', id);
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)