Changeset fa33214 in Klonkt for src/config/database.js


Ignore:
Timestamp:
08/04/2026 12:55:55 PM (5 weeks ago)
Author:
Bart <bart@…>
Branches:
main
Children:
04aca12, 12bed59
Parents:
3d882bd
Message:

FEP-633c §5.3 andersom: een ward vraagt eerst of het iemand mag volgen

Uitgaande follows gingen ongehinderd de deur uit; de guardians kregen achteraf
een bericht (1a2f206). Dat is informeren, niet gaten — de deur staat al open als
het bericht aankomt. Bead shaer-p729, ontwerp in
docs/ward-outbound-follows-design.md.

De regel: per geval goedkeuring, met twee uitzonderingen die geen gunst zijn
maar dezelfde beslissing die al genomen is. Je eigen guardian volgen is geen
vraag. En iemand die de ward al volgt DOOR DE POORT heen is door een guardian
bij naam goedgekeurd; die vraag nog eens stellen leert mensen alleen om de vraag
niet meer te lezen.

Daarvoor moet je weten wie er door de poort kwam, dus ap_followers krijgt
gate_approved, gezet bij acceptGatedFollow. Iedereen die al volgde toen die
kolom erbij kwam wordt eenmalig gegrandfatherd (Barts besluit): exact vanaf nu,
in plaats van met terugwerkende kracht wantrouwig tegen wat er al was.

Eigen tabel, want ap_pending_follows is gesleuteld met de ward als DOEL. Eigen
wachtrij (outgoingFollows), want een guardian moet "iemand wil je ward volgen"
kunnen onderscheiden van "je ward wil iemand volgen" — de AS2-test ving netjes
dat de nieuwe term aangemeld moest worden. En een tegengehouden follow reist als
derde uitkomst naar de app (state: awaiting_guardian), zodat Shaer "wacht op
toestemming" kan tonen in plaats van een tegel die er al volgend uitziet.

Co-Authored-By: Claude Opus 5 <claude@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/config/database.js

    r3d882bd rfa33214  
    5858  )`);
    5959  db.exec(`CREATE TABLE IF NOT EXISTS ap_pending_follow_approvals (
     60    follow_id TEXT NOT NULL,
     61    guardian_uri TEXT NOT NULL,
     62    decision TEXT NOT NULL,
     63    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
     64    PRIMARY KEY (follow_id, guardian_uri)
     65  )`);
     66  // FEP-633c §5.3, the OTHER direction (shaer-p729): a ward's own follow is
     67  // held until its guardians approve. Deliberately not ap_pending_follows —
     68  // that table is keyed with the ward as the TARGET ("who wants to follow me"),
     69  // and adding a direction column would make every existing query ambiguous.
     70  db.exec(`CREATE TABLE IF NOT EXISTS ap_pending_outgoing_follows (
     71    id TEXT PRIMARY KEY,
     72    ward_slug TEXT NOT NULL,
     73    target_uri TEXT NOT NULL,
     74    target_inbox TEXT,
     75    target_name TEXT,
     76    target_handle TEXT,
     77    target_icon TEXT,
     78    quorum TEXT DEFAULT 'any',
     79    status TEXT DEFAULT 'pending',
     80    created_at TEXT DEFAULT CURRENT_TIMESTAMP
     81  )`);
     82  db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_ap_outgoing_follows_target
     83           ON ap_pending_outgoing_follows(ward_slug, target_uri)`);
     84  db.exec(`CREATE TABLE IF NOT EXISTS ap_outgoing_follow_approvals (
    6085    follow_id TEXT NOT NULL,
    6186    guardian_uri TEXT NOT NULL,
     
    693718  ensureColumn('ap_outbox', 'away_until', 'INTEGER'); // FEP-633c 3.6.1 shaer:away + endTime (epoch ms)
    694719  ensureColumn('ap_gated_offers', 'proposer', 'TEXT'); // who proposed (5.6): the settle-answer goes back to them
     720  // Did a guardian actually say yes to this follower? That is what makes the
     721  // mutual shortcut sound: a ward may follow back anyone its guardians already
     722  // admitted, without asking the same question twice. Only follows that came
     723  // through the §5.3 gate carry the mark; a free actor's followers never faced
     724  // one. Everyone already following when this column arrives is grandfathered
     725  // in (Barts besluit, 3-8): the rule is exact from that moment forward rather
     726  // than retroactively suspicious of relationships that already exist.
     727  {
     728    const had = db.prepare("SELECT COUNT(*) AS n FROM pragma_table_info('ap_followers') WHERE name = 'gate_approved'").get();
     729    ensureColumn('ap_followers', 'gate_approved', 'INTEGER DEFAULT 0');
     730    if (!had || !had.n) {
     731      try { db.prepare('UPDATE ap_followers SET gate_approved = 1').run(); } catch { /* table still empty on a fresh init */ }
     732    }
     733  }
    695734  ensureColumn('posts', 'c2s_attachments', 'TEXT'); // media a C2S Note carried (JSON [{url,mediaType,name}]); buildNote federates them
    696735  // 30-7: C2S posts briefly got their content media copied onto the cover,
Note: See TracChangeset for help on using the changeset viewer.