The standard open-source Python library and CLI for the canonical NRDAX
registry (NullRabbit Decentralised Attack indeX). Structurally familiar to users
of MITRE's mitreattack-python, but designed for the NRDAX schema and semantics —
not forced into an ATT&CK model.
Inspected the NRDAX backend (nrdax/api, Rust), its pinned openapi.yaml, the
golden feed artifacts, and the live surfaces. Findings:
- Two machine interfaces exist:
- Static feed — files emitted by
nrdax-emit:index.json(version, doi, technique ids),registry.jsonl(one technique per line),families.json,coverage-matrix.json,stix.json, per-technique JSON,feed.xml(Atom),knowledge-pack.jsonld. Deterministic, golden-tested, version-pinnable. - Read API — live at
https://api.nrdax.com(/techniques,/techniques/{id},/families,/instances,/cve/{ref},/coverage,/search). Confirmed live (returns real JSON).
- Static feed — files emitted by
- Canonical dataset source chosen: the static feed. It is the version-pinnable,
offline, deterministic snapshot format (the NRDAX analogue of MITRE's
attack-stix-datastatic JSON). The read API is the live-query source. - Current dataset: version
v0.1-import, 381 techniques, no DOI. 291/381 techniques have zero instances (known-but-not-reproduced); 90 are reproduced. 19/29 families used, 24 instance chains (64 incl. known coverage), all 5 reference kinds present.
- Technique:
id(NRDAX-Tnnnn, opaque/stable),name(stable slug),display_name?,mechanism,family,status(active|deprecated|superseded),first_seen(date),instances[],external_references[],provenance_note?. - Instance:
chain,primitive_id,bundle_ref,fidelity(stub|proxy|lab|production-derived|production-captured),discovery_origin(original-research|reverse-engineered-cve|disclosed-advisory),external_references[]. - ExternalReference:
kind(cve|ghsa|vendor-advisory|nr-advisory|nr-brief),id,url?. - Coverage matrix (derived):
chains[],cells[](reproduced,strongest_fidelity+instance_count),known[](known-but-not-reproduced). - Families: fixed 29-name taxonomy with per-family counts.
- STIX 2.1:
attack-patternSDOs; NRDAX id anchored inexternal_references(source_name:"nrdax"); custom propsx_nrdax_family|status|chains; ids are UUIDv5 under namespace6e727669-615f-5f5f-8000-000000000001.
- No
implementationfield and nosurfacefield. CLI--implementation/ search on "surface" are therefore documented derived text filters overprimitive_id+mechanism+ reference text, not schema-backed. - No asserted technique↔technique relationships (no parent/child/related
field).
relatedsurfaces derived links: shared family, shared chain, shared reference. Documented as derived. - Reproduction status is derived, not a stored field:
reproduced(≥1 instance) vsknown(0). NRDAXstatusis the lifecycle enum (active/deprecated/superseded), a distinct axis. - No
schema_versionin the feed. We pinNRDAX_SCHEMA_VERSION = "1.4"(the contract version from the backend) and mark it tool-tracked. - No DOI minted (
v0.1-import). Citations never fabricate a DOI; they omit it and note absence. - No published historical versions. Cross-version
changesrequires two snapshots the user supplies/caches;--sinceworks fromfirst_seen. Documented as a source limitation, with a real diff engine (not simulated). - Feed vs OpenAPI drift: feed
coverage-matrix.jsoncarriesknown; OpenAPI/coveragedocumentsgaps. We model the feed (known) and note the drift. - The static feed is not yet published at a stable public URL
(
nrdax.com/index.json→ 404). Soupdate's working remote is the live API; the feed URL becomes the preferred canonical remote once published.
- Repo
nrdax-python, packagenrdax, CLInrdax. PyPInrdaxis available. - GitHub home
NullRabbitLabs/nrdax-python(siblings:nrdax-api,nrdax-web).
Existing NullRabbit code repos are UNLICENSED; the dataset has no explicit
license. Resolution: code is Apache-2.0 (MITRE-tooling norm, patent grant);
the NRDAX dataset licensing stays separate and undetermined (DATA_LICENSE.md
NOTICE). This tool grants no rights to the data. Flagged in the final report — NullRabbit should publish an explicit data license before wide distribution.
- Zero required runtime dependencies (stdlib
json,urllib,uuid,argparse,csv,dataclasses) → fast CLI startup, trivial install. Optional extra[stix]adds thestix2validator;[dev]adds pytest/mypy/ruff/build. - Source-independent domain ops.
Sourceprotocol →BundledSource,FeedSource(dir or URL),ApiSource,FileSource,StixSource,MemorySource.NRDAX.load()uses the bundled snapshot (offline, zero-config). - Typed dataclass models with
from_dict/to_dict, preserved unknown fields (extra), missing≠empty (Optional vs[]). Lenient parse + collectedValidationIssues (strict=Trueto raise); never discards data. - Deterministic search (weighted field matches, stable tie-break), composable
filters, derived relationships, coverage, JSON/CSV/STIX exporters
(STIX byte-parity with the Rust emitter), citations (text/md/bibtex/json),
snapshot-diff changes, cache (
~/.cache/nrdax,meta.json). - CLI (argparse):
search get list related export cite changes info version update cache schema. Structured errors + stable exit codes (0 ok, 2 usage, 3 not-found, 4 invalid-arg, 5 source/data).
src/nrdax/{__init__,_version,vocab,models,errors,registry,cache,relationships,coverage,citations,changes}.py
src/nrdax/sources/{bundled,feed,api,file,stix,memory}.py
src/nrdax/queries/{search,filters}.py
src/nrdax/exporters/{json_exporter,csv_exporter,stix_exporter}.py
src/nrdax/cli/{main,formatting}.py
src/nrdax/data/snapshot/{index.json,registry.jsonl,families.json,coverage-matrix.json} # real v0.1-import
tests/… examples/… docs/… .github/…
Run the library over the full real dataset to confirm every record loads and round-trips (a local validation script generates a report that is kept out of the repo). Then tests + mypy + ruff, build wheel+sdist, clean-install smoke test. GitHub + PyPI steps documented (no credentials committed).