Signing
These pages describe checker version VeronikAI/0.1+attestations.1 (signing.py). A2A card checks use suite veronikai-a2a-get-card version 1. MCP checks use suite veronikai-mcp-conformance version 2. If the code changes, the version string on a signed result should change too.
What is checked
After we save a check, we can sign it. Signing does not re-test the agent. It locks what we already observed, the way a notary stamp locks a statement without changing what the statement says.
Anyone who has our public key can later ask: "Did VeronikAI really record this status, at this time, for this address?"
How it works
Code: signing.py, with byte-stable JSON from canonical.py. Verification helper: verify.py.
- Algorithm: Ed25519, stored as a detached JWS (alg: EdDSA).
- Private keys stay on the checker machine and are never published. The crawler will not invent a key.
- Signing is opt-in with VERONIKAI_SIGN=1. If signing fails, the unsigned check is still saved. We do not throw away a measurement to protect the stamp.
- Each signed record includes: subject (the agent key), the URL we actually fetched (target), status, http_code if any, latency_ms, conformance, reasons, checked_at (when we looked, not when we signed), suite id and version, crawler_version, and a hash of the previous signed check for that same subject and key id (prev_hash). That hash chain means we cannot quietly insert an older result later.
- Record type: veronikai:daf:v0.1-draft.
We do not sign listings, descriptions, or raw response bodies. We sign the measurement.
What pass and fail mean
Signing itself is not pass/fail for the agent.
- A valid signature means: this exact record was produced by the holder of the matching key, and nobody has changed the bytes since.
- A missing signature means only that this check was not stamped (signing was off, or it failed). The check can still be true.
- A bad signature means the bytes do not match. Do not trust that copy.
Pass or fail inside the signed record is the agent's uptime or conformance result, copied unchanged.
Limits
- Private keys are never published.
- A public key for independent verification is coming later. It is not published yet.
- History backfill is refused: we cannot sign an older check after a newer one for the same subject.
- A check without an observed target cannot be signed.
Checker version
Every signed record carries "crawler_version": "VeronikAI/0.1+attestations.1" plus the suite (veronikai-a2a-get-card / veronikai-mcp-conformance and its version). If we change how a check is done, that string should change so old and new results are not mixed up.