Technical documentation

Under the hood of a TrueTake proof.

Everything a proof is made of is public and recomputable: formats, formulas, keys and ledger. This page summarises the protocol; the verifier is its reference implementation.

H(x) = SHA-256(C(x))
C(x) = UTF-8(JCS(x))   // RFC 8785

01Primitives

Standard building blocks, chosen to be easy to re-implement and audit. No home-made algorithm.

  • Ed25519

    Signatures of QR codes, checkpoints, the certificate and public projections.

    RFC 8032

  • SHA-256

    Every hash: segments, records, chain, checkpoints, final video.

    FIPS 180-4

  • JCS

    Canonical JSON: the same object always gives the same bytes, hence the same hash.

    RFC 8785

  • CBOR + COSE_Sign1

    Compact, signed payload of the capture QR codes.

    RFC 8949 · RFC 9052

  • Base45

    Dense alphanumeric encoding: smaller QR codes that are easier to film.

    RFC 9285

  • Merkle

    A sorted binary tree that commits a whole batch of proofs and checkpoints to a single root.

    truetake-merkle-1

02Signed QR codes

Every code shown by phone A is a COSE_Sign1 message signed by the server (Ed25519), Base45-encoded in an error-correction-M QR code. It holds the operation, the step number, a validity window and, from the second code on, a 16-byte link to the segment chain at the last accepted segment and to the previous QR code.

qr_(s+1).previous_link = first16(SHA-256("TrueTake-QR-link-v3\0" ‖ chain_j ‖ qr_payload_hash_s))
resume_qr.previous_link = first16(SHA-256("TrueTake-QR-resume-v1\0" ‖ closing_checkpoint_hash ‖ qr_payload_hash_(step-1)))
COSE_Sign1 = Ed25519("TrueTake-QR-v1\0" ‖ payload_cbor)
  • Validity: 20 seconds
  • Signature prefix: TrueTake-QR-v1\0
  • Stale codes are ignored, never accepted

03Segment chain and checkpoints

Every filmed segment produces a canonical truetake.unit record that the filming phone computes before sending it: SHA-256, size, duration and the context hash (location, sensors) when allowed. Each record is chained to the previous one. Every two minutes and at every interruption, the server signs a checkpoint that commits to the chain head, the QR sequence, its own observations and the coverage totals; checkpoints are anchored too. Notation: H(x) = SHA-256(JCS(x)).

unit_record_u = { type:"truetake.unit", version:1, kind:"segment", operation_id, span, u, step_believed, media_sha256, media_size, device_start_time, duration_ms, context_hash, run:null }
unit_hash_u = H(unit_record_u)
span_head_j = H({ type:"truetake.span-head", version:1, operation_id, span:j, qr_hash, closing_checkpoint_hash })
chain_u = H({ type:"truetake.unit-chain", version:1, operation_id, u, previous: chain_(u-1) | span_head_j, unit_hash })
qr_seq_head_s = H({ type:"truetake.qr-seq", version:1, previous, step:s, qr_payload_hash })
checkpoint_k = Ed25519("TrueTake-Proof-v1\0" ‖ C({ type:"truetake.checkpoint", k, chain_head, qr_seq_head, observations_hash, previous_checkpoint_hash, totals, … }))

The first segment of a span links to the span head, which commits to the span’s first QR code and, after an interruption, to the checkpoint that closed the previous span. Changing a single segment changes every hash after it, and the signed checkpoint no longer matches.

Video segments chained by their hashes up to the final chain hash.

04The final certificate

At the end of the loop, the server assembles the video and signs the truetake.final-proof certificate (version 2). It commits to the file hash, the final checkpoint and through it the whole signed history, the chain head, the spans, the coverage totals, the capture manifest and the forensic report.

signature = Ed25519("TrueTake-Proof-v1\0" ‖ C(final_proof ∖ signature))
proof_hash = H(final_proof)
merkle_leaf = H({ type:"truetake.proof-leaf", version:1, proof_hash })
checkpoint_leaf = H({ type:"truetake.checkpoint-leaf", version:1, checkpoint_hash })

