Open source-byggblock

Vad är värt att bygga på?

Att bygga hela ingestionsmotorn från grunden är onödigt — flera mogna open source-projekt löser stora delar av problemet. Här är en djupare titt på fynden. Allt nedan kan köras på Hetzner-värden och anpassas till hur många källor som helst.


Vägval

Tre arkitektoniska riktningar

Innan vi går igenom enskilda verktyg: tre kompletta arkitekturer som täcker hela behovet. De skiljer sig i hur mycket vi köper färdigt mot bygger själva.

Hybrid — sammanställning av mogna komponenter

Lägst risk Tre tjänster Mest färdigt köpt

┌──────────────────────────────────────────────────────────────┐
│ RSS-källor               → Miniflux        (Docker)          │
│  ├── jo, imy, ad, av…       └─ REST API ─┐                   │
│  └── eeln, hrw, nvc…                     │                   │
│                                          ▼                   │
│ Anti-bot HTML            → changedetection.io (Docker)       │
│  └── amnesty, coe, fra…    └─ webhook ──┐                    │
│                                         │                    │
│ Special API:er           → egna Python-fetchers              │
│  ├── HUDOC (ECtHR)                      │                    │
│  ├── CELLAR SPARQL (CJEU)               │                    │
│  └── UHRI dump (FN-organ)               │                    │
│                                         ▼                    │
│ Email-bridge             → IMAP-skript                       │
│  └── isf, mfd, riksrev…                 │                    │
│                                         ▼                    │
│                              Panorama events-DB (SQLite)     │
│                                         │                    │
│                  ┌──────────────────────┴───────┐            │
│                  ▼                              ▼            │
│              Flask UI                       Datasette        │
│         (jurist + admin)              (data.tritagonist.se)  │
└──────────────────────────────────────────────────────────────┘
Tjänster
Miniflux + Postgres, changedetection.io + Playwright, Panorama Flask, Datasette
Resurser
~600 MB RAM totalt, klarar Hetzner CX22 (4 GB)
Pausning
Per källa via Miniflux UI, cd.io UI, eller flagga i senders.yaml
Ny källa
RSS: lägg till URL i Miniflux. HTML: lägg till watcher i cd.io. Special: skriv ny fetcher-plugin.

All-in-one workflow — visuell flödesbyggare

Plattformsberoende En tjänst Mest visuell redigering

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│            Activepieces (eller n8n)                          │
│         ┌──────────────────────────────┐                     │
│         │  RSS-flow  →  HTML-flow      │                     │
│         │  ↓             ↓             │                     │
│         │  Normalize  →  Dedup         │                     │
│         │  ↓                           │                     │
│         │  HTTP POST → Panorama DB     │                     │
│         └──────────────────────────────┘                     │
│                       │                                      │
│                       ▼                                      │
│              Panorama events-DB                              │
│                       │                                      │
│              ┌────────┴────────┐                             │
│              ▼                 ▼                             │
│          Flask UI          Datasette                         │
└──────────────────────────────────────────────────────────────┘
Tjänster
Activepieces + Postgres, Panorama Flask, Datasette
Resurser
~800 MB RAM (Node.js är tyngre), CX22 räcker fortfarande
Pausning
Per flow i UI:t — knapptryck
Ny källa
Klona befintligt flow i UI, ändra URL/filter. Inga deploys.
Risk
Bundna till plattformens utveckling. Specialiserade API:er (SPARQL) blir custom-noder.

Clean-slate Python — full kontroll

Mest skräddarsytt Inga externa tjänster Mest kod att underhålla

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│   Prefect (eller apscheduler)                                │
│   ┌────────────────────────────────────────────────┐         │
│   │  Per källa: hämtfunktion + Pydantic-modell     │         │
│   │  ┌─────────┬──────────┬──────────┬──────────┐  │         │
│   │  │ rss.py  │ html.py  │ api.py   │ browser  │  │         │
│   │  └─────────┴──────────┴──────────┴──────────┘  │         │
│   │  Retries · backoff · dead-letter               │         │
│   └────────────────────────────────────────────────┘         │
│                       │                                      │
│                       ▼                                      │
│           Normalizer (Event-modell, deduplikering)           │
│                       │                                      │
│                       ▼                                      │
│              Panorama events-DB (SQLite)                     │
│                       │                                      │
│              ┌────────┴────────┐                             │
│              ▼                 ▼                             │
│          Flask UI          Datasette                         │
└──────────────────────────────────────────────────────────────┘
Tjänster
Bara Panorama Flask + Datasette (allt i en Python-process eller två)
Resurser
~150 MB RAM, snålast
Pausning
Flagga i YAML, custom UI
Ny källa
Lägg till YAML-entry + ev. ny fetcher-fil
Risk
Vi reimplementerar pausning, retries, retention, status-UI — saker som finns gratis i alternativ 1.

