Verify a Receipt
Use one of your own (every verification response carries a receipt_id; see the Quickstart), or fetch the published sample — no account needed:
curl -O https://www.bluefoxedge.ai/specimen-receipt.jsonThe verifier below reads a file named specimen-receipt.json — save whichever receipt you hold under that name. If you paid with x402 and hold no API key, the mint response body is your receipt (the signed envelope rides at data.receipt); from the read API — and in the published sample above — it rides at data.envelope instead. The script finds it either way — one script, every shape, no edits.
The verifier is Python 3.8+ standard library only — it carries its own RFC 8785 canonical-JSON implementation and its own RFC 8032 ed25519 verifier, so it runs on a locked-down box, in a hardened CI runner, or inside an agent sandbox where pip is unavailable. If the cryptography package happens to be installed, it is used as a faster path to the same answer over the same bytes. What you should do instead is pin the bytes you are about to run:
curl -sS -O https://api.bluefoxedge.ai/verify_receipt.py
curl -sS https://api.bluefoxedge.ai/verify_receipt.py.sha256 | shasum -a 256 -c -No Python on the machine? There is a JavaScript twin at https://api.bluefoxedge.ai/verify_receipt.js — one file, zero dependencies, no build step, Node 18+, with its checksum served beside it in the same shape. Run it as node verify_receipt.js my-receipt.json, as npx bluefox-verify-receipt my-receipt.json, or by pasting the whole file into any JavaScript runtime. That npx form works because the twin is published on npm as bluefox-verify-receipt — cut from this same file and packaged: installing it (npm install bluefox-verify-receipt) gives you a bluefox-verify-receipt command, for CI images and agent sandboxes where you would rather pin a dependency than curl a URL. The registry can lag the served file. The served file is the cut the house ships today; a published package version can be an older cut and lack a flag the served file documents. To tell which cut you hold, use the package README's History: for every published version it names both the package digest and the served digest of that same cut (the two differ by one trailing newline the serving route appends), so compare the served .sha256against the History's served digest for your installed version — equal is one cut, different is two. Take the Python one if you have a Python and the JavaScript one if you do not — they are twins, not alternatives: same steps, same order, same verdict, and the same refusal sentences, proven byte-for-byte against each other on the published specimen and a ten-plant tamper battery, online and again fully offline. Both twins take the same --jwks flag: save the key set once (curl -sSo pinned-jwks.json https://api.bluefoxedge.ai/.well-known/jwks.json), add --jwks pinned-jwks.json, and the whole check runs with the network unplugged. The inclusion check on Walk the Log runs in both checkers: the Python route is verify_inclusion.py, the JavaScript one is this same file with --inclusion.
The same script verifies your own receipts — paid or keyed — and the published sample, unchanged. It pins the key set out of band rather than following the receipt's own jwks_url, and it fetches that key set with an explicit User-Agent header on purpose — see the note on Walk the Log:
import base64, hashlib, json, re, sys, urllib.request
# DEPENDENCIES: none. Python 3.8+ standard library only. If the third-party
# cryptography package happens to be installed, its Ed25519 verifier is used;
# otherwise the self-contained RFC 8032 verifier further down checks the same
# bytes and reaches the same answer. Nothing here needs pip.
#
# python3 verify_receipt.py checks specimen-receipt.json
# python3 verify_receipt.py my-receipt.json checks the file you name
# python3 verify_receipt.py --jwks saved-keys.json my-receipt.json
# checks fully offline against keys you saved
# python3 verify_receipt.py --jwks saved-keys.json --bind-record my-record.json my-receipt.json
# also checks that the receipt names YOUR record
#
# Exit 0 means the check passed. Any non-zero exit means it did not, and the
# reason is one sentence -- never a stack trace.
#
# TWO KEY FAMILIES, AND THEY NEVER CROSS. Receipts are signed by the receipt
# key. The transparency log signs its checkpoints with a DIFFERENT key. Both are
# published in the same key set, and every entry declares which family it
# belongs to in a bfx:purpose member. DO NOT SELECT A KEY BY POSITION: keys[0]
# is the receipt key, so a checker that indexes the list instead of reading the
# label is one reordering away from checking the wrong thing. This file checks a
# RECEIPT, so it requires a purpose of "receipt" and refuses a receipt that
# names the log's key. Checkpoints are the other script's job, and it makes the
# mirror-image check: https://api.bluefoxedge.ai/verify_inclusion.py
#
# BINDING A RECEIPT TO A RECORD (--bind-record). The record is a JSON object
# you hold -- your own account of one tool call, as you presented it. Its
# RFC 8785 canonical form is hashed, and the receipt's source row must name
# exactly that digest. The retained presentation, when the tier kept one, must
# agree with the record. The receipt's own verdict word is printed from the
# file, never judged here: a pass says the receipt names your record, and the
# record stays the caller's own account of what happened.
# Pin the key set out of band -- never read a jwks_url out of the receipt.
# That field is unsigned: a forger can point it at keys they control, and a
# checker that follows it will bless the forgery. The explicit User-Agent is
# deliberate too: some edge firewalls refuse the default urllib signature.
#
# --jwks moves the pin FURTHER out of band, never less: name a key-set file
# you saved earlier and the whole check runs offline against those pinned
# bytes -- nothing is fetched at all -- or name an https address you trust
# and keys are fetched from there instead of the default below. Either way
# the choice is the operator's hand, never the receipt's: no field in the
# file being judged can pick its own judge.
JWKS_URL = "https://api.bluefoxedge.ai/.well-known/jwks.json"
PURPOSE_CLAIM = "bfx:purpose"
RECEIPT_PURPOSE = "receipt"
# Read-side, both IANA JOSE spellings, forever: "EdDSA" is what the key set
# publishes today and "Ed25519" is its fully-specified successor (RFC 9864).
ACCEPTED_ED25519_ALG_SPELLINGS = ("Ed25519", "EdDSA")
def not_implemented_here(sentence):
# A refusal about THIS CHECKER, never a result about the receipt: the
# input names an algorithm this build does not implement. One sentence,
# exit 3 (the JS twin's NotImplementedHere), so tooling can tell
# "checked and failed" from "cannot judge here".
sys.stderr.write(sentence + "
")
raise SystemExit(3)
def unb64url(s):
return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
def b64url(raw):
return base64.urlsafe_b64encode(raw).decode("ascii").rstrip("=")
def sha256s(raw):
return "sha256:" + hashlib.sha256(raw).hexdigest()
# RFC 8785 (JCS) canonical JSON, standard library only.
#
# Reproduced from BRIDIE's construction -- a stranger who, sent at this estate
# on 2026-08-15 under a no-install rule, rebuilt this in 25 lines of stdlib and
# got output BYTE-IDENTICAL to the rfc8785 package on the published specimen
# (1,324 bytes from both). Her point stands and is the reason this file no
# longer asks you to install anything: the spec is complete enough to
# reimplement, so the checker should not need a package to prove it.
#
# Three rules do all the work. Object keys sort by UTF-16 code unit -- which is
# what encoding each key to UTF-16 big-endian and comparing those bytes gives
# you, surrogate pairs included. There is no insignificant whitespace. And a
# number that is integral is spelled without its fraction: JCS spells 0.0 as 0,
# and one character breaks a hash. That trap is live in the flagship artifact --
# the specimen's confidence_score is 0.0.
#
# The number domain this accepts is the one the signer itself enforces before
# hashing (finite floats, integers within the JCS safe-integer range). Outside
# that, and in the narrow band where a shortest-round-trip float would have to
# be spelled with an exponent, this REFUSES rather than guessing -- a wrong
# canonical form is a wrong hash, and a wrong hash silently accuses an honest
# receipt. Reject, never normalise.
def jcs(value):
if value is True:
return b"true"
if value is False:
return b"false"
if value is None:
return b"null"
if isinstance(value, str):
try:
return json.dumps(value, ensure_ascii=False,
separators=(",", ":")).encode("utf-8")
except UnicodeEncodeError:
raise SystemExit("this receipt carries text that is not encodable as "
"UTF-8 (a lone surrogate) -- it cannot be canonicalised")
if isinstance(value, int):
if abs(value) > 2 ** 53 - 1:
raise SystemExit("this receipt carries an integer outside the JCS "
"safe-integer range -- refusing rather than hashing a "
"value two implementations would spell differently")
return repr(value).encode("ascii")
if isinstance(value, float):
if value != value or value in (float("inf"), float("-inf")):
raise SystemExit("this receipt carries a non-finite number, which has "
"no JSON canonical form")
text = repr(value)
if "e" in text or "E" in text:
raise SystemExit("this receipt carries a number whose canonical "
"spelling needs an exponent, which this stdlib "
"canonicaliser does not attempt -- refusing rather "
"than risking a wrong hash")
if value.is_integer():
return repr(int(value)).encode("ascii")
return text.encode("ascii")
if isinstance(value, list):
return b"[" + b",".join(jcs(item) for item in value) + b"]"
if isinstance(value, dict):
pairs = []
for key in sorted(value, key=lambda k: k.encode("utf-16-be")):
if not isinstance(key, str):
raise SystemExit("this receipt has a non-text object key -- it is "
"not canonicalisable JSON")
pairs.append(jcs(key) + b":" + jcs(value[key]))
return b"{" + b",".join(pairs) + b"}"
raise SystemExit("this receipt carries a value of a kind JSON has no "
"canonical form for -- it is not a receipt this recipe checks")
def copies(node, key): # every value a receipt's own subtree carries under key
if isinstance(node, dict):
for k, v in node.items():
if k == key:
yield v
yield from copies(v, key)
elif isinstance(node, list):
for item in node:
yield from copies(item, key)
# The file to check: the one you name, or the published specimen if you name
# nothing. Every way this can go wrong below is a sentence and a non-zero exit,
# because handing a stranger a traceback for a missing file is not a verdict --
# it is the vendor's stack frames in place of an answer. Flag parsing is spelled
# out rather than imported: argparse prints its own usage paragraphs and exits 2,
# and this file's contract is one sentence and its own exit, every time.
jwks_source = None
record_path = None
names = []
args = list(sys.argv[1:])
while args:
arg = args.pop(0)
if arg == "--jwks" or arg.startswith("--jwks="):
if jwks_source is not None:
raise SystemExit("--jwks is named twice -- one verdict means one "
"pinned key set, so name exactly one")
if arg == "--jwks":
value = args.pop(0) if args else ""
else:
value = arg[len("--jwks="):]
if not value:
raise SystemExit("--jwks needs a value -- the path of a key-set "
"file you saved earlier, or an https address to "
"fetch the keys from")
jwks_source = value
elif arg == "--bind-record" or arg.startswith("--bind-record="):
if record_path is not None:
raise SystemExit("--bind-record is named twice -- one verdict means one "
"record, so name exactly one")
if arg == "--bind-record":
value = args.pop(0) if args else ""
else:
value = arg[len("--bind-record="):]
if not value:
raise SystemExit("--bind-record needs a value -- the path of the record "
"file this receipt is meant to name")
record_path = value
else:
names.append(arg)
if len(names) > 1:
raise SystemExit("usage: python3 verify_receipt.py [PATH-TO-RECEIPT.json] -- "
"one file at a time, so one verdict means one receipt")
path = names[0] if names else "specimen-receipt.json"
try:
with open(path, encoding="utf-8") as handle:
specimen = json.load(handle)
except FileNotFoundError:
raise SystemExit("there is no file at " + path + " -- name the receipt you "
"want checked, or save it as specimen-receipt.json and pass "
"nothing")
except IsADirectoryError:
raise SystemExit(path + " is a directory, not a receipt file")
except PermissionError:
raise SystemExit(path + " cannot be read (permission denied)")
except UnicodeDecodeError:
raise SystemExit(path + " is not UTF-8 text, so it is not a receipt this "
"recipe can read")
except ValueError as exc:
raise SystemExit(path + " is not valid JSON, so no receipt can be read "
"from it")
except OSError as exc:
raise SystemExit(path + " could not be read (" + str(exc) + ")")
if not isinstance(specimen, dict):
raise SystemExit(path + " holds a " + type(specimen).__name__ + ", not a JSON "
"object -- a receipt is an object")
# The record to bind (--bind-record): read through the same door as the receipt,
# right behind it, so a record that cannot be read is refused before anything
# in the receipt is looked at. It is checked against the envelope only after
# every signed field has held, further down.
record = None
if record_path is not None:
try:
with open(record_path, encoding="utf-8") as handle:
record = json.load(handle)
except FileNotFoundError:
raise SystemExit("there is no record file at " + record_path + " -- name the "
"record this receipt is meant to bind")
except IsADirectoryError:
raise SystemExit(record_path + " is a directory, not a record file")
except PermissionError:
raise SystemExit(record_path + " cannot be read (permission denied)")
except UnicodeDecodeError:
raise SystemExit(record_path + " is not UTF-8 text, so it is not a record this "
"recipe can read")
except OSError:
raise SystemExit(record_path + " could not be read, so no record can be bound")
except ValueError:
raise SystemExit(record_path + " is not valid JSON, so no record can be read "
"from it")
if not isinstance(record, dict):
raise SystemExit(record_path + " holds a " + type(record).__name__ + ", not a "
"JSON object -- a record is an object")
# The signed surface is the envelope: the one dict that carries the chain
# block. Its home differs by door -- data.receipt in an x402 mint response,
# data.envelope in a read-API response or the published specimen, one per
# entry of an export bundle -- so this finds it instead of indexing a path.
#
# WHICH envelope, when a file offers more than one, is a security question,
# not a convenience one. Every door here answers with a "data" mapping and
# puts the envelope inside it, so if the file HAS that mapping, ITS slot
# decides and nothing found elsewhere in the file can speak for it. That is
# what stops a doctored receipt at the slot you read from being excused by a
# genuine envelope tucked somewhere else in the same file. Only a file with no
# such mapping -- an export bundle, or something you assembled yourself --
# falls back to searching, and then you are reading whatever the search found:
# run this once per entry and read each answer. Either way a pass is a
# statement about ONE envelope, never about the file. And a file that HAS a
# data block but names no receipt in it is refused outright rather than
# searched, because that is not a shape any of these doors answers with.
#
# Both routes also return the dict CARRYING the envelope -- data, or the entry
# an envelope was found under -- because that is where the unsigned convenience
# copies live, and it is the fence that keeps the copy check inside ONE receipt,
# so a two-receipt bundle never compares entry A's hashes against entry B's.
# An envelope found directly inside a list has no carrier to name, and then the
# envelope fences itself: only its own copies are checked.
def signed(node):
chain = node.get("chain") if isinstance(node, dict) else None
return isinstance(chain, dict) and "signature" in chain
def slot_in_data(doc):
data = doc.get("data") if isinstance(doc, dict) else None
if not isinstance(data, dict):
return None # no data mapping: search, below
named = [s for s in ("receipt", "envelope") if s in data]
if not named:
# A "data" mapping is how these doors answer, so a file that has one
# and names no receipt inside it is not a receipt response -- and the
# rest of the file does not get to volunteer a substitute.
raise SystemExit("this file has a data block with no receipt in it -- "
"refusing to hunt elsewhere for something to check")
# From here the file HAS named a receipt, so that name binds and the
# search never runs. Naming one and then failing to be one is a refusal,
# not a licence to go looking for something else in the file to bless.
if len(named) > 1:
raise SystemExit("this file fills both data.receipt and data.envelope "
"-- name one receipt, or check them one at a time")
slot = named[0]
if not signed(data[slot]):
raise SystemExit("data." + slot + " is not a signed envelope -- "
"refusing to check something else in its place")
return data, data[slot] # data is the carrier, as below
def find_envelope(node, parent=None):
if isinstance(node, dict):
if signed(node):
return parent, node
for child in node.values():
hit = find_envelope(child, node)
if hit is not None:
return hit
elif isinstance(node, list):
for child in node:
hit = find_envelope(child, None)
if hit is not None:
return hit
return None
found = slot_in_data(specimen) or find_envelope(specimen)
if found is None:
raise SystemExit("no signed envelope found -- not a signed receipt (yet?)")
scope, envelope = found
if scope is None:
scope = envelope
chain = envelope["chain"]
# Everything the recipe is about to read, present AND of the right kind
# before it is read. A receipt missing one of these -- or carrying null or a
# number where text belongs -- is refused in a sentence, never with a
# traceback: handing a stranger a stack trace is the failure this whole
# recipe was rewritten to stop. (key_id must only be PRESENT: a keyless
# receipt carries it as null and reaches its own honest exit further down.)
def needs(field, home, text=True):
if field not in home:
raise SystemExit("this envelope has no " + field + " -- it is not a "
"receipt of the shape this recipe checks")
if text and not isinstance(home[field], str):
raise SystemExit("this envelope's " + field + " is not text -- it is "
"not a receipt of the shape this recipe checks")
needs("chain_prev", chain)
needs("key_id", chain, text=False)
needs("schema_version", envelope)
# 1. content_digest recomputes from the envelope's own bytes: sha256 over the
# RFC 8785 canonical form of the envelope with the whole chain block
# removed. JCS, not json.dumps -- JCS spells 0.0 as 0, and one character
# breaks a hash.
body = {k: v for k, v in envelope.items() if k != "chain"}
digest = sha256s(jcs(body))
# 2. chain_self recomputes: sha256 over digest|chain_prev|schema_version,
# joined with literal pipes, hashed as UTF-8. chain_prev is the previous
# receipt's chain_self, or the literal "GENESIS" at a chain head.
head = sha256s("|".join(
(digest, chain["chain_prev"], body["schema_version"])).encode("utf-8"))
# 3. Every copy of the two hashes in THIS receipt's subtree agrees with the
# recomputation -- the chain block's and the top-level mirrors' alike.
# Presence is checked before agreement: all() is True over an empty
# iterator, so a receipt carrying NO copy of a hash would otherwise sail
# through this step without it ever being compared to anything. A detected
# forgery is a verdict, so it exits in a sentence, never a stack trace.
for key, want in (("content_digest", digest), ("chain_self", head)):
carried = list(copies(scope, key))
if not carried:
raise SystemExit(key + " is absent -- there is nothing here to check")
if not all(got == want for got in carried):
raise SystemExit(
key + " does not recompute from the receipt's own bytes")
if not chain.get("signature"):
raise SystemExit("keyless receipt: steps 1-3 hold, but with key_id and "
"signature null there is no signature to check")
# 4. The signing key is published, and its name is a derived fact:
# kid = sha256 of the raw 32-byte Ed25519 public key. The key is selected by
# kid and then checked against its declared family -- selecting by kid alone
# would let a checkpoint key verify a receipt if the two ever crossed, and
# the whole reason there are two keys is that they must not.
#
# With --jwks the same selection runs against the keys YOU pinned. A saved
# file is read here with no network at all; an https address you named is
# fetched in place of the default. The kid check below is what makes a
# pinned file trustworthy to hold: the key's name is derived from the key's
# own bytes, so a swapped key cannot keep its old name.
key_set_word = "published" if jwks_source is None else "pinned"
if jwks_source is None or jwks_source.startswith("https://"):
key_set_url = JWKS_URL if jwks_source is None else jwks_source
req = urllib.request.Request(key_set_url, headers={"User-Agent": "bluefox-verify/1"})
try:
jwks = json.load(urllib.request.urlopen(req, timeout=30))
except Exception as exc:
raise SystemExit("the " + key_set_word + " key set at " + key_set_url +
" could not be "
"fetched (" + type(exc).__name__ + ") -- this checker will "
"not fall back to any address inside the receipt, so it "
"stops here")
elif jwks_source.startswith("http://"):
raise SystemExit("--jwks " + jwks_source + " is plaintext http -- keys "
"fetched without TLS can be swapped in flight, so save the "
"key set to a file and name that instead, or use https")
else:
try:
with open(jwks_source, encoding="utf-8") as handle:
jwks = json.load(handle)
except FileNotFoundError:
raise SystemExit("there is no key-set file at " + jwks_source + " -- "
"save the published keys first (curl -sSo "
"pinned-jwks.json " + JWKS_URL + ") and name that file")
except IsADirectoryError:
raise SystemExit(jwks_source + " is a directory, not a key-set file")
except PermissionError:
raise SystemExit(jwks_source + " cannot be read (permission denied)")
except UnicodeDecodeError:
raise SystemExit(jwks_source + " is not UTF-8 text, so it is not a key "
"set this recipe can read")
except ValueError:
raise SystemExit(jwks_source + " is not valid JSON, so no key can be "
"pinned from it")
except OSError:
raise SystemExit(jwks_source + " could not be read, so no key can be "
"pinned from it")
if not isinstance(jwks, dict) or not isinstance(jwks.get("keys"), list):
raise SystemExit("the " + key_set_word + " key set has no keys array, so no "
"key can be pinned")
key = next((k for k in jwks["keys"]
if isinstance(k, dict) and k.get("kid") == chain["key_id"]), None)
if key is None:
if jwks_source is None:
raise SystemExit("key_id is not in the published key set")
raise SystemExit("key_id is not in the pinned key set at " + jwks_source +
" -- if that copy predates this receipt, refresh it from " +
JWKS_URL + " and pin the new file")
purpose = key.get(PURPOSE_CLAIM)
# ABSENT is tolerated on purpose; only a POSITIVE wrong designation refuses.
# The published set gained bfx:purpose at the 2026-08-18 FIRE, so every key-set
# file pinned before that day lacks the member -- and the --jwks door exists
# precisely for files saved earlier. Same philosophy as the settlement lane's
# network guard: refuse a wrong answer, never punish an absent one. (H98 read
# the fail-closed suggestion and refuted it against the repo's own genuine
# 2026-08-08 pinned fixture; a stricter world needs a worded tolerance-sunset.)
if isinstance(purpose, str) and purpose != RECEIPT_PURPOSE:
raise SystemExit("this receipt names the " + key_set_word + " key " + chain["key_id"] +
", which is designated " + repr(purpose) + " and not a "
"receipt signer -- the two key families never cross, so "
"this is refused rather than verified")
# The algorithm is READ before it is assumed. An entry that POSITIVELY
# declares something other than Ed25519 (a foreign kty like AKP, a foreign
# crv, or an alg outside the two Ed25519 spellings) is a gap in THIS CHECKER,
# not a defect in the receipt -- absent members stay tolerated, exactly like
# the purpose label above.
entry_alg, entry_kty, entry_crv = key.get("alg"), key.get("kty"), key.get("crv")
foreign_alg = ""
if isinstance(entry_alg, str) and entry_alg not in ACCEPTED_ED25519_ALG_SPELLINGS:
foreign_alg = entry_alg
elif isinstance(entry_kty, str) and entry_kty != "OKP":
foreign_alg = "a " + entry_kty + "-family key"
elif isinstance(entry_crv, str) and entry_crv != "Ed25519":
foreign_alg = "curve " + entry_crv
if foreign_alg:
not_implemented_here("this receipt names the " + key_set_word + " key " + chain["key_id"] +
", whose key set entry declares " + foreign_alg + " -- an "
"algorithm this checker does not implement; refused rather "
"than assumed: nothing here says the receipt is bad, only "
"that this build cannot judge it, and a checker that "
"implements that algorithm can")
# The declared-algorithm cross-binding: an OPTIONAL chain.sig_alg, when
# present, must agree with the resolved key set entry. By here the entry is
# Ed25519-family, so a foreign sig_alg is a DISAGREEMENT in the file -- a
# refusal about the receipt, not a capability gap.
if "sig_alg" in chain and not isinstance(chain["sig_alg"], str):
raise SystemExit("this envelope's chain.sig_alg is not text -- it is not a "
"receipt of the shape this recipe checks")
if isinstance(chain.get("sig_alg"), str) and chain["sig_alg"] not in ACCEPTED_ED25519_ALG_SPELLINGS:
raise SystemExit("this receipt declares sig_alg " + repr(chain["sig_alg"]) + " but its "
"key set entry is an Ed25519 key -- the declared algorithm and the "
"published key disagree, so this is refused rather than verified")
raw_key = unb64url(key["x"])
if hashlib.sha256(raw_key).hexdigest() != chain["key_id"]:
raise SystemExit("kid is not sha256 of the raw public key")
# The length is a declaration too: raw bytes that are not 32 cannot be an
# Ed25519 key, and pushing them into the crypto layer would surface as the
# step-6 tampering sentence -- confidently wrong about a receipt this build
# simply cannot judge.
if len(raw_key) != 32:
not_implemented_here("the resolved key is " + str(len(raw_key)) + " raw bytes, not a "
"32-byte Ed25519 key -- an algorithm this checker does not "
"implement; refused rather than assumed: nothing here says "
"the receipt is bad, only that this build cannot judge it")
# 5. The carried signature string is the one canonical spelling of its bytes
# (base64url, padding stripped) -- flip even a slack bit and this fails.
needs("signature", chain)
try:
sig = unb64url(chain["signature"])
except Exception:
raise SystemExit("this envelope's signature is not base64url text -- it "
"is not a receipt of the shape this recipe checks")
if b64url(sig) != chain["signature"]:
raise SystemExit("signature encoding is not canonical")
# Ed25519 verification (RFC 8032), self-contained, so this file runs on a bare
# Python. You never need to read this block: it is the arithmetic, not the
# recipe. It is here only so that "no install required" is literally true, and
# it is skipped entirely when the cryptography package is present.
P = 2 ** 255 - 19
L = 2 ** 252 + 27742317777372353535851937790883648493
def inv(x):
return pow(x, P - 2, P)
D = (-121665 * inv(121666)) % P
SQRT_M1 = pow(2, (P - 1) // 4, P)
def x_recover(y):
xx = ((y * y - 1) * inv(D * y * y + 1)) % P
x = pow(xx, (P + 3) // 8, P)
if (x * x - xx) % P != 0:
x = (x * SQRT_M1) % P
if (x * x - xx) % P != 0:
return None
return P - x if x % 2 != 0 else x
def point_add(pt1, pt2):
x1, y1 = pt1
x2, y2 = pt2
prod = (D * x1 * x2 * y1 * y2) % P
return (((x1 * y2 + x2 * y1) * inv(1 + prod)) % P,
((y1 * y2 + x1 * x2) * inv(1 - prod)) % P)
def scalar_mult(pt, scalar):
acc = (0, 1)
while scalar > 0:
if scalar & 1:
acc = point_add(acc, pt)
pt = point_add(pt, pt)
scalar >>= 1
return acc
BASE_Y = (4 * inv(5)) % P
BASE = (x_recover(BASE_Y), BASE_Y)
def on_curve(pt):
x, y = pt
return (-x * x + y * y - 1 - D * x * x * y * y) % P == 0
def decode_point(blob):
value = int.from_bytes(blob, "little")
y = value & ((1 << 255) - 1)
sign = value >> 255
if y >= P:
return None
x = x_recover(y)
if x is None:
return None
if x & 1 != sign:
x = P - x
pt = (x, y)
return pt if on_curve(pt) else None
def ed25519_verify_pure(public_raw, signature, message):
if len(public_raw) != 32 or len(signature) != 64:
return False
point_a = decode_point(public_raw)
point_r = decode_point(signature[:32])
if point_a is None or point_r is None:
return False
scalar_s = int.from_bytes(signature[32:], "little")
if scalar_s >= L:
return False
challenge = int.from_bytes(
hashlib.sha512(signature[:32] + public_raw + message).digest(),
"little") % L
return scalar_mult(BASE, scalar_s) == point_add(
point_r, scalar_mult(point_a, challenge))
def ed25519_verify(public_raw, signature, message):
try:
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PublicKey)
except ImportError:
return ed25519_verify_pure(public_raw, signature, message)
try:
Ed25519PublicKey.from_public_bytes(public_raw).verify(signature, message)
return True
except Exception:
return False
# 6. The Ed25519 signature verifies over the RECOMPUTED chain head -- so the
# signature commits to every envelope byte hashed in step 1, not merely to
# a hash string the file happened to carry. A failure HERE is the verdict
# this whole recipe exists to deliver, so it too is a sentence.
if not ed25519_verify(raw_key, sig, head.encode("utf-8")):
raise SystemExit("the signature does not verify over the recomputed chain "
"head -- these are not the bytes the published key signed")
# 7. Unsigned convenience mirrors: the dict carrying the envelope repeats some
# signed fields for readers, and nothing above checked those copies -- they
# sit outside the signature. Mirrors spell the exact bytes of the envelope's
# values (produced_at included: same Z-form, same instant), so a copy that
# disagrees is a REFUSAL, not a warning. The signature held -- but the file
# in your hand is showing a reader different bytes than the ones it signed,
# and a disagreeing mirror is exactly how a doctored copy rides a check
# that reads only the envelope. A warning here asked you to notice what
# this script exists to notice for you, so it stops instead.
if scope is not envelope:
# chain_prev rides along (2E116, S116 -- ll116's L5): it is the one signed
# input to the chain head that steps 1-3 never recompute (a link, not a
# hash), so a top-level copy of it was the one mirror nothing above
# compared -- a one-byte edit of it passed while item 7 of the README said
# otherwise. Same rule, same sentence, one more key.
mirrored = list(body.items()) + [("chain_prev", chain["chain_prev"])]
for key, value in mirrored:
if key in scope and scope[key] != value:
raise SystemExit("unsigned top-level " + key + " does not match the "
"signed envelope's " + key + " -- the signature held, "
"but this file presents altered bytes where a reader "
"reads, and that is a tampered file, not a footnote")
# BINDING (--bind-record). The record is hashed with the same canonicaliser the
# envelope was, and the receipt's first source row must carry that digest --
# that is the bind: a digest the caller presented, named by a signed row. The
# rest reads the receipt's OWN words back: the source row's shape says this was
# a presented digest, the self-disclosure fields say what kind of receipt it
# calls itself, the retained presentation (when the tier kept one) must agree
# with the record, and the verdict word is copied out of the file. Nothing is
# graded here. It runs only when a record was named, after every signed field
# above has held, and each refusal is one sentence.
def scrub(text): # control characters become "?", every printable character stays
return "".join("?" if ord(ch) < 32 or ord(ch) == 127 else ch for ch in text)
def show(value): # a quoted value: text single-quoted (backslash and quote escaped,
if isinstance(value, str): # the one spelling both twins use), anything else in JCS form
backslash = chr(92)
return ("'" + value.replace(backslash, backslash + backslash)
.replace("'", backslash + "'") + "'")
return jcs(value).decode("utf-8")
# The profile's pinned six-key meta, derived from the record -- what a tier
# that keeps meta stored beside the digest. Every key is tested for presence
# before it is read, so a record of another shape is refused in a sentence.
def meta_for(record):
def lacks(key):
raise SystemExit("the record at " + record_path + " is not a tool-call record "
"this recipe can bind -- it lacks " + key)
def text(value): # a count spelled the way both twins spell it: 3 and 3.0 both read 3
return value if isinstance(value, str) else jcs(value).decode("utf-8")
if "profile" not in record:
lacks("profile")
if "session" not in record:
lacks("session")
session = record["session"]
if not isinstance(session, dict) or "ref" not in session:
lacks("session.ref")
if "kind" in record and record["kind"] == "session-close":
if "final_count" not in session:
lacks("session.final_count")
return {"profile": record["profile"], "kind": "session-close", "tool": "-",
"scheme": "none", "seq": text(session["final_count"]),
"session": session["ref"]}
tool = record["tool"] if "tool" in record else None
if not isinstance(tool, dict) or "name" not in tool:
lacks("tool.name")
if "seq" not in session:
lacks("session.seq")
ident = record["identity"] if "identity" in record else None
if isinstance(ident, dict) and "scheme" not in ident:
lacks("identity.scheme")
return {"profile": record["profile"], "kind": "tool-call", "tool": tool["name"],
"scheme": ident["scheme"] if isinstance(ident, dict) else "none",
"seq": text(session["seq"]), "session": session["ref"]}
if record_path is not None:
record_digest = hashlib.sha256(jcs(record)).hexdigest()
sources = envelope.get("sources")
if not (isinstance(sources, list) and sources and isinstance(sources[0], dict)):
raise SystemExit("this envelope has no source row -- there is nothing for a "
"record to bind to")
src0 = sources[0]
if "source_bytes_digest" not in src0 or not isinstance(src0["source_bytes_digest"], str):
raise SystemExit("the source row binds nothing readable -- source_bytes_digest "
"is not text")
if src0["source_bytes_digest"] != "sha256:" + record_digest:
raise SystemExit("this receipt does not name the record at " + record_path +
" -- the record digests to sha256:" + record_digest +
" and the receipt's source row binds " +
scrub(src0["source_bytes_digest"]))
for field, expected in (("hash_alg", "sha256"),
("extraction_method", "client_submission"),
("asserted_by", "as-stated-by-caller")):
value = src0[field] if field in src0 else None
if value != expected:
raise SystemExit("the bound source row's " + field + " is " + show(value) +
", not " + show(expected) + " -- this is not a "
"presented-digest receipt for that record")
if ("relevant_text" in src0 and src0["relevant_text"] is not None
and src0["relevant_text"] != ""):
raise SystemExit("the bound source row retains text -- a presented digest "
"carries none, so this is not a presented-digest receipt for "
"that record")
for field, expected in (("not_an_attestation", True),
("assertion_type", "reliance"),
("edge_role", "evidence-layer")):
value = envelope[field] if field in envelope else None
agrees = value is True if expected is True else value == expected
if not agrees:
raise SystemExit("this receipt's self-disclosure does not read as a "
"reliance receipt -- " + field + " is " + show(value))
details = envelope["details"] if "details" in envelope else None
if not isinstance(details, dict):
details = {}
presentation = details["presentation"] if "presentation" in details else None
presented = isinstance(presentation, dict)
if not presented:
presentation = {}
kept = presentation["value"] if "value" in presentation else None
if not isinstance(kept, dict):
kept = {}
retained = kept["meta"] if "meta" in kept else None
if not isinstance(retained, dict):
retained = {}
expected_meta = meta_for(record)
if presented:
kept_hash = kept["hash"] if "hash" in kept else None
if kept_hash != record_digest:
raise SystemExit("the retained presentation names a different digest than "
"the record -- " + show(kept_hash) + " vs " + record_digest)
by = presentation["asserted_by"] if "asserted_by" in presentation else None
if by != "as-stated-by-caller":
raise SystemExit("the retained presentation is not asserted by the caller "
"-- asserted_by is " + show(by))
if retained and jcs(retained) != jcs(expected_meta):
raise SystemExit("the retained meta does not agree with the record -- "
"retained " + jcs(retained).decode("utf-8") +
" vs expected " + jcs(expected_meta).decode("utf-8"))
produced_at = envelope["produced_at"] if "produced_at" in envelope else None
if not isinstance(produced_at, str) or not re.fullmatch(
"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$", produced_at):
raise SystemExit("this receipt's produced_at is not an RFC 3339 UTC instant -- " +
show(produced_at))
if presented and retained:
variant = " -- the retained meta agrees with the record"
elif presented:
variant = " -- the retained presentation carries no meta, so the digest alone binds"
else:
variant = (" -- the retained presentation is absent, so the digest alone binds "
"via the source row")
print("the record binds: " + scrub(record_path) + " digests to sha256:" +
record_digest + ", the receipt's source row names that digest, and the "
"receipt's own verdict word is " + show(envelope.get("verdict")) + variant)
print("the record is the caller's own account of the call: this run shows the "
"receipt names it, not that the call happened as written")
# WHAT THIS RUN DID NOT CHECK -- printed on every successful run, which is what
# this file's README has always promised and what, until now, only a source
# comment said. One logical line per member, in the order below, always ALL of
# them: the count is the length of this list and is written nowhere as a
# literal, so a slug added here is a slug printed. The source of record is
# RECEIPT_CHECK_UNSUPPORTED_PATHS / RECEIPT_CHECK_DISCLOSURES in app/models.py
# and a test pins this copy against it.
#
# IT PRINTS BEFORE THE VERDICT, deliberately: the verdict lands last where a
# reader looks for it, and a REFUSAL never reaches this point at all -- a file
# that failed to be checked must not emit the vocabulary of a file that was.
NOT_VERIFIED = [
("verdict_recompute",
"It does not re-run any verdict"),
("underlying_claim_truth",
"Proof of reliance, never proof of truth"),
("transparency_log_inclusion",
"It does not check transparency-log inclusion; that check is /verify_inclusion.py, or"
" /verify_receipt.js handed the inclusion proof (--inclusion)"),
("external_witness_or_anchor",
"no cosignature and no external anchor were checked; trust in the log key"
" is trust in BlueFox until a witness lifts it"),
("source_url_resolution",
"The source's URL is not part of the durable envelope"),
("raw_source_bytes",
"each receipt names its sources by content digests"),
("revocation_status",
"Dispute/revocation status is not covered"),
("key_set_provenance",
"the key set was {key_set_word}, taken from {key_set_source}; nothing"
" here proves those keys are BlueFox's current published set"),
("split_view_or_equivocation",
"one file cannot detect a split view; that needs two independently"
" obtained checkpoints and /verify_consistency.py"),
("foreign_signature_relay",
"a foreign signature BlueFox checked at mint time was checked against a"
" key outside its published key set, and this run does not re-check it"),
]
key_set_source_raw = JWKS_URL if jwks_source is None else jwks_source
# ONE LINE PER SLUG MEANS ONE LINE, and the key-set path is the only thing in
# this block a stranger chose. A filename may legally carry a newline or an
# escape, and interpolated raw it could ADD a scope line, or spoof the pass
# line, on a run that SUCCEEDED -- the one place this output is read as
# evidence. Control characters become "?"; every printable character, non-ASCII
# included, is shown exactly as given, because a path you cannot recognise is
# not a disclosure.
key_set_source = "".join(
"?" if ord(ch) < 32 or ord(ch) == 127 else ch for ch in key_set_source_raw)
for slug, sentence in NOT_VERIFIED:
# ORDER MATTERS: the word first, the SOURCE last. Replacement re-scans what
# it has written, and the source is the path you named on the command line
# -- so it goes in last, where nothing can expand anything it contains.
sentence = sentence.replace("{key_set_word}", key_set_word)
sentence = sentence.replace("{key_set_source}", key_set_source)
print("NOT VERIFIED by this run -- " + slug.upper() + ": " + sentence + ".")
# THE OBSERVATION/0 LINE (P117, S117/D015; the listening lane's receipts, U116 spec
# section 13 row 12). When the retained presentation says the presented digest
# names an OBSERVATION RECORD -- meta.profile "observation/<n>" and meta.kind
# "observation", the /0 path's own statement about what the digest names -- one
# more line prints on the pass path, before the register line: the record's
# profile and the receipt's own grade word, both READ from the file and never
# judged here, and where to read what such a receipt does and does not prove:
# the limits register's row for observation/0, rendered verbatim from the spec.
# A receipt without that meta prints nothing new; a refusal never reaches this
# point. Both twins print the identical sentence, byte for byte.
def observation_zero_meta(envelope):
details = envelope["details"] if "details" in envelope else None
presentation = (details["presentation"]
if isinstance(details, dict) and "presentation" in details else None)
value = (presentation["value"]
if isinstance(presentation, dict) and "value" in presentation else None)
meta = value["meta"] if isinstance(value, dict) and "meta" in value else None
if not isinstance(meta, dict) or "profile" not in meta or "kind" not in meta:
return None
profile = meta["profile"]
if (isinstance(profile, str) and profile.startswith("observation/")
and meta["kind"] == "observation"):
return meta
return None
zero_meta = observation_zero_meta(envelope)
if zero_meta is not None:
print("observation/0: the digest presented names an observation record of profile " + show(zero_meta["profile"]) + " and the receipt's own grade word is " + show(envelope.get("verdict")) + " -- its limits register row (What a BlueFox receipt does not prove, observation/0) carries its proves and does_not_prove clauses, lifted verbatim from the spec")
# THE LIMITS REGISTER, IN ONE LINE (ff116, SS116/D006). The scope block above says
# what this RUN did not check; this line says what a PASSING receipt does not
# PROVE, in the words a reader meets -- the not_an_attestation discipline made
# legible where the verdict is read. It prints on the pass path only, right
# before the verdict; a refusal never reaches it. Both published checkers print
# this exact sentence (the parity suite compares them byte for byte), and the
# TS module beside the taught bytes exports it as DOES_NOT_PROVE_LINE so the
# site's register page can quote the same words; a test pins the two equal.
print("does_not_prove: that any claim inside this receipt is true, that the act it describes happened as written, or anything about the content behind a digest -- this check shows only that these bytes were signed under the key set this run used and have not changed since")
print("the check passes: every signed field recomputed from the file's own bytes")What the three checks prove
Content digest — content_digest is the SHA-256 of the envelope's canonical bytes (RFC 8785) with the chain block removed. If any signed fact in the envelope changed, this digest changes.
Chain head — chain_self commits to the content digest, the prior receipt's chain head (chain_prev, the literal GENESIS for an account's first receipt), and the schema version. Receipts form a per-account hash chain: history cannot be quietly rewritten.
Signature — a detached ed25519 signature over the chain_self string, verified against the published key set at /.well-known/jwks.json. Because chain_self commits to the content digest, a valid signature is a signature over the receipt's content.
Normative — the one pointer never to follow. A verifier MUST NOT follow the jwks_url carried inside a receipt. That field sits outside the signature, so a forged receipt can aim it at the forger's own key set and the forgery verifies against it. Pin the key set out of band at https://api.bluefoxedge.ai/.well-known/jwks.json — which is exactly why the script above writes that address in code rather than reading it from the file it is checking.
The full byte-level construction (and the schema-version freeze law) is published at api.bluefoxedge.ai/getting-started step 4. Field-by-field meaning: Receipt Anatomy.
What it does not prove
A receipt is proof of reliance, never proof of truth. Every envelope carries not_an_attestation: true and a non_claims list on purpose: verifying a receipt proves what was checked or presented, against what, and when — it does not certify the underlying content.