Changeset e311202 in Klonkt


Ignore:
Timestamp:
08/21/2026 08:44:47 AM (3 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
4e5fe29
Parents:
bb9e54c
Message:

A block now leaves the house: Block out, Undo(Block) back

Blocking was purely local — row in ap_blocks, purge the cached content,
drop the follower — and the other side never heard about it. The hub
kept the channel and every post, because nothing told it and its
unsigned crawl kept reading the public outbox (seen on dev.klonkt.com,
21-8).

Now blocking delivers a Block to the blocked actor's inbox and
unblocking an Undo(Block), so the way back stays open. Delivery can
never hold up the block itself: the row is written first and a
failed delivery is swallowed. Domain blocks send nothing — no inbox to
address.

Second door: a signed reader we block gets the same 404 on /ap/notes/:id
that a stranger gets, matching what the outbox already did. Unsigned
callers stay anonymous to us and keep the public view; that is what the
Block delivery is for.

Co-Authored-By: Claude Fable 5 <noreply@…>

Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • src/routes/activitypub.js

    rbb9e54c re311202  
    12581258  // cannot_resolve_inReplyTo. Strangers still get the exact same 404, so a
    12591259  // note's existence stays as private as before.
     1260  // EEN GEBLOKKEERDE KRIJGT DE DEUR DICHT (Robin, 21-8), net als bij de outbox:
     1261  // wie ondertekend aanklopt, klopt met zijn naam erop, en een blokkade is een
     1262  // gesloten deur. Dezelfde 404 als een vreemde, zodat het bestaan van een note
     1263  // niets extra's verraadt. Onbetekende verzoeken kunnen we niet thuisbrengen
     1264  // en houden de publieke weergave -- daarvoor is de Block-bezorging.
     1265  if (req.headers['signature']) {
     1266    const wie = await AP.verifyRequest(req).catch(() => null);
     1267    if (wie && wie.id && AP.isBlockedAny(wie.id)) return res.status(404).end();
     1268  }
    12601269  const post = db.prepare(
    12611270    "SELECT * FROM posts WHERE id = ? AND status = 'published'"
  • src/routes/posts.js

    rbb9e54c re311202  
    15341534router.post('/blocking/remove', requireSiteManager, (req, res) => {
    15351535  const site = res.locals.site;
    1536   if (site) { try { ActivityPubService.unblock(site, (req.body.target || '').toString()); } catch (e) { /* ignore */ } }
     1536  if (site) { try { ActivityPubService.unblock(site, (req.body.target || '').toString()).catch(() => {}); } catch (e) { /* ignore */ } }
    15371537  res.redirect('/blocking?success=' + encodeURIComponent('Deblokkeerd'));
    15381538});
  • src/services/ActivityPubService.js

    rbb9e54c re311202  
    36003600        if (innerType === 'Block') {
    36013601          if (!innerTarget) return { status: 400, error: 'missing_object' };
    3602           unblock(site, innerTarget);   // release from Orbit
     3602          unblock(site, innerTarget).catch(() => {});   // release from Orbit
    36033603          return { status: 202, url: innerTarget };
    36043604        }
     
    66666666// Block an actor (@handle or actor URL) or a whole domain; purges their content.
    66676667// The handle resolver is ours; the storage/purge lives in BlocklistService.
    6668 export async function blockTarget(site, input) { return Blocklist.blockTarget(site, input, webfingerResolve); }
    6669 
    6670 export function unblock(site, target) { return Blocklist.unblock(site, target); }
     6668//
     6669// De BEZORGING hoort ook hier: BlocklistService kent de database, niet het
     6670// afleveren. Een Block gaat naar de inbox van wie je blokkeert, een
     6671// Undo(Block) bij het opheffen -- zonder retry-wachtrij, want een blokkade
     6672// wacht niet op een server die even plat ligt (en bij opheffen komt de ander
     6673// vanzelf weer langs).
     6674async function bezorgBlokkade(site, target, undo) {
     6675  const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
     6676  const me = actorId(base, site.slug);
     6677  const blok = { id: `${me}#block-${Date.now()}-${rid()}`, type: 'Block', actor: me, object: target, to: [target] };
     6678  const activiteit = undo
     6679    ? { '@context': AP_CONTEXT, id: `${me}#unblock-${Date.now()}-${rid()}`, type: 'Undo', actor: me, object: blok, to: [target] }
     6680    : { '@context': AP_CONTEXT, ...blok };
     6681  const r = await deliverToActor(site, target, activiteit);
     6682  console.log('[AP]', undo ? 'Undo(Block)' : 'Block', site.slug, '→', target, r && r.delivered ? 'bezorgd' : 'niet bezorgd');
     6683}
     6684
     6685export async function blockTarget(site, input) { return Blocklist.blockTarget(site, input, webfingerResolve, bezorgBlokkade); }
     6686
     6687export function unblock(site, target) { return Blocklist.unblock(site, target, bezorgBlokkade); }
    66716688
    66726689// ── Guardianship module wiring (src/services/guardianship/) ────────
  • src/services/BlocklistService.js

    rbb9e54c re311202  
    5656// `resolveHandle` (async handle → actor URL) is injected by the caller so this
    5757// service needs nothing from ActivityPubService (no circular import).
    58 export async function blockTarget(site, input, resolveHandle) {
     58/**
     59 * Een blokkade is pas een blokkade als de ander het merkt (Robin, 21-8).
     60 *
     61 * Tot vandaag bleef hij binnenshuis: rij in ap_blocks, inhoud opruimen, volger
     62 * eruit -- en verder niets. De andere kant volgde je dan nog steeds in zijn
     63 * eigen boeken en bleef je publieke outbox lezen. Precies wat de hub deed:
     64 * kanaal en berichten stonden er gewoon nog. Vandaar dat we het nu ook
     65 * VERSTUREN, zoals Mastodon dat doet: een Block naar de inbox van wie je
     66 * blokkeert, en bij opheffen een Undo(Block) zodat de weg terug openligt.
     67 *
     68 * Alleen voor een actor-blokkade: een heel domein heeft geen inbox om aan te
     69 * schrijven. En bezorgen mag nooit de blokkade zelf tegenhouden -- die staat
     70 * al vast in de database voordat we ook maar iets proberen te versturen.
     71 */
     72async function meldBlokkade(site, target, kind, bezorg, undo = false) {
     73  if (kind !== 'actor' || typeof bezorg !== 'function') return;
     74  try { await bezorg(site, target, undo); } catch { /* de blokkade staat, de melding is een gunst */ }
     75}
     76
     77export async function blockTarget(site, input, resolveHandle, bezorg) {
    5978  const raw = String(input || '').trim();
    6079  if (!site || !site.slug || !raw) return { error: 'empty' };
     
    6988  purgeBlocked(kind, target);
    7089  console.log('[AP] block', site.slug, kind, target);
     90  await meldBlokkade(site, target, kind, bezorg);
    7191  return { ok: true, label };
    7292}
    7393
    74 export function unblock(site, target) { blStmts().del.run(site.slug, target); return { ok: true }; }
     94export async function unblock(site, target, bezorg) {
     95  const rij = blStmts().list.all(site.slug).find((b) => b.target === target);
     96  blStmts().del.run(site.slug, target);
     97  // Undo(Block) zodat de ander weet dat de deur weer open is; zonder dit blijft
     98  // hij bij zichzelf geblokkeerd staan en komt hij nooit terug.
     99  await meldBlokkade(site, target, (rij && rij.kind) || 'actor', bezorg, true);
     100  return { ok: true };
     101}
    75102
    76103export default { listBlocks, isBlockedAny, blockTarget, unblock };
Note: See TracChangeset for help on using the changeset viewer.