step 01
Create a ledger — choose the horizon.
A ledger is named, scoped to a tenant, and parametrised by one tier. T2 is the operational tier: 90 days, periodic Merkle anchoring, Ed25519 signatures.
POST /v1/ledger HTTP/1.1
Authorization: Bearer eph_live_...
Content-Type: application/json
{
"id": "orders-2026",
"tier": "T2",
"region": "eu-fra",
"anchor": "ots:bitcoin"
}
201 Created
{
"id": "orders-2026",
"tier": "T2",
"head": { "seq": 0, "hash": "b3:0000...0000" },
"signer_id": "hatp:host:eu-fra-07",
"created_ms": 1748621037912
} A T2 ledger costs nothing to keep idle. Storage and anchor fees are incurred per entry and per checkpoint, not per ledger.
step 02
Append your first entry.
The payload is opaque bytes — we do not interpret it. The header is canonical: 32 bytes of payload_cid, 32 of prev_hash, plus 67 bytes of metadata. The total is 131 bytes, every time, regardless of the payload.
POST /v1/ledger/orders-2026/entry
Authorization: Bearer eph_live_...
Idempotency-Key: 7e3a8c2b-1c14-4f9a-9b8f-d2c7e8e9a004
Content-Type: application/octet-stream
(binary payload — 412 bytes of CBOR)
201 Created
{
"seq": 1,
"ts_ms": 1748621037912,
"payload_cid": "b3:3c1fa0b2d917e44c8e7a91b30f2d6c81",
"prev_hash": "b3:0000000000000000000000000000",
"signer_id": "hatp:host:eu-fra-07",
"scheme": "ed25519",
"signature": "ed:5b77e2d9c1804af692a14c3876e0b95d..."
} step 03
The chain forms. Every write references the last.
The second entry's prev_hash is the BLAKE3 of the first entry's header. Modifying entry one invalidates the hash of entry two, the hash of entry three, and so on — forever.
This is the structural property: tamper-evidence is not an after-the-fact audit; it is a precondition for the read.
step 04
Checkpoint — every epoch, a Merkle root.
Once per epoch (one hour for T2 by default) the node builds a Merkle tree over the epoch's entries and publishes the root. Inclusion proofs become O(log N) from that point: roughly twenty hashes prove membership in a tree of a million leaves.
epoch_178 root = merkle_root([h_1000, h_1001, ..., h_1099])
= "b3:c1804af692a14c3876e0b95dd0f83b1e"
published as a signed checkpoint:
{
"epoch": 178,
"first_seq": 1000,
"last_seq": 1099,
"root": "b3:c180...3b1e",
"scheme": "ed25519",
"signature": "ed:..."
} step 05
Anchor — the root pays for a postage stamp.
The checkpoint root is committed to a public chain for tamper-proof time. The default backend is OpenTimestamps on Bitcoin: a free, neutral, slow timestamp service.
The node never settles value on-chain; it only writes a 32-byte commitment. Nothing is bridged, no token is moved, no smart contract is invoked.
{
"epoch": 178,
"root": "b3:c180...3b1e",
"anchor": "ots:bitcoin",
"anchored_at": 1748625600000,
"btc_block": 870182,
"ots_proof": "base64:..."
} step 06
Read with a proof attached.
A reader asks for a range and (optionally) an inclusion proof. The proof is the path of hashes that connects each entry to its epoch root, plus the anchor receipt for that root.
200 OK
Content-Type: application/cbor
entries: [
{
"seq": 1042,
"ts_ms": 1748622001124,
"payload_cid": "b3:8e7a...6c81",
"prev_hash": "b3:3c1f...e44c",
"signature": "ed:..."
}
],
checkpoint: {
"epoch": 178,
"root": "b3:c180...3b1e",
"anchor": "ots:bitcoin",
"btc_block": 870182
},
inclusion_path: [
"b3:5b77...e2d9",
"b3:92a1...4c38",
"b3:fa02...6e8c",
...
] step 07
Verify offline — six years later.
A customer (or a regulator) takes the export plus the operator's root public key — both small, both public — and runs the verifier against them. No network access; no trusted middlebox; no API key.
$ ephernity verify ./export.cbor --root-key ./operator-root.pub --anchor ots ✔ chain 1 entries verified · BLAKE3 ✔ signature Ed25519 · signer hatp:host:eu-fra-07 ✔ HATP root → tenant → host (3 levels, valid) ✔ checkpoint epoch 178 · root b3:c180...3b1e ✔ anchor OTS · Bitcoin block 870182 @ 2026-05-30T08:00:00Z OK — entry 1042 included at position 42 of epoch 178, anchored to Bitcoin within 75 minutes of writing.
The verifier is a 4 MB static binary. The root public key is 32 bytes. The whole trust statement fits on a business card.
step 08
Crypto-shred — erase the data, keep the proof.
The customer of the customer requests erasure. The operator destroys the per-record AES-256 key that encrypts the payload. The 32-byte payload_cid remains in the chain; the bytes behind it are gone.
200 OK
{
"seq": 1042,
"shredded_ms": 1942617600000,
"payload_cid": "b3:8e7a...6c81", // still in the chain
"payload": null, // gone
"receipt": "b3:1d4f...8a07" // proof of erasure
}
# subsequent verify
$ ephernity verify ./export.cbor --root-key ./operator-root.pub
✔ chain 1042 entries verified · 1 shredded
✔ shred receipt 1042 · b3:1d4f...8a07
ⓘ payload for seq 1042 is unrecoverable; integrity preserved. Erasure compatibility is not a bolt-on. It is the reason the architecture can stand up under GDPR Article 17 at the T3 and T4 tiers — including for records that must otherwise be kept for six years.
That is the whole protocol.
Eight steps. Two HTTP verbs. Three signatures. One root public key. Every additional feature — tier promotion, region pinning, alternative anchors, post-quantum durability — composes on top of this same eight-step shape without changing it.