Avow
Seals a record into a signed receipt anyone can check offline — for teams that must later prove it was not changed.
input: the record {"service": "checkout-api", "decision": "approved"}
and a private signing key that stays with you
output: original: {'service': 'checkout-api', 'decision': 'approved'} -> verified
altered: {'service': 'checkout-api', 'decision': 'rejected'} -> refused: avow.payload_hash_mismatch
Real output of the example below.
At a glance
- What it does — Like a notary's stamp on a document, but for any JSON record (the plain text format most software exchanges data in): Avow turns the record into a signed receipt, and anyone holding your public checking key can later confirm, with no internet and no server, that the record is exactly what you signed. Change one word and the check refuses it.
- Who it's for — A team that has to show later what it decided or shipped — "this release was approved, with these checks passed" — to an auditor, a customer, or its own future self, without running a service or trusting a third party to hold the proof.
- What stays on your device / what leaves it — Stays: everything. Signing and checking are local file and math operations; Avow's code opens no network connection (the example below is tested with network access switched off). Leaves: only what you choose to send — and a receipt carries the record in plain, readable text, so do not put secrets in it.
- Runs on — Python 3.12 or newer on any operating system, or Node.js 22.13 or newer for the TypeScript package.
- Not for — Hiding data (a receipt is not encrypted) or proving a record is true, fresh, or fair; Avow proves only that it is unchanged and who signed it. Also not a defence against someone presenting the same valid receipt twice.
- Status — Beta:
0.5.0is the first release from this repository, asavowon PyPI and@edgeproc/avowon npm. Python releases0.1.0through0.4.1predate this repository and installed stray packages that overwrote another project's files; they are being yanked, so install0.5.0or newer.0.5.1makes the command line optional:pip install avowis the library only,pip install "avow[cli]"adds theavowcommand. See CHANGELOG.
Try it in 60 seconds
Needs Python 3.12 or newer and uv.
git clone https://github.com/hseshadr/avow.git && cd avow && uv sync
Save this as seal.py in the checkout and run uv run python seal.py:
from avow import generate_signing_key, public_key_hex, sign_payload, verify_receipt
from avow.errors import AvowError
private_key = generate_signing_key() # stays with whoever signs
public_key = public_key_hex(private_key) # handed to whoever checks, ahead of time
receipt = sign_payload({"service": "checkout-api", "decision": "approved"}, private_key)
verify_receipt(receipt, expected_public_key=public_key)
print("original:", receipt.payload, "-> verified")
altered = receipt.model_copy(update={"payload": {"service": "checkout-api", "decision": "rejected"}})
try:
verify_receipt(altered, expected_public_key=public_key)
except AvowError as error:
print("altered: ", altered.payload, "-> refused:", error.code)
Its real output:
original: {'service': 'checkout-api', 'decision': 'approved'} -> verified
altered: {'service': 'checkout-api', 'decision': 'rejected'} -> refused: avow.payload_hash_mismatch
The first check passes because the record is exactly what was signed. The second is refused with a stable error code because one word of the record changed after signing.
More runnable examples: examples/.
How it works
You give Avow a JSON record and a private signing key. It writes the record in one canonical byte form (RFC 8785, so the same data always produces the same bytes), takes its SHA-256 fingerprint, and signs those bytes with Ed25519. The receipt holds the record, the fingerprint, the signer's public key, and the signature. A verifier recomputes the fingerprint, requires the signer to be the public key it was given separately (never the one inside the receipt), and checks the signature — all offline.
flowchart LR
A["Your JSON record"] --> B["Canonical bytes<br/>same data, same bytes"]
B --> C["Signed receipt<br/>record + fingerprint + Ed25519 signature"]
C --> D["Offline check<br/>verified, or refused with a code"]
E["Public key you trust<br/>shared ahead of time"] -.->|pinned by the verifier| D
classDef blue fill:#e8f4f8,stroke:#7aa7b8,color:#171717
classDef purple fill:#f0e8f8,stroke:#9a7ab8,color:#171717
classDef green fill:#e8f8e8,stroke:#7ab87a,color:#171717
classDef orange fill:#f8f0e8,stroke:#b8987a,color:#171717
class A blue
class B purple
class C,D green
class E orange
Explore the interactive architecture map →
(Archify, generated from docs/architecture/runtime.architecture.json).
Deep dive: docs/OPERATIONS.md.
Package surfaces
Avow has two production package surfaces and no hidden third core:
src/avow/ → Python wheel: avow/
ts/src/ → npm tarball: dist/
The Python wheel owns canonical JSON, hashes, Ed25519 key handling, receipts, the
avow command, and the append-only ledger. The npm package @edgeproc/avow owns the
portable TypeScript canonicalization and receipt surface; it does not ship the Python
ledger. Contract tests build both artifacts and check this mapping against their real
contents.
The cores do not interpret the evidence they seal. Domain calculations and action-policy logic belong to their applications, not to Avow.
What you can do
- Create a signing key pair and seal any JSON record into a receipt — Quickstart
- Verify a receipt offline against a public key you pinned — Trust anchors
- Chain receipts into an append-only ledger that detects deletion, insertion, reordering, and truncation — Ledger recovery
- Sign and verify the same receipt format from TypeScript, held to Python by shared test vectors — TypeScript package
- Branch on stable error codes instead of messages — Stable command boundary
Why this and not X
| Alternative | Better when | Avow is better when |
|---|---|---|
| A plain hash or checksum | You only need to spot accidental corruption. | You must also prove who sealed the record. |
GPG, minisign, or ssh-keygen -Y sign over a file |
You sign files for people who already use those tools. | The thing you seal is structured data that different programs re-serialize: canonical JSON keeps the check stable, and Python and TypeScript share one receipt format. |
| Sigstore or a transparency log | You want public, time-stamped, third-party-witnessed signing of software releases. | You want no account, online signing service, or public log — only a key pair and files you control. |
| JWS / JWT libraries | You need standard web tokens for login or APIs. | You want a small, fixed receipt format with the evidence kept inside, verified against a key pinned out of band. |
| A database audit table | Everyone who checks can query your database. | The proof has to travel to someone who cannot reach, or should not have to trust, your systems. |
Security and trust model
- Verified: that the record's canonical SHA-256 fingerprint matches its content, that the signer is the public key the verifier supplied independently, and that the Ed25519 signature is valid. A ledger additionally verifies every entry, its order and links, and a final head you pinned somewhere the writer cannot rewrite.
- Refuses rather than warns: any mismatch — altered record, wrong signer, bad
signature, unknown receipt schema — raises a typed error with a stable
avow.*code and never returns a partial result.avow signrefuses a private key file that other users can read (avow.key_permissions_insecure), and a ledger whose head was not installed stops withavow.ledger_recovery_required. - Not protected: the truth or freshness of the record; replay of a valid receipt; confidentiality (the record is plain text); a private key someone else has read — it is an unencrypted seed guarded only by file permissions; and a ledger plus its head file both rewritten by an attacker, unless you pinned the head elsewhere.
- Verify a release: check the PyPI and npm provenance attestations for
0.5.0, or build from a commit you have reviewed (uv build, thenpnpm --dir ts buildandpnpm --dir ts pack) and compare. The release workflow publishes with PyPI and npm provenance, and refuses when registry bytes differ from the reviewed build.
See SECURITY.md for reporting a vulnerability.
What this proves / what it does not prove
Run the evidence loop
From the checkout, run the complete evidence loop on a realistic deployment decision:
bash examples/run_evidence_loop.sh
Expected output:
Receipt schema: avow.receipt/v1
Original receipt: avow.verify.ok
Altered receipt: avow.payload_hash_mismatch (expected)
The schema line names the exact receipt envelope Avow emitted and verified. The next line means the payload hash, pinned signer, and signature all matched. The final line is an expected rejection: the demo changed the deployment outcome inside a copy of the receipt, so its stored hash no longer matched its payload.
examples/evidence.json records a deployment decision with
an artifact digest, policy identity, environment, and completed checks. The script:
- generates a local Ed25519 key pair;
- signs that JSON into a self-contained receipt;
- verifies the receipt against the separately pinned public key;
- changes only the copied receipt's deployment outcome; and
- requires the altered copy to fail with
avow.payload_hash_mismatch.
The script creates a temporary directory and removes it on exit. Set AVOW_DEMO_DIR
to an empty directory if you want to inspect receipt.json,
altered-receipt.json, and the generated keys afterward.
What this proves
avow.verify.ok proves that the evidence is unchanged from the bytes Avow sealed,
that the embedded signer matches the public key the verifier supplied independently,
and that the Ed25519 signature is valid for that evidence. Verification runs locally;
the runtime makes no network request.
A Python ledger can additionally prove that every retained receipt is valid, ordered, linked, and ends at an externally pinned head. See operations.
The claims are backed by uv run poe gate (lint, strict types, Grade A complexity, and
the Python tests at 90% branch coverage or more), pnpm --dir ts gate (Biome, strict
TypeScript, coverage, build, and benchmark), the shared Python/TypeScript vectors in
testdata/, and the tamper and mutation jobs in CI.
What this does not prove
A valid receipt does not prove that the evidence is correct, complete, fair, current, or honest. It does not prove wall-clock time or prevent a valid receipt from being presented again. A neighboring ledger-head file is only a convenience copy, not an independent trust anchor. Receipts contain their JSON payload in cleartext; signing is not encryption or redaction.
- Not a replay defence. A valid receipt verifies identically every time it is presented; Avow keeps no nonce, audience, or expiry state. Callers that need replay protection put those claims inside their own signed payload and check them after verification — see replay protection is caller-owned.
- The signing key is an unencrypted seed. The private key is a plaintext 32-byte
Ed25519 seed protected only by filesystem permissions (owner-only
0600). On POSIX systemsload_signing_keyrefuses a key file that group or other can access, withavow.key_permissions_insecure. There is no KMS/HSM seam yet: anyone who can read the file can sign as you. See key rotation.
Install
Prerequisites: Bash, Python 3.12 or newer, and uv.
This checkout contains the source; it is not a published or installed artifact. Prepare its local environment with:
uv sync
Beside this repository's pyproject.toml, the demo deliberately selects the checkout's
uv run ... avow path before any installed avow on PATH, so it exercises this
source checkout. When only examples/ is copied away, it uses the installed command
instead. To prove that packaged path, build and install the wheel as shown in the
quickstart.
Version and publication status
The Python and npm source versions are both 0.5.1. Pushing the exact tag v0.5.1 is
the only thing that publishes them, through the trusted-publishing workflow. Python
releases 0.1.0 through 0.4.1 were built from the pre-split repository and shipped
top-level assay/ and writ/ packages that overwrote assay-engine; they will be
yanked once 0.5.0 is verified on PyPI. If an older avow removed assay-engine's
files, run pip uninstall assay-engine && pip install assay-engine. No command in this
README publishes, tags, or changes a registry release.
Usage & API
- Python library —
sign_payload,verify_receipt, the key helpers, and the ledger functions are exported fromavow; the example above is the smallest complete use. - Command line — install
avow[cli]foravow keygen,avow sign,avow verify,avow ledger append, andavow ledger verify, each printing a stable success or error code (plainavowis the library alone and needs no Typer); see the quickstart. - TypeScript —
signPayloadandverifySignaturefrom@edgeproc/avow; see the TypeScript package.
The JSON values Avow accepts, and why integers stay within ±(2^53 − 1), are listed in supported JSON values.
Configuration
Avow has no configuration file and reads no environment variables. Everything is passed
explicitly: the record, the private key file (or, in TypeScript, the caller-held seed), and
the public key and ledger head the verifier pins. Private key files (*.key) are
gitignored here and must never be committed; share only the .pub file.
Limitations & roadmap
Shipped: 0.5.0, the first tagged release of this repository. It includes Python and TypeScript receipts, the Python avow command, the Python ledger,
and the refusal of group- or other-readable key files.
Planned (not shipped): yanking the broken 0.1.0–0.4.1 Python releases once
0.5.0 is verified on PyPI.
Not shipped: a KMS/HSM seam for signing keys — none exists yet. Replay protection stays caller-owned by design; see replay protection is caller-owned.
Getting help
- GitHub Issues — Best for: bugs and concrete feature requests.
- Email (private) — Best for: security reports; see SECURITY.md. Never use a public issue for a vulnerability.
Contributing / development
CI runs these two gates on every pull request, plus vector-parity, tamper, example, and artifact jobs:
uv run poe gate && pnpm --dir ts gate
Further reading: Quickstart, Operations, TypeScript package, and Provenance for repository and release identity.
Maintainer release gate
The end-user demo above needs only Bash, Python 3.12 or newer, and uv. Contributors
running the complete release gate additionally need Node 22, Corepack, pnpm 11.5.0,
actionlint, gitleaks, and ShellCheck. Corepack selects the pinned pnpm version from
ts/package.json.
node --version
pnpm --version
uv run poe release-candidate
The gate rejects any active Node major other than 22 before installing dependencies.
License / Citation
MIT — see LICENSE.
Metadata
Release files for avow 0.5.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 | |
|---|---|---|---|
| avow-0.5.1.tar.gz | 25.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| avow-0.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.8 kB
Release files / avow-0.5.1.tar.gz
| Download URL | avow-0.5.1.tar.gz |
|---|---|
| Size | 25.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7164ec1dc6932ed26e5dc2a45b4c0adfd81b108c9c6af20eac006004edc20d1d
|
|
BLAKE2b-256 checksum How to use checksums |
9ed36eb036f488d9ba6e91b2f01eb2885bd4518942e7877876a3c15739551dd7
|
| 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 Oct 1, 2026.
Transparency logRelease files / avow-0.5.1-py3-none-any.whl
| Download URL | avow-0.5.1-py3-none-any.whl |
|---|---|
| Size | 30.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
79fc30a98d851fb82acdea9790c53062395fe35cc6ea6b5e781e925028dc128c
|
|
BLAKE2b-256 checksum How to use checksums |
dfc90bbab985e166985b52917bb32bb40e8ef4ba79dfab4544d2e13b967dff5c
|
| 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 Oct 1, 2026.
Transparency log