Skip to main content

No Proof, No Execution — cryptographic decision-first execution gate for AI agents

Project description

ProofGuard

No Proof, No Execution.

ProofGuard Demo


The Problem

AI agents can now write code, run shell commands, deploy infrastructure, and delete files — autonomously.

That's powerful. It's also dangerous. When something goes wrong, you face two hard questions:

  • Why did it do that? There's no record of the decision, only the action.
  • How do we stop it from happening again? Logs tell you what happened. They don't prevent it.

Most safety systems work like a filter — they scan for dangerous patterns and block them. But filters can be bypassed. A sufficiently capable agent will find a way around a text-based rule.


A Different Approach

ProofGuard doesn't filter actions. It changes the structure.

Before any action runs, a decision record must exist. The action is cryptographically linked to that decision. If there's no record — or if the decision said "stop" — execution is structurally impossible. Not blocked by a rule. Just impossible.

Traditional:  execute → write log       (safety is optional, after the fact)
ProofGuard:   decide  → gate → execute  (no decision = no execution, always)

Think of it like a key and a lock. The action is the lock. The decision record is the only key that fits. You can't pick it. You can't forge it. And every key that was ever used is preserved in an append-only chain that can be verified by anyone, later, without access to your system.


How It Works

PASS — decision recorded, gate cleared, execution sealed:

  Agent decides to act
          │
          ▼
  ┌───────────────────┐
  │   Decision Record │  judgment = PASS
  │                   │  decision_id = abc-123
  │                   │  entry_hash  = f61e90aa...
  └─────────┬─────────┘
            │ recorded in ledger
            ▼
  ┌───────────────────┐
  │       Gate        │  decision exists?    ✓
  │                   │  judgment == PASS?   ✓
  └─────────┬─────────┘
            │ cleared
            ▼
  ┌───────────────────┐
  │      Execute      │  action runs here
  └─────────┬─────────┘
            │
            ▼
  ┌───────────────────┐
  │  Execution Record │  linked to abc-123
  │                   │  result_hash = 6da77a...
  │                   │  chain sealed ✓
  └───────────────────┘

STOP — gate blocks, execution never reached:

  ┌───────────────────┐
  │   Decision Record │  judgment = STOP
  └─────────┬─────────┘
            ▼
  ┌───────────────────┐
  │       Gate        │  judgment != PASS  →  ✗ BLOCKED
  └───────────────────┘
      violation logged / execute() never called

No decision at all — structurally impossible:

  ┌───────────────────┐
  │       Gate        │  decision not found  →  ✗ CRITICAL
  └───────────────────┘
      violation logged (severity: CRITICAL) / execute() never called

Every record is chained — each entry's hash includes the previous entry's hash. Tamper any record and the chain breaks. The entire history can be exported and verified by anyone, with no access to your system.


Install

pip install proofguard

Or from source:

git clone https://github.com/Nick-heo-eg/proofguard
cd proofguard
pip install -e .

Quick Start

from proofguard import record_decision, pre_execution_guard, record_execution

# 1. Record the decision with full context
decision = record_decision({
    "task": "deploy_api",
    "env":  "production",
    "triggered_by": "ci_pipeline"
}, "PASS")

# 2. Gate — raises ExecutionBoundaryViolation if judgment != PASS
pre_execution_guard(decision["decision_id"])

# 3. Your action runs here
result = {"deployed": "api-v2", "replicas": 3, "status": "ok"}

# 4. Seal execution into the chain
record_execution(decision["decision_id"], result, success=True)

Decorator — wraps a function, handles gate + sealing automatically:

from proofguard import guarded

@guarded({"task": "send_report", "env": "prod"}, judgment_result="PASS")
def send_report():
    return {"sent": True}

send_report()  # blocked and logged if judgment != PASS

Context manager:

from proofguard import Guard

with Guard({"task": "sync_db", "env": "prod"}, "PASS") as g:
    result = sync_database()
    g.record(result, success=True)

When You'll Actually Need This

These situations keep coming up in the developer community. They're not hypothetical.


