Skip to content

Python API

The runtime is stdlib-only. The main entry points:

from dragnet.graph import KnowledgeGraph
from dragnet.ingest import load_case
from dragnet.ach import assess
from dragnet.report import to_markdown
from dragnet.stix import to_stix

kg = KnowledgeGraph.load("dragnet/data/campaigns.json")
case_id, items, signals, custody = load_case("dragnet/data/cases/wannacry_like.json")
a = assess(case_id, signals, kg, custody=custody)
print(a.leading, a.confidence.value)

dragnet.ach.assess

assess(case_id: str, signals: list[Signal], kg: KnowledgeGraph, weights: dict[SignalKind, float] | None = None, custody: CustodyLog | None = None, config: EngineConfig = DEFAULT_CONFIG) -> Assessment

Score every actor hypothesis for one case and grade the result.

Parameters:

Name Type Description Default
case_id str

identifier echoed into the report.

required
signals list[Signal]

typed signals from :func:dragnet.ingest.build_case.

required
kg KnowledgeGraph

knowledge graph to match against.

required
weights dict[SignalKind, float] | None

per-kind weight overrides (merged over the defaults).

None
custody CustodyLog | None

optional custody log attached to the report.

None
config EngineConfig

thresholds for the confidence ladder.

DEFAULT_CONFIG

Returns:

Type Description
Assessment

an :class:Assessment with the ACH matrix, flags, grade and guidance.

dragnet.ingest.load_case

load_case(path: str | Path, custody: CustodyLog | None = None)

Load a case JSON: {"case_id", "evidence": [{"id","kind","content"}]}.

dragnet.ingest.build_case

build_case(data: dict, custody: CustodyLog | None = None)

Validate a case document and turn it into (case_id, items, signals, custody).

Raises :class:IngestError on malformed input or when a size limit is exceeded.

dragnet.models.Assessment dataclass

dragnet.custody.CustodyLog

Append-only, hash-chained log of evidence items; verify() detects any change.

dragnet.custody.sign_entries

sign_entries(entries: list[dict], private_pem: bytes, report: dict | None = None) -> dict

Sign a report with an Ed25519 PEM key (needs the sign extra).

With report (the JSON report dict, without a signature block) the signature covers the report body and the custody chain head (v2); without it only the chain head (v1, legacy).

Returns:

Type Description
dict

a dict with the algorithm, version, public key, head hash, body hash and signature.

dragnet.custody.verify_signed

verify_signed(entries: list[dict], sig: dict, public_pem: bytes | None = None, report: dict | None = None) -> bool

True iff the chain is intact and the signature is valid.

For a v2 signature pass the full report: its body is re-hashed, so any edit to the verdict, confidence, hypotheses, weights or matrix fails verification. Pass public_pem to pin the expected signer; otherwise the embedded key is used (integrity only, not identity).

dragnet.stix.to_stix

to_stix(a: Assessment, created: str | None = None) -> dict

Convert an assessment into a STIX 2.1 bundle (campaign, intrusion-set, attributed-to, note).

Parameters:

Name Type Description Default
created str | None

fixed timestamp for reproducible output; defaults to now (UTC).

None

dragnet.adapters.from_revenant

from_revenant(doc: dict, item_id: str = 'revenant') -> tuple[dict, list[str]]

REVENANT JSON export -> (forensic evidence item, analyst notes).

dragnet.adapters.from_vitrine

from_vitrine(doc: dict, item_id: str | None = None) -> dict

VITRINE triage JSON -> malware evidence item (feature record, never a binary).

dragnet.bench.bootstrap_ci

bootstrap_ci(rows: list[dict], keys=CI_KEYS, n_boot: int = 2000, seed: int = 0, alpha: float = 0.05) -> dict[str, list[float]]

Percentile bootstrap CIs over cases (resampling rows with replacement).

dragnet.bench.paired_bootstrap_diff

paired_bootstrap_diff(rows_a: list[dict], rows_b: list[dict], key: str = 'top1', n_boot: int = 2000, seed: int = 0) -> dict

Paired bootstrap of mean(a) - mean(b) over the same cases (in-KG rows).