# DECNET Development Roadmap — V2

Post-v1 direction. Everything here is *after* the v1 box is closed; this
document exists to make sure the schema and architectural decisions we take
*before* v1 ships don't box us out of the interesting post-v1 work.

---

## Keystroke Dynamics & Session Profiling

**Goal:** graduate the Profiler from IP-keyed attribution to
identity-independent correlation using structured per-session feature
vectors. Attackers rotate IPs; they don't rotate their hands.

The sessrec pipeline (v1) already lands every keystroke as a `ch:"i"` event
with a `t` timestamp in the asciinema day-shard. The raw data is sitting
on disk. The work is *not* collection — it's feature extraction, schema,
and correlation primitives.

### Features — cheap, post-processing over existing shards

All of these are derived from a single pass over a session's `"i"` events.
No new capture infra.

- **Inter-keystroke interval (IKI) distribution**
  `kd_iki_mean`, `kd_iki_stdev`, `kd_iki_p50`, `kd_iki_p95`.
  Humans: 80–250ms, high variance. `sshpass`/`paramiko`/`expect`: <5ms,
  near-zero variance. Paste attacks: bimodal (one huge gap, then a burst).
- **Burst ratio**
  `kd_burst_ratio` = fraction of keystrokes within <30ms of the previous
  one. High = pasted commands, low = typed. One number; cleanly separates
  operator-at-keyboard from automation.
- **Control-character mix** (not just backspace — the whole family)
  `kd_ctrl_backspace`, `kd_ctrl_wkill` (`\x17`), `kd_ctrl_ukill` (`\x15`),
  `kd_ctrl_abort` (`\x03`), `kd_ctrl_eof` (`\x04`), `kd_arrow_rate`
  (`\x1b[A/B/C/D`), `kd_tab_rate` (`\x09`).
  *Presence of any control char* → bot/human split. *Mix* →
  tooling/experience fingerprint. Heavy Ctrl-W = experienced Unix user.
  Heavy arrows = history editing. Heavy tab = exploratory recon. Bots
  emit `\r`-terminated literals and nothing else.
- **Prompt-to-enter latency distribution**
  `kd_enter_latency_p50`, `kd_enter_latency_p95`, and crucially the
  **ratio p95/p50** as a cheap tail-heaviness indicator. Shape matters
  more than median. Readers have long right-tails; memorized playbooks
  have tight distributions; bots have cliff-edge distributions at whatever
  `sleep` is hardcoded. p50 alone blurs these together.
- **Typing-to-think ratio**
  `kd_think_ratio` = idle gap (>2s before Enter) / total session time.
  Recon/read behavior vs. memorized execution.
- **Digraph rhythm fingerprint** — `kd_digraph_simhash`, 64-bit.
  **Use SimHash (or MinHash) over quantile-bucketed digraph timings**,
  not a regular hash. Hamming-distance comparable — similar rhythms get
  similar hashes, which is the entire point. A plain hash of quantized
  timings loses this: one digraph off = totally different hash. SimHash
  is ~30 lines. This is the feature that graduates "fingerprint" into
  "identity."

### Schema — the single most important decision on this page

The features above *must* live in a dedicated `session_profile` table,
UUID-keyed, foreign key to the owning `session_recorded` Log row.
**Not** in `meta_json_b64`. **Not** as ad-hoc bounty strings.

Rationale:
- Correlation wants `find_similar_sessions(sid, ε)` — that's a SQL query
  over indexed float columns, not a 50k-row JSON parse.
- Retrofitting is brutal. Decide the shape now, when the table is empty.
- Federation (see below) needs these as structured columns to be
  gossipable without per-operator parsing quirks.

Sketch:

