Compliance gate for SPANFORGE RFC-0001 JSONL event streams: schema validation + HMAC chain verification.
Project description
sf-validate
A compliance gate for SPANFORGE RFC-0001 JSONL audit-log streams.
sf-validate combines per-event JSON schema validation and HMAC chain verification into a single auditable pass — something that spanforge's individual validate and audit verify commands do not provide as a unified, CI-friendly operation.
Installation
pip install sf-validate
# Or via pipx for an isolated CLI install:
pipx install sf-validate
Python 3.11+ required. Single runtime dependency: spanforge==2.0.2.
Quick Start
# Validate a local audit log
sf-validate --input audit.jsonl
# Add HMAC chain verification
sf-validate --input audit.jsonl --key "$SPANFORGE_SIGNING_KEY"
# Read the key from a file (safer — never exposed in process listings)
sf-validate --input audit.jsonl --key-file .signing-key
# Validate a glob across many files
sf-validate --input 'logs/**/*.jsonl' --key-file .signing-key
# Pipe from stdin
cat audit.jsonl | sf-validate --input -
Sample output:
sf-validate (spanforge schema 2.0 / RFC-0001-STRICT)
Files : 3
Events : 1204
Schema errors: 0
Chain check : skipped (no signing key)
Duration : 14.2 ms
Result : PASS
CLI Reference
sf-validate --input PATTERN [options]
Input / Output
| Flag | Description |
|---|---|
--input PATTERN |
Path, glob (logs/**/*.jsonl), comma-separated list, or - for stdin. Required. |
--output FILE |
Write the formatted report to FILE in addition to stdout. |
--format FORMAT |
Output format: text (default), json, github, junit, sarif. |
Signing / Chain
| Flag | Description |
|---|---|
--key SECRET |
HMAC signing key. Prefer $SPANFORGE_SIGNING_KEY or --key-file. |
--key-file PATH |
Read signing key from PATH (one line). Never appears in process listings. |
--key-map PAIRS |
Key-rotation map: EVT01:newkey,EVT02:newerkey. |
--require-chain |
Exit 2 if no signing key is available. |
--no-chain |
Skip HMAC chain verification entirely. |
--per-file-chain |
Treat each matched file as an independent chain (log-rotated files). |
--strict |
Treat a skipped chain check (no key available) as a failure (exit 1). |
Schema / Parsing
| Flag | Description |
|---|---|
--no-schema |
Skip per-event JSON schema validation. |
--skip-parse-errors |
Treat malformed JSONL lines as errors instead of aborting. |
--max-errors N |
Stop after N total errors (parse + schema). |
--event-type TYPES |
Comma-separated event types to validate (others are counted only). |
Performance
| Flag | Description |
|---|---|
--workers N |
Parallel file-reading threads (default: auto, up to 4). |
Output Control
| Flag | Description |
|---|---|
-v / --verbose |
Show the expanded list of matched files before the report. |
-q / --quiet |
Print only a one-line PASS/FAIL summary. |
--no-color |
Disable ANSI color output. Respects NO_COLOR and SF_VALIDATE_NO_COLOR. |
Watch Mode
| Flag | Description |
|---|---|
--watch |
Re-validate whenever matched files change (Ctrl+C to stop). |
--watch-interval S |
Polling interval in seconds for --watch (default: 1.0). |
Exit codes
| Code | Meaning |
|---|---|
0 |
All checks passed. |
1 |
Validation failures (schema errors, chain tampered/gapped). |
2 |
Configuration error (missing key, no files found, bad --key-file). |
Output Formats
text (default)
Human-readable terminal output with optional ANSI color (auto-detected from TTY).
json
Machine-readable JSON. Pipe to jq for filtering:
sf-validate --input audit.jsonl --format json | jq '.schema_errors'
github
GitHub Actions ::error:: / ::notice:: workflow commands. Errors appear as
inline annotations on pull requests.
- name: Validate audit log
run: sf-validate --input audit.jsonl --format github
junit
JUnit XML compatible with Jenkins, GitLab CI, Azure DevOps, and most CI dashboards.
sarif
SARIF 2.1.0 — the standard format for static analysis tools. Upload to GitHub Code Scanning for inline PR annotations:
- name: Validate and upload SARIF
run: sf-validate --input audit.jsonl --format sarif --output results.sarif || true
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
CI Integration
GitHub Actions
name: Audit Log Compliance
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install sf-validate
- name: Validate (inline PR annotations)
run: sf-validate --input 'audit-*.jsonl' --key-file .signing-key --format github
- name: Upload SARIF (Code Scanning)
if: always()
run: sf-validate --input 'audit-*.jsonl' --format sarif --output sf-validate.sarif || true
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: sf-validate.sarif
GitLab CI
validate-audit-log:
image: python:3.11
script:
- pip install sf-validate
- sf-validate --input audit-*.jsonl --format junit --output report.xml
artifacts:
reports:
junit: report.xml
Jenkins
sh 'pip install sf-validate'
sh 'sf-validate --input audit-*.jsonl --format junit --output junit-report.xml'
junit 'junit-report.xml'
Pre-commit Integration
# .pre-commit-config.yaml
repos:
- repo: https://github.com/veerarag1973/sf-validate
rev: v1.0.0
hooks:
- id: sf-validate
args: [--input, 'audit-*.jsonl', --format, github]
# Optional: add --key-file .signing-key for chain verification
Config File
Place defaults in pyproject.toml or a standalone .sf-validate.toml anywhere in the
directory tree. CLI flags always override config file values.
# pyproject.toml
[tool.sf-validate]
format = "text"
strict = true
max-errors = 100
workers = 4
Or standalone .sf-validate.toml in the project root:
format = "github"
strict = true
require-chain = true
Supported config keys:
| Key | Type | CLI equivalent |
|---|---|---|
format |
string | --format |
no-color |
bool | --no-color |
strict |
bool | --strict |
require-chain |
bool | --require-chain |
validate-schema |
bool | inverse of --no-schema |
verify-hmac |
bool | inverse of --no-chain |
skip-parse-errors |
bool | --skip-parse-errors |
max-errors |
int | --max-errors |
workers |
int | --workers |
event-type |
list or string | --event-type |
key-map |
string | --key-map |
output |
string | --output |
Stdin Support
# Pipe a JSONL stream directly
cat audit.jsonl | sf-validate --input -
# Fetch and validate in one step
curl https://logs.example.com/audit.jsonl | sf-validate --input - --key-file .key
Watch Mode
Automatically re-validates whenever matched files change:
sf-validate --input 'audit-*.jsonl' --key-file .signing-key --watch
# Adjust polling sensitivity
sf-validate --input audit.jsonl --watch --watch-interval 0.5
Python API
from sf_validate import validate_stream, format_report, ValidateOptions
options = ValidateOptions(
org_secret="my-signing-key",
validate_schema=True,
verify_hmac=True,
strict=True,
workers=4,
)
report = validate_stream("audit-*.jsonl", options)
print(format_report(report, fmt="text")) # color auto-detected
if not report.passed:
print(f"Errors: {len(report.schema_errors)} schema, "
f"{len(report.parse_errors)} parse")
raise SystemExit(1)
Development
git clone https://github.com/veerarag1973/sf-validate
cd sf-validate
pip install -e ".[dev]"
pytest
License
MIT — see LICENSE.
Standalone compliance gate for SPANFORGE RFC-0001 JSONL event streams.
Requires spanforge 2.0.2 and re-uses all of its signing/validation infrastructure. The only new logic in this package is the combined single-pass gate (schema + HMAC + chain gaps in one command) and CI-friendly output formats.
What it does
| Check | spanforge API reused |
|---|---|
| Per-event JSON schema validation (RFC-0001) | spanforge.validate_event() |
| HMAC-SHA256 signature verification | spanforge.verify_chain() |
Chain gap detection (prev_id linkage) |
spanforge.verify_chain() → ChainVerificationResult.gaps |
| JSONL deserialization | spanforge.Event.from_json() |
New in sf-validate (not a single combined operation in spanforge):
validate_stream()— one call that runs all three checks and returns a unifiedValidationReport--format github— GitHub Actions::error::/::notice::annotations--format json— machine-readable JSON report for downstream tooling--require-chain— hard-fail if no signing key is available--skip-parse-errors— collect bad lines instead of aborting
Installation
pip install sf-validate
Requires Python ≥ 3.11 and spanforge==2.0.2 (pinned as a mandatory dependency).
CLI
sf-validate --input PATTERN [options]
| Flag | Description |
|---|---|
--input PATTERN |
Path, glob, or comma-separated list of JSONL files |
--key SECRET |
HMAC signing key (default: $SPANFORGE_SIGNING_KEY) |
--require-chain |
Exit 2 if no signing key is available |
--no-schema |
Skip per-event JSON schema validation |
--no-chain |
Skip HMAC chain verification |
--skip-parse-errors |
Collect bad lines instead of aborting |
--format text|json|github |
Output format (default: text) |
--version |
Print version and exit |
Exit codes
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
Validation failures (schema errors, tampered events, chain gaps) |
2 |
Configuration / usage error (no files found, missing key with --require-chain) |
Examples
# Basic schema + chain check
sf-validate --input audit.jsonl --key "$SPANFORGE_SIGNING_KEY"
# Glob over rotated log files
sf-validate --input "logs/audit-*.jsonl" --key "$SPANFORGE_SIGNING_KEY"
# CI: GitHub Actions annotations
sf-validate --input audit.jsonl --key "$KEY" --format github
# Schema only (no signing key needed)
sf-validate --input audit.jsonl --no-chain
# Machine-readable output
sf-validate --input audit.jsonl --key "$KEY" --format json | jq .passed
GitHub Actions
- name: Validate SPANFORGE audit log
run: sf-validate --input audit.jsonl --key "${{ secrets.SPANFORGE_KEY }}" --format github
env:
SPANFORGE_SIGNING_KEY: ${{ secrets.SPANFORGE_KEY }}
Python API
from sf_validate import validate_stream, format_report, ValidateOptions
report = validate_stream(
"audit-*.jsonl",
ValidateOptions(org_secret="my-key"),
)
if not report.passed:
print(format_report(report, fmt="json"))
raise SystemExit(1)
Project structure
src/sf_validate/
__init__.py — public API exports
_validator.py — validate_stream(), ValidationReport, ValidateOptions
_report.py — format_report() for text / json / github formats
_cli.py — sf-validate entry point
tests/
test_validator.py
test_cli.py
test_report.py
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 sf_validate-1.0.0.tar.gz.
File metadata
- Download URL: sf_validate-1.0.0.tar.gz
- Upload date:
- Size: 40.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ccf221f969c384b23dbba30a80fd834fd6471d4f99aabd861569909e0e4f313b
|
|
| MD5 |
e6c1ac121867ec71c2e4dac6eb0a75c7
|
|
| BLAKE2b-256 |
ba1bde93f0790ec48856dc59b938b2d2dc39d550283daa13eb6f463cb3f379cd
|
File details
Details for the file sf_validate-1.0.0-py3-none-any.whl.
File metadata
- Download URL: sf_validate-1.0.0-py3-none-any.whl
- Upload date:
- Size: 26.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68a67e60d77e4c255d97ed48fc2f6e7a7b7562bef6039adff543d57f9d8e5bdf
|
|
| MD5 |
e219c8cc4f4d9c1f4c891567c12aa9fd
|
|
| BLAKE2b-256 |
a59d69896c6669ea39800fd3021ba5056c090171874d1f4b456370efad417389
|