Skip to main content
The JSON emitter is the source of truth. SARIF, JUnit, Markdown and PDF are all derived from the same Scan model that --output json serialises. When in doubt, read the JSON. Source: reports/json_report.py.

When to use this page

  • You want the raw machine-readable scan output for a custom dashboard or threshold checker.
  • You’re writing a parser and need the canonical-form guarantees (sorted keys, deterministic shape).
  • You need a signed artifact for an audit trail.

Generate one

The same JSON is always persisted to ~/.agentguardian/scans/<scan_id>/scan.json regardless of which --output you ask for — even a --output pdf run writes the canonical JSON alongside the PDF. --output json --output-path scan.json just adds a second copy at the path you name. You can also regenerate the JSON from a stored scan:

What you get

The schema is agentguardian-scan-v1 (SCHEMA_VERSION in reports/json_report.py:52). Top-level keys, in canonical (sorted) order: The audit envelope is added under contract-driven scans (contract sha256, authorization ref, suppressed tool attempts, egress-refused turns). See reports/json_report.py:169.

Canonical form (the rules that make it deterministic)

Same inputs always produce byte-identical output. Three rules guarantee that:
  1. json.dumps(..., indent=2, sort_keys=True) — every object’s keys are sorted lexicographically before serialisation.
  2. signatures is excluded from the signing input. The HMAC + Ed25519 blocks are computed over _strip_signatures(payload) (everything except the signatures field) so signing an already-signed payload twice yields the same bytes.
  3. PII redaction is on by default (redact_pii=True). The shared redact_finding helper scrubs all five fields before serialisation — see Evidence timeline.
Together these mean: same scan, same redaction policy, same signing key → same bytes. That’s the property agent-guardian verify relies on.

Signatures

Every JSON report carries two signature channels under signatures:
  • HMAC-SHA256 — uses AGENT_GUARDIAN_SIGNING_SECRET (or the documented default for local-only smoke). The public default is never accepted on verify — without the real secret, the HMAC channel is integrity-only.
  • Ed25519 — uses a long-lived signer key under ~/.agentguardian/keys/. The public key is embedded so the report is self-verifying for integrity; trust requires anchoring (see below).
The signing input is canonical JSON of the payload minus the signatures key. Verifiers reconstruct the same bytes and recompute both channels.

Verifying a JSON report

verify is fail-closed: a green result requires a pinned trust anchor. Without one, integrity passes but the result is UNANCHORED and the command exits non-zero.
The verify exit code is the same EXIT_FAIL_UNDER (1) a failed --fail-under gate uses — CI gates treat tamper and risk-floor identically.

Read it in this order

When you first open a scan.json:
  1. band — first glance. Human label.
  2. aivss — score behind the band. Trust only when scoring_valid: true AND mode_authoritative: true.
  3. evaluation_modereal = a real LLM judged turns. stub forces band: not_evaluated and the number is meaningless.
  4. mode_authoritativetrue only for --mode full.
  5. coverage_grade + undertested — categories launched but too thinly tested for “no findings” to be safety evidence.
  6. findings — sorted by severity then descending confidence.
The Markdown emitter’s top-5 follows the same rank, so a JSON-first reader and a Markdown-PR-comment reader see the same top findings in the same order.

Anti-patterns

Don’t treat aivss as authoritative without first checking mode_authoritative AND scoring_valid. A --mode fast stub run can still emit a 100 — and --fail-under will refuse to gate-pass on it for exactly that reason.
Don’t re-sign a modified payload to mask edits. The Ed25519 public key is embedded; anchoring on the real trusted public key will reject a forged re-sign. The verify command’s UNANCHORED state is not an OK — gate on ok=true, not integrity_ok=true.
Don’t parse aivss out of stdout. Read the JSON. The canonical artifact is always written; stdout is a UX surface that may change.

Next step

SARIF export

The same findings as a SARIF 2.1.0 file for GitHub Code Scanning.

Markdown export

The same findings as a flat Markdown report for PR comments.

Report schema

Field-by-field schema reference.

Upload SARIF to GitHub

Walk-through for github/codeql-action/upload-sarif@v3.