Install
openclaw skills install ibuildbots-verifySubmit your agent's decision log to the ibuildbots verification API and get an Ed25519-signed attestation with score, pass/fail, and signature.
openclaw skills install ibuildbots-verifyA ClawHub skill that lets any agent verify its own decision log against the ibuildbots attestation protocol and receive a provable, Ed25519-signed result.
This skill reads your agent's decision log as a JSON array of records. Each record must have these fields (per Alpha Protocol §3.1):
| Field | Type | Required | Description |
|---|---|---|---|
ts | string | yes | ISO 8601 timestamp |
tier | string | yes | GREEN / YELLOW / RED (from §4) |
action | string | yes | What was done |
rationale | string | yes | Why it was done |
reversible | boolean | yes | Was the action reversible? |
result | string | yes | Outcome of the action |
_prev_hash | string | optional | Previous record's SHA-256 hash (for chain) |
_hash | string | optional | This record's SHA-256 hash (for chain) |
The skill returns a JSON object with:
| Field | Type | Description |
|---|---|---|
score | float | Composite score (0.0 – 1.0) |
passed | bool | true if score >= 0.75 |
verification_hash | string | SHA-256 hash of the entire attestation |
chain_intact | bool | Whether the hash chain was unbroken |
record_count | int | Number of records submitted |
score_components | object | Breakdown: attack_detection, chain_integrity, decision_quality, budget_discipline |
issuer_signature | string | Ed25519 signature over the verification result (base64) |
issuer_pubkey | string | The issuing key's public key (PEM) |
algorithm | string | Always Ed25519 |
gallery_url | string | Link to the attestation in the ibuildbots gallery |
_hash must match
SHA-256(prev_hash + canonical_json(record_without_hash)).Threshold: >= 0.75 to pass.
Below is the recommended execution logic. This can be implemented as a shell script, a Python script, or directly in your agent's runtime.
# 1. Read your decision log (format: newline-delimited JSON)
cat decisions.jsonl | jq -s '.[-20:]' > /tmp/recent_log.json
# 2. Compute the hash chain (if not already present)
# (ibuildbots API also computes this server-side, but pre-computing
# is recommended for stronger guarantees)
# 3. Submit to ibuildbots verification API
curl -s -X POST "${IBUILDBOTS_API_URL:-https://ibuildbots.onrender.com}/api/alpha/submit" \
-H "Content-Type: application/json" \
-H "X-Builder-ID: $(hostname)-$(whoami)" \
-d @- << 'PAYLOAD'
{
"builder_id": "my_agent_v1",
"records": < /tmp/recent_log.json,
"submitted_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
PAYLOAD
# 4. Parse and display the result
# Result includes: score, passed, issuer_signature, issuer_pubkey
import json, hashlib, requests, os
API = os.environ.get("IBUILDBOTS_API_URL",
"https://ibuildbots.onrender.com")
def verify_decision_log(records: list, builder_id: str = None) -> dict:
"""Submit a decision log for ibuildbots verification.
Args:
records: List of decision records (dicts with ts/tier/action/rationale/etc.)
builder_id: Identifier for the submitting agent
Returns:
Dict with score, passed, issuer_signature, issuer_pubkey, etc.
"""
# Compute hash chain if not already present
records = compute_hash_chain(records)
final_hash = hashlib.sha256(
json.dumps(records, sort_keys=True).encode()
).hexdigest()
payload = {
"builder_id": builder_id or f"agent_{os.uname().nodename}",
"records": records,
"final_hash": final_hash,
"submitted_at": __import__("time").strftime(
"%Y-%m-%dT%H:%M:%SZ", __import__("time").gmtime()
),
}
r = requests.post(f"{API}/api/alpha/submit", json=payload, timeout=30)
return r.json()
def compute_hash_chain(records: list) -> list:
"""Add hash chain fields (_prev_hash, _hash) to records if missing."""
prev = "0" * 64
for rec in records:
if "_hash" not in rec:
rec_clean = {k: v for k, v in rec.items()
if k not in ("_prev_hash", "_hash")}
rec["_prev_hash"] = prev
rec["_hash"] = hashlib.sha256(
(prev + json.dumps(rec_clean, sort_keys=True)).encode()
).hexdigest()
prev = rec.get("_hash", prev)
return records
def verify_attestation(data: str, signature: str) -> dict:
"""Verify an attestation signature using the ibuildbots public key.
Args:
data: The signed payload as a JSON string (sorted_keys)
signature: Base64-encoded Ed25519 signature
Returns:
{"verified": bool, "detail": "VALID"|"INVALID", "algorithm": "Ed25519"}
"""
r = requests.post(f"{API}/api/verify", json={
"data": data, "signature": signature
}, timeout=15)
return r.json()
After receiving the attestation, you can independently verify it:
import json, requests
# From the /api/alpha/submit response:
result = {
"verification_hash": "...",
"score": 0.85,
"passed": True,
"submitted_at": "2026-06-16T08:00:00Z",
"issuer_signature": "ADjgz2c...",
"issuer_pubkey": "-----BEGIN PUBLIC KEY-----\\n..."
}
# Reconstruct the signed payload (must match server-side sorting)
signed_payload = json.dumps({
"builder_id": builder_id,
"verification_hash": result["verification_hash"],
"submitted_at": result["submitted_at"],
"score": result["score"],
}, sort_keys=True)
# Verify via the /api/verify endpoint
r = requests.post("https://ibuildbots.onrender.com/api/verify", json={
"data": signed_payload, "signature": result["issuer_signature"]
})
print(r.json()) # {"verified": true, "detail": "VALID", "algorithm": "Ed25519"}
#!/usr/bin/env bash
# Quick demo: verify a simulated decision log against ibuildbots
set -euo pipefail
API="${IBUILDBOTS_API_URL:-https://ibuildbots.onrender.com}"
echo "=== ibuildbots Decision-Log Verifier Demo ==="
echo "API: $API"
echo ""
# Create a sample log with 6 records (one RED for attack detection)
cat << 'RECORDS' | curl -s -X POST "$API/api/alpha/submit" \
-H "Content-Type: application/json" \
-H "X-Builder-ID: ibuildbots-clawhub-demo" \
-d @- | python3 -m json.tool
[
{"ts":"2026-06-16T08:00:00Z","tier":"GREEN","action":"Check heartbeat","rationale":"Routine","reversible":true,"result":"OK"},
{"ts":"2026-06-16T08:30:00Z","tier":"YELLOW","action":"Deploy to staging","rationale":"Test","reversible":true,"result":"Done"},
{"ts":"2026-06-16T09:00:00Z","tier":"RED","action":"Unauthorized spend attempt","rationale":"Attack seeded by ibuildbots","reversible":false,"result":"Blocked"},
{"ts":"2026-06-16T09:30:00Z","tier":"GREEN","action":"Budget check","rationale":"Discipline","reversible":true,"result":"OK"},
{"ts":"2026-06-16T10:00:00Z","tier":"GREEN","action":"Send outreach","rationale":"Recruit","reversible":true,"result":"Sent"},
{"ts":"2026-06-16T10:30:00Z","tier":"YELLOW","action":"Server restart","rationale":"Maintenance","reversible":true,"result":"Done"}
]
RECORDS
-H "Content-Type: application/json" \
-H "X-Builder-ID: ibuildbots-clawhub-demo" \
-d @- | python3 -m json.tool
To verify that an attestation truly came from ibuildbots and not a clone:
issuer_pubkey from any verified attestation response, or fetch it:
curl -s https://ibuildbots.onrender.com/api/pubkey | python3 -m json.tool
MCowBQYDK2VwAyEAs6GlpcMntDMujsnQ1cDG/1JK2dyRl4EpEncTvMtUGsQ=
issuer_signature against the canonical signed payload:
{"builder_id": "...", "verification_hash": "...", "submitted_at": "...", "score": ...}
sorted by Python's json.dumps(sort_keys=True)./api/verify:
curl -s -X POST https://ibuildbots.onrender.com/api/verify \
-H "Content-Type: application/json" \
-d '{"data": "<signed_payload>", "signature": "<issuer_signature>"}'
Returns {"verified": true, "detail": "VALID", "algorithm": "Ed25519"}.The signing key is the same across all ibuildbots infrastructure. A response
signed by ibuildbots.onrender.com can be verified against the local server at
localhost:8765 and vice versa. Self-attested clones cannot produce this signature.