Phase-1 MVP: ~10 weeks, two orthogonal capture tracks in parallel, a shared open platform API with one bootstrap mirror in the EU. K1–K7 are the binding acceptance tests.
1. Scope#
| In scope | Out of scope (phase 2/3) |
|---|---|
| Track A — registry scraping (npm, PyPI, Maven Central, crates.io, Go mod) + GitHub manifest crawl | End-user agent (mode B), hybrid aggregator (mode C) |
| Track B — self-instrumentation SDK (JavaScript, Python) | Further SDK languages (Java, Go, Rust, .NET) |
| One reference mirror in the EU (single-node ClickHouse + API server + snapshot worker + Rekor client) | A second independent mirror, gossip federation |
| Submission protocol v1.0 (declarative k-anon attestation) | ZK-SNARK attestation, threshold consensus |
| Open Data snapshots (Parquet, CSV.gz), Public Audit Log | Mix-net submission, threshold decryption |
| Documentation and federation registry skeleton | Final mirrors.json governance |
2. Track A — public registry scraping#
Goal: an immediately visible reach figure from public data, without maintainers or end users having to take part.
2.1 Sources#
| Registry | Top list (discovery) | Package / dependency source | Frequency |
|---|---|---|---|
| npm | curated in-tree seed (src/scraper/.../seeds/npm.json) | https://registry.npmjs.org/<name>/latest | daily |
| PyPI | https://hugovk.dev/top-pypi-packages/top-pypi-packages.min.json | https://pypi.org/pypi/<name>/json | daily |
| Maven Central | https://search.maven.org/solrsearch/select?q=*:* (Solr caps rows≤200) | Dependencies from the POM XML: https://repo1.maven.org/maven2/<g>/<a>/<v>/<a>-<v>.pom | daily |
| crates.io | https://crates.io/api/v1/crates?sort=downloads | https://crates.io/api/v1/crates/<name> | daily |
| Go modules | curated in-tree seed (src/scraper/.../seeds/golang.json) | https://proxy.golang.org/<module>/@latest | daily |
| GitHub manifest crawl | Top-N public repository list (public package.json, requirements.txt, pom.xml, Cargo.toml, go.mod) | — | weekly, top 1000 per language |
A note on the top lists: npm and Go modules have no maintained, compact "top packages" JSON endpoint (npm: only a 106 MB volume list of all ~4.1 million names; Go:
index.golang.org/indexis a chronological "recently added" feed, not a usage ranking). Both are therefore fed from a curated, checked-in seed (reviewable, and K1 stays meaningful). Maven Central supplies no dependencies over Solr — those exist only in the POM (XML), which is therefore fetched separately fromrepo1.maven.organd parsed withdefusedxml.
2.2 Aggregate shape (track A)#
{
"project_id": "pkg:npm/example-lib",
"version_id": "1.4.2",
"cohort_id": "dep-edges-from-public-manifests",
"metric": "observed_dependency_count",
"value": "1247",
"window_start":"2026-05-26T00:00:00Z",
"window_end": "2026-05-27T00:00:00Z",
"source_mode": "D",
"provenance": "registry-scraper@mirror-eu-1"
}
2.3 k-anon in track A#
The data comes from public sources → the k constraint is trivially satisfied. Schema-reject-PII remains in place as defense in depth (for instance no maintainer e-mails in the aggregate, no owner names in the clear from GitHub metadata).
3. Track B — self-instrumentation SDK (JS + Python)#
Goal: the runtime usage-duration view that track A cannot deliver.
3.1 SDK API (abbreviated)#
JavaScript:
import { instrument } from "@prometheus/sdk";
instrument({
projectId: "pkg:npm/example-lib",
versionId: "1.4.2",
cohorts: ["rt-runtime-mins", "install-count"],
mirror: "https://mirror-eu-1.example", // Default mirror via the federation registry
did: process.env.PROMETHEUS_DID // optional in phase 1, mandatory from phase 2
});
Python:
from prometheus_sdk import instrument
instrument(
project_id="pkg:pypi/example-lib",
version_id="2.0.0",
cohorts=["rt-runtime-mins", "install-count"],
mirror="https://mirror-eu-1.example",
)
3.2 Events the SDK emits#
| Event | Trigger | Cohort mapping |
|---|---|---|
install | Module load for the first time in the process | install-count |
session-start | First use of a function in the process | rt-runtime-mins (counter start) |
runtime-tick | Every 5 min, as long as the library is in use | Updates the rt-runtime-mins band |
feature-flag | Optional custom hook (e.g. prometheus.flag("v2-api-used")) | feature-flag-counter |
3.3 Aggregate shape (track B)#
{
"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"
}
3.4 Banding (prevents PII inference)#
Numeric values are mapped onto bands, never carried as a number in the clear:
| Band | Meaning |
|---|---|
0-5, 5-15, 15-30, 30-60, 60-180, 180-1440, 1440+ (in minutes) | Runtime bands |
1, 2-5, 5-25, 25-100, 100-1000, 1000+ | Install count bands |
Custom bands per cohort_id definition | Published in the federation standard |
4. Bootstrap mirror#
Phase-1 configuration: single node, EU-hosted, Apache-2.0 open-source stack.
| Component | Stack |
|---|---|
| Aggregate store | ClickHouse 24+ single node (or a TimescaleDB equivalent) |
| Submission API | Rust/Go/Python — FastAPI or axum recommended |
| Public Audit Log | Sigstore Rekor (self-hosted instance) |
| Snapshot generator | Daily Parquet and CSV.gz generation at 00:00 UTC |
| Open Data API | REST + GraphQL, no auth |
| Federation registry entry | An entry in mirrors.json (the central bootstrap repository) |
| Local compose variant | docker compose up for reproducible research setups |
5. Success criteria K1–K7#
| ID | Criterion | Measurement |
|---|---|---|
| K1 | Reach discovery: the top 1000 OSS packages per registry are captured in track A at ≥ 95 % | Daily sampling check against the npm/PyPI/Maven lists |
| K2 | False-positive rate <unclassified>: ≤ 5 % of the captured dep edges remain without a project_id resolution (PURL match) | Validator against the PURL standard |
| K3 | Schema-reject PII: track A and track B produce 0 submissions with PII fields. The backend schema actively rejects attempts | Negative test with deliberate PII payloads, a mirror reject is expected |
| K4 | k-anon effect in track B: no cohort_id with unique_sessions < 5 (default) in the Open Data snapshot | Sample audit after snapshot generation |
| K5 | Availability & resilience: a 30-minute mirror outage → 0 data loss (the SDK buffers locally and retransmits; the scraper is idempotent) | Chaos test in staging |
| K6 | Open Data usability: a researcher answers the question "which top-10 OSS packages have doubled their reach in the last 12 months?" over the Open Data dump in under 2 minutes (no login needed) | User test with a research persona who has not been briefed |
| K7 | SDK footprint: the track-B SDK consumes < 5 ms CPU/h and < 2 MB RAM in steady state in a Node.js or Python process | Profiler run per CI build |
6. Sprint plan (10 weeks, provisional)#
| Sprint | Weeks | Focus |
|---|---|---|
| 1 | 1–2 | Federation registry skeleton (mirrors.json with one entry), submission protocol spec, reference mirror stub |
| 2 | 3–4 | Track A: scraper cores (npm + PyPI), aggregate store, a first snapshot |
| 3 | 5–6 | Track A: Maven + crates + Go, GitHub manifest crawl, K1 check |
| 4 | 7–8 | Track B: JS SDK, Python SDK, banding, submission roundtrip, K4 check |
| 5 | 9–10 | Open Data API, Sigstore Rekor integration (K3 + K5), Public Audit Log, K6 researcher test, K7 footprint profile |
7. Out of scope for phase 1 (explicitly)#
- End-user agent (mode B as a daemon variant outside the library)
- Hybrid aggregator daemon (mode C)
- ZK-SNARK attestation (
05-zero-knowledge-vorschlag.md, building block B) - Cross-mirror gossip (arrives with mirror #2 in phase 2)
- Threshold consensus (phase 3)
- SDK languages other than JS and Python
- Build-tool plugins (Maven/Gradle/cargo hooks)
- Dashboards with live visualisation (streaming charts, real-time updates, server-side rendering) — research and vendors build their own. This does not mean the static dashboard: that is mandatory deliverable no. 9 (
07-prototyp-requirements.md§2 / §7) and carries the K6 evidence. Phase 1 therefore delivers API + Open Data dumps plus a static frontend served by the mirror without a build step.
8. Cross-references#
01-konzept.md— the overall v2 concept03-risikoprofil.md— phase-1-relevant risks (D-2-1, M-2-1, M-2-2 prioritised)04-executive-summary.md— stakeholder brief05-zero-knowledge-vorschlag.md— ZK building blocks (phase 2+)06-offene-punkte.md— phase-1 blockers (mirror discovery, licence, anti-Sybil, GDPR per mirror)- ADRs:
ADR-v2-0001(federation),ADR-v2-0002(four modes),ADR-v2-0003(aggregate key),ADR-v2-0004(licence + audit log)