client.verify_run()
Ask the Verdifax server to re-derive a recorded run's manifest hash
from the run's stored manifest fields and compare it with the seal it
recorded. Returns a typed VerifyResponse. Added in SDK
0.6.0; it replaces client.verify(), which now raises.
Signature
# sync
client.verify_run(run_id: int) -> VerifyResponse
# async
await async_client.verify_run(run_id: int) -> VerifyResponse
# module-level shortcut (one-shot client, reads VERDIFAX_API_URL / VERDIFAX_API_KEY)
verdifax.verify_run(run_id: int, *, base_url=None, api_key=None, timeout=None) -> VerifyResponse
| Argument | Type | Notes |
|---|---|---|
run_id | int | The server-side run ID, from receipt.run_id on the receipt attest() returned. Must be a positive integer. |
The call is GET /runs/{id}/verify, authenticated with the client's API key (X-Verdifax-Key), the same as every other authenticated call. Runs are scoped to the key that produced them, so a run owned by a different key returns 404. See GET /runs/{id}/verify for the raw endpoint.
Return value
A verdifax.VerifyResponse, mirroring VerifyResponse in the Go and Node SDKs:
| Field | Type | Meaning |
|---|---|---|
ok | bool | The request was served. |
verified | bool | True exactly when recomputed_hash == manifest_hash. |
manifest_hash | str | The manifest hash the server recorded when the run was sealed. |
recomputed_hash | str or None | The hash the server just re-derived from the stored manifest fields. |
verification_at | str or None | Server time of this check (RFC 3339, UTC). |
verification_kind | str or None | "manifest-self-seal" for a completed run. |
formula_version | str or None | The manifest-hash formula the stored manifest was recomputed under. |
dcae_engine_hint | DCAEEngineHint or None | available_at and engine_version of the DCAE verification endpoint. |
reason | str or None | Set only when the run has no stored manifest to verify. |
Stage-error runs. A run recorded as a stage error has no stored manifest. The server returns ok=True, verified=False, the recorded manifest_hash (possibly empty), and reason="original run is a stage_error; no manifest to verify". recomputed_hash, verification_at, verification_kind, formula_version and dcae_engine_hint are None.
Raises
ValidationErrorifrun_idis not a positive integer (checked before any request).APIErrorfor a non-2xx response, for example401without a valid key or404when the run does not exist or belongs to another key.ConnectionErrorif the API is unreachable.
Example
import verdifax
from verdifax import VerdifaxClient
with VerdifaxClient() as client:
receipt = client.attest(
payload="hello verdifax",
program_id="a" * 64,
route_id="route-test",
registry_record_hash="b" * 64,
)
result = client.verify_run(receipt.run_id)
assert result.verified is True
assert result.manifest_hash == receipt.manifest_hash
What this proves
If verified is True, the run's stored manifest fields, hashed under the recorded formula version, produce exactly the manifest hash the server recorded for that run. Changing any stored manifest field would produce a different recomputed_hash, and verified would be False.
What this is not
- Not independent. The check runs on the Verdifax server that recorded the run, against the manifest that server stored. It is a self-check, not third-party evidence.
- Not a replay. It does not re-run the pipeline and does not see your original payload; the server stores only the payload's hash.
- Not a statement about your copy. It compares the server's stored manifest with the server's recorded seal. To check a hash you saved yourself, compare it with
result.manifest_hash.
Independent offline check
For verification that does not depend on Verdifax, run the open-source verdifax-verify CLI over the run's sealed audit bundle on your own machine:
curl -s -H "X-Verdifax-Key: $VERDIFAX_API_KEY" \
https://api.verdifax.com/runs/42/artifacts -o bundle.json
verdifax-verify bundle.json
Offline, it recomputes the section hashes, the bundle hash, the pre-anchor digest and the anchored public-log leaves (including, on runs sealed from run 238 on, the complete-record anchor) from the bundle's own fields. It does not recompute the manifest hash itself. Use v0.14.5 or later; for a run sealed from run 238 on, require the verdict COMPLETE RECORD ANCHORED (see Verify).
client.verify() raises as of 0.6.0
Before 0.6.0, verify(manifest_hash, payload, program_id, route_id, registry_record_hash) called attest() again with the same inputs and compared the new manifest hash with the one you passed. In production that comparison can never be True: every /execute call seals its own admission clock, nonce, and public-log entry into its manifest hash, so replaying a production record can never reproduce its hash. The method silently returned False for every genuine run.
In 0.6.0, client.verify(), AsyncVerdifaxClient.verify() and verdifax.verify() keep their signatures but raise verdifax.UnsupportedOperationError with that explanation, without calling the API. Production records are verified by recomputation instead: verify_run() on the server, or verdifax-verify offline over the bundle.
To check that the pipeline itself replays deterministically, use verdifax.research.verify_determinism(). It calls POST /execute/verify-determinism, which runs the same input twice under one pinned clock and nonce and compares the two manifest hashes.
