Documentation
Sign a record. Verify it anywhere.
Avow is a Python and TypeScript library for portable, tamper-evident JSON receipts. This page covers installation, the three core operations, and the rules that keep receipts verifiable across languages.
Install
Python 3.12 or newer. The library is avow; the command needs the cli extra.
pip install "avow[cli]>=0.5.3"
For only the Python library:
pip install avow
TypeScript, Node 22.13 or newer:
npm install @gainratio/avow
@edgeproc/avow package is deprecated. Version 0.5.3 of @gainratio/avow can verify receipts sealed by 0.4.x and older.CLI
Generate a signing key and its public verifier key:
avow keygen --out signing.key
signing.key is the private key. Keep it private. signing.key.pub is the public key you give to verifiers ahead of time.
Sign a JSON file:
avow sign --payload decision.json --key signing.key --out receipt.json
Verify against the pinned public key:
avow verify --receipt receipt.json --public-key signing.key.pub
Successful commands print stable success lines and exit 0:
avow.keygen.ok
avow.sign.ok
avow.verify.ok
Every refusal is a stable code on standard output, with no rejected JSON, key material, paths, or traceback. Ordinary validation and I/O failures exit 2.
Python
from avow import generate_signing_key, public_key_hex, sign_payload, verify_receipt
from avow.errors import AvowError
private_key = generate_signing_key()
public_key = public_key_hex(private_key)
receipt = sign_payload({"service": "checkout-api", "decision": "approved"}, private_key)
verify_receipt(receipt, expected_public_key=public_key)
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(error.code)
The public key must come from your own configuration or distribution channel. Do not learn the expected key from the receipt you are checking; that would let a forger supply both halves.
TypeScript
import {
generateSeedHex,
publicKeyHex,
signPayload,
verifySignature,
} from "@gainratio/avow";
const signingSeed = generateSeedHex();
const pinnedPublicKey = await publicKeyHex(signingSeed);
const receipt = await signPayload({ artifact: "sha256:abc" }, signingSeed);
await verifySignature(receipt, pinnedPublicKey);
The TypeScript package never reads key files. The signing seed is unencrypted hex held by the caller, so storage is the integrating application’s responsibility. There is no KMS/HSM seam in this package.
Receipt format
A receipt is JSON with five fields:
| Field | Type | Purpose |
|---|---|---|
schema | string | avow.receipt/v1; absent only on legacy receipts |
payload | JSON value | The record being protected, in plain text |
payload_hash | string | sha256: plus hex of the RFC 8785 canonical payload |
public_key | hex string | Ed25519 public key |
signature | hex string | Ed25519 signature over the canonical payload |
Canonicalisation matters: two JSON documents can represent the same object with different key order or whitespace. RFC 8785 (JCS) fixes one byte sequence so Python and TypeScript produce the same signature.
Supported payloads are I-JSON-compatible: objects, arrays, Unicode strings, finite numbers, booleans, and null. Integers must be within ±(2^53−1); encode larger exact quantities as strings. NaN, infinities, lone surrogates, and non-string keys are rejected before signing.
Error codes
| Code | Meaning |
|---|---|
avow.payload_hash_mismatch | The payload no longer matches the signed fingerprint. |
avow.signer_mismatch | The receipt was signed by a different key than the one supplied. |
avow.key_permissions_insecure | A signing-key file is readable by group or others. |
avow.ledger_recovery_required | A ledger append failed after the head moved; do not retry blindly. |
Ledger
Avow can append receipts to a hash-linked ledger and verify order and head:
avow ledger append --receipt receipt.json --ledger evidence.jsonl --head evidence.head
avow ledger verify --ledger evidence.jsonl --head evidence.head --public-key signing.key.pub
Success lines are avow.ledger.append.ok and avow.ledger.verify.ok. A durable append whose head was not installed, or a later append against a mismatched head, exits 3 with avow.ledger_recovery_required.
Operations
Key handling
The Python loader refuses a key file that is group- or world-readable. Generate keys on the machine that signs, distribute only the public key, and keep an offline copy of the private key.
Replay protection
A valid receipt verifies identically every time it is presented. Avow does not decide whether a receipt is fresh. If replay matters, sign your own nonce, audience, and expiry claims into the payload and check them after verification.
What verification proves
Verification proves the payload is unchanged and was signed by the caller-pinned key. It does not prove correctness, honesty, freshness, or wall-clock time. It is not encryption; the payload is readable.