```sql
CREATE TABLE session_profile (
    sid              TEXT PRIMARY KEY,           -- session UUID
    log_id           INTEGER REFERENCES logs(id), -- owning session_recorded
    schema_version   INTEGER NOT NULL,            -- evolve features without breaking gossip
    -- timing moments
    kd_iki_mean              REAL,
    kd_iki_stdev             REAL,
    kd_iki_p50               REAL,
    kd_iki_p95               REAL,
    kd_enter_latency_p50     REAL,
    kd_enter_latency_p95     REAL,
    -- ratios
    kd_burst_ratio           REAL,
    kd_think_ratio           REAL,
    -- control-char rates
    kd_ctrl_backspace        REAL,
    kd_ctrl_wkill            REAL,
    kd_ctrl_ukill            REAL,
    kd_ctrl_abort            REAL,
    kd_ctrl_eof              REAL,
    kd_arrow_rate            REAL,
    kd_tab_rate              REAL,
    -- rhythm fingerprint
    kd_digraph_simhash       BLOB,                -- 8 bytes, Hamming-comparable
    -- derived
    total_keystrokes         INTEGER,
    session_duration_s       REAL,
    created_at               TIMESTAMP
);
CREATE INDEX ix_session_profile_simhash ON session_profile(kd_digraph_simhash);
```

`schema_version` is non-negotiable from day one. Federation gossip in v2
requires cross-operator compatibility; bumping feature definitions without
a version field will silently poison other operators' clustering.

### Sequencing — build the shell before the features

The natural instinct is: features first, then correlation. **Invert it.**

1. **`session_profile` table + empty write path** — one row per session, all
   nulls. Ships immediately.
2. **Correlator `find_similar_sessions(sid, ε)` primitive — stubbed.**
   Returns empty. Wire the API, wire the UI surface in `SessionDrawer`
   ("Similar Sessions: none yet").
3. **First features** — the five cheapest (IKI moments, burst ratio,
   control-char mix). Populate the table.
4. **Similarity function goes live** — Euclidean distance over normalized
   float features, Hamming distance over simhash. No ML needed.
5. **Digraph simhash** — once cheap features are validated as useful.
6. **Correlation graph integration** — `CorrelationEngine` learns to
   follow profile-similarity edges, not just IP edges.

**Why inverted:** once operators see a session profile with no "similar
sessions" surface, they'll ask for it, and the UX (what's shown, how
distance is rendered, what actions the link affords) will drive which
features matter. Build the shell, let demand signal feature priority.

### Correlation — what this enables

Today, `CorrelationEngine` keys on `attacker_ip`. Session profiles let it
graduate to **identity-independent correlation**.

Concrete scenario:
> Attacker hits operator A's maze from IP X. Three weeks later, hits
> operator B's maze from IP Y. IPs don't match. But:
> - `kd_digraph_simhash` Hamming distance: 3
> - HASSH fingerprint: identical
> - JARM: identical
> - Command-sequence 3-gram overlap: 60%
>
> That's a cross-operator identity claim with receipts. SQL query, not
> research project.

Without structured session profiles, that analysis is literally impossible.
With them, it's a join.

### Federation implication (v2/v3)

Session profile vectors are **exactly** the thing to gossip in the
federation layer. They are:
- **Small** — a few floats + an 8-byte hash. Cheap on the wire.
- **Semantically meaningful** — encode identity without encoding
  operator-specific infrastructure or PII.
- **Collision-rich** — similar vectors across operators = shared adversary,
  same pattern as the fingerprint-tuple idea, but richer and noisier-signal.

The `session_profile` schema is effectively the v2 federation wire format.
Design it that way from day one:
- `schema_version` field (mentioned above).
- No operator-identifying fields (decky name, internal IP, host labels).
- SimHash specifically because Hamming distance works across operators
  without needing shared training data.

### Cost estimate

- Five cheap features + table + stubbed `find_similar_sessions`:
  **½ to 1 day** of implementation once the codebase is known.
- Digraph simhash + live similarity: **another 1–2 days**.
- Correlation engine integration: **depends on how deep the graph walk
  goes** — 2–5 days for a first pass.

The expensive part is not implementation. It's **deciding the schema well
enough that we don't regret it in six months.** Hence this document.

