Skip to content

feat: classify endpoint locality and filter by traffic direction - #108

Open
flamboh wants to merge 5 commits into
maad/01-10m-bucketsfrom
maad/02-locality-direction
Open

flamboh wants to merge 5 commits into
maad/01-10m-bucketsfrom
maad/02-locality-direction

Conversation

@flamboh

@flamboh flamboh commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Note

🤖 Claude Opus 5.5 on behalf of Oliver

ELI5

Instead of guessing "inside vs outside" from a network-specific anonymization flag, each dataset now lists rules for what counts as internal, and the dashboard can filter traffic by direction: incoming, outgoing, inside-to-inside, or neither.

Why

Endpoint "visibility" came from the anonymizer's ToS bits, which only works on our network and misclassifies known literal internal hosts. Direction (ingress/egress/lateral) is what analyses actually need.

Implementation

  • Per-dataset locality rules: tos_anonymized (the old behavior), prefixes (CIDRs), addresses (inline list or file). An endpoint is internal if any rule matches. Validation is strict; IPv4-mapped IPv6 is canonicalized.
  • Direction from (src, dst): egress, ingress, lateral, transit (external→external, kept so bad records stay visible).
  • Storage keeps its shape: src_locality/dst_locality (internal/external/all) replace src_visibility/dst_visibility.
  • Product identity records rule counts and SHA-256 digests (never the addresses), so editing rules yields a new product.
  • Address files are read only for the dataset being run, so one unreadable file doesn't break other datasets.
  • PR feat(netflow-db): add coordinated active-source subsets #105's daily_active_sources selection migrated to locality.
  • Web: shared direction filter (all/ingress/egress/lateral/transit) on every chart; hidden for datasets without rules (datasets.has_locality).

Review path

  1. Registry mode. Add to a dataset in datasets.json:
    "locality": [
      { "type": "tos_anonymized" },
      { "type": "addresses", "path": "data/private/internal.txt" }
    ]
    Run pipeline --dataset <id> for one day. Expected: rows use src_locality/dst_locality; the four directions sum exactly to all; datasets.has_locality = 1. Relative paths resolve from the repository root.
  2. Config mode. Put "locality": [...] at the top level of a pipeline config (a per-dataset locality inside a config is rejected), with a relative address file next to the config. Run pipeline --config <path> from another directory. Expected: the file resolves from the config's directory, and every dataset in the product gets has_locality = 1.
  3. Dashboard. The direction filter changes every chart; a dataset without rules shows no filter.
  4. Obsolete local DBs. Leave a pre-locality netflow.sqlite (with src_visibility columns and no has_locality) under data/, including one that reuses a current dataset ID. Expected: the dashboard lists only current products; the old DB is skipped and doesn't shadow the current one or break the listing.

Edge cases and decisions

  • Local discovery skips any SQLite file that lacks bucket_coverage or datasets.has_locality, or that has a stats table without both locality columns. It doesn't error on them.
  • A locality filter (--src-locality/--dst-locality, daily_active_sources) without rules is rejected, because every endpoint would be external.
  • Private address lists belong under the gitignored data/.
  • ⚠️ Drizzle migration 0003 keeps only all/all rows: literal/anonymized can't be mapped to internal/external. Migration 0004 adds datasets.has_locality.
  • Production D1 validation and cutover are pending. Production will be cut over to a fresh database in the infra stack rather than migrated in place. This PR does not validate against production D1.

Verification

  • Automated: bun run format, bun run lint, bun run typecheck, bun run test:web (includes the pre-locality/duplicate-ID discovery regression and the migrations for all five stats tables), bun run test:db (rule evaluation, direction mapping, fingerprint changes, registry errors, rollup parity, config-mode has_locality and config-relative address files), bun run test:e2e (passes; the fixture now uses the shared local schema and checks the served dataset metadata), cargo fmt --check, cargo clippy -D warnings.
  • Manual: run a real one-day dataset through the dashboard direction filter; production D1 cutover (pending, see above).

Made by Claude Opus 5.5 (with Opus 5.5 subagents) via Claude Code.

Replace ToS-derived endpoint visibility with internal/external locality
decided by ordered registry rules (tos_anonymized, prefixes, addresses or
address files). Stats tables store src_locality/dst_locality pairs that map
to ingress, egress, lateral, and transit. The canonical rules join the
product identity as counts and digests, and table/contract versions bump.
Replace the visibility scope picker and srcVisibility/dstVisibility params
with a shared direction filter (all, ingress, egress, lateral, transit)
mapped to the new src_locality/dst_locality columns, with a Drizzle
migration that keeps only locality-independent rollup rows.
Registry loading now checks locality rule syntax only, so one unreadable address file no longer breaks feed and pipeline for every dataset; the pipeline reads files for the datasets it runs. IPv4-mapped IPv6 prefixes, addresses and endpoints are canonicalized to IPv4. The unused Rust Direction enum is removed.

datasets gains has_locality (Rust DDL and upsert, Drizzle migration, local schema). The dashboard hides the direction filter and queries all traffic for datasets without rules. Docs keep private address lists under data/ and fix the coordinated-subset example to use /16 prefixes. The 10m rollup joins the locality additivity check, and the locality migration test covers every stats table.
@flamboh
flamboh added this pull request to stack #113 September 25, 2026 10:02
@flamboh flamboh changed the title maad/02 locality direction feat: classify endpoint locality and filter by traffic direction Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant