Status: Public Beta v0.1 · ProofSpec v0.1 · Privacy-first · No blockchain
Protocol — ProofSpec
Open, normative specification to prove when data existed and that it has not changed. Hash-only inputs, signed timestamps, public verification.
Overview
ProofSpec defines how to create, sign, publish, and verify digital proofs of existence.
Model: hash locally → get a signed, timestamped proof → anyone can verify later without access to your original data.
This document is normative for issuers and verifiers that claim to be ProofSpec v0.1 compliant.
Normative language & scope
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119 and RFC 8174.
- This specification applies to the structure of proof objects, signing rules, verification behaviour, and error reporting.
- Implementation details (storage engine, infrastructure, billing, UI) are non-normative unless explicitly stated.
- Where this document uses “example”, the content is informative and does not create additional normative requirements.
Conformance
The following roles are defined:
- Issuer: a system that accepts a hash and returns a signed proof.
- Verifier: a system that checks a proof and returns a validation result.
- Client: any caller (app, backend, CLI, agent) that interacts with an issuer or verifier.
A ProofSpec-compliant Issuer:
- MUST only accept canonical hashes (see Canonicalization) and MUST NOT require original content.
- MUST produce proof objects that satisfy the JSON schema and data model.
- MUST sign over at least
hash|tsusing the declaredalgandkid. - MUST expose a stable verify URL for each proof or hash.
A ProofSpec-compliant Verifier:
- MUST validate the structure of the proof, including required fields and formats.
- MUST recompute the expected signature for the given
algandkidand compare using a timing-safe method. - MUST reject proofs whose
tsis not a valid UTC timestamp or is unreasonably far in the future. - SHOULD provide clear error codes as defined in the Errors section.
Clients that claim ProofSpec compliance SHOULD keep the full proof JSON and SHOULD NOT rely only on a verify URL.
Canonicalization
- Issuers and clients MUST hash the exact bytes they intend to prove (“bytes-in, bytes-out”).
- Text inputs SHOULD be encoded as UTF-8 and SHOULD normalize line endings to LF (
\n) before hashing. - JSON metadata MAY be pre-hashed using a stable key ordering if needed; if so, the same process MUST be documented.
Data model
| Field | Type | Notes |
|---|---|---|
| hash | string(64 hex) | SHA-256 of input. REQUIRED. |
| ts | ISO 8601 | UTC timestamp of issuance. REQUIRED. |
| sig | hex | Signature over hash|ts. REQUIRED. |
| alg | enum | HMAC-SHA256 now; Ed25519 planned. REQUIRED. |
| kid | string | Key id / version used to produce sig. SHOULD be present. |
| issuer | string | Authority issuing the proof (e.g. timeproofs.io). |
| verify_url | URL | Public verify endpoint for this hash/proof. RECOMMENDED. |
| meta | object | Small, no PII. Optional implementation-specific metadata. |
Proof format
{
"hash":"<64-hex>",
"ts":"2025-01-01T00:00:00.000Z",
"sig":"<hex>",
"alg":"HMAC-SHA256",
"kid":"v1",
"issuer":"timeproofs.io",
"verify_url":"https://api.timeproofs.io/api/verify?hash=...",
"meta":{
"type":"event",
"source":"web"
}
}
Signing
// Node.js HMAC verify (example)
import crypto from "node:crypto";
function verify({ hash, ts, sig }, secret) {
const msg = `${hash}|${ts}`;
const expect = crypto
.createHmac("sha256", secret)
.update(msg)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expect, "hex"),
Buffer.from(sig, "hex")
);
}
Validation
hashMUST be a 64-character lowercase hex string.tsMUST be a valid ISO 8601 UTC timestamp and SHOULD NOT be in the future beyond a reasonable tolerance.- The verifier MUST recompute the expected signature according to
algandkidand compare using a timing-safe method. - If any required field is missing or invalid, the verifier MUST return a failure with an appropriate error code.
JSON schema
{
"$schema":"https://json-schema.org/draft/2020-12/schema",
"type":"object",
"required":["hash","ts","sig","alg"],
"properties":{
"hash":{"type":"string","pattern":"^[a-f0-9]{64}$"},
"ts":{"type":"string","format":"date-time"},
"sig":{"type":"string"},
"alg":{"type":"string"},
"kid":{"type":"string"},
"issuer":{"type":"string"},
"verify_url":{"type":"string","format":"uri"},
"meta":{"type":"object","additionalProperties":true}
}
}
Test vectors
// sha256("TimeProofs")
7c040f8633b8823d72ed63da9b3b2dfe9846e912c66a64c9433ec9c4815d76c2
Verification
GET /api/verify?hash=<64-hex>
→ {"ok":true,"valid":true,"timestamp":"2025-01-01T00:00:00.000Z","hash":"..."}
Verify URL
The verify URL is a stable endpoint that returns JSON status for the given hash or proof. It SHOULD be included in the proof JSON as verify_url.
Clients MAY choose to rely on the verify URL alone, but keeping the full proof JSON is RECOMMENDED for long-term verification.
Proof bundle
Artifacts (files, datasets, binaries) SHOULD be shipped with their .tproof.json proof alongside them, or at least with a stable verify link.
Bundles SHOULD be small, self-contained, and not contain personal data.
Reserved fields
Names starting with _tp_ are reserved for the ProofSpec and MUST NOT be used for custom metadata.
Errors
{"ok":false,"error":"invalid_hash"}
// Other examples:
// {"ok":false,"error":"invalid_signature"}
// {"ok":false,"error":"not_found"}
// {"ok":false,"error":"rate_limited"}
Operational policy
- Issuers SHOULD keep minimal logs and avoid storing original content.
- Issuers SHOULD apply rate limits and abuse protection on timestamp and verify endpoints.
- Key rotation SHOULD be implemented using
kidand a clear key lifecycle policy.
Checklist
- Hash locally (no original content leaves the device).
- Keep the proof JSON (
.tproof.json). - Ensure a stable verify URL is present or derivable.
- Validate against the JSON schema and data model.
Versioning
This document describes ProofSpec v0.1. Additive changes SHOULD prefer minor/patch bumps; breaking changes MUST bump the major version.
Issuers and verifiers SHOULD clearly expose which ProofSpec version(s) they support.
This page is verified by TimeProofs
Release: … · Hash: … ·
Verify
The hash above identifies a specific build of this ProofSpec v0.1 document. The verify link returns the signed record used to prove its existence and integrity.