### What *not* to build

- **Typing biometric login.** That's the research-paper framing. Wrong
  frame for a honeypot. We're doing *tooling attribution* and *operator
  clustering*, not authentication.
- **Hold time / pressure / velocity.** Not on the SSH wire. Dead-end
  without attacker-side instrumentation they will not run.
- **ML clustering before similarity.** Euclidean + Hamming over normalized
  features handles the first useful year of data. Don't reach for sklearn
  until the simple thing demonstrably fails.

---

## Open questions to resolve before writing code

1. **Normalization strategy for Euclidean distance** — z-score per-feature
   over rolling window? Fixed population stats? Operator-local vs.
   gossip-aligned?
2. **ε tuning** — start empirically. Seed the UI with "show top-N nearest"
   rather than a distance threshold. Learn ε from operator feedback.
3. **Retention** — session profiles are small; keep indefinitely? Or
   co-expire with the owning log row?
4. **Privacy boundary on gossip** — do we hash the sid on the wire, or
   exchange it plaintext? First pass: hashed, with a challenge-response
   if two operators want to confirm same-session.

---

## Federation

**Goal:** cross-operator threat-intel sharing. An operator in country A
observes an attacker, and an operator in country B benefits — without
either operator leaking internal infrastructure, attracting legal
exposure, or becoming part of the other's attack surface.

### Framing — federation, not P2P

"Federation API + P2P" is two contradictory models. Pick **federation
(Mastodon/ActivityPub shape), not P2P.** Reasons:

- Operators already run persistent, addressable infrastructure. There is
  a DECNET master host with a stable identity. That's a server, not a
  transient peer. The hard problem libp2p/Nostr exist to solve is already
  solved here.
- Threat-intel sharing is fundamentally **many-to-many gossip with audit
  trails**, not many-to-many streaming. Federated server-to-server gossip
  maps naturally; DHT/P2P overhead buys nothing.
- SWARM already ships mTLS + per-host cert fingerprint pinning. Promoting
  that to cross-operator is a small, understood step. Bolting on libp2p
  is a ground-up rewrite.

### Scale — design for thousands, not millions

Realistic ceiling for a security-operator federation is **low thousands**.
Points of reference: Mastodon ~10k servers, Tor ~7k relays, Nostr ~2k
active relays. A niche-of-a-niche like threat-intel federation will not
exceed these.

**Design explicitly for 1k operators, with an escape hatch at 10k.**
Million-scale assumptions force Kafka/DHT/consensus theater that
strangles actual work.

### The hard problem is trust, not protocol

Every threat-intel federation that ignored trust became a spam cesspool
(early AlienVault OTX, half the ISAC world). Answers required:

- **Sybil resistance** — what stops an adversary spinning 50 fake
  operators to poison clustering? First-pass answer: **gated enrollment
  via a central registry signed by the project root**. Yes, centralized.
  "Centralized root, federated leaves" is Mastodon's model and it works.
  Decentralize only if adoption forces it. Don't premature-decentralize.
- **Adversarial join** — what stops an attacker running a decoy operator
  specifically to map *what other operators observe*? This is the
  terrifying one. Gossip must be **asymmetric by design**: publish
  simhashes and other lossy fingerprints, not raw session data. Answer
  queries with binary matches (yes/no + count + first-seen), not full
  session payloads. The attacker-operator learns "this simhash is
  known to someone," nothing more.
- **Jurisdictional blast radius** — IP addresses are PII under GDPR. An
  operator in Germany gossiping an attacker IP to an operator in
  Singapore may commit a crime. **Per-operator, per-field opt-out with a
  default-deny posture for PII-adjacent data** is non-negotiable.
  Geo-tagged operator registry entries let the federation enforce this at
  the protocol layer rather than the honor system.
- **Legal chill** — CFAA, NIS2, sector-specific rules. Having a clear
  "this operator chose to share X" audit trail per record protects
  everyone. Every gossiped fact carries the originating operator's
  signature.

