Changeset 5bf63b7 in Klonkt for src/routes


Ignore:
Timestamp:
06/24/2026 10:35:48 AM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
dd1028a
Parents:
6bd25d1
Message:

feat(activitypub): phase 1b — Follow/Accept, HTTP Signatures, delivery

Inbox handles Follow (store follower + send signed Accept) and Undo Follow;
incoming requests are signature-verified (best-effort). Outgoing POSTs to inboxes
are signed (draft-cavage RSA-SHA256). New published public posts are delivered as
Create(Note) to followers' inboxes. Makes a Klonkt actor truly followable from
Mastodon; live interop test pending with Bart.

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

Location:
src/routes
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/routes/activitypub.js

    r6bd25d1 r5bf63b7  
    8080});
    8181
    82 // ── Inbox (Phase 1 stub: accept; Follow/Accept + sig verify next step) ──
    83 const apJson = express.json({ type: ['application/activity+json', 'application/ld+json', 'application/json'], limit: '1mb' });
    84 router.post(['/ap/users/:slug/inbox', '/ap/inbox'], apJson, (req, res) => {
    85   try { console.log('[AP inbox]', (req.body && req.body.type) || 'unknown', '→', req.params.slug || 'shared'); } catch { /* ignore */ }
    86   res.status(202).end();
     82// ── Inbox — Follow→Accept, Undo Follow (best-effort signature verify) ──
     83const apJson = express.json({
     84  type: ['application/activity+json', 'application/ld+json', 'application/json'],
     85  limit: '1mb',
     86  verify: (req, _res, buf) => { req.rawBody = buf; }, // raw body for digest verification
     87});
     88router.post(['/ap/users/:slug/inbox', '/ap/inbox'], apJson, async (req, res) => {
     89  try { return res.status(await AP.handleInbox(req, req.params.slug || null) || 202).end(); }
     90  catch (e) { console.warn('[AP inbox] error:', e.message); return res.status(202).end(); }
    8791});
    8892
  • src/routes/posts.js

    r6bd25d1 r5bf63b7  
    1919import { audioUrl } from '../services/AudioStreamService.js';
    2020import { toWebp } from '../services/ImageWebpService.js';
     21import ActivityPubService from '../services/ActivityPubService.js';
    2122
    2223const __dirname = path.dirname(fileURLToPath(import.meta.url));
     
    230231      ).run(HtmlSanitizerService.toPlainText(cleanContent), title || '', req.session.user.username, postId);
    231232    } catch (e) { /* FTS index issues are non-fatal */ }
     233
     234    // ActivityPub: federate a freshly published public post to followers.
     235    if (!fanOnly) {
     236      ActivityPubService.deliverCreate(site, {
     237        id: postId, slug: finalSlug, title: title || finalSlug,
     238        content: cleanContent, published_at: publishedAt, created_at: now,
     239      }).catch(() => { /* best-effort */ });
     240    }
    232241  }
    233242
Note: See TracChangeset for help on using the changeset viewer.