Verification
BitGraph verification is deterministic and runs offline. No network calls, no API keys, no accounts.
Six-step algorithm
Input: a proof (BitGraphProof), the artifact bytes (Uint8Array), and an optional verification policy.
1.Structural validation
Check that all required fields are present with correct types. version must be "bitgraph/1", hashAlg must be "sha256", enforcement must be one of the valid tiers, all base64 fields must decode correctly.
2.Artifact digest verification
Compute SHA-256 of the provided bytes. Compare against proof.artifact.digestB64 using constant-time comparison. If they don't match, the proof does not apply to these bytes.
3.Signed body reconstruction
Build the SignedBody object from the proof fields (including attribution and attestation format when present). Canonicalize to sorted-key JSON, encode as UTF-8 bytes. This is what the Ed25519 signature covers.
4.Ed25519 signature verification
Decode publicKeyB64 (must be 32 bytes) and signatureB64 (must be 64 bytes). Verify the Ed25519 signature against the canonical bytes. If invalid, the proof has been tampered with.
5.Attestation binding (measured-tee)
For measured-tee proofs, verify the AWS Nitro attestation in environment.attestation. Parse the COSE_Sign1 document (ES384) and validate the embedded certificate chain from the enclave leaf to the pinned AWS Nitro Enclaves root, verifying each certificate is signed by its parent. Confirm PCR0 equals environment.measurement. Then confirm the binding to this exact proof: the attestation's user_data must equal proofHash. The public_key field is intentionally null, the binding runs through user_data, not public_key. Because proofHash is the SHA-256 of the canonical SignedBody, which commits to signer.publicKeyB64, this ties the genuine enclave to this proof and to the exact key that signed it. The chain is bundled in the proof and validates offline; only certificate revocation (CRL) status requires network and is outside this algorithm.
6.Policy checks
If a VerificationPolicy is provided, enforce its constraints: enforcement tier, allowed measurements, allowed public keys, attestation requirements, counter range, time range, epoch requirements.
Step 5 confirms PCR0 matches the measurement the proof claims, and the certificate chain proves that measurement came from genuine Nitro hardware. To confirm the measurement itself corresponds to the open enclave source, the build is bit-for-bit reproducible: rebuild it and re-derive the exact PCR0 yourself, trusting no one. See reproducible builds.
Fused artifacts
A fused artifact carries a commitment to its own slot record inside its bytes, written before the artifact was finished. Its proof is an ordinary bitgraph/1 proof whose signed attribution names the placement and the origin, so the six steps above run unchanged. verifyFuse({ proof, bytes, frame? }) in @mikeargento/bitgraph-verify then adds one comparison, chosen by what the bytes hash to. The commitment and the registered placements are defined in Proof Format.
| Bytes hash to | Check | Result |
|---|---|---|
| the artifact digest, no fused marker | none beyond the six steps | RECORDED |
| the artifact digest, fused marker present | locate the commitment in the bytes with the declared placement; compare with the commitment recomputed from the proof's slot record | FUSED_DIRECT; INVALID_SLOT_COMMITMENT on mismatch; INVALID_ORIGIN_ATTRIBUTION if an origin digest embedded in the bytes disagrees with the signed one |
| the signed origin digest | rebuild the fused artifact from these bytes with the placement; compare its digest with the committed one | FUSED_FROM_ORIGIN; RECONSTRUCTION_MISMATCH otherwise |
| neither | none | NO_MATCH: the proof says nothing about these bytes |
The statements a verifier may print, verbatim:
- “The supplied original rebuilds the committed fused artifact byte for byte, so these exact original bytes existed no later than commit position M.”
- “The fused bytes carry an origin digest that matches the signed marker; the original itself was not supplied and was not checked.”
- “The exact fused bytes could not feasibly have been finalized before their signed slot allocation at position N, and were committed no later than position M.”
Ordering follows from the counters. When two proofs are comparable (same key, epoch and chain), commitCounter(A) < slotCounter(B) means B was assembled after A was committed. A fused failure is never reported as a valid recording, and bitgraph/1 verification (verify, verifyProofIntegrity) is unchanged.
Verification policy
interface VerificationPolicy {
requireEnforcement?: "stub" | "hw-key" | "measured-tee";
allowedMeasurements?: string[]; // exact match
allowedPublicKeys?: string[]; // exact match
requireAttestation?: boolean;
requireAttestationFormat?: string[];
minCounter?: string; // BigInt-safe
maxCounter?: string;
minTime?: number; // Unix ms
maxTime?: number;
requireEpochId?: boolean;
requireActor?: boolean; // legacy
}Trust anchor hierarchy
requireEnforcementalone - prevents in-transit downgrade onlyrequireEnforcement + allowedMeasurements- pins to specific enclave image+ requireAttestation- full trust (vendor-attested hardware boundary)What the verifier does NOT check
| Item | Why |
|---|---|
| Attestation revocation status | Offline verification validates the bundled certificate chain and attestation binding; network CRL checks are outside this algorithm. |
| prevB64 chain integrity | Chain traversal is application-layer logic |
| Counter continuity | Gap detection is application-layer logic |
| Slot allocation validity | Slot signature and hash binding are structural checks; application can verify slotHashB64 matches canonicalized slot body |
| Key provenance | Requires attestation verification |
| Batch context completeness | Verifying all proofs in a batch is application-layer logic |
| Wall-clock floor of a fused artifact | The last anchored block before the slot needs Ethereum anchor evidence the proof does not carry; the Player computes it from a bundle |