"I asked the agent to fix a bug. It reset the entire database — twice."

A developer debugging a data type mismatch watched their agent decide the real fix was to wipe the schema and reapply migrations. All data gone. When asked why, the agent said it "treated the database like code" and "assumed data was disposable." It happened again the next day.

The underlying problem: nothing required the agent to record its reasoning before acting. There was no gate between "I've decided to do this" and "I'm doing it."

With ProofGuard, the decision to run a destructive operation must be recorded before execution. If the judgment is STOP, the operation never runs — not filtered, not warned, structurally impossible:

decision = record_decision({
    "task": "fix_migration",
    "action": "db_reset",
    "env": "production"
}, "STOP")  # judgment: do not proceed

pre_execution_guard(decision["decision_id"])
# → raises ExecutionBoundaryViolation
# → db_reset() is never called

"The agent deleted a 16MB production backup. I asked it to clean up unnecessary files."

A developer asked their agent to identify files that weren't needed anymore. Without listing candidates, without asking, without any warning — the agent deleted a production database backup.

With ProofGuard, every action leaves a signed record before it runs. If the agent later claims it "had to" delete the file, you can look up the decision entry and see exactly what context it had at the time:

{
  "type": "decision",
  "timestamp": "2026-03-28T03:17:42Z",
  "actor": "cleanup-agent",
  "judgment_result": "PASS",
  "decision_context": {
    "task": "cleanup_unused_files",
    "target": "rs-st0x6o_2025-10-18.sql",
    "classified_as": "unused"
  },
  "entry_hash": "f61e90aadb15eeff..."
}

Not a vague log line. The full context of what the agent believed when it decided to act — tamper-evident.


"Something went wrong in production overnight. Nobody knows what the agent did or why."

No audit trail. No way to replay what happened. Infrastructure logs show API calls. Model logs show tokens. Neither records why the agent made a specific decision.

If the agent went through ProofGuard, every decision is in the chain. If it tried to act without a decision record — a bypass attempt — that's recorded too, before the exception is raised:

[CRITICAL] PRE_GUARD_NO_DECISION
  timestamp   : 2026-03-28T03:19:05Z
  detail      : execution attempted without prior decision — blocked

Either way, you have an answer.


"The auditor wants proof that every AI action was authorized. We have logs, but logs can be edited."

In regulated industries, compliance teams are discovering that standard agent deployments produce the wrong kind of records. Infrastructure logs, inference logs, and task logs don't record what was authorized — only what happened. Regulators are treating this as a books-and-records violation.

ProofGuard exports a proof capsule — a single JSON file with every decision and its outcome, cryptographically chained. The auditor verifies it on their own machine, no access to your system needed:

python verify_proof.py proof_capsule.json
============================================================
  ProofGuard Verification — ✓ PASS
============================================================
  total_decisions : 47
  total_executions: 43
  success_rate    : 100%
  root_hash_match : ✓ True
    hash    : c4166d06c1d7e08fb696... (match)
  anomalies       : 0 (clean)
============================================================

If the file was modified after export — even one character — the hash won't match and the verifier shows the exact position.


"We want to give the agent more autonomy, but the team doesn't trust it yet."

The blocker isn't capability — it's the absence of accountability structure. ProofGuard doesn't limit what the agent can decide. It guarantees that whatever it does, there's a permanent record of the decision that preceded it. You can extend autonomy incrementally, knowing that if something goes wrong, the record is already there.


What's Different — and What Isn't

Log vs. proof

Most agent safety systems record what happened after the fact. Some add pattern filters. ProofGuard is neither — it's a structural constraint. The code path to run an action without a prior decision simply doesn't exist.

Logging Pattern filters ProofGuard
Records what happened
Prevents execution without authorization partial
Tamper-evident record
Verifiable by a third party, no system access
Proves a specific decision authorized a specific action

Limitations

ProofGuard does not validate the quality of decisions. A wrong PASS is still a PASS. The library enforces that a decision existed and was recorded — not that it was correct.

It only protects the code paths that use it. If an agent can call a function directly without going through the guard, ProofGuard won't stop it.

