Gunnar

mikronova/modgunn

.nornir/modgunn-security-scanner.md

<!-- nornir:class: design -->
# Móðguðr — the nordisk security / SCIP world-delta scanner (3rd repo proposal)

> Roadmap design. Driver: [`holger/.nornir/holger-demo-plan.md`](../../holger/.nornir/holger-demo-plan.md)
> (Tools menu #3). Umbrella: [`holger/.nornir/holger-evolution.md`](../../holger/.nornir/holger-evolution.md).
> Workspace decision stub: `workspace_nordisk/PLAN.md`.

## Why a 3rd repo
Holger needs security verdicts; nornir owns the security plane (`nornir/src/security.rs`) and the
SCIP/knowledge/warehouse machinery. Having holger depend on nornir (and nornir embed holger) would
create a **dependency cycle**. So the scanner moves to its own leaf repo both consume:

```
        Móðguðr  (security + SCIP world-delta engine, leaf)
        /      \
   holger      nornir        ← both depend on Móðguðr; no cycle
```

**Name:** **Móðguðr** (Modgud) — **CONFIRMED (Rickard, 2026-06-28)**. The giantess who guards
**Gjallarbrú**, the bridge over the river Gjöll on the road to Hel; she challenges everyone who
tries to cross and demands to know who they are. A gatekeeper that scrutinizes what passes —
exactly the role. (Female, on-myth.)

## Two engines, one repo
The user wants both, and they are genuinely different concerns:

### Engine A — artifact security verdict (the "scan")
- CVE / license / provenance scan over an artifact or a whole znippy generation.
- Extract + generalize nornir's existing `src/security.rs` (`scan` / `components` / `query_vulns`
  / `to_cyclonedx`, cargo-only today) into Móðguðr; make it **multi-ecosystem** (the whole point
  of an artifact server that serves Maven/PyPI/npm/… ).
- Emits a cached verdict keyed by `(blob_sha256, policy_version, advisory_db_rev)` → `block|warn|pass`
  + CycloneDX SBOM. This is the verdict holger's policy gate / promotion gate and static-holger
  quarantine consume.

### Engine B — SCIP whole-world dep-tree behavior-delta tracking (government-grade)
- Take in the **whole world** (or a sub-part of a product's deps) and keep **delta track of EVERY
  single change in the code tree**, to the smallest bit — including **deep transitive behavior
  change**: "a 5th-level dependency jar that hasn't changed in years suddenly changed behavior."
- Built on nornir's SCIP / knowledge / call-graph / arch-wiring machinery, lifted to Móðguðr.
- **Multiple tripwires + graph algorithms + anomaly detection** — not just a diff: behavior
  fingerprints, call-graph deltas, provenance/timeline anomalies, dependency-confusion / typosquat
  / maintainer-change signals, statistical outliers over the historized world.
- **Resource posture: government site, knows exactly what happens to the smallest bit** — designed
  to use **128 GB – 1 TB RAM** when running a full-world delta. Heavy by design; lives in the
  dynamic plane only.

## Static-holger angle (keep static dumb-but-safe)
- The scanner is **NOT compiled into static-holger** (no egress, no heavy engine).
- Static's only security action: **validate a new znippy, then switch to it** — i.e. consume a
  verdict produced by Móðguðr/dynamic, and atomically swap the served archive to a validated one.
  Quarantine = swap to a validated generation. No scanning, no filtering logic inside static.

## Consumers
- **holger** (dynamic): Tools menu #3 "Security scan"; policy gate before serve/promote; the
  verdict that static later switches on.
- **nornir**: the viz security pane, warehouse historization (`sbom_components`/`vuln_findings`),
  and the world-delta tripwire feed.

## Storage decision (2026-06-29, owner-confirmed)
Engine B persists via **`modgunn → skade`** (skade = the Apache Iceberg warehouse lib, a leaf below
both nornir and modgunn). modgunn owns its **own** world-delta tables (`world_snapshots`,
`behavior_deltas`, `tripwire_events`) in the shared Iceberg warehouse — independent writer, same
physical catalog. modgunn depends on **skade only, NEVER on nornir** (a nornir dep would recreate the
holger↔nornir cycle modgunn exists to break). Phasing: **Engine A / demo = pure leaf** (no storage dep;
nornir hands SCIP facts in and historizes results) → migrate to `modgunn → skade` when Engine B's
world-delta data volume justifies owning tables. nornir stays the **consumer** (viz pane, tripwire feed).

## Open decisions
- Repo bootstrap: extract from `nornir/src/security.rs` + the SCIP/knowledge modules, or greenfield
  with nornir as the first internal user.
- Engine A and Engine B share a repo but are separate crates/features (lean A for holger's gate;
  heavy B for the world-delta site).
- Advisory DB sourcing in airgap (offline OSV/CVE snapshots carried in a sealed znippy).
- Exact tripwire/anomaly algorithm set (graph diff, behavior fingerprint, statistical models).