Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.agentguardian.io/llms.txt

Use this file to discover all available pages before exploring further.

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

agent-guardian scan --system-prompt prompt.txt --model stub \
  --output json --output-path scan.json
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:
agent-guardian report cli-3a4c1d9c2840 --output json --output-path scan.json

What you get

The schema is agentguardian-scan-v1 (SCHEMA_VERSION in reports/json_report.py:52). Top-level keys, in canonical (sorted) order:
KeyTypeNotes
aivssint 0–100The headline score.
aivss_formula_versionstraivss-v1. Bumps on any scoring change.
asi_scores{str: float}Per-category 0–100, keyed by ASI01ASI10.
bandstrEXCELLENT / GOOD / WARNING / POOR / CRITICAL / not_evaluated.
budgetobject | nullRuntime USD outcome.
completenessobject | nullHow much of the planned attack work ran.
cost_usdfloatActual LLM spend.
coverageobjectCounters reconstructed from memory.jsonl.
coverage_gradestrAF. A = every ASI category covered by real evidence.
created_atstr (ISO 8601)UTC if no tzinfo, else local-with-offset.
duration_secondsfloatWall-clock runtime.
engineobject | nullLLM model spec per role ({commander, attacker, evaluator} → model id).
evaluation_modestrreal ⇔ a real LLM judged turns; stub forces band: not_evaluated.
findingsFinding[]One entry per landed attack. See Evidence timeline.
findings_summaryobjectPre-aggregated counts by ASI / severity.
modestrfast / smart / full.
mode_authoritativebooltrue only for full. Veto for --fail-under.
package_versionstrThe agent-guardian PyPI version that emitted this.
probe_library_versionstr2026.05 etc. — bumps when the corpus changes.
scan_idstrGenerated id (e.g. cli-3a4c1d9c2840).
scoring_validboolfalse for stub LLM / empty corpus.
signaturesobjectHMAC + Ed25519 blocks. Stripped before recomputing the signing input.
stopped_reasonstrcompleted / budget / early_stop / cancelled.
sub_scores{str: float}The six PRD §6 sub-scores.
targetobject{mode, ref, inferred_goal, profile_source}.
tierstrDetected or forced tier (T1T4).
tokens_totalintTotal input+output tokens across all roles.
undertestedstr[]ASI categories the scan launched but exercised too thinly.
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:
{
  "signatures": {
    "hmac_sha256": {
      "alg": "hmac-sha256",
      "value": "…",
      "secret_id": "default"
    },
    "ed25519": {
      "alg": "ed25519",
      "public_key": "…",
      "value": "…"
    }
  }
}
  • 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.
agent-guardian verify scan.json \
  --pubkey-file ./trusted-signer.pub \
  --secret "$AGENT_GUARDIAN_SIGNING_SECRET"
OutcomeWhat it meansExit
schema: OK + HMAC: OK + Ed25519: OK + trust anchor: PINNEDBytes match + anchored to your pinned key. Quotable.0
schema: OK + integrity green + trust anchor: UNANCHOREDBytes match but no anchor supplied. Don’t quote.1
Any channel FAIL or schema invalidTampered or wrong-anchor.1
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.