Skip to content

The path of a record

A machine opens an ingest session: a partition label, a mode (initial or incremental) and JSON pointers. It then sends NDJSON batches over mutual TLS, optionally compressed with zstd. Every batch carries a SHA-256 Content-Digest, and nothing is kept when the digest does not match. Initial sessions publish at commit; incremental sessions publish every verified batch.

Every record is compressed and sealed with AES-256-GCM under its own random 32-byte key and a 96-bit nonce. Keys and nonces are never reused. Associated data binds the tenant, the chunk, the batch and the record id to the seal. Cryptography uses the Go standard library only.

Sealed records go into an immutable chunk file; every batch is one chunk. The chunk’s record keys, offsets, timestamps and owner pseudonyms form a key block, sealed under a per-chunk data key that is generated anew for every version.

The data key is wrapped twice:

  • by the epoch key (AES-256), generated in the primary HSM for every epoch and not extractable;
  • by the DR key (RSA-OAEP SHA-256, 3072 bits or more), whose private key stays in the DR HSM.

Record and owner ids appear in clear only inside sealed key blocks. Matching uses HMAC-SHA256 pseudonyms under a 32-byte tenant key.

Data Place
Encrypted chunks Private IPFS network (CIDv1, pinned and read back before the CID is returned)
Key blocks A separate S3-compatible store
Catalog, key journals and API state PostgreSQL
Epoch, root, receipts and client CA keys Two PKCS#11 HSM partitions: primary and DR

The catalog keeps only clear metadata such as partition, day range, counts and sizes. Plaintext exists only in the data plane’s memory.

Nodes share a 32-byte swarm key and refuse to start without it. Bootstrap, DHT routing, provider records, the gateway, relays and hole punching are off. There is no blockchain anywhere in the design.

What happens when you delete the record is described in Deletion and erasure.