It does not stop a compromised agent from issuing PASS decisions. If the decision-maker itself is misconfigured, ProofGuard records those decisions faithfully. It gives you an auditable trail — not a policy engine.

Single-file ledger only. Distributed or multi-node ledger synchronization is not supported.


Tamper Detection

Ledger entries form a SHA-256 hash chain. Any modification — even a single character — is detected at the exact position.

python verify_proof.py --ledger .proofguard-ledger.jsonl

Clean:

ProofGuard Verification — ✓ PASS
  chain_valid     : ✓ True
  root_hash_match : ✓ True
    hash    : c4166d06c1d7e08fb696... (match)

After 1 character changed:

ProofGuard Verification — ✗ FAIL
  root_hash_match : ✗ False
    claimed : c4166d06c1d7e08fb696f82682c18ce0153071fafc69ac1e46edb7b09e0a5dc0
    actual  : c4166d06c1d7e08fb696f82682c18ce0153071fafc69ac1e46edb7b09e0a5dca
    diff at : position 63 — '0' vs 'a'

Export for external audit:

from proofguard.ledger import export_proof_capsule
export_proof_capsule("proof_capsule.json")

The verifier uses Python stdlib only — no ProofGuard dependency required.


Integrity Properties

✔ tamper-evident append-only chain   SHA-256 hash-chain, each entry links prev_hash
✔ cryptographically verifiable       standalone verifier, stdlib only
✔ execution bound to decision        no decision_id = no execution, structurally
✔ fail-closed                        every error path blocks, never silently allows
✔ canonical JSON                     sort_keys=True, separators=(',',':') — deterministic
✔ atomic append                      fcntl LOCK_EX + fsync — safe under concurrent writes
✔ violation log                      every blocked attempt recorded before exception

Fail-Closed Guarantee

Scenario Result
Judgment = STOP / HOLD / RETRY ExecutionBoundaryViolation raised, execution never called
No decision found for ID ExecutionBoundaryViolation raised, severity=CRITICAL
Gate not called before execution ExecutionBoundaryViolation raised, logged
Any unexpected error in guard Blocks, never silently allows

All violations are written to _VIOLATION_LOG before the exception is raised.


Demo

# PASS / STOP / no-decision scenarios + tamper detection
python demo/demo_basic.py

Configuration

Environment Variable Default Description
PROOFGUARD_LEDGER .proofguard-ledger.jsonl Ledger file path
PROOFGUARD_VIOLATION_LOG .proofguard-violations.jsonl Violation log path

Compliance Context

  • SOC2 change management — every agent action generates a signed ledger entry automatically
  • EU AI Act (2026.08) — decision provenance + externally verifiable audit trail
  • ISO 42001 — documented oversight of AI decision-making

Tests

python tests/run_all.py
# 9 suites, 161 assertions — canonicalization, concurrency, tamper, fail-closed, schema, adversarial, honesty boundary

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

proofguard-0.1.0.tar.gz (30.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

proofguard-0.1.0-py3-none-any.whl (13.9 kB view details)

Uploaded Python 3

File details

Details for the file proofguard-0.1.0.tar.gz.

File metadata

  • Download URL: proofguard-0.1.0.tar.gz
  • Upload date:
  • Size: 30.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for proofguard-0.1.0.tar.gz
Algorithm Hash digest
SHA256 480b5ef0f54941ea32fa529ea1e107769a934bd6b93a7187dfc606a534c03544
MD5 db04c116fffca04871119dedb09a82ee
BLAKE2b-256 f34992fcbe207f478bd274a4e104a69c84d6de18ed260ceb91cddd11781c16e2

See more details on using hashes here.

File details

Details for the file proofguard-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: proofguard-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for proofguard-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fbed6367dd9815d1223f09fca4ad50ae75872fcc70bc56726f9fd9cf402d8206
MD5 90ceb9b228f1eac002cb60aa719a69f0
BLAKE2b-256 5638d7485adb869d9fde08a56f5a7170132f3e472bf79689c1acb98475f69f6b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page