### What to build first — the two-operator handshake

Build **one primitive and nothing else**: two operators who've manually
exchanged pubkeys making signed queries to each other to answer one
question — **"have you seen this SimHash?"**

Response: `{ seen: bool, count: int, first_seen: timestamp }`. Nothing
more. No sid, no decky, no IP, no raw session data.

Why: if that primitive doesn't produce value for two operators, scaling
it to a thousand won't either. If it does, the scaling is mostly
operational — directory service, retry/backoff, rate limits — which are
all solved problems. **The design risk lives entirely in the primitive,
not in the scale-out.**

Explicit non-goals for first iteration:
- No pub-sub.
- No DHT.
- No gossip protocol.
- No operator discovery.
- No multi-hop.

Just two pubkeys, one question, a signed answer.

### Sequencing

1. **Operator identity** — Ed25519 keypair per operator, generated at
   install. Self-signed manifest (operator name, pubkey, contact, geo).
2. **Two-operator handshake** — mTLS over HTTPS, pubkey pinning, one
   RPC: `QuerySimHash(hash) → {seen, count, first_seen}`. Manual peer
   config in YAML.
3. **Registry** — central signed directory of known operators, fetched
   on boot. Enables discovery without mandating central routing.
4. **Additional query types** — JA3/JA4 lookup, HASSH lookup, command-
   n-gram match. Same shape: lossy fingerprint in, binary+metadata out.
5. **Publish path** — operators periodically push new fingerprints to
   peers (gossiped, not polled). Signed, deduplicated by fingerprint.
6. **Clustering & visualization** — UI surface for "this simhash is
   known across N operators, first seen by operator-X on date-Y."

### Codebase-aware observations

- **`session_profile` *is* the federation wire format.** `schema_version`
  from day one is non-negotiable — retrofitting cross-operator
  compatibility after the fact is a nightmare.
- **SWARM mTLS is the starting point**, not the finishing point. The
  per-host fingerprint-pin pattern (memory:
  feedback_mtls_pin_per_host.md) extends naturally to per-operator pins.
