Vom Rohereignis im Endnutzerprozess (oder im Registry-Crawl) bis zum Open-Data-Snapshot. Jede Stufe markiert, ob PII theoretisch durchlaufen könnte und welche Defense-in-Depth-Schicht dort greift.
1. Stufen-Übersicht#
Stufen im Endnutzer-Prozess (1–8):
- RAW Event (RAM, nie auf Disk) — install · session-start · runtime-tick · feature-flag · dep-load.
- Sanitize — Hosts, Pfade, Env-Vars droppen oder hashen.
- Cohort-Map — Identifier auf
(project_id, version_id, cohort_id)mappen per lokaler Konfiguration. - Aggregate-Window — Tumbling 1h-Window (lokal auch 1min für Debugging).
- k-Anon-Guard — wenn
k_effective < k_min, dann incohort_id="__small_cohorts"umschreiben. - Schema-Reject-PII — JSON-Schema-Validierung; verbotene Felder lehnen ab.
- Sign + Attestation — ed25519-Signatur; deklarative Claims (Phase 1) / SNARK (Phase 2).
- Egress-Buffer — lokale Disk, nur signierte Aggregate; Retry mit exponential backoff; max 30 min vor Verlust-Risiko.
Übergang über HTTPS POST /v1/submit (mTLS optional).
Stufen am Mirror (9–14):
- Submission-Validierung — Signatur, Schema,
k_effective ≥ k_min, Window-Plausibilität. - Append-Only-Log — Sigstore-Rekor-Hash-Chain, public verifiable.
- Aggregate-Store — ClickHouse, Partition
toYYYYMM(window_start). - Gossip / Pull-Federation — Mirror-zu-Mirror Konsistenz-Replikation.
- Open Data Snapshot — täglich 00:00 UTC, Parquet + CSV.gz, CC-BY-4.0.
- Open Data API — Read-only, keine Auth.
Parallel — Modus D (Registry-Scraping) läuft Mirror-intern und beginnt bei Stufe 3 (Cohort-Map auf bekannte PURLs); die Stufen 1, 2 entfallen, weil keine privaten Endnutzer-Events gelesen werden.
2. Was wo passiert — Verantwortlichkeiten#
| Stufe | Verantwortlich | Datenschutz-Schicht |
|---|---|---|
| 1 RAW EVENT | Endnutzer-Prozess (RAM) | Nie persistent. Wird vom OS bei Prozess-Ende verworfen. |
| 2 SANITIZE | Lokaler Aggregator | Erste PII-Schicht: bekannte sensible Felder leeren/hashen. |
| 3 COHORT-MAP | Lokaler Aggregator + Maintainer-Config | Identifier werden auf Kategorien gemappt (session-15-30min statt Klartext). |
| 4 AGGREGATE-WINDOW | Lokaler Aggregator | Reduziert auf Counts/Sums pro Window — keine Einzelevents mehr. |
| 5 K-ANON GUARD | Lokaler Aggregator | Zweite PII-Schicht: Kohorten unter k_min werden in __small_cohorts zusammengefasst. |
| 6 SCHEMA-REJECT-PII | Lokaler Aggregator | Dritte PII-Schicht (Defense-in-Depth): JSON-Schema rejected verbotene Felder. |
| 7 SIGN + ATTESTATION | Lokaler Aggregator | Signatur belegt Provenienz; Attestation belegt Anonymitäts-Constraints. |
| 8 EGRESS-BUFFER | Lokale Disk (nur signierte Aggregate) | Disk-Daten enthalten keine PII mehr (Stufen 2–6 vorher). |
| 9 SUBMISSION-VALIDIERUNG | Mirror | Vierte PII-Schicht (Defense-in-Depth): Mirror prüft Schema, lehnt PII ab. |
| 10 APPEND-ONLY-LOG | Mirror | Hash-Chain — keine PII enthalten, weil Aggregate vorher gefiltert sind. |
| 11 AGGREGATE-STORE | Mirror | ClickHouse-Partition. Public-Read. |
| 12 GOSSIP | Mirror ↔ Mirror | Replikation derselben PII-freien Aggregate. |
| 13 SNAPSHOT | Mirror | Tägliches Open-Data-Artefakt (Parquet/CSV.gz). |
| 14 OPEN DATA API | Mirror | Lese-API ohne Auth. |
3. Was niemals den Endnutzer-Prozess verlässt#
Folgende Begriffe sind in keiner Stufe ≥ 7 erlaubt — Schema-Reject-PII würde sie zurückweisen, und das ist Defense-in-Depth, nicht der primäre Filter:
- IP-Adresse, MAC-Adresse, Hostname, FQDN
- Username, E-Mail, OS-User, Process-PID
- Datei-Pfade aus dem Host-Filesystem
- Klartext-Git-Repo-Pfade oder Branch-Namen
- Maintainer-Klartextnamen oder Maintainer-E-Mails
- Inhalt von Environment-Variablen
- Zeitstempel feiner als die Window-Granularität (typ. 1 h)
Was darf drin sein:
project_id(PURL — öffentliche Paket-Kennung)version_id(semver / ähnliche — öffentlich)cohort_id(vordefinierte Kategorie wiert-runtime-mins,install-count)- Banded numeric values (
30-60,100-1000) k_effective,k_min(Audit-Felder)- Provenienz:
submitter_did, Signatur, Mirror-Routing
4. Aggregat-Beispiele#
Modus A (Self-Instrumentation in OSS-Lib pkg:npm/example-lib):
{
"project_id": "pkg:npm/example-lib",
"version_id": "1.4.2",
"cohort_id": "rt-runtime-mins",
"metric": "runtime_minutes_band",
"value": "30-60",
"k_effective": 23,
"k_min": 5,
"window_start": "2026-05-26T12:00:00Z",
"window_end": "2026-05-26T13:00:00Z",
"source_mode": "A"
}
Modus D (Registry-Scraping über pkg:pypi/example-lib):
{
"project_id": "pkg:pypi/example-lib",
"version_id": "2.0.0",
"cohort_id": "dep-edges-from-public-manifests",
"metric": "observed_dependency_count",
"value": "1247",
"k_effective": null, // Track A liest nur öffentliche Manifeste, k-Anon trivial
"k_min": null,
"window_start": "2026-05-26T00:00:00Z",
"window_end": "2026-05-27T00:00:00Z",
"source_mode": "D"
}
5. Outage- und Replay-Verhalten#
| Szenario | Verhalten |
|---|---|
| Mirror unerreichbar | Lokaler Aggregator puffert signierte Aggregate auf lokaler Disk (Stufe 8). Retry mit exponential backoff; nach 30 min Pufferüberlauf-Risiko (K5). |
| Duplicate Submission | Mirror hat submission_hash bereits im Audit-Log → idempotenter 200 OK ohne Doppel-Insert. |
| Cross-Mirror-Divergenz | Gossip-Job vergleicht audit_log.head; bei Divergenz wird im eigenen /v1/status ein Disclosure-Eintrag gesetzt. Forschung kann dann beide Sichten exportieren und vergleichen. |
| Maintainer rotiert DID | Neuer DID wird in submitter_id verwendet; alter DID bleibt im Audit-Log historisch nachweisbar. Keine Migration nötig. |
6. Querverweise#
system-overview.md— Komponentensichtthreat-model.md— STRIDE über diesen Fluss03-risikoprofil.md— Risiken D-2-1 … S-2-305-zero-knowledge-vorschlag.md— ZK-Bausteine zur Härtung02-poc-spezifikation.md— Phase-1-MVP-Tracks A + B