Verktygen, ett i taget

Bedömning per komponent

Miniflux

Go · MIT Postgres 5–10 MB RAM Hög passform miniflux.app

Minimalistisk self-hosted RSS-läsare med fullständigt REST-API och Fever-API. Skriven i Go, en statisk binär, körs gärna i Docker. Hanterar etag/last-modified, parallell polling, deduplikering, läs-status och full text-search ur lådan. Stöder per-feed filter-regler (regex på title/description) och custom user-agent.

Fördelar
  • Stabil REST-API som vi kan polla från Flask
  • Inbyggd pausning per feed (UI eller API)
  • Hanterar 100+ feeds utan stress
  • OPML-import för bulk-tillägg
Nackdelar
  • Kräver Postgres (ingen SQLite-option)
  • Bara RSS/Atom — täcker ungefär en tredjedel av våra källor
  • Begränsad scraping för sajter utan feed

Naturlig botten för RSS-skiktet. Värt att starta här.

FreshRSS

PHP · AGPL-3.0 SQLite / MySQL / Postgres ~50 MB RAM Hög passform freshrss.org

Featurerik konkurrent till Miniflux. Stort extension-ekosystem, inkluderar moduler för att generera feeds från sajter som saknar dem (XPath-baserad scraping). Stöder Google Reader-API och har multi-user-stöd om det skulle behövas.

Fördelar
  • XPath-extensions kan ersätta delar av HTML-scrapen
  • SQLite-option — färre tjänster att hantera
  • Rikare UI, fler vyer
Nackdelar
  • AGPL kräver källkodspublicering om vi extension-bygger
  • Tyngre stack (PHP + webbserver)
  • Mindre clean REST-API än Miniflux

Bra om XPath-extensions kan ersätta 5–10 av HTML-scrape-källorna. Annars är Miniflux enklare.

changedetection.io

Python · Apache-2.0 SQLite ~150 MB + Playwright Hög passform changedetection.io

"Berätta för mig när den här sidan ändras." CSS/XPath-selektorer per URL, visual diff, optional Playwright-container för JS-tunga sajter. Webhook-out vid förändring. Specifikt utformad för att kringgå anti-bot-skydd via headless browser-rendering.

Fördelar
  • Löser de svåra sju (Amnesty, coe.int, FRA, Norden)
  • Visual diff hjälper kvalitetssäkring
  • Inbyggd Playwright-pool — slipper bygga själv
  • Pausning per watcher
Nackdelar
  • Playwright-container ~doublar minnesåtgång
  • Detektering av "ny publikation" är mindre exakt än RSS
  • Webhook-mottagare måste byggas i Flask

Den enskilt bästa lösningen på anti-bot-problemet. Värt sin minnesförbrukning.

n8n

Node.js · Sustainable Use License Postgres / SQLite ~500 MB RAM Licensvarning n8n.io

Visuell workflow-byggare med 400+ noder, AI-integrationer, vector stores. Drag-and-drop-flöden där varje nod är ett steg (HTTP, Cron, Transform, IF-then…). Den polerade lösningen för no-code-användare som vill äga sina flows.

Fördelar
  • Visuell editor — låg tröskel för icke-utvecklare
  • Stor community + många integrationer
  • AI-noder för transformation / sammanfattning
Nackdelar
  • "Sustainable Use License" är inte ren OSS — begränsar kommersiell hosting
  • Resurs-intensivt
  • SPARQL och specifika API:er kräver custom-noder

Tekniskt kapabel, men licensen gör det olämpligt för en myndighetsdriven prototyp.

Activepieces

TypeScript · MIT Postgres ~400 MB RAM Hög passform activepieces.com

Yngre n8n-konkurrent med riktig open source-licens. Visuell workflow-byggare, "pieces" istället för "nodes" (~200 färdiga). Aktivt utvecklat 2025–2026, har vunnit mark från n8n efter dess licensändring.

Fördelar
  • MIT-licens — inga restriktioner
  • Modern stack (TypeScript, Next.js)
  • Lättviktigare än n8n
Nackdelar
  • Mindre ekosystem än n8n (200 vs 400+ noder)
  • Yngre projekt — färre quirks dokumenterade
  • Special-API:er kräver fortfarande custom-pieces

