| 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":"closed","priority":1,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-06-21T20:29:43Z","created_by":"roboburr","updated_at":"2026-07-01T22:02:25Z","closed_at":"2026-07-01T22:02:25Z","close_reason":"Done: public GitHub repo live 2026-06-23 (roboburr/klonkt, AGPL), install.sh + klonkt.com/install, stable release channel, Docker image build+run verified 2026-07-01. Leftovers (optional KLONKT_GIT_DIR repoint, one-click PaaS templates) intentionally deferred.","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":"closed","priority":1,"issue_type":"bug","owner":"robin@roboburr.com","created_at":"2026-05-07T22:38:42Z","created_by":"Robin","updated_at":"2026-07-02T13:39:58Z","closed_at":"2026-07-02T13:39:58Z","close_reason":"Fixed on main (421046c). Root cause: every track change did pause + src-swap + load + play, which browsers treat as a NEW playback session - background policy pauses those, and repeated element resets corrupted state. The player now appends the whole queue into one MediaSource (audio/mpeg SourceBuffer), so auto-advance is a continuous timeline with zero pause/play events. Verified in-browser: boundary cross with no play/pause pair, per-track UI correct, gapless wrap. Blob fallback remains for iOS.","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-m43","title":"Music federation phase 2: standard Audio/Playlist objects","description":"Federate hosted music as the de-facto fediverse audio objects (Funkwhale Audio/Library style) so audio platforms can find tracks/playlists by URL — a Note is invisible to their search. Also answers the playlist-search report. Phase 3.","status":"open","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:03:00Z","created_by":"roboburr","updated_at":"2026-07-01T22:03:00Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 5 | {"_type":"issue","id":"prutfolio-src-u2e","title":"Paid posts: per-site Patreon + passkey unlock (design first)","description":"Premium feature (like downloads). Site owner configures their own Patreon app + amount in admin; readers unlock a paid post with a passkey (no account, no cookies, token in DB not traceable to a person). One-time Patreon link + refresh on de-auth. Klonkt Premium unlock stays as-is. Needs a design round before building. Also evaluate payment alternatives (OpenCollective, Open Payments). Phase 3.","status":"open","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:02:59Z","created_by":"roboburr","updated_at":"2026-07-01T22:02:59Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 6 | {"_type":"issue","id":"prutfolio-src-h1q","title":"PWA: web push notifications","description":"Push messages for the installed PWA (per-instance VAPID keys, subscribe UI, push on new post/notification). PWA id is already site-slug specific. Phase 2.","status":"open","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:02:58Z","created_by":"roboburr","updated_at":"2026-07-01T22:02:58Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 7 | {"_type":"issue","id":"prutfolio-src-k1q","title":"News/Circle: some remote video embeds do not render","description":"A Loops video in the News feed did not load like other providers; the Circle also lacks a video thumbnail for such posts. Reproduce first (regression or never supported?), then fix. Phase 1.","status":"closed","priority":2,"issue_type":"bug","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:02:57Z","created_by":"roboburr","updated_at":"2026-07-01T22:38:47Z","closed_at":"2026-07-01T22:38:47Z","close_reason":"Diagnosed + fixed on main. Root cause: the image proxy refused video content, so remote-video posters 302d to the raw mp4 and tiles/feed showed a black box until play. Proxy now extracts a poster frame via a bounded 4MB range fetch; News feed videos carry a poster. Verified in-browser with the actual Loops video. Note: clips over 30s keeping controls instead of autoplaying is by design.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 8 | {"_type":"issue","id":"prutfolio-src-ovj","title":"Fediverse: verify outgoing reply mentions against remote receivers","description":"Reported case: a Klonkt reply to a remote (mastodon.xyz) post where the mention handling looked off on the receiving side. First verify against current main (the reporting instance may run older code), diff the note JSON against the expected Mention shape, then fix if real. Phase 1.","status":"closed","priority":2,"issue_type":"bug","assignee":"roboburr","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:02:55Z","created_by":"roboburr","updated_at":"2026-07-01T22:22:19Z","started_at":"2026-07-01T22:16:41Z","closed_at":"2026-07-01T22:22:19Z","close_reason":"Root cause found + fixed on main: the mention/hashtag/URL prefix regexes required whitespace/\u003e before the token, so a bracketed (@user@host federated as plain text and the target was never notified (the reported reply showed exactly this). Opening brackets are now valid prefixes; quotes deliberately excluded (attribute safety). Verified live: bracketed mention beta-\u003edemocirkel resolved, linked and notified.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 9 | {"_type":"issue","id":"prutfolio-src-xgg","title":"Fediverse: notification when mentioned in a non-reply post","description":"An inbound Create whose Mention tag targets one of our actors, but which is not a reply to our content, is currently ignored. Store + show it in the fediverse notifications. Phase 1.","status":"closed","priority":2,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:02:53Z","created_by":"roboburr","updated_at":"2026-07-01T22:15:26Z","closed_at":"2026-07-01T22:15:26Z","close_reason":"Built on main: inbound Create with a Mention tag targeting one of our actors (non-reply) is stored (ap_mentions) and shown in fediverse notifications with a link to the original. Verified live Klonkt-to-Klonkt (democirkel -\u003e beta).","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 10 | {"_type":"issue","id":"prutfolio-src-412","title":"Fediverse: auto-link plain URLs in outgoing replies and posts","description":"A bare URL typed in a reply (and in post content) federates as plain text; it should become a clickable link in the outgoing note. Phase 1 bug sprint.","status":"closed","priority":2,"issue_type":"bug","assignee":"roboburr","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:02:52Z","created_by":"roboburr","updated_at":"2026-07-01T22:07:43Z","started_at":"2026-07-01T22:03:23Z","closed_at":"2026-07-01T22:07:43Z","close_reason":"Fixed on main: bare URLs in outgoing posts + replies now federate as clickable links (anchor-aware, punctuation-safe). Verified live on beta.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 11 | {"_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}
|
|---|
| 12 | {"_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}
|
|---|
| 13 | {"_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}
|
|---|
| 14 | {"_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}
|
|---|
| 15 | {"_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}
|
|---|
| 16 | {"_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}
|
|---|
| 17 | {"_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}
|
|---|
| 18 | {"_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}
|
|---|
| 19 | {"_type":"issue","id":"prutfolio-src-2t1","title":"Agenda: richer event types","description":"Extend shows/agenda with event types (show large/small, signing, workshop, generic) and fields like tickets/participation, location, theme, notes. Possibly external calendar integrations later.","status":"open","priority":3,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:03:02Z","created_by":"roboburr","updated_at":"2026-07-01T22:03:02Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 20 | {"_type":"issue","id":"prutfolio-src-wsp","title":"Header: optional store/merch link","description":"A per-site setting for a generic store link in the header (external shop). Small; fits the merch strategy.","status":"open","priority":3,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:03:01Z","created_by":"roboburr","updated_at":"2026-07-01T22:03:01Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 21 | {"_type":"issue","id":"prutfolio-src-krm","title":"SEO: og:image light/dark override setting","description":"1.2.0 picks the OG card variant from the site default theme; add an explicit light/dark override in Admin -\u003e SEO. Phase 1.","status":"closed","priority":3,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T22:02:56Z","created_by":"roboburr","updated_at":"2026-07-01T22:28:28Z","closed_at":"2026-07-01T22:28:28Z","close_reason":"Built on main: Admin -\u003e SEO has an og:image variant override (Auto/Light/Dark); OgImageService honours it over the site theme. Verified: saving Light regenerates a different card.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 22 | {"_type":"issue","id":"prutfolio-src-vyr","title":"Polls phase 2: create your own polls (outbound Question)","description":"Phase 1 (inbound: show + vote on remote polls in the News feed) shipped. Phase 2 = author your own polls:\n- Post editor: a poll post type (2-8 options + a duration / endTime, single vs multiple choice).\n- DB: poll_options + poll_votes (one vote per remote actor enforced).\n- buildNote -\u003e emit a Question (oneOf/anyOf of Note options each with replies.totalItems, endTime, votersCount) instead of a Note; declare votersCount/toot terms in AP_CONTEXT (AS2-validity test).\n- Inbound votes: a Create(Note) with name + inReplyTo our Question -\u003e tally.\n- Update(Question) delivery to refresh counts on remote caches; enforce endTime (closed); show results on-site.\nReuses the delivery/queue + interaction machinery. No anti-steal tension (text/interaction, no audio file).","status":"closed","priority":3,"issue_type":"feature","owner":"roboburr@gmail.com","created_at":"2026-07-01T07:43:41Z","created_by":"roboburr","updated_at":"2026-07-01T22:02:23Z","closed_at":"2026-07-01T22:02:23Z","close_reason":"Built 2026-07-01: posts can carry a poll that federates as an AS2 Question (oneOf/anyOf, endTime, votersCount), inbound ballots tallied + Update(Question) pushed, scheduler closes on endTime, voting also from the interact page. On main.","labels":["fediverse","polls"],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 23 | {"_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}
|
|---|
| 24 | {"_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":"closed","priority":3,"issue_type":"task","assignee":"roboburr","owner":"roboburr@gmail.com","created_at":"2026-06-25T09:53:21Z","created_by":"roboburr","updated_at":"2026-07-01T00:57:35Z","started_at":"2026-07-01T00:55:28Z","closed_at":"2026-07-01T00:57:35Z","close_reason":"Fixed: resyncFeaturedPins() is now a per-site serializing+coalescing wrapper around doResyncFeaturedPins(); concurrent /save calls can no longer interleave the Remove/Add sequences (which scrambled StatusPin order), and rapid saves coalesce into one rerun instead of N.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 25 | {"_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":"closed","priority":3,"issue_type":"task","assignee":"roboburr","owner":"roboburr@gmail.com","created_at":"2026-06-25T09:53:19Z","created_by":"roboburr","updated_at":"2026-07-01T01:04:20Z","started_at":"2026-07-01T01:01:49Z","closed_at":"2026-07-01T01:04:20Z","close_reason":"Fixed: Announce handler drops a boost whose original author is blocked (isBlockedAny(origUri)), and purgeBlocked matches a blocked domain by exact parsed host instead of a URL LIKE. npm test 15/15 on VPS.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 26 | {"_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":"closed","priority":3,"issue_type":"task","assignee":"roboburr","owner":"roboburr@gmail.com","created_at":"2026-06-25T09:53:18Z","created_by":"roboburr","updated_at":"2026-07-01T01:04:18Z","started_at":"2026-07-01T01:01:47Z","closed_at":"2026-07-01T01:04:18Z","close_reason":"Fixed: verifyRequest now requires the Date header to be signed + recent (default +/-60min, env AP_SIG_MAX_SKEW_MIN) and requires a signed Digest on any body-bearing request — closes indefinite-replay and body-swap. npm test 15/15 on VPS.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|---|
| 27 | {"_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}
|
|---|