- **The bus stays local.** Federation is cross-host in a way the bus was
  explicitly scoped away from ("cross-host federation is out of MVP
  scope"). A separate `decnet federation` worker is the right shape, not
  bridging the bus over TCP.
- **Attack surface.** Federation endpoints on operator hosts *are*
  targets. If the coordination layer is compromised, honeypots become
  attack infra. Bind federation RPC to a separate interface, separate
  cert chain, separate systemd unit. Assume the federation daemon will
  eventually be breached and design blast-radius containment into the
  architecture — it must not share credentials, sockets, or filesystem
  trust with the local DECNET workers.

### Open questions

1. **Who runs the root registry?** Project root (ANTI) as v2 default;
   path to handoff/multi-root federation in v3.
2. **Revocation** — how is a compromised operator kicked? Registry
   signs a revocation list, peers refuse queries from revoked pubkeys.
   Cache TTL?
3. **Rate-limiting adversarial joins** — a registered operator can still
   query-flood to enumerate fingerprints. Per-peer query budgets, with
   a reputation signal that decays silence and rewards useful
   publishing.
4. **Consent UX** — what does an operator opt into when they enable
   federation? Single toggle is wrong; per-category (fingerprints /
   profiles / commands / IPs) is right. Defaults matter more than
   flexibility.

### Trust model refinement — 2026-04-22 design review

The framing above (central signed registry, gated enrollment, revocation
lists, reputation algorithms) is **superseded by a social-trust model**
arrived at through adversarial design review. Captured here verbatim so
the iteration trail isn't lost.

**The governing insight:** trust is not technical, it is human. Instead
of solving cross-operator trust with crypto/PKI/reputation, **leave it
to humans**. Two operators meet at a conference, have beers, decide to
federate. Recurse ad infinitum. No zero-knowledge proofs, no
decentralized governance, no CRL theater.

This is a deliberate deferral of a hard problem, not a claim that the
hard problem is solved. The rest of this subsection documents why the
social-trust model holds up under attack and where its residual weaknesses
live.

#### Attacks considered and outcomes

**1. Transitive trust collapse.** First framing ("recurse ad infinitum")
implied A→B→C gossip flow, which is how PGP's web of trust died.
**Resolution:** model is hub-and-spoke, not transitive. Every federation
edge is a manual, mutually-made handshake ("beershake"). A learns that C
exists (because B mentioned C), but A does not federate with C until
A and C separately beershake. Topology metadata leaks (B tells A that
C exists, which C may not have consented to share), but gossip does not.

**2. Attackers go to conferences too.** Social trust filters for
"drinks beer at BSides," not "not-an-adversary." Ransomware affiliates
and red teams can stand up DECNET, be charming, and join. **Accepted.**
Social consequences scale better than cryptographic ones for this class
of problem: if operator A's sponsored peer B starts gossiping garbage,
A's other federates see that A brought B in — reputation damage is the
brake. Not perfect, but it's a real cost.

**3. The query IS the intel.** Aggregate-only responses
(`{seen, count, first_seen}`) don't defeat recon — a phishing operator
querying "has anyone seen `paypa1-security.com`?" learns whether their
cover domain has burned. **Resolution: federation is push-only, not
pull.** Peers send what they chose to send; nobody can ask on demand.
C still gets data, but not on-demand data. This closes the dangerous
recon lane outright.

**4. Compromised-peer inheritance.** A friend's DECNET master is a box
on the internet. When it gets rooted, the attacker inherits every
federation edge that admin held. **Conceded as a real risk.** No clean
mitigation beyond the push-only constraint (limits what the compromised
node can exfiltrate in real-time) and the hub-and-spoke constraint
(limits blast radius to that operator's direct peers).

**5. Revocation non-transitivity.** If trust is social, so is distrust.
A kicks B; Carol (who also federates with B) still relays to B.
**Resolution: see #2 — A's kick is visible to A's other federates,
sponsorship accountability propagates socially in real-time via the
topology-transparency mechanism (see below). Not a coordination
problem DECNET solves; one it exposes so humans can solve it.**

**6. Legal/compliance at enterprise scale.** GDPR, HIPAA, FFIEC,
data-residency — informal federation has no DPA and will not survive
first enterprise deal. **Resolution: write down the deferral.** v1
federation is explicitly for informal peer networks only. A DPA
framework gates any regulated-org federation; that is v3 scope.

#### The full model

- **Hub-and-spoke, pairwise beershakes.** No transitive trust.
- **Push-only, never pull.** Peers push what they choose; no on-demand
  queries, no recon surface.
- **Sponsorship-as-reputation.** A brought B in; if B misbehaves, A's
  reputation across A's other federates degrades. Social cost, real-time.
- **Hash-evidence on every contribution.** To prevent fabricated pushes
  that game contribution ratios, every shared fact must include a
  verifiable fingerprint (message sha256, cert fingerprint, artifact
  sha256) — not a free-text claim. "I saw domain X" without an artifact
  hash is not a contribution.
- **Contribution ratios, enforced per peer.** Pure consumers get starved;
  peers must push roughly as much as they receive. Paired with
  hash-evidence above so ratios can't be gamed with garbage.
- **Topology transparency.** Every federate can see the full federation
  graph from their vantage point: who sponsored whom, who kicked whom,
  contribution volumes. Makes sponsorship accountability observable in
  real-time rather than post-incident.
- **Omission-based canary watermarking.** Individualizing "saw domain X"
  across N peers is impossible (you can't watermark a string). Instead,
  A withholds X from peer 23 specifically; if X surfaces externally, A
  can triangulate across multiple omission canaries over time. Forensic
  tool, not preventive.

#### Accepted residuals

These are structural to gossip systems and are **accepted with
mitigation, not eliminated:**

- **Audit gap.** Misbehaving federates leak silently — queries (or in
  push-only, consumption) are supposed to happen. By the time a peer
  notices the leak, the data has already moved. Mitigation: omission
  canaries provide post-hoc forensic attribution; sponsorship
  accountability provides the social pressure to catch it faster.
- **Correlation-at-receiver.** C receives push from A, B, D, E. None
  shared much individually; C correlates across them to build a picture
  no sender authorized. Cannot be designed out without killing the
  federation's entire value proposition. Priced in, documented.
- **Push cadence as metadata.** Even push-only, timing/volume of what A
  pushes tells receivers about A's current coverage/posture. Low
  bandwidth, probably unfixable without batching jitter that hurts
  timeliness. Accepted.
- **Topology metadata leak.** B telling A that C exists (as part of the
  "recurse ad infinitum" socialization) is itself signal C may not have
  consented to share. In regulated sectors, even "bank X runs deception"
  is information. Minor, but noted.

#### Why the social-trust framing is correct *now*

The earlier subsection (central registry, gated enrollment, Ed25519
per-operator identities, signed revocation lists) is not wrong, it is
**premature**. Building that machinery before there is a real
federation with real users is the "million-scale assumptions that
strangle actual work" trap called out in the Scale section above. The
social-trust model ships when there are two friends with two
deployments who want to try it. The crypto/registry model ships when
there is a customer whose compliance team requires it.

What cannot be deferred: **the wire format**. `session_profile`,
`smtp_targets`, and future federation-adjacent tables must still carry
`schema_version` from day one. Privacy-preserving shape
(`{seen, count, first_seen}` aggregate-only, no attacker identity) is
the right posture independent of trust model — minimizing leak surface
is always correct.

#### What this changes in the earlier Open Questions

- **#1 Root registry** — no longer v2-blocking. Deferred to v3 or
  whenever a federation outgrows social coordination.
- **#2 Revocation** — answered. "A kicks B; A's other federates see it
  in the topology-transparency view and make their own call." No CRL.
- **#3 Rate-limiting adversarial joins** — partially answered. Push-only
  eliminates the query-flood vector. Per-peer rate limits on push still
  apply to prevent contribution-ratio gaming via spam.
- **#4 Consent UX** — unchanged. Per-category opt-in with default-deny
  on PII-adjacent data is still the right shape.

#### Second round — scope-verified pull as a complementary channel

Follow-up design review revisited the "query IS the intel" problem from
a different angle. Push-only closes the recon attack but sacrifices the
defender's most useful query shape: "has anyone seen anything new about
MY brand today?"

**Proposal:** operators verify domain scope at registration time (ACME
dns-01 pattern — TXT record challenge), and pull queries are restricted
to data about scope-verified domains. BigBank can query about
`bigbank.com`; they cannot query about `competitor.com`.

**This is a genuine addition, not a replacement for push-only.** Both
channels coexist with different threat models:

- **Push-only channel** — peer-volunteered gossip. Handles the case
  where the domain being attacked is one the defender does NOT own
  (lookalikes, typosquats, newly-registered phishing infra). This is
  the defender's primary use case and scope-verified pull cannot serve
  it without fuzzy matching, which attackers abuse.
- **Scope-verified pull channel** — bounded "what's new about my
  verified scope" queries. Narrow, auditable, recon-resistant.

**Attacks considered on the pull channel:**

1. **Indirect-reference / lookalike queries.** The intel defenders most
   need is about domains they DON'T own (`bigbank-secure-login.support`
   targeting BigBank). Allowing fuzzy/lookalike matching under "claimed
   typo of my scope" reopens the recon lane. **Resolution: exact-match
   only on the pull channel. Lookalike intel flows through push-only.**
2. **Domain graveyard.** Attacker buys an expired domain, DNS-verifies,
   pulls historical phishing intel for a brand they're about to revive.
   **Mitigation: scope applies prospectively only — queries bounded to
   intel indexed since the operator's verification timestamp.** Bake in
   from day one, hard to retrofit.
3. **Subdomain scope inference.** Wildcard matching (`*.bigbank.com`)
   invites overreach. **Resolution: explicit list of scoped domains,
   each individually DNS-verified. No wildcard inference.**
4. **Self-lookup leaks coverage maps to the asker.** BigBank querying
   their scope learns which peers have visibility into BigBank-targeting
   campaigns. **Resolution: aggregate-only response
   (`{seen, count, first_seen}`) with no per-peer attribution.** Already
   implicit.
5. **MSSP multi-tenant churn.** An MSSP claims scope over 200 client
   brands; clients leave, the DECNET retains ex-scope.
   **Mitigation: periodic re-verification (weekly cadence) of every
   scoped domain's TXT record.**

**Residual, accepted:** scope-verified pull cannot serve queries about
domains the defender doesn't control. That's the structural limit —
push-only covers it.

**Net model for v2 federation:**
- Identity: Ed25519 keypair + DNS-verified scope list (explicit, not
  wildcard, periodic re-verify).
- Channel 1 — push: hash-evidenced contributions, peer-volunteered, no
  query surface.
- Channel 2 — pull: scope-verified, exact-match, prospective-only,
  aggregate-response, rate-limited.

---

## Campaign Clustering — DSL Evolution

The DSL currently models campaigns as linear phase sequences with clear actor assignments. Real campaigns are messier — phases overlap, actors share responsibilities, tool signatures drift over time. The fixtures don't test for overlapping phases or ambiguous actor assignments. That's probably fine for v1 — the six fixtures cover the known failure modes — but the replay tier will reveal whether you need to add fixtures for phase overlap or role ambiguity. The DSL has a natural extension path: concurrent phases, multi-actor per phase, probabilistic phase ordering. You don't need it now, but the design doesn't block it.

---

## Threat Intel Enrichment — Provider Backlog

Long list of candidate sources for `decnet/intel/`. Open / free-tier
prioritized; Shodan is the explicit paid exception. v1 ships three
(GreyNoise Community, AbuseIPDB, abuse.ch); the rest are post-v1 fodder
slotted in as demand surfaces.

### Reputation / abuse reports
- AbuseIPDB — community abuse scores, free 1k/day **[v1]**
- CrowdSec CTI — community blocklist API, free
- Spamhaus DROP/EDROP — hijacked netblocks, free
- CINS Score (Sentinel IPS) — reputation feed, free
- FireHOL IP lists — aggregated reputation (GitHub), free
- Project Honey Pot HTTP:BL — DNSBL for HTTP attackers, free
- Emerging Threats open — free blocklist

### Scanner / noise classification
- GreyNoise Community API — purpose-built for honeypot noise filtering, free **[v1]**
- DShield / SANS ISC — scanned-IP feeds, free
- Tor Project exit-node list — free, no key

### Active C2 / malware attribution
- abuse.ch Feodo Tracker — botnet C2 IPs, free, no key **[v1]**
- abuse.ch ThreatFox — IOCs from malware analysis, free **[v1]**
- abuse.ch URLhaus — malicious URLs, free
- abuse.ch SSLBL — malicious TLS certs, free
- abuse.ch MalwareBazaar — payload hashes (pairs with payload capture)
- AlienVault OTX — pulse-based IOCs, free with key

### Host scan / infrastructure
- Shodan — paid, cheap tiers (approved exception)
- Censys — free tier, host scan data
- BinaryEdge — ~250/mo free
- CIRCL passive DNS / passive SSL — free for researchers
- VirusTotal — 4 lookups/min free

### Network ownership / geo
- Team Cymru IP-to-ASN whois — free DNS-based, no key
- IPinfo — free tier, ASN/company
- MaxMind GeoLite2 — already in use (GeoIP mapping)

### Misc
- Cloudflare Radar — aggregate intel, free
- Pulsedive — IOC enrichment, free tier
- MISP communities — federated OSINT

