Reproducible Research with Verdifax

Seal your computational result together with the declared environment fingerprint that produced it. Anyone holding the sealed record, reviewer, regulator, replicator, can independently recompute its section hashes and check its anchored values against the public log, and the determinism check below detects drift between two replays of one computation. On records sealed from run 238 on the complete record, declared environment included, is anchored. On earlier v5 records only the result's payload hash and other core values are anchored, and the declared environment is sealed inside the bundle only.

What this is for

A growing share of scientific, regulatory, and AI-governance work depends on a piece of code, run by a specific person, on a specific machine, producing a specific number. The standard answers to "can I trust this result?", a Docker image, a pip freeze, an RStudio session info appendix, are useful but unsealed. None of them tell a third party: "this number sealed to this cryptographic hash, the producer declared this environment, and the transparency log recorded it at this time."

Verdifax fills that gap with a small, technology-neutral primitive: a sealed run whose payload hash is bound to the public-log entry, with your declared reproducibility context, runtime version, pinned dependencies, git SHA, declared random seeds, platform, and optional container image hash, recorded in the audit bundle as Category 6. That context is not part of the manifest hash. On records sealed from run 238 on, it is covered by the complete-record anchor on the public log. On earlier records it is sealed only by a section hash inside the bundle, so it shows the bundle is internally consistent, not that a party able to rewrite the whole bundle has not changed it.

Every ordinary run seals its own admission clock and per-run nonce, so two separate attestations of the same payload carry different manifest hashes. When POST /execute/verify-determinism replays one payload twice under a pinned clock and a pinned nonce, with the public-log anchoring stage held fixed, a deterministic computation yields a byte-identical manifest hash. When it doesn't, the diff tells you which field moved. On Rekor-anchored deployments, every attestation is published to the Sigstore transparency log within seconds.

Who this is for

  • Academic researchers publishing computational results that reviewers or replicators will need to verify months or years later.
  • Regulated-research teams (clinical trials, FDA submissions, EU AI Act conformity assessments) that want the audit trail to record a model's output alongside a declared environment.
  • Reproducibility-focused organizations, research labs, journals, preprint servers, registries, that want a third-party-verifiable proof artifact attached to each computational result.
  • Engineering teams in finance, healthcare, or governed AI who need to retire "trust me, it ran the same" and replace it with "here is the sealed record; check it yourself."

The two-language stack

We ship official SDKs in the two languages that cover the overwhelming majority of computational research:

PythonR
Installpip install verdifaxremotes::install_github("Verdifax/verdifax-sdk-r")
ClientVerdifaxClient()verdifax_client()
Capture environmentcapture_environment()verdifax_capture_environment()
Attest a resultclient.attest(...)verdifax_attest(client, ...)
Prove determinismverify_determinism(...)verdifax_verify_determinism(client, ...)
Reference workflowreproducible_research.ipynbreproducible-research.Rmd

Both SDKs implement the same wire protocol against the same orchestrator, so a run attested from either language yields the same kind of record and manifest hash. The R SDK attests and checks determinism; it has no run-verification function, so an R-using auditor re-verifies a run with the open-source verdifax-verify CLI (or asks the server to re-derive the manifest hash through the Python SDK's verify_run()).

What capture_environment records

FieldPython sourceR source
runtime_namehardcoded "python"hardcoded "R"
runtime_versionsys.version_infoR.version$major.minor
pinned_dependenciesimportlib.metadata (name + version, sorted)installed.packages() (name + version, sorted)
git_commit_shagit rev-parse HEAD (best-effort)git rev-parse HEAD (best-effort)
random_seedscaller-supplied dict, sortedcaller-supplied list, sorted
platformplatform.system() / platform.machine() → GOOS/GOARCH formSys.info() → GOOS/GOARCH form
container_image_hash/proc/self/cgroup on Linux (best-effort)/proc/self/cgroup on Linux (best-effort)

Every field is optional. Auto-detection failures silently leave the field null. The orchestrator records null as "not declared" rather than fabricating a claim, there is no path by which the orchestrator invents an environment fingerprint on the producer's behalf.

A 30-second example (Python)

import verdifax
from verdifax.research import capture_environment, verify_determinism

client = verdifax.VerdifaxClient()  # env: VERDIFAX_API_URL, VERDIFAX_API_KEY

# 1. Declare what you ran.
ctx = capture_environment(declared_seeds={"numpy": 42, "torch": 1337})

# 2. Attest a result with the environment bound in.
receipt = client.attest(
    payload="my-analysis-output",
    program_id="0" * 64,
    route_id="paper-figure-3",
    registry_record_hash="0" * 64,
    reproducibility_context=ctx,
)
print("ManifestHash:", receipt.manifest_hash)

# 3. Check that the pipeline replays deterministically (pinned clock and nonce).
det = verify_determinism(
    client=client,
    payload="my-analysis-output",
    program_id="0" * 64,
    route_id="paper-figure-3-verify",
    registry_record_hash="0" * 64,
    reproducibility_context=ctx,
)
assert det.deterministic, det.diff.differing_fields

The R equivalent reads identically; see the R Markdown template for the full version.

What the determinism check answers

verify_determinism runs your payload through the pipeline twice, under one pinned admission clock and one pinned nonce, with the public-log anchoring stage held fixed, and compares the resulting canonical manifest hashes. Holding those per-run values fixed is what makes the comparison meaningful: ordinary /execute calls each mint their own clock and nonce and anchor their own public-log entry, so their manifest hashes always differ. A production record is verified by recomputing its seals from its recorded fields, not by re-running it. The top-level deterministic flag is grounded on manifest-hash equality, the seal of the pipeline output.

Bundle-hash differences, when surfaced in diff.differing_fields, indicate server-observed timing variation (e.g. LatencyTotalMs, EnvSnapshot.Time) and are labeled informational. They never cause deterministic to flip false on their own.

In production end-to-end testing, both Python and R SDKs have demonstrated deterministic == true with byte-identical manifest hashes across replays.

What to do with the manifest hash

  • Cite it. Treat the manifest hash like a DOI for the computational result, short, unambiguous, machine-checkable.
  • Pin it in supplementary materials. Reviewers and replicators holding the run's sealed audit bundle can check it with the open-source verifier, which recomputes every section hash from the bundle's own fields, confirms every section is bound to that manifest hash, and shows the environment you declared.
  • Bind it to the artifact. Sign your dataset or paper PDF over the manifest hash so the link between paper and result is non-repudiable.
  • Anchor it. On Rekor-anchored deployments, every attestation publishes to the Sigstore transparency log. A later change to the run's anchored values, including the payload hash, no longer matches its log entry, and the entry stays on the log. Since verifier 0.12.0, a public-log entry counts only if it is signed with a published Verdifax log-signing key and the timestamp the log signs for it verifies, so a rewritten record that anyone else logs as a new entry fails. Verdifax holds that key. From run 240 on, each record seals a 600-second anchoring window, so a later re-log, even by Verdifax, fails or must carry a new sealed date; earlier records show their authenticated anchoring time but cannot fail on it. On records sealed from run 238 on the anchored content includes the declared environment; on earlier records it does not.

Reference workflows

We provide runnable, end-to-end demos in both ecosystems: