jfind
Canonical findings + evidence schema — Ed25519-signed, Merkle-rooted, deterministic JSON
v1.0.0
Linux
Quick Start
Install via jcli (recommended)
jcli install jfind
Sign-and-verify smoke test
jfind new-key --out /tmp/jfind.key # fresh Ed25519 keypair
jfind sign --key /tmp/jfind.key finding.json # sign in place
jfind verify finding.json # exit 0
sed -i 's/AWS/AwS/' finding.json
jfind verify finding.json # exit 3 — signature broken
What it does
jfind is the contract between every tool in the cli.johlem.net suite and the
consulting deliverable layer (complymap, dossier,
inciclass, roigen, tiberscope). It defines a single
versioned shape — jfind/v1 for findings, jfind-bundle/v1 for
engagement-scoped bundles — and ships the library + CLI to validate, sign, merge, filter,
and report on it.
- Deterministic canonical JSON. All object keys sorted lexicographically; identical logical documents produce byte-identical output, regardless of the producer's key insertion order. This is what makes signatures portable.
- Ed25519 signatures. Every finding can carry its own signature; bundles carry one signature over a BLAKE3 Merkle root of their findings. Removing or reordering findings invalidates the bundle.
- Custody chain. Optional per-finding event log where each event's
hashlinks to the previous one. Tampering at any step breaks the chain. - Offline-only. No network calls anywhere. Operates on local
.jsonfiles only. - Suite contract. Every other tool in the consulting layer reads and writes this format. Build the contract right and the rest is wiring.
Subcommands
| Command | What it does |
|---|---|
jfind validate <file…> | Schema + signature + custody chain. Warns on unknown vocabulary, exits 3 on integrity failure. |
jfind verify <file…> | Strict signature + chain check. Missing signature is a hard error (exit 3). |
jfind sign --key <path> <file> | Sign a finding or bundle in place. Bundles auto-sign every contained finding first, then sign the Merkle root. |
jfind merge <bundle…> -o <path> | Engagement-scoped, deterministic dedupe. Same inputs → byte-identical output. |
jfind filter <bundle> | Sub-bundle by --severity high.., --since, --tool, --category. Output on stdout. |
jfind stats <bundle> [--format json] | Counts by severity, category, tool, asset type. |
jfind new-key --out <path> | Fresh Ed25519 keypair. Private seed → file (mode 0600); public key → stdout. |
Schema (jfind/v1)
{
"schema": "jfind/v1",
"finding_id": "<uuid-v4>",
"tool": "credsweep",
"tool_version": "1.2.2",
"timestamp": "2026-06-10T12:00:00Z",
"title": "AWS secret key committed to git history",
"severity": "info|low|medium|high|critical",
"confidence": "low|medium|high|confirmed",
"category": "credential-exposure",
"asset": { "type": "file", "identifier": "...", "owner": "client:..." },
"mitre": { "tactics": ["TA0006"], "techniques": ["T1552.001"] },
"controls": [ { "framework": "DORA", "ref": "Art.9", "rts": "RTS 2024/1774 Art.11" } ],
"evidence": [ { "kind": "file", "path": "...", "sha256": "...", "blake3": "...", "bytes": 1234 } ],
"remediation": "...",
"raw": { /* opaque, producer-specific */ },
"provenance": { "host": "...", "engagement_id": "ENG-...", "chain": [ ... ] },
"signature": { "alg": "ed25519", "pubkey": "<b64>", "sig": "<b64>" }
}
Exit codes
| Code | Meaning |
|---|---|
0 | Ok |
1 | Reserved for downstream tools (e.g. complymap gap) |
2 | Usage error (clap) |
3 | Integrity / signature / parse failure |
Touches / Produces / Gates
- Touches (read-only): the
.jsonfiles you point it at; a private-seed file forsign. - Touches (write):
signrewrites the input file in place (canonical JSON).new-keywrites the private seed to--outwith mode 0600. - Produces: stdout —
[ ok ]lines, canonical JSON (filter, stats --format json), or the public key (new-key). - Gates: none.
jfindis offline and side-effect-light;--i-am-authorizedis not required.
Build from source
cd tools/jfind/rust
cargo build --release
cargo test # 3 unit + 8 integration tests
Toolchain pin: Rust 1.85.0. Dependencies are all in the suite-permitted allowlist
(serde, clap, chrono, uuid,
sha2, blake3, ed25519-dalek,
base64, anyhow, thiserror).