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
The older @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:

FieldTypePurpose
schemastringavow.receipt/v1; absent only on legacy receipts
payloadJSON valueThe record being protected, in plain text
payload_hashstringsha256: plus hex of the RFC 8785 canonical payload
public_keyhex stringEd25519 public key
signaturehex stringEd25519 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

CodeMeaning
avow.payload_hash_mismatchThe payload no longer matches the signed fingerprint.
avow.signer_mismatchThe receipt was signed by a different key than the one supplied.
avow.key_permissions_insecureA signing-key file is readable by group or others.
avow.ledger_recovery_requiredA 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.

Next