{"$id":"https://resiliencechain.co.uk/schema/v1/tsa-anchoring","title":"TSA Anchoring Protocol (Open Evidence Schema v1)","version":"1.0.0","description":"How ResilienceChain anchors event hash chain entries to an RFC 3161 Time-Stamp Authority, and how a third party can verify an anchor.","what_this_gives_you":{"before_anchoring":"The event hash chain proves events have not been rewritten since they were written. It does NOT prove WHEN they were written.","after_anchoring":"Each event additionally carries a signed token from a trusted third-party TSA committing the event_hash to a specific wall-clock time. A regulator cannot accuse the tenant (or ResilienceChain) of backdating an event, because an independent third party signed that hash at time T."},"tsa_providers":[{"name":"freetsa.org","url":"https://freetsa.org/tsr","description":"Default provider. Free, no registration, RFC 3161 compliant, DE-hosted. Public certificate bundle at https://freetsa.org/files/tsa.crt and https://freetsa.org/files/cacert.pem.","public_cert":"https://freetsa.org/files/tsa.crt","public_ca":"https://freetsa.org/files/cacert.pem"}],"anchoring_protocol":{"step_1_build_request":{"description":"Build an RFC 3161 TimeStampReq with the event_hash (SHA-256) as the messageImprint. Set certReq=TRUE so the signing certificate embeds in the response.","rfc_reference":"RFC 3161 §2.4.1 — TimeStampReq ::= SEQUENCE { version INTEGER { v1(1) }, messageImprint MessageImprint, reqPolicy TSAPolicyID OPTIONAL, nonce INTEGER OPTIONAL, certReq BOOLEAN DEFAULT FALSE, extensions [0] IMPLICIT Extensions OPTIONAL }"},"step_2_submit":{"description":"POST the DER-encoded request to the TSA with Content-Type: application/timestamp-query.","expected_response_content_type":"application/timestamp-reply"},"step_3_verify_response_status":{"description":"The TSA response is a TimeStampResp. The first field is a PKIStatusInfo; status must be granted (0) or grantedWithMods (1) for the token to be usable."},"step_4_store":{"description":"Store the raw DER-encoded TimeStampResp as-is. Do not mutate it, re-wrap it, or re-encode it."},"step_5_parse_time":{"description":"The authoritative time lives in the TSTInfo embedded in the SignedData content. Extract the genTime (a GeneralizedTime ASN.1 value, typically YYYYMMDDHHMMSSZ format)."}},"verification_by_third_party":{"goal":"A regulator, auditor, or external verifier — with only the token, the event_hash, and the TSA public certificate — can confirm the token is authentic and the hash existed by the claimed time.","openssl_snippet":["# 1. Save the TSA-signed token locally (from the export endpoint):","#      GET /api/v1/event-chain/events/<event_id>/tsa-token?provider=freetsa.org","#    Response body is the raw DER-encoded TimeStampResp.","","# 2. Fetch the TSA public cert + CA bundle (one-time setup):","curl -sS -o tsa.crt    https://freetsa.org/files/tsa.crt","curl -sS -o cacert.pem https://freetsa.org/files/cacert.pem","","# 3. Write the event_hash as 32 raw bytes to a file \"data.bin\":","#    (event_hash is the hex in /api/v1/event-chain/orders/<id>/verify)","printf \"%s\" \"$EVENT_HASH_HEX\" | xxd -r -p > data.bin","","# 4. Verify:","openssl ts -verify -data data.bin -in token.tsr -CAfile cacert.pem -untrusted tsa.crt","","# Expected output line: 'Verification: OK'"],"python_snippet":["# rfc3161-client is the reference Python library for parsing + verifying TSA tokens.","pip install rfc3161-client","","from rfc3161_client import decode_timestamp_response","import hashlib","","with open(\"token.tsr\", \"rb\") as f: token_der = f.read()","tsr = decode_timestamp_response(token_der)","print(\"Granted:\", tsr.status.granted)","print(\"TSA time (UTC):\", tsr.time_stamp_token.tst_info.gen_time)","print(\"Hashed value:\", tsr.time_stamp_token.tst_info.message_imprint.hashed_message.hex())","assert tsr.time_stamp_token.tst_info.message_imprint.hashed_message.hex() == EVENT_HASH_HEX"]},"chain_integration":{"description":"Anchoring is applied to the event_hash column of each chained event. Because the chain itself is built via event_hash_n = SHA-256(prev_event_hash_n_minus_1 || canonical(event_n)), a TSA signature on event_hash_N cryptographically commits to every earlier event in the same tenant's chain (tamper-evident property of the chain + time-evident property of the TSA).","export_endpoint":"GET /api/v1/event-chain/events/:event_id/tsa-token?provider=freetsa.org — returns raw DER (Content-Type: application/timestamp-reply).","anchoring_cadence":"Anchoring is asynchronous: every new chained event is queued to event_tsa_anchors with status=PENDING. A worker on a 1-minute Cloud Scheduler cadence drains PENDING rows and patches in the token. Typical latency from event write to anchored: <2 minutes. A TSA outage slows anchoring but does not block event writes — the chain remains valid, the anchor just catches up when the TSA is reachable again.","failure_mode":"If anchoring exhausts its retry budget (5 attempts with exponential backoff, ~2h total), the event row is marked status=FAILED. The chain itself remains valid — the event is still hash-linked — it simply is not third-party time-anchored. The verify endpoint reports anchored_events vs pending_events vs failed_events honestly."},"summary_for_regulators":{"one_sentence":"Every chained event ResilienceChain writes can be independently verified by a regulator to have existed at a specific wall-clock time, using only the event_hash, the exported TSA token, and the TSA provider's public certificate — no ResilienceChain credentials, APIs, or code required."}}