Ignore:
Timestamp:
08/08/2026 05:25:06 AM (5 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
34e63f0
Parents:
2220c49
git-author:
Robin <roboburr@…> (08/08/2026 05:25:04 AM)
git-committer:
roboburr <roboburr@…> (08/08/2026 05:25:06 AM)
Message:

Antwoorden is ook iets: een eigen poort (shaer-r4c)

Bart, 8-8: "Ook meedoen aan een gesprek valt onder een gate, maak die en maak die
zichtbaar."

HIER STOND EEN AANNAME, EN DIE WAS VAN MIJ. compose liet een antwoord bewust
door, met als reden "een antwoord is meedoen aan een gesprek, geen eigen podium".
Dat is een ontwerpkeuze die ik als vanzelfsprekend had opgeschreven, en Bart
draait hem terug: meedoen aan een gesprek is ook iets waar guardians over gaan.

EEN EIGEN POORT, NIET ONDER COMPOSE. Los in beide richtingen, en daar staan
toetsen op: je kunt willen dat een kind meepraat zonder eigen podium, en ook
precies andersom. Zou compose antwoorden meesluiten, dan is de nieuwe rij in het
paneel een knop die niets doet.

DE DEUR NAAST DE POORT. De innamepoort in ingestOutboxActivity dekt alleen C2S --
Shaer. routes/posts.js roept deliverReply op drie plekken RECHTSTREEKS aan, dus
de eigen webinterface van Klonkt loopt er nooit langs. Een poort die alleen in de
app staat is geen poort. De echte controle staat nu in deliverReply zelf, het
knooppunt dat beide paden delen; de outbox houdt zijn eigen check zodat de app
een nette 403 gated_replies krijgt in plaats van een stille null.

De boei komt daar niet langs en dat is geen toeval: een hulpvraag is altijd
direct en loopt via deliverDirectNote. Er is dus geen uitzondering nodig om hem
open te houden -- maar er staat wel een toets op, want dit is het gevaarlijkste
dat deze poort kan doen: een kind dat om hulp vraagt in het draadje waar het
misgaat.

Een direct antwoord wordt door allebei de poorten gedekt (messages en replies).
Een prive-antwoord is allebei, en dan mag allebei hem tegenhouden.

Paneel en shaer:capabilities krijgen hem gratis uit de catalogus. Labels in
nl/en/de. Zes toetsen, drie mutaties gecontroleerd. Suite 646/646.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r2220c49 rffb371c  
    31953195            if (!direct && !object.inReplyTo && !Guardianship.wardGateAllowed(site.gate_compose, isWard)) {
    31963196              return { status: 403, error: 'gated_compose' };
     3197            }
     3198            // Meedoen aan een gesprek is ook iets (Bart, 8-8). Hier stond de
     3199            // aanname dat een antwoord geen eigen podium is en dus onder compose
     3200            // door mocht. Dat is teruggedraaid: antwoorden heeft een EIGEN poort,
     3201            // los van compose in beide richtingen -- je kunt willen dat een kind
     3202            // meepraat zonder podium, en ook andersom.
     3203            //
     3204            // Geldt ook voor een DIRECT antwoord, bovenop de messages-poort: een
     3205            // privé-antwoord is allebei, en dan mag allebei hem tegenhouden.
     3206            if (object.inReplyTo && !Guardianship.wardGateAllowed(site.gate_replies, isWard)) {
     3207              return { status: 403, error: 'gated_replies' };
    31973208            }
    31983209          }
     
    34823493  // A media-only reply (no text) is a valid reply.
    34833494  if (!base || !site || !site.slug || !parent || (!String(text || '').trim() && !rich && !media.length)) return null;
     3495  // DE POORT STAAT HIER en niet alleen in de outbox (shaer-r4c). routes/posts.js
     3496  // roept deliverReply op drie plekken rechtstreeks aan -- de eigen webinterface
     3497  // van Klonkt gaat dus nooit langs ingestOutboxActivity. Een poort die alleen in
     3498  // C2S staat is een poort met een deur ernaast.
     3499  //
     3500  // Dit is het knooppunt dat beide paden delen. De reddingsboei komt hier niet
     3501  // langs: een hulpvraag is altijd direct en loopt via deliverDirectNote, dus de
     3502  // boei blijft open zonder dat daar een uitzondering voor nodig is.
     3503  {
     3504    const isWard = (() => { try { return Guardianship.listGuardians(site.slug).length > 0; } catch { return false; } })();
     3505    if (!Guardianship.wardGateAllowed(site.gate_replies, isWard)) return null;
     3506  }
    34843507  const me = actorId(base, site.slug);
    34853508  // u02, the mentions bar: `mentions` undefined = legacy behavior (mention the
Note: See TracChangeset for help on using the changeset viewer.