Verify the chain. Any language. No credentials.
Fonteum’s attestation chain is a Certificate-Transparency-style cryptographic chain over every snapshot we attest. Every link is Ed25519-signed, references the previous link’s hash, and is verifiable from inputs alone with the published public key. This page is the canonical reference for verifiers.
← Chain index Underlying snapshot attestations → Live metadata JSON →
What the chain proves.
Every chain_links row is one signed link:
link_index— monotonically increasing bigint, starting at 0 (genesis).prev_hash— 64-char lowercase hex SHA-256 of the prior link’scontent_hash. Genesis prev_hash is"00".repeat(32).attestation_id— bigint reference to the underlyingsnapshot_attestationsrow this link attests over.NULLfor genesis only.attestation_content_hash— echoed fromsnapshot_attestations.content_hash. Same zero-pad asprev_hashfor genesis.content_hash— SHA-256 over the canonical serialization (next section). Locked once written.signature— Ed25519 signature ofcontent_hashusing the chain’s private key.public_key_id— identifier for the signing key. Phase 1 uses a single key (rotation is Phase 2 — see Public-key publication below).signed_at— ISO 8601 timestamp the link was signed. Part of the canonical content-hash payload.
A verifier walks link_index 0..head, recomputes eachcontent_hash, verifies eachsignature against the published public key, and checks prev_hash[N] == content_hash[N-1]. Any tamper anywhere in the chain breaks at least one of these three checks.
Atomic chain extension: each new link is appended via the extend_chain SECURITY DEFINER plpgsql function which performs INSERT chain_links + UPDATE chain_state head pointer in one transaction. The chain head pointer never lags the disk row.
Frozen contract.
The content_hash is SHA-256 over the following UTF-8 byte string. This format is FROZEN — changing it breaks every external verifier:
contentHash = SHA256(
String(linkIndex) || "||" ||
prevHash || "||" ||
attestationIdStr || "||" || // "null" for genesis
attestationContentHash || "||" ||
signedAtIso
)linkIndex— base-10 string, no leading zeros (e.g."0","123").prevHash,attestationContentHash— 64-char lowercase hex SHA-256.attestationIdStr— base-10 string for non-genesis links; literal"null"for genesis (attestation_id IS NULLonly atlink_index=0).signedAtIso— ISO 8601 with millisecond precision and trailing "Z" (e.g."2026-05-09T17:42:31.123Z")."||"— the literal two-character separator (U+007C twice). Not escaped inside any field because no field can contain "||" by construction.
link_index = 0.
The genesis link anchors the chain. It is created once, post-deploy, by the operator. Its values are fixed by spec:
link_index = 0prev_hash = "00".repeat(32)(64 zero hex chars)attestation_id = NULL(no underlying attestation)attestation_content_hash = "00".repeat(32)content_hash= canonical SHA-256 over the above +signed_atsignature= Ed25519 overcontent_hash
The genesis link’s attestation_id serializes to the literal string "null" in the canonical content-hash payload. This guarantees genesis cannot collide with any future link that has attestation_id = 0.
Single key, two equivalent surfaces.
The chain’s Ed25519 public key is published at two endpoints, both serving the same value (canonical source: chain_state.public_key):
https://fonteum.com/.well-known/chain-public-key— text/plain hex (64 lowercase hex chars + trailing newline). RFC-blessed location for automated discovery.https://fonteum.com/api/v1/chain→public_keyfield. Same value as a JSON field alongside chain metadata.
Phase 1 uses a single signing key. Key rotation is deferred to a separate wave (§sprint3-chain-key-rotation) which will introduce multi-key support with overlapping validity windows so verifiers can transition gracefully. Until rotation lands, the published public_key_id on every link points to the same key.
Three reads, one verify.
GET /api/v1/chain— chain head + total + published public key.GET /api/v1/chain/[link_index]— full record for one link. Cachedpublic, max-age=31536000, immutable(chain links are immutable once signed).GET /.well-known/chain-public-key— text/plain hex public key.POST /api/v1/chain/verify— accepts a chain link JSON in the body, returns{ valid, errors[] }. Verifies content_hash, signature, and prev_hash linkage to the prior on-disk link.
Two checks, no DB access.
// Node 20+ — npm i @noble/ed25519
import { createHash } from 'node:crypto';
import { verifyAsync } from '@noble/ed25519';
const link = await fetch('https://fonteum.com/api/v1/chain/123').then((r) => r.json());
const meta = await fetch('https://fonteum.com/api/v1/chain').then((r) => r.json());
// 1. Re-derive content_hash via the canonical serialization.
const payload =
String(link.link_index) + '||' +
link.prev_hash + '||' +
(link.attestation_id === null ? 'null' : String(link.attestation_id)) + '||' +
link.attestation_content_hash + '||' +
link.signed_at;
const recomputed = createHash('sha256').update(payload, 'utf8').digest('hex');
console.log('content_hash matches:', recomputed === link.content_hash);
// 2. Verify the Ed25519 signature.
const hexToBytes = (h) => Uint8Array.from(h.match(/.{2}/g).map((b) => parseInt(b, 16)));
const valid = await verifyAsync(
hexToBytes(link.signature),
hexToBytes(link.content_hash),
hexToBytes(meta.public_key),
);
console.log('valid:', valid);
stdlib + cryptography.
# Python 3.10+ — pip install cryptography requests
import hashlib, requests
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.exceptions import InvalidSignature
link = requests.get('https://fonteum.com/api/v1/chain/123').json()
meta = requests.get('https://fonteum.com/api/v1/chain').json()
# 1. Re-derive content_hash via the canonical serialization.
attestation_id_str = 'null' if link['attestation_id'] is None else str(link['attestation_id'])
payload = '||'.join([
str(link['link_index']),
link['prev_hash'],
attestation_id_str,
link['attestation_content_hash'],
link['signed_at'],
])
recomputed = hashlib.sha256(payload.encode('utf-8')).hexdigest()
print('content_hash matches:', recomputed == link['content_hash'])
# 2. Verify the Ed25519 signature.
public_key = Ed25519PublicKey.from_public_bytes(bytes.fromhex(meta['public_key']))
try:
public_key.verify(bytes.fromhex(link['signature']), bytes.fromhex(link['content_hash']))
print('valid: True')
except InvalidSignature:
print('valid: False')
stdlib only.
// Go 1.21+ (stdlib only)
package main
import (
"crypto/ed25519"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
)
type ChainLink struct {
LinkIndex int `json:"link_index"`
PrevHash string `json:"prev_hash"`
AttestationID *int `json:"attestation_id"`
AttestationContentHash string `json:"attestation_content_hash"`
ContentHash string `json:"content_hash"`
Signature string `json:"signature"`
SignedAt string `json:"signed_at"`
}
type ChainMeta struct {
PublicKey string `json:"public_key"`
}
func mustGet(url string, out any) {
res, err := http.Get(url)
if err != nil {
panic(err)
}
defer res.Body.Close()
if err := json.NewDecoder(res.Body).Decode(out); err != nil {
panic(err)
}
}
func main() {
var link ChainLink
var meta ChainMeta
mustGet("https://fonteum.com/api/v1/chain/123", &link)
mustGet("https://fonteum.com/api/v1/chain", &meta)
// 1. Re-derive content_hash.
attID := "null"
if link.AttestationID != nil {
attID = fmt.Sprintf("%d", *link.AttestationID)
}
payload := fmt.Sprintf("%d||%s||%s||%s||%s",
link.LinkIndex, link.PrevHash, attID,
link.AttestationContentHash, link.SignedAt,
)
sum := sha256.Sum256([]byte(payload))
recomputed := hex.EncodeToString(sum[:])
fmt.Println("content_hash matches:", recomputed == link.ContentHash)
// 2. Verify the Ed25519 signature.
pubBytes, _ := hex.DecodeString(meta.PublicKey)
sigBytes, _ := hex.DecodeString(link.Signature)
msgBytes, _ := hex.DecodeString(link.ContentHash)
valid := ed25519.Verify(ed25519.PublicKey(pubBytes), msgBytes, sigBytes)
fmt.Println("valid:", valid)
}