Den bästa visuella plattformen för det här ändamålet om vi vill ha drag-and-drop. Annars är hybrid-vägen lättare.

Huginn

Ruby · MIT MySQL / Postgres Avtagande utveckling github.com/huginn/huginn

Den klassiska "agent-baserade" web-monitoringsplattformen. Varje agent är en konfigurerbar enhet som lyssnar efter händelser, hämtar data eller utlöser åtgärder. Kombinera dem i kedjor. Mycket flexibelt för exakt det vi behöver.

Fördelar
  • Skapad för precis det här problemet
  • MIT, helt OSS
  • YAML-baserade agenter — versionsstyrbart
Nackdelar
  • Utvecklingen har avtagit märkbart sedan 2023
  • Ruby on Rails-stack — friktion mot Python
  • UI:t känns daterat (2014-arv)

Hade varit förstavalet 2017. Idag finns färskare alternativ.

Prefect

Python · Apache-2.0 Postgres / SQLite ~200 MB RAM Bra för clean-slate prefect.io

Modern workflow-orkestrerare. Inga rigida DAG:s — vanlig Python-kod med @flow/@task-dekorerare. Schemaläggning, retries, observability, parallellism. Tänk Airflow utan komplexitet.

Fördelar
  • Skriv hämtfunktioner som vanlig Python
  • Inbyggd retries, backoff, dead-letter
  • UI för flow-status
  • Skalbart om vi någon gång behöver det
Nackdelar
  • Kräver Prefect-server (eller cloud) för UI
  • Overhead för 41 källor är onödig
  • Ingen färdig RSS-poller — vi bygger ändå

Rätt val om vi gör clean-slate-vägen. För hybriden räcker apscheduler eller cron.

Scrapy + Scrapyd + ScrapydWeb / Gerapy

Python · BSD SQLite ~300 MB RAM Medel passform scrapy.org

Det de facto-mogna Python-ramverket för web-scraping. Scrapy är biblioteket, Scrapyd är scheduler-daemonen, ScrapydWeb/Gerapy är web-UI:n ovanpå. Tillsammans en komplett pipeline för spiders med statistik, loggar, scheduling.

Fördelar
  • Mest mogna scraping-stacken som finns
  • Bra för komplexa HTML-källor
  • Inbyggd robots.txt-respect, rate-limiting, retry
Nackdelar
  • Overkill för enkla scrapes — vi måste skriva spiders
  • Hanterar inte JS-rendering nativt (behöver Splash/Playwright)
  • ScrapydWeb är sparsamt underhållet

Bättre att använda changedetection.io för enkla "ny publikation"-detekteringar och bara dra in Scrapy om vi har källor där vi behöver djup HTML-parsning.

Juriscraper (Free Law Project)

Python · BSD Endast amerikanska domstolar Låg passform direkt free.law/projects/juriscraper

Plugin-baserat scraping-bibliotek som täcker alla federala domstolar och de flesta delstatsdomstolar i USA. Backboner för CourtListener. Arkitekturen — en scraper-modul per domstol med gemensamt interface — är värd att studera för vår design.

Fördelar
  • Plugin-mönster värt att kopiera
  • Robust felhantering över ~200 källor
  • Aktivt underhållet
Nackdelar
  • Inga europeiska domstolar — vi använder inte koden direkt
  • USA-specifika data­modeller

Inte direkt användbart, men kodbasen är referensläsning för "hur man bygger en plugin-driven domstols-scraper".

ECHR-OD (academic dataset)

CC-BY-4.0 Dataset, inte verktyg Direkt användbart echr-opendata.eu

Forskningsprojekt från Poznan University of Technology som har laddat ner, normaliserat och taxonomerat hela HUDOC. Resultatet är en versionerad open dataset med metadata-features, klar för machine learning eller direkt databasimport.

Fördelar
  • Sparar månader av scraping för historisk ECtHR-data
  • Strukturerad metadata (artiklar, utfall, kammare)
  • CC-BY — kan inkluderas direkt i Panorama
Nackdelar
  • Behöver fortfarande live-poll mot HUDOC för nya domar
  • Akademiskt projekt — uppdateras inte i realtid

Ladda in dumpen en gång som baslinje. Polla HUDOC backend-API för deltadata.

Datasette

Python · Apache-2.0 SQLite Redan i stacken datasette.io

Web-UI för utforskande analys av SQLite-databaser. Vi kör redan en instans på data.tritagonist.se. Stöder facets, sökning, JSON-export, ärftliga vyer, plugin-ekosystem.

