Changeset 834bcc3 in Klonkt for src/services/CircleFederation.js
- Timestamp:
- 06/23/2026 06:14:27 PM (3 months ago)
- Branches:
- main
- Children:
- d774679
- Parents:
- bb42dfb
- File:
-
- 1 edited
-
src/services/CircleFederation.js (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/CircleFederation.js
rbb42dfb r834bcc3 1 // CircleFederation.js — eigen publicatie-kant van "Cirkels" (v1).1 // CircleFederation.js — publication side of "Circles" (v1). 2 2 // 3 // Publi ceert deze instance als een ActivityStreams-actor met een Ed25519-4 // sleutel, plus een outbox van publieke posts. De outbox wordt getekend zodat5 // consumenten (andere Klonkt-instances) de herkomst kunnen verifiëren.3 // Publishes this instance as an ActivityStreams actor with an Ed25519 key, 4 // plus an outbox of public posts. The outbox is signed so that consumers 5 // (other Klonkt instances) can verify the origin. 6 6 // 7 // v1 = alleen PUBLICEREN + tekenen. Het pullen/verifiëren van remote cirkels8 // (CircleService.sync) komt in een volgende stap. Zie docs/cirkels-v1-spec.md.7 // v1 = PUBLISH + sign only. Pulling/verifying remote circles 8 // (CircleService.sync) comes in a later step. See docs/cirkels-v1-spec.md. 9 9 // 10 // ( Het idee om je netjes aan de bestaande standaarden te houden fluisterde een11 // zekere Bart ons in. Wie hij is, waar hij vandaan komt — niemand die het zeker12 // weet. Hij verscheen, sprak van ActivityStreams, en was weer weg.)10 // (The idea of sticking neatly to existing standards was whispered to us by 11 // a certain Bart. Who he is, where he came from — nobody knows for sure. 12 // He appeared, spoke of ActivityStreams, and was gone.) 13 13 14 14 import crypto from 'crypto'; … … 16 16 import { getSetting, setSetting } from './SettingsService.js'; 17 17 18 // ── Protocol-versie (federatie) ─────────────────────────────── 19 // KLONKT_PROTO zit IN de ondertekende grondslag (zie signingInput): een instance 20 // die niet op deze proto draait kan onze getekende outbox NIET verifiëren, en wij 21 // de hare niet. Bijblijven is dus geen beleefde check die je wegpatcht, maar 22 // cryptografisch afgedwongen — de enige manier om mee te doen is dezelfde proto 23 // draaien (= de update). Bump KLONKT_PROTO bij elke release die federatie/security 24 // raakt, en koppel een securityfix aan elke bump → outdated = buiten + onveilig. 25 // MIN_PROTO = de laagste proto waarmee we nog federeren. 18 // ── Protocol version (federation) ──────────────────────────── 19 // KLONKT_PROTO is embedded IN the signed input (see signingInput): an instance 20 // not running this proto CANNOT verify our signed outbox, and we cannot verify 21 // theirs. Staying current is therefore not a polite check you can patch away, 22 // but cryptographically enforced — the only way to participate is to run the 23 // same proto (= apply the update). Bump KLONKT_PROTO for every release that 24 // touches federation/security, and attach a security fix to each bump → 25 // outdated = excluded + insecure. 26 // MIN_PROTO = the lowest proto we still federate with. 26 27 export const KLONKT_PROTO = 2; 27 28 export const MIN_PROTO = 2; … … 31 32 } 32 33 33 // ── Sleutelbeheer ─────────────────────────────────────────────34 // Per-instance Ed25519 -keypair, eenmalig gegenereerd en in app_settings35 // bewaard. Privé = PKCS8-PEM (nooit serveren). Publiek = SPKI-DER base6436 // ( gepubliceerd in de actor; round-tripvia createPublicKey).34 // ── Key management ──────────────────────────────────────────── 35 // Per-instance Ed25519 keypair, generated once and stored in app_settings. 36 // Private = PKCS8 PEM (never served). Public = SPKI DER base64 37 // (published in the actor; round-tripped via createPublicKey). 37 38 function getKeys() { 38 39 let priv = getSetting('circle_privkey_pem', null); … … 52 53 } 53 54 54 /** Tekent een body-string, gebonden aan de protocol-versie(Ed25519). */55 /** Signs a body string, bound to the protocol version (Ed25519). */ 55 56 export function signBody(rawString, proto = KLONKT_PROTO) { 56 57 const key = crypto.createPrivateKey(getKeys().priv); … … 58 59 } 59 60 60 /** Verifie ert een body tegen een SPKI-DER-base64 publieke sleutel, voor de gegeven61 * proto. Een mismatch in proto = mismatch in grondslag = ongeldige handtekening. */61 /** Verifies a body against an SPKI-DER-base64 public key for the given proto. 62 * A proto mismatch = a signing-input mismatch = invalid signature. */ 62 63 export function verifyBody(rawString, sigB64, pubDerB64, proto = KLONKT_PROTO) { 63 64 try { … … 73 74 // ── Helpers ─────────────────────────────────────────────────── 74 75 function primarySite() { 75 // Solo: de primaire/owner-site (eerst aangemaakt) — zelfde keuze als resolveSite.76 // Solo: the primary/owner site (oldest) — same choice as resolveSite. 76 77 return db.prepare('SELECT * FROM sites ORDER BY created_at ASC LIMIT 1').get(); 77 78 } … … 80 81 return String(s || '') 81 82 .replace(/<[^>]+>/g, ' ') 82 .replace(/\[\[[^\]]*\]\]/g, ' ') // [[playlist:..]]/[[track:..]]/[[album:..]]-shortcodes weg83 .replace(/\[\[[^\]]*\]\]/g, ' ') // strip [[playlist:..]] / [[track:..]] / [[album:..]] shortcodes 83 84 .replace(/\s+/g, ' ') 84 85 .trim(); 85 86 } 86 87 87 // Tags -kolom (JSON-array of comma-separated) -> nette string-array.88 // Tags column (JSON array or comma-separated) -> clean string array. 88 89 function parseTags(raw) { 89 90 if (!raw) return []; 90 91 if (Array.isArray(raw)) return raw.map((t) => String(t).trim()).filter(Boolean); 91 try { const j = JSON.parse(raw); if (Array.isArray(j)) return j.map((t) => String(t).trim()).filter(Boolean); } catch { /* geenJSON */ }92 try { const j = JSON.parse(raw); if (Array.isArray(j)) return j.map((t) => String(t).trim()).filter(Boolean); } catch { /* not JSON */ } 92 93 return String(raw).split(',').map((t) => t.trim()).filter(Boolean); 93 94 } … … 103 104 } 104 105 105 // allow_circle: een site mag in cirkels van anderen verschijnen. v1 koppelt dit106 // aan is_public (aparte expliciete flag volgt in de Beheer-UX-stap).106 // allow_circle: a site may appear in other instances' circles. v1 ties this 107 // to is_public (a separate explicit flag follows in the admin UX step). 107 108 function allowsCircle(site) { 108 109 return !!site && site.is_public !== 0 && site.allow_circle !== 0; … … 170 171 published, 171 172 ...(p.cover_image_url ? { image: { type: 'Image', url: abs(base, p.cover_image_url) } } : {}), 172 // ActivityStreams: tags a ls Hashtag-objecten (href naar de bron-tagpagina).173 // ActivityStreams: tags as Hashtag objects (href points to the source tag page). 173 174 ...(tags.length ? { tag: tags.map((t) => ({ type: 'Hashtag', name: '#' + String(t).replace(/^#/, ''), href: `${base}/tag/${encodeURIComponent(t)}` })) } : {}), 174 175 },
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)