Changeset b300682 in Klonkt


Ignore:
Timestamp:
06/16/2026 12:38:38 AM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
25d4041
Parents:
af5407f
Message:

Circles v1 — foundation: migration + signed own publication

Steps 1+2 of the Circles federation (3rd tenancy mode alongside solo/hub):

  • DB: circle_links / remote_actors / remote_posts (idempotent in initializeDatabase)
  • SettingsService: tenancy now accepts 'circle'
  • CircleFederation.js: per-instance Ed25519 keypair (app_settings) + actor/outbox builders + sign/verify (SPKI-DER pubkey, signature over raw body)
  • routes/federation.js: GET /.klonkt/actor.json + signed /.klonkt/outbox.json, mounted in server.js before resolveSite
  • docs/cirkels-v1-spec.md: full v1 spec

Still to do: CircleService.sync (pull+verify), Admin UI, /cirkel feed, hardening.
Ed25519 sign/verify round-trip verified in isolation; syntax clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@…>

Files:
3 added
3 edited

Legend:

Unmodified
Added
Removed
  • src/config/database.js

    raf5407f rb300682  
    106106  `);
    107107  db.prepare("INSERT OR IGNORE INTO app_settings (key, value) VALUES ('tenancy', 'solo')").run();
     108
     109  // ── Cirkels (federatie) ─────────────────────────────────────
     110  // Decentrale, asymmetrische verbindingen tussen solo-instances.
     111  db.exec(`
     112    CREATE TABLE IF NOT EXISTS circle_links (
     113      id TEXT PRIMARY KEY,
     114      local_site_id TEXT NOT NULL,
     115      remote_url TEXT NOT NULL,
     116      remote_actor_id TEXT,
     117      label TEXT,
     118      status TEXT DEFAULT 'active',
     119      added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
     120      last_synced DATETIME,
     121      last_error TEXT,
     122      UNIQUE(local_site_id, remote_url),
     123      FOREIGN KEY (local_site_id) REFERENCES sites(id)
     124    );
     125    CREATE TABLE IF NOT EXISTS remote_actors (
     126      id TEXT PRIMARY KEY,
     127      url TEXT UNIQUE NOT NULL,
     128      name TEXT,
     129      summary TEXT,
     130      avatar TEXT,
     131      public_key TEXT NOT NULL,
     132      fetched_at DATETIME DEFAULT CURRENT_TIMESTAMP
     133    );
     134    CREATE TABLE IF NOT EXISTS remote_posts (
     135      id TEXT PRIMARY KEY,
     136      actor_id TEXT NOT NULL,
     137      published DATETIME,
     138      title TEXT,
     139      summary TEXT,
     140      url TEXT,
     141      media_json TEXT,
     142      raw_json TEXT,
     143      fetched_at DATETIME DEFAULT CURRENT_TIMESTAMP,
     144      FOREIGN KEY (actor_id) REFERENCES remote_actors(id)
     145    );
     146  `);
    108147}
    109148
  • src/server.js

    raf5407f rb300682  
    4343import artistsRoutes from './routes/artists.js';
    4444import postsRoutes from './routes/posts.js';
     45import federationRoutes from './routes/federation.js';
    4546
    4647if (!process.env.SESSION_SECRET) {
     
    166167app.locals.prutter = prutter;
    167168
     169// Cirkels-federatie: publieke, site-agnostische endpoints (/.klonkt/*).
     170// Vóór resolveSite/theme — ze hebben geen site-context nodig.
     171app.use(federationRoutes);
     172
    168173app.use(resolveSite);
    169174app.use(loadAudioTracks);
  • src/services/SettingsService.js

    raf5407f rb300682  
    3535
    3636export function getTenancy() {
    37   return getSetting('tenancy', 'solo') === 'hub' ? 'hub' : 'solo';
     37  const v = getSetting('tenancy', 'solo');
     38  return v === 'hub' ? 'hub' : v === 'circle' ? 'circle' : 'solo';
    3839}
    3940
    4041export function setTenancy(mode) {
    41   setSetting('tenancy', mode === 'hub' ? 'hub' : 'solo');
     42  const m = (mode === 'hub' || mode === 'circle') ? mode : 'solo';
     43  setSetting('tenancy', m);
    4244}
Note: See TracChangeset for help on using the changeset viewer.