01 phishing & scam reporting, filtered

Turn forwarded suspicious mail into signed, federated threat feeds.

Karst is a self-hostable platform for phishing- and scam-reporting intelligence. Ordinary people forward a suspect message; Karst redacts the personal data on-device, parses authentication signals, classifies the threat in plain language, clusters related reports, and publishes signed intelligence to downstream CERTs, SIEMs, and on-device blocklists.

self-hosted explains every verdict single-tenant per deployment ed25519-signed feeds
fig. i — cross-section raw signal → clear water

Karst takes its name from limestone landscapes that filter water naturally — a precise reference to filtering signal from noise across a sensor network of human reporters. Citizens forward suspicious emails to a deployment they trust; Karst redacts personal data, parses authentication signals, classifies the threat in plain language, clusters related reports into campaigns, and publishes signed intelligence to downstream CERTs, SIEMs, and on-device blocklists.

02 canonical data flow

Seven stages, one stream, isolated all the way down.

A report enters through one of six clients, is sanitised on-device, then traverses the pipeline as messages on NATS JetStream, isolated by per-service subject ACLs. Hover any stage for the subject name it publishes on.

Reporter
one of six clients
client → /v1/reports
Ingest
accept, version, persist
karst.reports.received
Parser
RFC822 + headers + auth
karst.reports.parsed
Privacy filter
11-layer PII redaction
karst.reports.filtered
Classifier
multi-label + top-3 features
karst.reports.classified
Clustering
incidents → campaigns
karst.reports.clustered
Dispatcher
sign + fan-out
karst.feeds.published
postgres
structured record — reports, incidents, indicators, audit log.
garage (s3)
encrypted RFC822 bodies. AES-256-GCM. Never on the bus.
nats jetstream
message broker. Per-service credentials and subject ACLs isolate every stage.
ed25519
signs published feeds & the on-device URL blocklist.

03 education as a feature

Every verdict ships with a three-layer explanation.

Karst doesn't just classify — it teaches. Each report comes back with a headline, a plain-language expansion, and an actionable tip. The reporter learns what to look for next time; the operator gets a paper trail of why the model said what it said.

report · r-7f3a9c
phishing — high

DMARC failed. This message claims to be from PayPal but wasn't signed by them.

The sender wrote From: service@paypal.com, but the message was actually sent from account-update.paypa1-secure.app and carries no valid DKIM signature for paypal.com. The reply-to address differs from the return-path, and the domain was registered 4 days ago.

How to spot this next time. Look at the actual domain, not the display name. Real PayPal mail comes from paypal.com — not paypa1-secure.app. A brand-new domain pretending to be a household name is the single strongest tell.
r-7f3a9c · 2026-05-23 14:02 UTC
SPF DKIM DMARC ARC ok
1

Headline

One sentence in plain language. No jargon. Tells the reporter, in human terms, what went wrong.

2

Expansion

The specific evidence — the actual sender domain, the headers that failed, the age of the domain. Auditable; nothing hidden.

3

Tip

A concrete pattern the reporter can apply themselves next time. The feature, not a footnote — Karst gets less useful the more people learn to spot phishing without it.

surfaced auth signals: SPF · DKIM · DMARC · ARC chain · BIMI · RDAP domain age · Reply-To vs Return-Path

04 trust boundary

Bodies never travel on the message bus.

Each report crosses five concentric zones. The raw RFC822 body is encrypted on the ingest edge, lifted into the evidence vault, and referenced by hash from every downstream stage — it is never serialised back onto NATS.

Client

Web Extension · Thunderbird add-on · web app · iOS · Android · admin console. Recipient identities are stripped here, before the report leaves the device.

trust = self

Ingest edge

HTTPS termination, rate limit, sender verification. Body is AES-256-GCM encrypted and the ciphertext is sent to the vault. Metadata continues on the bus.

tls · rate-limit

Pipeline

NATS JetStream. Parser, privacy filter, classifier, clustering, dispatcher each consume and publish on ACL-isolated subjects. Bodies referenced by hash only.

acl-isolated

Evidence vault

Encrypted RFC822 bodies in Garage object storage. Read only by an explicit operator action, audit-logged. Never serialised back onto NATS.

aes-256-gcm

Published feeds

Signed outputs leave the deployment: MISP, STIX/TAXII, webhooks, Ed25519-signed on-device blocklists. The only material that ever crosses the deployment boundary.

ed25519-signed

05 federation, not multi-tenancy

One deployment per operator. Federation via signed feeds.

Each CERT, company, or regional authority runs its own isolated Karst instance. Deployments share output, not databases — there is no multi-tenant code path, no row-level security, no cross-tenant trust boundary to defend.

KARST · CERT-NL Single deployment Postgres · NATS · Garage No shared backend with peers. ed25519 :: 4f9c…b21a MISP instance peer CERT · pushes upstream DE STIX / TAXII 2.1 SIEMs, threat intel platforms v2 Signed webhook corporate SOC, custom tooling HMAC On-device blocklist extensions, mobile clients ed25519 Peer Karst national CERT, sysadmin on a 4-vCPU box FR signed feed signed feed signed feed signed feed signed feed FIG. V — FEDERATION CONSUMERS no shared database. no shared trust root. signed feeds only.

What flows out

Indicators & incidents — never the encrypted body, never the reporter.

What's signed

Every published feed and every on-device blocklist, with a per-deployment Ed25519 key.

Who runs it

National CERTs, small companies, individual sysadmins — same code, same shape.

06 what makes karst different

Three commitments, in code rather than copy.

Privacy-first by construction

Recipient identities are stripped on the client. An 11-layer Unicode-aware redaction pipeline runs server-side. Raw RFC822 bodies are encrypted at the ingest edge and never traverse the message bus — they live in the evidence vault and are referenced by hash.

aes-256-gcm · sha-256 addressing · no body on the bus

Education is a feature, not a footnote

Every classification ships with a three-layer explanation: headline → plain-language expansion → how-to-spot-this-next-time tip. Karst gets less useful the more people learn to spot phishing without it — and that's the point.

headline · expansion · actionable tip

Single-tenant per deployment

Each operator — CERT, company, regional authority, sysadmin on a 4-vCPU box — runs their own isolated instance. Federation happens via signed published feeds, not shared backends. No multi-tenant code paths, no RLS, no cross-tenant trust boundary to defend.

no tenant switcher · no shared db · no rls
Email auth as first-class input
SPF, DKIM, DMARC, ARC chain, BIMI, RDAP domain age, Reply-To vs Return-Path mismatches — surfaced directly in the user-visible explanation, not buried in admin logs.
On-device blocklisting
Reporters' clients consult a local, Ed25519-signed URL cache. No per-URL network lookups — protection without telling anyone which URLs you hovered over.