Docs · Attestation

Verify a chain in 30 seconds.

The full how-to: from grabbing the JSON appendix in a report to running cryptographic verification in your own environment. No Felarity account required to verify — that is the whole point.

Every Felarity meeting report ends with a signed attestation chain. The chain proves, to anyone holding our public key, that the audio that entered the pipeline is the same audio that produced the transcript, that produced the contradictions, that produced the speaker attributions, that produced the final report. Eight nodes, each hashed, each linked to the previous, the terminal hash signed with Ed25519.

You can verify a chain three ways. Pick whichever fits your workflow.

1. What you need

Three things, none of them sensitive:

Chain JSON Public key (PEM) A few minutes

The chain JSON ships inside every signed report. In the web app, open the report and click Download → Attestation appendix (JSON). From the API, fetch GET /api/v1/reports/<id> — the attestation key contains the full chain. The public key is published at https://felarity.com/.well-known/felarity-signing-key.pem and mirrored at GET /api/verify/public-key.

Anatomy of a chain. Eight nodes in fixed order: audio_capture, transcription, contradiction_detection_pre_attribution, diarization, attribution_binding, acoustic_analysis, topology, final_report. Each node has an index, a name, a canonical payload, a prev_hash, and a node_hash. The terminal node carries the Ed25519 signature and the signing_key_fingerprint.

2. Option 1 — Verify via the public endpoint

The fastest path. POST the chain JSON to our public verifier. No authentication required; no information about the meeting is sent — the verifier only sees node hashes, the chain structure, and the signature.

curl -X POST https://api.felarity.com/api/verify \
  -H "Content-Type: application/json" \
  --data @attestation_appendix.json

Expected response:

{
  "valid": true,
  "hash_chain_valid": true,
  "signature_valid": true,
  "nodes_verified": 8,
  "signing_key_fingerprint": "SHA256:b6e1c0c4a3...",
  "signing_key_status": "active",
  "verified_at": "2026-06-07T18:42:11Z"
}

If anything is off — a single byte changed in any node payload, a missing link, a signature minted with a key we do not recognize — the response will set valid: false and include a reason field naming the first failure. The endpoint is deliberately conservative: it will not return partial success.

3. Option 2 — Verify locally with Python

For courts, auditors, regulators, or anyone who needs to verify without depending on a Felarity-operated endpoint. The whole script is below. Dependencies: requests and cryptography.

import json, hashlib, sys, requests
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.exceptions import InvalidSignature

# 1. Load the chain.
chain = json.load(open(sys.argv[1]))
nodes = chain["nodes"]
sig_hex = chain["signature"]
fingerprint = chain["signing_key_fingerprint"]

# 2. Fetch the public key. Cache this locally for offline verification.
pem = requests.get("https://api.felarity.com/api/verify/public-key").text
pub = load_pem_public_key(pem.encode())
assert isinstance(pub, Ed25519PublicKey), "unexpected key type"

# 3. Recompute each node hash and verify the chain links.
prev = "0" * 64
for node in nodes:
    canonical = json.dumps(node["payload"], sort_keys=True,
                           separators=(",", ":")).encode()
    expected = hashlib.sha256(canonical + prev.encode()).hexdigest()
    if expected != node["node_hash"]:
        sys.exit(f"node {node['index']} ({node['name']}): hash mismatch")
    if node["prev_hash"] != prev:
        sys.exit(f"node {node['index']}: broken link")
    prev = node["node_hash"]

# 4. Verify the Ed25519 signature over the terminal node hash.
try:
    pub.verify(bytes.fromhex(sig_hex), prev.encode())
except InvalidSignature:
    sys.exit("signature invalid")

print(f"OK: {len(nodes)} nodes, signed by {fingerprint}")

The canonicalization rule is intentionally boring: sort keys, no whitespace, UTF-8 bytes. We use this rule everywhere — in the pipeline that produces the chain, in the public verifier, in this script. If you implement it in another language, match those two flags (sort_keys and the compact separators) and you will get the same hashes.

Offline verification. Save the PEM file once, pin its fingerprint, and skip step 2 in subsequent runs. Verification then has no network dependency at all — a court can re-run it years later from a sealed evidence drive.

4. Option 3 — In the browser

Drag-and-drop the attestation JSON onto the widget at felarity.com/trust/attestation/. It runs the same verification in WebCrypto, client-side. Nothing leaves your machine. Useful for showing a chain to someone who does not have a Python environment handy.

5. What the result tells you

The four fields in the response answer four distinct questions.

FieldMeaning
valid Top-level AND of the three checks below. If this is true, the chain is intact, the signature is good, and the key is one of ours.
hash_chain_valid Every node's recomputed hash matches its claimed node_hash, and every prev_hash matches the previous node. Detects any payload tampering, reordering, insertion, or deletion.
signature_valid The Ed25519 signature over the terminal node hash verifies under the published public key. Detects forgery and key-substitution attempts.
nodes_verified Count of nodes that hashed cleanly. A complete Felarity chain is always 8. A truncated chain (fewer nodes) will set valid: false even if the partial hashes line up.

6. What it does NOT prove

The chain is a forensic record of what our pipeline did to the input it received. It does not, by itself, prove that the input was unedited audio of a real meeting, or that the people speaking in the audio are who someone claims they are, or that the contradictions our model surfaced are legally meaningful. It proves the processing chain. Treat it the way you would treat a chain-of-custody log on a physical evidence bag: necessary, useful, and not a substitute for the underlying judgement about what is in the bag.

In practice this means: a valid Felarity attestation is strong evidence that a transcript and contradiction set were produced from a specific audio file by a specific version of our pipeline at a specific time, and that no one has altered the artifacts since. It is not a court ruling about whether a witness lied.

7. Key rotation

We rotate signing keys on a published schedule and on any incident that even hints at compromise. We never delete a retired key. The PEM file at /.well-known/felarity-signing-key.pem may therefore contain more than one public key over time — current first, retired keys appended below with status comments.

The signing_key_fingerprint on the chain tells the verifier which key was in use the day the report was signed. The verifier accepts a signature if the fingerprint matches any key in the PEM bundle whose signing_key_status is active or retired_valid_for_verification. Keys marked revoked — the only status that ever invalidates historical chains — would be announced on /trust/security/ with a date and a reason, and we have never done so.

8. For courts and regulators

Suitable for affidavit. A signed Felarity attestation chain is a tamper-evident record of pipeline processing. The hash chain establishes that no node's payload has been altered relative to its position in the sequence. The Ed25519 signature, verifiable against a public key whose authenticity is established by publication at a stable, HTTPS-served well-known URL controlled by Felarity Inc., establishes that the terminal hash was bound to Felarity's signing infrastructure at a specific moment.

Affiants and expert witnesses are welcome to cite this guide, the Trust — Attestation page, and the source canonicalization rule (sorted JSON keys, compact separators, UTF-8) when describing the verification method. The verification can be reproduced in any sealed environment using the Python script above and a copy of the PEM file preserved at time of report issuance.

For chain-of-custody questions specific to a matter, contact legal@felarity.com. We respond to lawful process and to bona fide regulatory inquiries; we do not provide opinion testimony about the meaning of contradictions surfaced by the pipeline.

Where to go next

Trust — Attestation

The conceptual page: what each of the eight nodes is, what it hashes, why the order matters.

Read the trust page

API reference

POST /api/verify and GET /api/verify/public-key documented with full request and response schemas.

Open the API docs

Questions about a specific chain? hello@felarity.com. Suspected key compromise or signature anomaly? security@felarity.com.