Sign-in

PoE keeps you anonymous. Google/OIDC binds receipts to your Google account.

Checking session...

Timestamp Service

Documentation overview

Fideseal is a full-featured proof-of-existence platform that normalizes a file, hashes it, batches it into a Merkle tree, and anchors the root in an EVM chain.

🚀 What this service does

The platform gives you an end-to-end workflow: drop a file and receive a receipt that contains a Merkle root, anchor transaction metadata, and a JWKS-backed service signature. Every step happens deterministically so the receipt can be reproduced later.

Under the hood Fideseal keeps browser hashing isolated, submits only commitment_hex and raw_file_hash_hex values to the backend, and anchors the aggregated Merkle root in a transaction that you can verify on Base blockchain.

Blockchain Proof of Existence vs. Trusted Timestamp Authorities (TSA/QTS)

Our service uses public blockchain networks to create a Proof of Existence (PoE) for your data. It is important to distinguish this from timestamps issued by Trusted Timestamp Authorities (TSA) and Qualified Electronic Timestamps (QTS) under eIDAS.

What our blockchain-based PoE provides

Data integrity. Before anything is anchored, your content is transformed into a cryptographic hash. This hash changes if even a single bit of the original data is modified. By comparing the hash in your receipt with the hash of the document you hold, anyone can verify that the document has not been altered since it was anchored.

Public, independently verifiable record. The hash (or a Merkle root derived from it) is written into a transaction on a public blockchain. The inclusion of this transaction in a block can be checked by any third party using standard blockchain tools, without relying on our systems.

Evidence of existence by a certain time. Once the transaction is included in a block, it shows that the corresponding data existed no later than the time associated with that block. Different jurisdictions may treat this fact in different ways, but technically the record is durable and globally verifiable.

No access to your original content. Only hashes are anchored. The blockchain does not store or reveal your original documents.

We describe this as strong technical evidence of integrity and existence at a given point in time. It is not, by itself, a qualified timestamp under eIDAS.

How this differs from TSA / Qualified Timestamps (QTS)

Trust model. A TSA or QTS is issued by a regulated trust service provider, operating under specific legal and technical requirements (for example, within the EU under eIDAS). A blockchain-based PoE relies on the consensus and security properties of the underlying network rather than on a single regulated authority.

Legal status. Qualified timestamps (QTS) have a defined legal status in certain jurisdictions and enjoy specific presumptions in court, as set out in the relevant legislation. Blockchain PoE does not automatically fall into that category. It can be used as electronic evidence, but how it is evaluated depends on the applicable law, the facts of the case, and the assessment of the court or competent authority.

Time source. TSAs use certified time sources and are audited with respect to time accuracy and control. Blockchains record time indirectly via block production; this is usually sufficient for practical purposes, but it follows the consensus rules of the network, not a certified time server.We can implement QTS in our service if it is needed by the customer.

How we position our service

Our goal is to provide a robust, transparent and vendor-independent technical foundation for proving that specific data existed and has not been changed since anchoring.

For now we do not present ourselves as a Qualified Trust Service Provider (QTSP) and we do not claim that our timestamps are, by themselves, Qualified Electronic Timestamps (QTS) under eIDAS. In use cases that require a formally qualified timestamp or electronic signature, our PoE can be combined with external trust services (for example, QTS or qualified e-signature providers), according to your legal and compliance requirements.

How the pipeline works

  • Accept files such as PDF, word documents, scans, or images and keep the bytes in the browser.
  • Normalize the data into a canonical representation (strip EXIF, stabilize PDF structure).
  • Hash locally with keccak256 and produce commitment_hex + raw_file_hash_hex values.
  • Batch leaves (keccak256(commitment || raw_hash)) into a Merkle tree so multiple files share a single anchor.
  • Send the Merkle root to a smart contract in an EVM network (Base, Arbitrum, Ethereum, etc.).
  • Emit receipts both as on-site HTML and downloadable PDF/JSON artifacts with a provable timestamp.
  • Expose verification tools so anyone can inspect a receipt and cross-check it with chain data.

