Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 85 additions & 1 deletion docs/agent-payment-guard-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,94 @@ Behavior:

Records come from `.proofpath/audit.jsonl` and include hash chaining (`previous_hash`, `hash`).

## Evidence export bundle

The evidence export script packages all ProofPath decision artifacts into a
portable local bundle for offline inspection, auditing, or grant evidence.

### Export

```bash
python3 scripts/export_payment_guard_evidence.py --out proofpath-evidence-bundle/
```

Optional path overrides (all have sensible defaults):

```bash
python3 scripts/export_payment_guard_evidence.py \
--out proofpath-evidence-bundle/ \
--audit-path .proofpath/audit.jsonl \
--replay-store-path .proofpath/replay-store.json \
--config examples/agent-payment-guard/payment_guard_service_config.json \
--policy examples/agent-payment-guard/payment_policy.json
```

### Bundle contents

```text
proofpath-evidence-bundle/
audit.jsonl — hash-chained decision log
replay-store.json — spent signed-intent nonces
payment_guard_service_config.json — service configuration snapshot
payment_policy.json — payment policy snapshot
verification_report.json — generated summary (see below)
```

### Inspect the bundle

Verify hash-chain integrity of the bundled audit log:

```bash
python3 scripts/verify_audit_log.py proofpath-evidence-bundle/audit.jsonl
```

Read the verification report:

```bash
python3 -m json.tool proofpath-evidence-bundle/verification_report.json
```

Example `verification_report.json`:

```json
{
"bundle_type": "agent-payment-guard-evidence",
"generated_at": "2026-05-27T00:00:00Z",
"audit_records_count": 8,
"replay_store_nonces": 1,
"hash_chain_valid": true,
"hash_chain_message": "chain valid (8 records)",
"source_files": {
"audit": ".proofpath/audit.jsonl",
"replay_store": ".proofpath/replay-store.json",
"config": "examples/agent-payment-guard/payment_guard_service_config.json",
"policy": "examples/agent-payment-guard/payment_policy.json"
},
"copied_files": [
"audit.jsonl",
"replay-store.json",
"payment_guard_service_config.json",
"payment_policy.json",
"verification_report.json"
]
}
```

### Edge cases

| Situation | Behavior |
|---|---|
| `audit.jsonl` missing | Hard fail — exits with error |
| `replay-store.json` missing | Warning printed; exports empty `{}` |
| Hash chain invalid | Warning printed; `hash_chain_valid: false` in report; export continues |
| Output directory exists | Files overwritten; extra files left in place |

## Local validation

```bash
bash examples/agent-payment-guard/run_demo_check.sh
bash examples/agent-payment-guard/run_service_check.sh
python3 scripts/verify_audit_log.py .proofpath/audit.jsonl
python3 scripts/export_payment_guard_evidence.py --out proofpath-evidence-bundle/
python3 scripts/verify_audit_log.py proofpath-evidence-bundle/audit.jsonl
bash examples/agent-payment-guard/run_evidence_export_check.sh
```
84 changes: 84 additions & 0 deletions examples/agent-payment-guard/run_evidence_export_check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Smoke-test for the evidence export bundle (closes #151).
#
# What this does:
# 1. Starts the payment guard service (enforce mode, signed intent required)
# 2. Sends a valid signed-intent ACCEPT request (creates audit record + replay-store nonce)
# 3. Stops the service
# 4. Runs export_payment_guard_evidence.py
# 5. Verifies the bundle structure and verification_report.json fields
# 6. Re-runs verify_audit_log.py against the bundled audit.jsonl
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT_DIR"

rm -rf proofpath-evidence-bundle/
rm -f .proofpath/audit.jsonl .proofpath/replay-store.json

HOST="127.0.0.1"
PORT="18788"
SERVICE="examples/agent-payment-guard/payment_guard_service.py"
CONFIG="examples/agent-payment-guard/payment_guard_service_config.json"

python3 "$SERVICE" \
--config "$CONFIG" \
--port "$PORT" \
>/tmp/payment_guard_evidence_svc.log 2>&1 &
SERVICE_PID=$!
cleanup() {
kill "$SERVICE_PID" >/dev/null 2>&1 || true
rm -rf proofpath-evidence-bundle/
}
trap cleanup EXIT

# Wait for service to be ready
for _ in $(seq 1 50); do
if curl -fsS "http://$HOST:$PORT/v1/health" >/dev/null 2>&1; then
break
fi
sleep 0.1
done

# Send one valid signed-intent request (ACCEPT)
VALID_INTENT=$(cat examples/agent-payment-guard/intent_envelopes/intent.valid.json)
VALID_PROPOSAL=$(cat examples/agent-payment-guard/payment_proposal.valid_micro_payment.json)

curl -fsS -X POST "http://$HOST:$PORT/v1/payment-proposals/evaluate" \
-H 'content-type: application/json' \
-d "{\"mode\":\"enforce\",\"proposal\":$VALID_PROPOSAL,\"intent_envelope\":$VALID_INTENT}" \
| python3 -c 'import json,sys; r=json.load(sys.stdin); assert r["decision"]=="ACCEPT", f"expected ACCEPT, got {r}"'

# Stop service before export
kill "$SERVICE_PID" >/dev/null 2>&1 || true
sleep 0.2

# --- run export ---
python3 scripts/export_payment_guard_evidence.py \
--out proofpath-evidence-bundle/

# --- verify bundle structure ---
for f in audit.jsonl replay-store.json payment_guard_service_config.json payment_policy.json verification_report.json; do
[ -f "proofpath-evidence-bundle/$f" ] || { echo "FAIL: missing proofpath-evidence-bundle/$f" >&2; exit 1; }
done

# --- verify verification_report.json ---
python3 - <<'EOF'
import json, pathlib, sys
r = json.loads(pathlib.Path("proofpath-evidence-bundle/verification_report.json").read_text())
assert r["bundle_type"] == "agent-payment-guard-evidence", f"bundle_type mismatch: {r}"
assert r["audit_records_count"] >= 1, f"expected >= 1 audit record, got {r['audit_records_count']}"
assert r["replay_store_nonces"] >= 1, f"expected >= 1 nonce, got {r['replay_store_nonces']}"
assert r["hash_chain_valid"] is True, f"hash_chain_valid is not True: {r}"
assert "generated_at" in r, "missing generated_at"
assert "source_files" in r, "missing source_files"
assert "copied_files" in r, "missing copied_files"
for k in ("audit", "replay_store", "config", "policy"):
assert k in r["source_files"], f"missing source_files.{k}"
print(f" report OK: {r['audit_records_count']} records, {r['replay_store_nonces']} nonces, chain={r['hash_chain_valid']}")
EOF

# --- re-verify hash chain against bundled audit.jsonl ---
python3 scripts/verify_audit_log.py proofpath-evidence-bundle/audit.jsonl >/dev/null

echo "Evidence export check passed."
Loading
Loading