| 1 | {"_type":"issue","id":"prutfolio-src-aaj","title":"GitHub-distributie: canonieke repo naar GitHub + self-host release (Docker/zip)","description":"Volgorde (Robin 2026-06-21): EERST GitHub-support + canonieke code van de VPS af, DAARNA pas Docker/zip-distributie.\n\nDOEL: muzikanten kunnen Klonkt zelf hosten (WordPress-achtig), updates via GitHub i.p.v. Robins VPS.\n\nSTAP 1 — repo naar GitHub (van VPS af):\n- Canonieke bare repo staat nu op de VPS (~/git-repos/prutfolio.git). Verplaatsen/spiegelen naar een GitHub-repo (Robin is niet GitHub-vaardig -\u003e Claude zet repo+releases op).\n- KLONKT_GIT_DIR (update-functie Beheer-\u003eUpdates / klonkt-self-update.sh) ompunten naar GitHub i.p.v. lokale bare repo.\n- Robins eigen vloot (demo's/jeffree/jasonhacky) blijft werken; deploy-loop evt. naar git pull van GitHub.\n- Secrets/branding check voor publiek maken (geen .env, geen storage, geen interne notities in de repo).\n\nSTAP 2 — self-host distributie (later, was al uitgewerkt; recon gedaan):\n- Dockerfile (multi-stage node:20-bookworm) + docker-compose + .dockerignore = de 'WordPress-zip' (1 commando).\n- Quickstart-README (Docker + kale VPS met Node+pm2+Caddy).\n- Evt. one-click templates (Railway/PikaPods).\n\nSELF-HOST READINESS (geverifieerd 2026-06-21):\n- Node-app (\u003e=20), GEEN PHP -\u003e draait op VPS/Docker/PaaS, niet op shared PHP-hosting.\n- ffmpeg = MEEGEBUNDELD via ffmpeg-static (geen system-install).\n- cwebp = OPTIONEEL (ImageWebpService valt terug op origineel als 'cwebp' ontbreekt).\n- better-sqlite3 = native (npm prebuilt binary; in Docker bookworm-base voor glibc).\n- DB migreert zichzelf bij boot (57x CREATE TABLE IF NOT EXISTS/ensureColumn) -\u003e geen los migratiescript.\n- .env.example + README.md bestaan al. Premium staat default UIT (KLONKT_PREMIUM_ENABLED) -\u003e self-hoster krijgt volledige gratis app.\n- Eerste /auth/register = god, daarna dicht.\n- server.js: PORT env, listen(PORT). LET OP: check of 'ie op 0.0.0.0 bindt (nodig in Docker) -- nog verifieren.\n- Docker 29 lokaal beschikbaar voor testen.\n\nGATING/PLAN-context: zie memory project_klonkt_monetization (self-host + Patreon $10 lifetime; security/updates gratis voor iedereen, 3 modules achter Patreon).","status":"open","priority":1,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-06-21T20:29:43Z","created_by":"roboburr","updated_at":"2026-06-21T20:29:43Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 2 | {"_type":"issue","id":"prutfolio-src-3fl","title":"Audio: continuous playback breekt na 3-4 tracks (browser-policy / state corruption)","description":"Na fix v1 (next() unconditional play) werkt 1-\u003e2 en 2-\u003e3, maar stopt bij 3-\u003e4. Verdacht: iOS Safari autoplay-policy revoke OF audio element state-corruption na meerdere src changes. Fix: pause()+load()+play() pattern in loadTrack, error-event auto-skip naar volgende, NotAllowedError visuele hint (pulse op play-btn).","status":"open","priority":1,"issue_type":"bug","owner":"robin@roboburr.com","created_at":"2026-05-07T22:38:42Z","created_by":"Robin","updated_at":"2026-05-07T22:38:42Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 3 | {"_type":"issue","id":"prutfolio-src-0tg","title":"Audio: continuous playback fix — next() altijd play() ipv conditional","description":"Bug: next() checkte 'if (isPlaying) play()' maar isPlaying was al false door de pause-event die de browser firet vóór ended. Resultaat: track stopt na 1 keer. Fix: drop de conditional, roep altijd play() aan. Zelfde voor prev(). Bestand: src/assets/js/audio-player.js regels 221-230.","status":"closed","priority":1,"issue_type":"bug","assignee":"Robin","owner":"robin@roboburr.com","created_at":"2026-05-06T22:18:40Z","created_by":"Robin","updated_at":"2026-05-06T22:28:20Z","started_at":"2026-05-06T22:26:37Z","closed_at":"2026-05-06T22:28:20Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 4 | {"_type":"issue","id":"prutfolio-src-byh","title":"Retry Follow delivery so follows don't get stuck on 'pending'","description":"followActor() inserts the ap_following row as 'pending' and then delivers the signed Follow once, with no retry (unlike deliverCreate, which uses the ap_delivery retry queue). A single transient delivery failure therefore leaves the follow permanently 'pending' — the remote never received it, so no Accept ever comes back.\n\nBuild later:\n- Route the Follow delivery through the existing ap_delivery retry queue (backoff), OR\n- Add a periodic re-send / re-poll for follows still 'pending' after N minutes.\n\nNote: a legitimately locked (manual-approval) account also shows 'pending' until the person approves — that is NOT this bug and must stay pending. Only retry the *delivery*, don't auto-flip status.\n\nWorkaround today: unfollow + follow again sends a fresh Follow.","status":"closed","priority":2,"issue_type":"feature","assignee":"roboburr","owner":"roboburr@gmail.com","created_at":"2026-06-26T13:36:15Z","created_by":"roboburr","updated_at":"2026-07-01T00:45:55Z","started_at":"2026-07-01T00:43:31Z","closed_at":"2026-07-01T00:45:55Z","close_reason":"Fixed: followActor now delivers the Follow via deliverWithRetry() (immediate attempt + ap_delivery backoff queue), so a failed first delivery no longer leaves the follow stuck on 'pending'.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 5 | {"_type":"issue","id":"prutfolio-src-5gl","title":"Self-heal van de fediverse-cache (ap_timeline) bij ingrijpende updates","description":"Robin (2026-06-26): een self-heal als vaste functie, die getriggerd wordt bij ingrijpende updates -- zodat de fediverse-cache zichzelf repareert na een release waar posts in een flux-window coverless/stale bezorgd kunnen zijn (zoals de Cirkels-op-AP-migratie: gelijktijdige vloot-update + new-follower-backfill -\u003e sommige Creates kwamen zonder cover binnen, en de timeline is een snapshot-op-bezorging die nooit auto-bijwerkt).\n\nAanpak (generaliseer het eenmalige scripts/backfill-cirkel-covers.mjs tot een ingebouwde functie):\n- selfHealTimeline() in ActivityPubService: her-fetch ap_timeline-notes (AP) en werk content + media bij (covers/edits); 404/Tombstone -\u003e rij verwijderen.\n- Versie-gate: constante SELFHEAL_VERSION + app_setting selfheal_done_v\u003cN\u003e. Draait EENMAAL bij boot wanneer N wijzigt. Je bumpt N bij een ingrijpende release.\n- Net als autoMigrateCircles: async, non-blocking, best-effort, GEBOUND (bv. laatste ~50 of rate-limited) zodat het de boot niet belast en geen fetch-storm geeft.\n- Aangeroepen in server.js naast startScheduler/startDeliveryWorker/autoMigrateCircles.\n\nComplementair aan prutfolio-src-ymj (live inkomende Update afhandelen = continue self-heal); dit is het catch-up-vangnet voor wat live gemist is. Hergebruik de coverFrom/fetchNote-helpers uit het backfill-script.","status":"closed","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-06-26T10:00:19Z","created_by":"roboburr","updated_at":"2026-06-26T10:04:54Z","closed_at":"2026-06-26T10:04:54Z","close_reason":"Gebouwd: selfHealTimeline() in ActivityPubService, gegate op selfheal_version vs SELFHEAL_VERSION (draait 1x per bump), aangeroepen in server.js bij boot. Geverifieerd op democirkel (7/20 ververst, 2e reload skipt).","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 6 | {"_type":"issue","id":"prutfolio-src-8z2","title":"Cirkel-feed: boosts van cirkel-vrienden tonen ZONDER ze opnieuw auto-te-boosten (anti-feedback-loop)","description":"Toekomstige feature: ook boosts (Announce) van accounts in je cirkel tonen in de feed/Cirkel.\n\nKRITIEKE GUARD (Robin 2026-06-26): auto-boost mag NOOIT een binnengekomen boost (Announce) opnieuw boosten -- alleen ORIGINELE posts (Create, geauteurd door het gevolgde account zelf). Anders feedback-loop: A boost X -\u003e B auto-boost A-z\\\"n-boost -\u003e C auto-boost B-z\\\"n-boost -\u003e ...\n\nHuidige status: VEILIG. De auto-boost-hook in handleInbox zit alleen op de Create-tak (top-level post van een gevolgd account), niet op Announce. Announces worden nu niet in ap_timeline opgeslagen, dus boosts worden nog niet getoond.\n\nTe doen zodra we boosts tonen:\n- Inkomende Announce van een gevolgd account opslaan/tonen in de feed (los van Create).\n- Auto-boost EXPLICIET beperken tot Create-van-de-auteur; nooit een Announce her-announcen.\n- Evt. extra dedup: niet auto-boosten wat je zelf al geboost hebt, en geen Announce van een Announce.","status":"closed","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-06-26T09:39:44Z","created_by":"roboburr","updated_at":"2026-06-26T12:01:22Z","closed_at":"2026-06-26T12:01:22Z","close_reason":"Opgelost door het nieuwe model: auto-boost is volledig verwijderd (uitlichten = lokaal), dus geen auto-re-Announce → loop-vrij. Boosts tonen in cirkel = lokaal (se8), geen her-boost.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 7 | {"_type":"issue","id":"prutfolio-src-ymj","title":"Inkomende Update(Note) genegeerd -\u003e bewerkte (geboooste) post blijft origineel op Klonkt","description":"Bart: een post die hij geboost heeft en daarna op Mastodon BEWERKT, blijft op de Klonkt-site de originele versie tonen. Klonkt neemt de edit niet over.\n\nLog: [AP] inbox Update -\u003e shared (ignored)\n\nOorzaak: handleInbox (ActivityPubService) negeert inkomende Update-activiteiten. Een post-edit komt binnen als Update(Note) -\u003e de gecachte kopie wordt niet bijgewerkt.\n\nTe doen:\n- handleInbox: een Update met object.type Note/Article afhandelen i.p.v. negeren -\u003e de opgeslagen kopie updaten waar de note voorkomt: ap_timeline (content / media_json / url voor die note-id) EN ap_interactions (reply-content) als het een eerder opgeslagen reply/post is.\n- Scope op de ondertekenaar: alleen de auteur mag z\\\"n eigen note updaten (signer == object author / attributedTo).\n- updated-timestamp respecteren (alleen toepassen als nieuwer).\n\nHangt samen met prutfolio-src-b12 (quote-posts): die negeerde OOK een inkomende Update. Beide vragen om nette Update-afhandeling in handleInbox -- mogelijk samen oppakken.","status":"closed","priority":2,"issue_type":"bug","owner":"roboburr@gmail.com","created_at":"2026-06-26T09:23:48Z","created_by":"roboburr","updated_at":"2026-07-01T00:40:07Z","closed_at":"2026-07-01T00:40:07Z","close_reason":"Already implemented: inbound Update(Note/Article) handler in ActivityPubService.js handleInbox (commit bea59a1), scope-guarded on author_uri; refreshes ap_timeline content/media/nsfw/cw + cached replies.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 8 | {"_type":"issue","id":"prutfolio-src-b12","title":"Quote-posts (fediverse): zelf kunnen quoten + inkomende quote tonen/notificeren","description":"Robin wilde een post quoten vanuit Klonkt, maar dat kan nog niet (geen UI + geen uitgaande AP-quote). Andersom: WordPress quotte een Klonkt-post, maar Klonkt gaf geen melding; in de log alleen: [AP] inbox Update -\u003e shared (ignored).\n\nTe doen:\n- Inkomend: een quote herkennen in handleInbox i.p.v. de Update te negeren. Quote-velden: FEP-e232 quoteUrl / quote / _misskey_quote, of een Create/Update met een quote-link naar onze eigen post. Opslaan als interactie (ap_interactions, kind quote) en tonen op de post onder \"Vanuit de fediverse\".\n- Melding: \"X quotete je post\" in de meldingen-inbox (/meldingen).\n- Uitgaand: in de post-editor een andere post kunnen quoten, met de quote-link in de Note zodat Mastodon/WordPress het als quote rendert.\n\nLog-clue: [AP] inbox Update -\u003e shared (ignored) = de inkomende quote werd als Update genegeerd (ActivityPubService handleInbox).","status":"open","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-06-26T08:45:00Z","created_by":"roboburr","updated_at":"2026-06-26T08:45:00Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 9 | {"_type":"issue","id":"prutfolio-src-se8","title":"Fediverse: boost/repost zou ook in de Cirkel-feed moeten verschijnen","description":"Een boost (Announce) van een fediverse-post vanuit de tijdlijn federeert wel als Announce, maar verschijnt NIET in de eigen Cirkel-feed (/cirkel). Bart (2026-06-25): een boost zou ook een 'repost' in je Cirkels moeten opleveren, zodat je circle-volgers zien wat je boost. Te beslissen: datamodel (verwijzing naar de geboostte post vs. gekopieerde post), en surfacen in /cirkel + de circle/AP-outbox. NB: like + reply uitgaand werken al; dit gaat specifiek om de boost↔Cirkels-koppeling.","status":"closed","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-06-25T21:13:34Z","created_by":"roboburr","updated_at":"2026-06-26T12:01:20Z","closed_at":"2026-06-26T12:01:20Z","close_reason":"Optie B gebouwd (5045c30): geboooste posts verschijnen in de Cirkel via ap_timeline.boosted, gemengd op datum, dedup structureel (1 rij/post).","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 10 | {"_type":"issue","id":"prutfolio-src-txh","title":"Fediverse: rate-limiting on public AP endpoints","description":"Hardening follow-up. Specifics are intentionally kept in the maintainer's private notes, not in this public tracker. Priority medium/low; the critical fediverse issues (SSRF, stored-XSS, cross-actor delete) are already fixed.","status":"closed","priority":2,"issue_type":"task","owner":"roboburr@gmail.com","created_at":"2026-06-25T09:53:15Z","created_by":"roboburr","updated_at":"2026-06-25T20:45:53Z","closed_at":"2026-06-25T20:45:53Z","close_reason":"rate-limiting added to /ap/* (inbox + reads), per-IP with IPv6 /64 keying","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 11 | {"_type":"issue","id":"prutfolio-src-5qp","title":"Premium anti-bypass hardening (demotiveren) — lagen 1+2+4","description":"Doel (Robin 2026-06-21): bypassen van de premium-gate DEMOTIVEREN, niet 100% voorkomen (kan niet bij open self-host-code). Economie = $10 \u003c moeite-om-te-kraken (Duo-logica) is de hoofdlever; prijs laag houden.\n\nAKKOORD — implementeren:\n- LAAG 1 (HEB JE AL): Ed25519-getekend entitlement-token, OFFLINE geverifieerd met publieke sleutel. Niemand kan een geldig token vervalsen (geen private key). Kost 0 calls bij gebruik -\u003e schaalt gratis. Evt. pubkey bundelen i.p.v. /pubkey-fetch = nul server-contact op verify-pad.\n- LAAG 2: verspreide/inline checks i.p.v. één premiumUnlocked()-chokepoint -\u003e één 'return true'-patch unlockt niet meer alles; kraker moet ~10 plekken vinden.\n- LAAG 4: integriteits-zelfcheck (hash van gating-bestanden) + permanente 'niet-gelicenseerd'-banner/zachte degradatie bij geforceerd premium zonder geldig token.\n- (bonus) obfuscatie/minify van de gate + token-verify in de GEPUBLICEERDE build.\n- NETWERK-LEVER (al voor Cirkels): cracked instance = verkeerde proto = ongeldige handtekeningen = doet niet mee aan het echte netwerk. Geldt alleen voor netwerk-features.\n\nGESCHRAPT:\n- LAAG 5 (server-afhankelijke feature / phone-home): schaalt mee met GEBRUIK, niet met koppelingen -\u003e bij 1M users continu verkeer + 24/7-beschikbaarheid verplicht + centraal storingspunt + breekt 'no phone-home'. Niet de moeite voor een $10 massa-product.\n\nWAARSCHUWING: niet overinvesteren in DRM -\u003e raakt eerder eerlijke self-hosters (false positives/support) dan krakers, en ondermijnt de gratis-\u0026-open funnel. Volgorde: na Patreon Fase 0 + GitHub-migratie (prutfolio-src-aaj). Context: memory project_klonkt_monetization.","status":"open","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-06-21T23:01:36Z","created_by":"roboburr","updated_at":"2026-06-21T23:01:36Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 12 | {"_type":"issue","id":"prutfolio-src-7cz","title":"Discovery: how Klonkt users find each other (klonkt.com hub + discoverable + FoF)","description":"Turn klonkt.com from a static salespage into the network's front door WITHOUT\nbecoming a content host. Decided direction: Form B (a map/lens over the network),\nNOT Form A (a multi-user signup server — that takes on the host/moderation/liability\nrole we deliberately avoided; that path is managed hosting, a separate business\ndecision, not this).\n\n## What\n- A `discoverable` per-site toggle (Beheer) that sets `discoverable: true/false` on\n the site's ActivityPub actor (AS2/Mastodon-standard property, already declared in\n AP_CONTEXT). Opt-in = clean GDPR consent basis. Honored locally by other instances.\n- Phase 1: opt-in artist directory on klonkt.com — pointers only (avatar, name, genre,\n link). No accounts, no hosted content.\n- Phase 2: network feed — a Klonkt \"network\" actor that FOLLOWS the opt-in artists;\n their public posts land in its ap_timeline and render as a browsable \"what's\n happening on Klonkt\" feed. Reuses existing AP machinery (inbox, follow, timeline\n cache, Delete/Tombstone handling, offline-instance handling) — little new code.\n- Optional: an AP relay so new instances can plug into the public firehose (cold-start),\n and/or a single-user showcase Klonkt instance next to the salespage as a live demo.\n\n## Why (discovery)\nSingle-user-per-server (our model) has no local timeline, so discovery is inherently\nharder than Mastodon. Layered approach: (0) plain fediverse, (1) friend-of-a-friend\nover the Cirkel graph, (2) auto-detect fellow Klonkt instances via NodeInfo, (3)\ngenre/hashtag browse, (4) this central opt-in on-ramp for cold-start. This issue = layer 4.\n\n## Invariants\n- No auto-follow / auto-boost — discovery = suggestions the user clicks, never an\n automatic outward action (the no-auto-fediverse rule).\n- Gracefully degrade — a lonely instance still works; discovery is a plus.\n- Opt-in per site (`discoverable`), honored locally; not-discoverable != private\n (an AP actor is always followable if you know the handle).\n- Scale-safe: cache NodeInfo, use the SSRF guard (safeFetch), prune.\n\n## Maintenance / GDPR (assessed light)\n- Tech: one more pm2 service in the klonkt-stats pattern; reuses the AP stack. Low.\n- GDPR: only public, self-published, opt-in content; we are a cache/lens, not the\n origin; federated Delete/Tombstone already drops cached posts (erasure largely\n automatic). Need: a delist/opt-out path, data minimization + pruning, a\n privacy-policy paragraph (klonkt.com/privacy exists), a takedown contact\n (info@robingenis.com). Materially lighter than Form A.\n- Moderation: delist a spammy instance = drop the follow (remove a pointer), reversible.\n\n## Suggested order\n1. `discoverable` toggle (Beheer -\u003e actor). Cheap, standalone.\n2. \"Discover: neighbours of your Cirkel\" strip on /cirkel (layer 1, friend-of-a-friend)\n that honors `discoverable`. Most \"aha\" per effort, fully decentralized, no klonkt.com.\n3. klonkt.com opt-in directory (pointers-only).\n4. klonkt.com network feed (network actor follows opt-in artists) + optional relay.\n","status":"open","priority":3,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T00:34:33Z","created_by":"roboburr","updated_at":"2026-07-01T00:34:33Z","labels":["discovery","fediverse"],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 13 | {"_type":"issue","id":"prutfolio-src-4fk","title":"Fediverse: serialize pin-resync per site (parallelle /save race)","description":"Hardening-review: twee gelijktijdige /save-calls die pin-rank wijzigen draaien beide resyncFeaturedPins (Remove-all -\u003e 5s -\u003e Add) interleaved -\u003e niet-deterministische StatusPin-volgorde op Mastodon. Serialiseer per slug (in-process Map\u003cslug,Promise\u003e-chain) of snapshot de pins onder de commit-transactie.","status":"open","priority":3,"issue_type":"task","owner":"roboburr@gmail.com","created_at":"2026-06-25T09:53:21Z","created_by":"roboburr","updated_at":"2026-06-25T09:53:21Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 14 | {"_type":"issue","id":"prutfolio-src-4lq","title":"Fediverse: block-handling completeness follow-up","description":"Hardening follow-up. Specifics are intentionally kept in the maintainer's private notes, not in this public tracker. Priority medium/low; the critical fediverse issues (SSRF, stored-XSS, cross-actor delete) are already fixed.","status":"open","priority":3,"issue_type":"task","owner":"roboburr@gmail.com","created_at":"2026-06-25T09:53:19Z","created_by":"roboburr","updated_at":"2026-06-25T19:29:08Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 15 | {"_type":"issue","id":"prutfolio-src-lqx","title":"Fediverse: HTTP-signature hardening follow-up","description":"Hardening follow-up. Specifics are intentionally kept in the maintainer's private notes, not in this public tracker. Priority medium/low; the critical fediverse issues (SSRF, stored-XSS, cross-actor delete) are already fixed.","status":"open","priority":3,"issue_type":"task","owner":"roboburr@gmail.com","created_at":"2026-06-25T09:53:18Z","created_by":"roboburr","updated_at":"2026-06-25T19:29:07Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 16 | {"_type":"issue","id":"prutfolio-src-e44","title":"Asset cache-busting via commit-hash ipv handmatig bumpen","description":"Nu staan ?v=N hardcoded in shell.ejs. Beter: middleware zet res.locals.assetVersion = git-commit-sha (build-time of runtime) zodat elke deploy auto-cache-bust krijgt. Behandel alle static script/css tags in shell.ejs (htmx.min.js, audio-player.js, install-app.js).","status":"open","priority":3,"issue_type":"task","owner":"robin@roboburr.com","created_at":"2026-05-07T20:03:17Z","created_by":"Robin","updated_at":"2026-05-07T20:03:17Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|