runseal
Prove which inputs produced a result — and that nothing changed since.
Content-addressed provenance for computational research. Four primitives, no required dependencies, no daemon, no server.
pip install runseal # hashing, merkle, registry
pip install runseal[signing] # adds RSA-PSS artifact signing
Thirty seconds
from runseal import HashGenerator, ArtifactRegistry, Artifact
h = HashGenerator()
training_frame = {"rows": [1, 2, 3]}
config = {"window": 30, "seed": 7}
hashes = dict(
source_hash=h.hash_data_snapshot(training_frame),
code_hash=h.hash_code("src/"),
config_hash=h.hash_config(config),
)
# A run is identified by what went into it, never by when you ran it.
run_id = h.compute_run_id(
data_hash=hashes["source_hash"],
config_hash=hashes["config_hash"],
code_hash=hashes["code_hash"],
env_hash=h.hash_environment(h.get_environment_manifest()),
)
registry = ArtifactRegistry("./provenance")
registry.register(Artifact(id=f"{run_id}/raw", type="dataset", layer="ingest", **hashes))
registry.register(Artifact(id=f"{run_id}/model", type="model", layer="train",
parent=f"{run_id}/raw", metrics={"auc": 0.71}, **hashes))
print(run_id[:32])
print(" -> ".join(a.id.split("/")[-1] for a in registry.get_lineage(f"{run_id}/model")))
print(registry.verify_integrity()["integrity_ok"])
45cd8972f036917e3a89ee3363c7119e
model -> raw
True
Re-run the same pipeline on the same inputs and run_id is byte-identical. Change one config
value, one byte of data, or one line of code, and it is not.
What is in it
HashGenerator |
Content-addressed run IDs from data, config, code and environment. Deterministic across processes and machines. |
MerkleTree |
Append-only log with root verification and per-leaf inclusion proofs. |
ArtifactRegistry |
JSON-backed store with parent lineage, filtered queries and integrity checks. |
SignatureManager |
RSA-PSS signing and verification of artifact records. Optional extra. |
hash_config is order-insensitive, hash_data_snapshot handles dicts, sequences and numpy
arrays by content, and hash_code walks a directory's .py files in sorted order.
What this is not
It is easy to pattern-match this to a pipeline tool. It is not one.
| If you want | Use |
|---|---|
| Data and model versioning with remote storage | DVC |
| Experiment tracking with a UI and a server | MLflow, Weights & Biases |
| Pipeline orchestration | Airflow, Prefect, Dagster |
| A small library that answers "what produced this, and has it changed?" | runseal |
No service to run, no storage backend to configure, nothing to log into. It records and verifies; it does not schedule, execute, or store your data.
Why this exists
Extracted from a private research program where a wrong number that reconciles is more expensive than one that crashes. Writing the test suite surfaced two defects, both fixed here with regression tests named after them.
Merkle proofs verified only for leaf 0. get_proof returned bare sibling hashes with no
record of which side each sibling sat on, while verify_proof always combined
H(current + sibling). Parents are built as H(left + right), so any leaf that was a right child
at any level failed to verify. On a four-leaf tree, three of four valid proofs were rejected.
get_proof now returns (sibling_hash, side) pairs, verify_proof honours the side, and the
odd-leaf self-pairing case is explicit. Covered for trees of 1–33 leaves.
Environment hashes were never stable. get_environment_manifest() embedded time.time(), so
hashing the manifest gave a different digest on every call and compute_run_id produced a
different RUN_ID for an identical environment — defeating the whole point of content addressing.
The manifest is now deterministic, with wall-clock time left to Artifact.created_at where it
belongs. Package enumeration also moved off the deprecated pkg_resources.
Scope and limits
Signing covers artifact identity and the provenance hashes — not mutable metrics or metadata,
which are expected to be annotated after the fact. A test asserts that boundary so it stays
explicit.
The Merkle implementation duplicates the final node on odd levels, the common convention, which carries the known second-preimage ambiguity between a tree of N leaves and certain smaller trees. That is fine for tamper-evidence inside one append-only registry and unsuitable as a general-purpose commitment scheme.
Tests
pip install -e ".[test]"
pytest -q
80 tests. The suite runs with or without cryptography — signing tests skip cleanly, and CI
exercises both paths across Python 3.9–3.13.
Licence
MIT. See CHANGELOG.md and CONTRIBUTING.md.
Metadata
Release files for runseal 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| runseal-0.1.1.tar.gz | 21.7 kB | With provenance | Details for runseal-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| runseal-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for runseal-0.1.1-py3-none-any.whl |
Total release size: 38.3 kB
Release files / runseal-0.1.1.tar.gz
| Download URL | runseal-0.1.1.tar.gz |
|---|---|
| Size | 21.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c041b5d1036e19e879324db8a3a84101bb9ba325f9a3deb28469b1c69f7c6085
|
|
BLAKE2b-256 checksum How to use checksums |
a792bd203532a07e021f7f765ffa9b4c596a33b72e1d2261cfaec6a4bb825434
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logRelease files / runseal-0.1.1-py3-none-any.whl
| Download URL | runseal-0.1.1-py3-none-any.whl |
|---|---|
| Size | 16.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
212795de464ade7255c65db14ac045331cbbb2f00b98643f14e7cb6cd2dbf3d6
|
|
BLAKE2b-256 checksum How to use checksums |
2afd5d2f1d279e0ed6503f21c18f1cbcbe99c130306f43a43aadb38718ea95bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logSecurity for runseal 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for runseal-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.