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: |
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: |
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).