Fördelar
  • Inga ändringar behövs — den hanterar nya tabeller automatiskt
  • JSON-export gör att andra system kan konsumera data
  • Snabb facet-filtrering över hundratusentals rader
Nackdelar
  • Bara utforskning — inga skrivningar via UI
  • Stora databaser (10 GB+) kräver speciellt index-arbete

Behåll och utöka. När events-tabellen växer kommer Datasette vara guld.

Meilisearch / Typesense

Rust / C++ · MIT Egen index-fil Senare fas meilisearch.com typesense.org

Typo-toleranta sökmotorer som indexerar dokument och returnerar relevanta träffar i millisekunder. Båda har enkla REST-API:er och kräver minimal konfiguration. Typesense har bättre fasetterad sökning, Meilisearch har snyggare default-relevans.

Fördelar
  • Sökning över alla events-titlar och fulltexter
  • Multispråk-stöd (svenska, engelska)
  • Lättare att deploya än Elasticsearch
Nackdelar
  • Onödigt tidigt — SQLite FTS5 räcker till ~100 k rader
  • En tjänst till att hantera

Vänta tills vi har volym. SQLite full-text search räcker länge.


Jämförelse

Snabb översikt — sortera per kategori

Verktyg Kategori Licens Stack Hetzner-vänlig Passform
MinifluxRSS-aggregator MITGo + Postgres Ja, 10 MBHög
FreshRSSRSS-aggregator AGPL-3.0PHP + SQLite Ja, 50 MBHög
Tiny Tiny RSSRSS-aggregator GPL-3.0PHP + Postgres JaLåg (maintainerproblem)
changedetection.ioWeb-monitor Apache-2.0Python + Playwright Ja, 150 MB+Hög
urlwatchCLI-monitor BSDPython Ja, ingen tjänstMedel
Scrapy + ScrapydScraping-framework BSDPython Ja, 300 MBMedel
JuriscraperLegal scraping (USA) BSDPython JaLåg (USA)
n8nVisual workflow Sustainable UseNode.js + Postgres Ja, 500 MBLåg (licens)
ActivepiecesVisual workflow MITTypeScript + Postgres Ja, 400 MBHög
HuginnAgent-based MITRuby on Rails + MySQL Ja, 250 MBMedel
Node-REDVisual workflow Apache-2.0Node.js Ja, 100 MBMedel
PrefectWorkflow-orchestrator Apache-2.0Python Ja, 200 MBHög (clean-slate)
DagsterData-orchestrator Apache-2.0Python Ja, 400 MBMedel (för komplext)
Apache AirflowWorkflow-orchestrator Apache-2.0Python + Postgres Ja, 800 MBLåg (overkill)
apschedulerScheduler-bibliotek MITPython (in-process) Ja, ~0 MBHög (för enkelt)
DatasetteData-explorer Apache-2.0Python + SQLite Ja, redan i stackHög
MeilisearchSökmotor MITRust Ja, 100 MBMedel (senare)
TypesenseSökmotor GPL-3.0C++ Ja, 80 MBMedel (senare)
ECHR-ODDataset CC-BY-4.0 Ja, statiska filerHög

Skalning bortom dagens källurval

Vad händer när vi lägger till fler källor

Källkatalogen kommer att växa. Bedömningen av hur olika arkitekturer skalar:

Hybriden skalar nästan automatiskt

Miniflux klarar 1000+ feeds utan konfigändring. changedetection.io skalar genom att lägga till watchers via UI eller dess API. De egna special-fetcherna är CPU-bundna men källspecifika — fler källor ger inte fler API:er att lära sig.

Activepieces skalar via fler workflows

Varje ny källa = ett nytt workflow. UI:t kan hantera hundratals flows, men efter en viss volym blir det rörigt utan tagging/foldering. Workflow-databasen kan växa snabbt och behöver underhåll.

Clean-slate-Python tappar tempo

För varje ny källa behövs YAML-entry + ev. ny fetcher-kod + deploy. Smidigt vid 50 källor, friktion vid 200. Lägg till "ny källa via UI" som krav i den arkitekturen om vi går den vägen.


Vad jag skulle göra först

Konkret första steg

Sätt upp en Miniflux-container på Hetzner-värden bredvid de befintliga Panorama-containrarna. Lägg in de 13 verifierade RSS-feedarna manuellt. Polla Miniflux REST-API från Flask en gång per timme och persistera nya items till en events-tabell i SQLite. Det ger oss:

Inget av besluten är låsta — Miniflux REST-API är standard nog att vi kan byta ut det senare utan att röra resten av stacken.