05Merkle anchoring

Certificates and checkpoints are grouped in batches. Each batch publishes a single root; each proof keeps its Merkle path to show it belongs to the batch, without revealing the others.

Node(l, r) = H({ type:"truetake.merkle-node", version:1, left:l, right:r })
tx_hash = "0x" ‖ H({ type:"truetake.simulated-anchor", version:1, batch_id, merkle_root })
  • Leaves are deduplicated and sorted by byte value
  • An odd node at the end of a level is duplicated
  • The path says on which side each sibling sits
  • A single leaf: the root is the leaf, empty path
A Merkle tree: the proof leaf and its siblings lead up to the anchored root.

Simulated network

Default mode of the beta. The transaction is a recomputable hash of the batch (0x + H(batch, root)), published in the public ledger. The verifier shows a “Simulated network” badge: it is valid, but it is not a public blockchain.

Test network

Optional: a real anchor(root, batch) transaction on Base Sepolia. The verifier shows “Test network”.

06Forensic heuristics

Eleven simple, documented checks, recomputed for every proof. They are consistency heuristics, not AI detection: the score is not a probability of tampering. No detector of filmed screens or generated images is active; these analyses are planned.

CheckMeasureRule
qr_presence Share of frames where the right QR code is visible (minimum over live segments) Pass ≥ 30%, warn ≥ 10%
segment_duration Duration of each segment Between 0.7 and 2 s
upload_latency Delay between the code being issued and the segment being received Pass ≤ 15 s, warn ≤ 50 s
device_clock_skew Gap between the phone clock and the server clock Pass ≤ 5 s, warn ≤ 30 s
resolution_consistency Number of distinct resolutions Exactly one
codec_consistency Number of distinct codecs Exactly one
frame_rate_consistency Frame-rate spread Pass ≤ 5 fps, warn ≤ 15 fps
sequence_monotonic Order of steps Strictly increasing
context_continuity Segments that come with their context All, or not evaluated
coverage Share of the capture time filmed live Informative, never a failure
capture_integrity Segments whose code contradicts the sequence Pass at zero, warn otherwise

Decision: no failure → “no significant anomaly”; one failure → “suspicious”; two or more → “highly suspicious”. A QR or ordering check that cannot be evaluated makes the analysis “inconclusive”.

07Verification

The verifier replays thirteen ordered checks in your browser, page by page. A failure propagates to the checks that depend on it: nothing is swept under the rug.

  1. 01Format and cryptographic profileschema_profile
  2. 02Certificate signed by a published keyfinal_signature
  3. 03Signed checkpoints, each linked to the previous onecheckpoint_chain
  4. 04Every page replayed: segment chain, QR codes and their links, observationspages
  5. 05Live coverage recomputed against every checkpointcoverage
  6. 06Capture manifest and segment contextssnapshots_and_contexts
  7. 07Forensic report committed by the certificateforensic_report
  8. 08Proof hash and Merkle leafproof_hash_and_leaf
  9. 09Path up to the batch rootmerkle_path
  10. 10Anchoring of the checkpointscheckpoint_anchors
  11. 11Hash of the video you providefile_sha256
  12. 12Perceptual fingerprint (not implemented yet)perceptual_video_hash
  13. 13Root published in the ledgernetwork_contract_confirmation

Open the verifier

08Privacy

A proof only needs the minimum. What is not needed is not collected; what is optional is asked for explicitly.

  • Optional, coarse location

    Coordinates rounded to a thousandth of a degree (about 100 m), only if B allows it.

  • Reduced sensors

    One root-mean-square value per segment, as integers. No raw stream.

  • Nothing on chain

    The ledger only holds Merkle roots: no video, no location, no identifier.

  • Local verification

    The verifier computes everything in the browser. Your files are not uploaded.

  • Limited retention

    Videos and traces are deleted when they expire; see the privacy policy.