Offline verification for GuardSpine evidence bundles
Project description
guardspine-verify
Verify GuardSpine evidence bundles offline -- no trust required.
Spec Version: v0.2.0 + v0.2.1 | Package Version: 0.2.1
What This Is
An offline CLI and Python library for verifying GuardSpine evidence bundles. Given a sealed bundle (JSON or ZIP), it recomputes every hash from scratch and compares against stored values. If anything was tampered with, verification fails.
No network access. No trust assumptions. Fully auditable.
Installation
pip install guardspine-verify
Or install from source:
git clone https://github.com/DNYoussef/guardspine-verify.git
cd guardspine-verify
pip install -e .
Dependencies
guardspine-kernel>=0.2.0-- canonical JSON serialization (RFC 8785). The verifier delegates all canonical JSON computation to the kernel to guarantee cross-language hash parity.cryptography>=41.0-- signature verification (Ed25519, RSA, ECDSA).click>=8.0-- CLI framework.rich>=13.0-- terminal output formatting.jsonschema>=4.0-- schema validation.
Quick Start
# Verify a JSON bundle
guardspine-verify bundle.json
# Verify a ZIP export
guardspine-verify evidence-bundle-2024-01-15.zip
# Verbose output
guardspine-verify bundle.json --verbose
# Output JSON report
guardspine-verify bundle.json --format json > report.json
# Verify with a public key (cryptographic signature check)
guardspine-verify bundle.json --public-key signer.pub
How Verification Works
The verifier runs six checks in order:
-
Version validation -- bundle version must be
0.2.0or0.2.1. -
Hash chain integrity -- each chain entry's
chain_hashis recomputed asSHA-256("sequence|item_id|content_type|content_hash|previous_hash")and compared to the stored value. Previous-hash linkage is verified entry by entry, starting from"genesis". -
Root hash validation -- the Merkle root is recomputed as
SHA-256(concat(all chain_hash values))and compared to the storedroot_hash. -
Content hash validation -- for each item, the content is serialized to canonical JSON (via
guardspine-kernel) and hashed with SHA-256. The result must match the storedcontent_hash. -
Chain-to-items binding -- every item must have exactly one chain entry with matching
item_id,content_hash,content_type, andsequence. Unbound items and orphaned chain entries are both errors. -
Signature verification -- validates signature format (algorithm, base64 encoding). When a PEM public key is provided via
--public-key, performs cryptographic verification. Supports Ed25519, RSA-SHA256, ECDSA-P256, and HMAC-SHA256.
All content hashing uses canonical JSON from guardspine-kernel to guarantee
byte-identical output across Python, TypeScript, and Lean implementations.
What It Verifies
| Check | Description |
|---|---|
| Version | Bundle version must be 0.2.0 or 0.2.1 |
| Hash Chain | Each entry's previous_hash matches prior chain_hash; chain_hash recomputed and compared |
| Chain Binding | Chain entries map 1:1 to items (count, item_id, content_hash, content_type, sequence) |
| Root Hash | Computed Merkle root matches stored root |
| Content Hashes | Each item's content_hash matches SHA-256 of canonical JSON content |
| Signatures | Format validation; cryptographic verification with --public-key |
| Sanitization | Optional: --check-sanitized validates redaction metadata and token consistency |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Bundle verified successfully |
| 1 | Verification failed |
| 2 | Invalid input (file not found, parse error, bad public key) |
CLI Reference
guardspine-verify BUNDLE_PATH [OPTIONS]
Options:
-v, --verbose Show detailed output
-f, --format [text|json] Output format (default: text)
-k, --public-key PATH PEM public key for cryptographic signature verification
--check-sanitized Validate sanitization attestations and [HIDDEN:*] token consistency
--require-sanitized Fail if sanitization block is missing or invalid
--fail-on-raw-entropy Treat high-entropy survivor candidates as hard failures
--version Show version and exit
Python API
from guardspine_verify import verify_bundle, verify_bundle_data, VerificationResult
# Verify from file path
result = verify_bundle("bundle.json")
# Verify from dict
import json
with open("bundle.json") as f:
bundle = json.load(f)
result = verify_bundle_data(bundle)
if result.verified:
print("Bundle verified")
else:
for error in result.errors:
print(f" - {error}")
# With cryptographic signature verification
with open("signer.pub", "rb") as f:
public_key_pem = f.read()
result = verify_bundle("bundle.json", public_key_pem=public_key_pem)
# With sanitization checks
result = verify_bundle_data(
bundle,
check_sanitized=True,
require_sanitized=True,
fail_on_raw_entropy=True,
)
VerificationResult
@dataclass
class VerificationResult:
verified: bool
status: str # "verified" | "mismatch" | "error"
hash_chain_status: str
root_hash_status: str
content_hash_status: str
signature_status: str
errors: list[str]
warnings: list[str]
verified_at: datetime
details: dict[str, Any]
Exported Functions
| Function | Purpose |
|---|---|
verify_bundle(path, ...) |
Verify from file path (JSON or ZIP) |
verify_bundle_data(bundle, ...) |
Verify from dict |
verify_hash_chain(bundle) |
Hash chain check only |
verify_root_hash(bundle) |
Root hash check only |
verify_content_hashes(bundle) |
Content hash check only |
verify_signatures(bundle, ...) |
Signature check only |
verify_sanitization(bundle, ...) |
Sanitization check only |
Optional Lean Binary Integration
The verifier can optionally delegate hash-chain and content-hash verification to a compiled Lean binary for formal-verification-grade assurance. This is entirely optional -- the Python path is the default.
To enable Lean verification, set the GUARDSPINE_LEAN_BIN environment variable
to the path of the compiled GuardSpine Lean binary:
export GUARDSPINE_LEAN_BIN=/path/to/guardspine-lean
The Lean binary handles: version validation, hash chain verification, root hash verification, content hash verification, and cross-check. Python continues to handle signature verification, sanitization checks, and CLI formatting.
The shim communicates with the Lean binary via JSON over stdin/stdout with a 30-second timeout.
Sanitization Verification
guardspine-verify validates PII-Shield sanitization attestations embedded in evidence bundles.
When bundles are sanitized before sealing (e.g., by codeguard-action), the
sanitization block attests what engine was used, how many redactions were
applied, and what token format was used. The verifier checks:
sanitization.redaction_countmatches actual[HIDDEN:<id>]token count- Required fields:
engine_name,engine_version,method,token_format,status token_formatmatches the canonical[HIDDEN:<id>]declaration- High-entropy strings that survived sanitization (optional hard fail)
Hash fields (content_hash, chain_hash, root_hash, *_hash) and
cryptographic fields (signatures, signature_value, public_key_id,
immutability_proof) are excluded from entropy analysis to avoid false positives.
# Check sanitization attestation (warn on issues)
guardspine-verify bundle.json --check-sanitized
# Require sanitization (fail if missing or invalid)
guardspine-verify bundle.json --require-sanitized
# Treat post-sanitization high-entropy survivors as failures
guardspine-verify bundle.json --require-sanitized --fail-on-raw-entropy
ZIP Safety
ZIP bundle loading enforces safety limits to prevent zip bombs and resource exhaustion:
- Maximum ZIP file size: 100 MB
- Maximum entries per ZIP: 1000
- Maximum
bundle.jsonsize: 50 MB - Compression ratio check: rejects ratios above 100x
Supported Algorithms
| Algorithm | Type | Status |
|---|---|---|
| SHA-256 | Hash | Content hashing, chain hashing |
| Ed25519 | Signature | Supported |
| RSA-SHA256 | Signature | Supported |
| ECDSA-P256 | Signature | Supported |
| HMAC-SHA256 | Signature | Supported (requires --hmac-secret or API param) |
Supported Input Formats
| Format | Extension | Description |
|---|---|---|
| JSON | .json |
Single bundle file |
| ZIP | .zip |
Exported bundle package (must contain bundle.json) |
Integration with CI/CD
# GitHub Actions
- name: Verify Evidence Bundles
run: |
pip install guardspine-verify
guardspine-verify ./evidence/*.json
# GitLab CI
verify-evidence:
script:
- pip install guardspine-verify
- guardspine-verify ./evidence/*.json
Cross-Language Parity
guardspine-verify uses guardspine-kernel (the Python port of the canonical
TypeScript kernel) for all canonical JSON serialization. This guarantees that
bundles sealed by any language implementation (TypeScript, Python, Lean) produce
byte-identical hashes and can be verified by any other implementation.
Golden vector tests validate hash parity against
guardspine-spec/fixtures/golden-vectors/v0.2.0.json.
Architecture
guardspine_verify/
__init__.py Public API exports
cli.py Click CLI (text + JSON output via Rich)
verifier.py Core verification logic
lean_shim.py Optional Lean binary bridge (GUARDSPINE_LEAN_BIN)
Key design decisions:
- No duplicated code. Canonical JSON delegates to
guardspine-kernel. Previously this module carried its own 40-line implementation; that has been removed in favor of the single canonical source. - Lean is optional. The
lean_shim.pymodule discovers the Lean binary exclusively via theGUARDSPINE_LEAN_BINenvironment variable. No hard-coded paths, no fallback search. - Signatures are Python-only. Even when using the Lean backend, signature
verification and sanitization checks run in Python (via
cryptography).
Related Projects
| Project | Description |
|---|---|
| guardspine-kernel | Canonical TypeScript kernel |
| guardspine-kernel-py | Python port (canonical JSON, hashing) |
| guardspine-spec | Bundle specification |
| GuardSpine | Full governance platform |
| codeguard-action | GitHub Action for code review governance |
Contributing
See CONTRIBUTING.md.
License
Business Source License 1.1 (source-available) -- see LICENSE. You may always use this verifier to verify, validate, or audit GuardSpine evidence -- commercially and free of charge -- so anyone can check our evidence without having to trust us. Other production use is free for non-commercial, evaluation, and small-organization use (annual revenue under USD 1,000,000); otherwise a commercial license applies. Converts to Apache-2.0 four years after each release.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file guardspine_verify-2.0.0.tar.gz.
File metadata
- Download URL: guardspine_verify-2.0.0.tar.gz
- Upload date:
- Size: 42.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1d26faebaecc13cfe975a4a5be454174729da6e4602cdbdc62a3592eb44857f
|
|
| MD5 |
52575e1a526dd28e54a5df35726cb725
|
|
| BLAKE2b-256 |
1786db6cf976a2ca35f2e807fdfbfe0216ee5aabac9caa7b30d716f45d10d4e9
|
File details
Details for the file guardspine_verify-2.0.0-py3-none-any.whl.
File metadata
- Download URL: guardspine_verify-2.0.0-py3-none-any.whl
- Upload date:
- Size: 22.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbae29f1281efe1bb1445fe694d5310630d2477aa6c044041f02f2a29469ed67
|
|
| MD5 |
bde59d71b43fc75ed4ecc31f5e68ffe2
|
|
| BLAKE2b-256 |
984282f34f638bcd5d6d1b9d36548e70c2a76b790de21685c9f02b6a966ee349
|