Status: Public Beta v0.2 · ProofSpec v0.1 · API v0.2 · 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.

↑ Top

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.

v0.2 note: TimeProofs v0.2 remains stateless (hash-only). Proofs can be packaged as portable .tproof.json bundles and validated publicly or inspected offline.

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.

Conformance

The following roles are defined:

A ProofSpec-compliant Issuer:

A ProofSpec-compliant Verifier:

Clients that claim ProofSpec compliance SHOULD keep the full proof JSON and SHOULD NOT rely only on a verify URL.

Canonicalization

Data model

FieldTypeNotes
hashstring(64 hex)SHA-256 of input. REQUIRED.
tsISO 8601UTC timestamp of issuance. REQUIRED.
sighexPrimary signature over hash|ts. REQUIRED.
algenumHMAC-SHA256 or Ed25519. REQUIRED.
kidstringKey id / version used to produce sig. SHOULD be present.
sig2hexSecondary signature over hash|ts (optional, v0.2+).
alg2enumAlgorithm for sig2 (optional, v0.2+).
kid2stringKey id for sig2 (optional, v0.2+).
issuerstringAuthority issuing the proof (e.g. timeproofs.io).
verify_urlURLPublic verify endpoint for this hash/proof. RECOMMENDED.
jwks_urlURLPublic keys for Ed25519 verification (optional, v0.2+).
metaobjectSmall, 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",

  "sig2":"<hex>",
  "alg2":"Ed25519",
  "kid2":"ed25519-2025-01",
  "jwks_url":"https://timeproofs.io/.well-known/jwks.json",

  "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 verifyHmac({ 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")
  );
}
// Node.js Ed25519 verify (example, v0.2+)
// publicKeyPem: PEM encoded Ed25519 public key
import crypto from "node:crypto";

function verifyEd25519({ hash, ts, sig2 }, publicKeyPem) {
  const msg = Buffer.from(`${hash}|${ts}`, "utf8");
  const sig = Buffer.from(sig2, "hex");
  const key = crypto.createPublicKey(publicKeyPem);
  return crypto.verify(null, msg, key, sig);
}

Validation

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"},

    "sig2":{"type":"string"},
    "alg2":{"type":"string"},
    "kid2":{"type":"string"},
    "jwks_url":{"type":"string","format":"uri"},

    "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.

v0.2 guidance: a bundle SHOULD include the proof fields (hash, ts, signatures, issuer, verify_url) and MAY include a minimal meta object. Offline inspection MUST be possible without contacting the issuer; online verification MAY be used to re-fetch status and keys (e.g. jwks_url).

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

Checklist

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.