Platform capabilities

Add-ons that make the timestamp trustworthy

Local-first hashing, so sensitive documents never leave the user device.
Automatic Merkle batching with batch IDs for deterministic anchoring and traceability.
RPC-assisted verification that can re-validate on-chain events and block timestamps.
JWKS-signed receipts to prove that the evidence bundle was issued by the service.
REST API parity with the UI so integrators can script anchoring flows easily.

SDK / client integration

Ship the same canon+hash logic in your app

The SDK wraps our WASM core and REST helpers. It exposes canonicalizeAndHash,anchorPoe, anchorOidc, and pollReceipt. The bundled core_wasm.wasm lives next to the JS entrypoints; override wasmUrl if you host it on your CDN. Canon profiles remain public in specs/canon/*.yaml.

NPM / bundlers

npm install @fideseal/sdk

import { canonicalizeAndHash, anchorPoe } from "@fideseal/sdk";

const bytes = new Uint8Array(...); // file bytes
const { commitmentHex, rawFileHashHex, canonProfile } = await canonicalizeAndHash("pdf.v1.basic", bytes);

await anchorPoe(
  {
    commitment_hex: commitmentHex,
    raw_file_hash_hex: rawFileHashHex,
    hash_alg: "keccak256",
    canon_profile: canonProfile,
    client_version: "your-app/1.0.0",
    consent: { checked: true, terms_hash: "0x..." }
  },
  { baseUrl: process.env.SERVER_BASE_URL },
);

CDN / script tag

<!-- CDN / script tag option -->
<script type="module">
import { canonicalizeAndHash, anchorPoe } from "https://unpkg.com/@fideseal/[email protected]/dist/esm/index.js";

const fileBytes = new Uint8Array(...);
const wasmUrl = "https://unpkg.com/@fideseal/[email protected]/dist/esm/core_wasm.wasm";
const { commitmentHex, rawFileHashHex } = await canonicalizeAndHash("pdf.v1.basic", fileBytes, { wasmUrl });

await anchorPoe({
  commitment_hex: commitmentHex,
  raw_file_hash_hex: rawFileHashHex,
  hash_alg: "keccak256",
  canon_profile: "pdf.v1.basic",
  client_version: "cdn-demo/0.1.0",
  consent: { checked: true, terms_hash: "0x..." }
});
</script>

Use the SDK with fideseal.com

Point your client at the hosted API

  • Install the published package: npm install @fideseal/[email protected].
  • API base defaults to https://fideseal.com; override with baseUrl if you self-host.
  • Terms hash for production: 0x4d7101379330022dc3d40d87f48e37a42a509df7cd3ee1c4d1f7659e8186c8c0; include it in consent.terms_hash.
  • Browser: set wasmUrl to https://unpkg.com/@fideseal/[email protected]/dist/esm/core_wasm.wasm
  • Flow: canonicalizeAndHash anchorPoe (or anchorOidc) → pollReceipt until anchored; fetch PDF via /v1/receipts/<id>/pdf if needed.

Need more?

Use the Timestamp page to produce new anchors, the Verify page to validate a PDF or JSON evidence bundle offline, and the Receipts area to access the HTML rendering that mirrors the PDF layout. API consumers can hit/v1/poe/anchor with the same payload the UI sends.

Every artifact follows the YAML specs in this repository, so upgrades are reviewed and versioned. If you need deterministic proofs for compliance or courts, the JSON evidence plus the on-chain transaction hash provide a verifiable trail end-to-end.

API

HTTP endpoints that power the UI and CLI flows

Base URL defaults to https://fideseal.com; configure NEXT_PUBLIC_SERVER_BASE_URL / SERVER_BASE_URL to point the web app elsewhere. The OpenAPI file lives in specs/api.yaml.

POST/v1/poe/anchor

Queue a PoE anchoring job after hashing locally in the browser.

Request: Body: commitment_hex (keccak256 of canonical bytes), raw_file_hash_hex (keccak256 of original bytes), hash_alg=keccak256, canon_profile (pdf.v1.basic), client_version, consent { checked: true, terms_hash }.

Response: 200 → { receipt_id, status } where status ∈ [pending, confirming, anchored, failed]. Reuses the same receipt_id if this payload was already submitted; worker batches into a Merkle root and anchors.

POST/v1/oidc/sign_and_anchor

Same payload with id_token so the evidence is bound to an authenticated actor.

Request: Adds id_token plus optional actor_claims { issuer, subject, email_hash, amr }. 401 on invalid token.

Response: 200 → { receipt_id, status } where status ∈ [pending, confirming, anchored, failed]; idempotent for the same actor/payload.

GET/v1/receipts/{receipt_id}

Poll status. Returns pending | confirming | anchored | failed and optional evidence or error { code, error }.

Response: 404 if the receipt is unknown.

GET/v1/evidence/{receipt_id}

Direct EvidenceV1 JSON once anchoring succeeded (merkle proof, anchor block, service_sig).

Response: 404 if the receipt is missing or not anchored yet.

GET/v1/receipts/{receipt_id}/pdf

Download the PDF rendering of the anchored evidence (same fields as HTML/JSON).

Response: 404 until anchored; 409 if service_sig is missing.

GET/v1/jwks

JWKS bundle with the ES256 public key whose kid matches evidence.service_sig.

Response: 200 → { keys: [...] }; 404 if the signer is unavailable.

GET/health

Lightweight health/ops probe with queue size, last retention run and RPC health (if enabled).

Request: No body. Call the API base (e.g., https://fideseal.com/health), not the Next.js port.

Response: 200 → { status: 'ok', queue_size, retention_last_run_ms, rpc: { healthy?, last_error? } }

Anchor request shape

  • commitment_hex — 0x-prefixed keccak256 hash of canonical bytes (browser never uploads the PDF).
  • raw_file_hash_hex — 0x-prefixed keccak256 hash of the original file bytes (bit-for-bit).
  • hash_alg — currently only keccak256.
  • canon_profile — profile name validated against specs/canon/*.yaml (UI uses pdf.v1.basic).
  • client_version — free-form string for tracing (e.g., web-demo/0.2.0).
  • consent — { checked: true, terms_hash } where terms_hash is a 0x... keccak256 of the Terms content.
  • OIDC mode adds id_token plus optional actor_claims hints.

Receipts & evidence

  • Status lifecycle: pending → anchored → failed. Anchoring batches commitments into a Merkle tree before sending the root on-chain.
  • ReceiptStatus echoes the receipt_id, status, optional evidence, and structured error with stable code from specs/errors.yaml.
  • EvidenceV1 includes commitment_hex, raw_file_hash_hex, hash_alg, canon_profile, merkle_leaf_hex, merkle_proof[], merkle_root_hex, and anchor { chain, network_id, contract, batch_id, txid, block, timestamp }.
  • Evidence also carries created_at_iso, client_version, nonce, optional actor (OIDC), and service_sig (compact JWS ES256). The kid matches GET /v1/jwks and the PDF export embeds the same JWKS for offline verification.

Sample flow

curl -X POST https://fideseal.com/v1/poe/anchor \
  -H "content-type: application/json" \
  -d '{
    "commitment_hex":"0xcf86f104db11a01c990f080b1350b6aea026e3b224c13a8633ab7f74d07629ac",
    "raw_file_hash_hex":"0x0427a9843bda4fe06a968f6ec178cd1b1bc9264d78958fc7dcb6d49a93ade82b",
    "hash_alg":"keccak256",
    "canon_profile":"pdf.v1.basic",
    "client_version":"ops-cli/0.1.0",
    "consent":{"checked":true,"terms_hash":"0x4d7101379330022dc3d40d87f48e37a42a509df7cd3ee1c4d1f7659e8186c8c0"}
  }'

# Poll status until anchored
curl https://fideseal.com/v1/receipts/<receipt_id>

# Fetch evidence JSON or the signed PDF receipt
curl https://fideseal.com/v1/evidence/<receipt_id>
curl -o receipt.pdf https://fideseal.com/v1/receipts/<receipt_id>/pdf