HavenAn archive network for communitiesApplication layer · L7

CodexThe Rule SetII.02

Key derivation and epochs

The VetKD preimage binds chain, token, threshold and epoch into a single derivation — one key per corpus per month, not one per file.

Two generations

Haven has shipped two derivation schemes. V1 remains operational for backwards compatibility; V3 is the current protocol version and the one new publishers should target.

Property V1 (accessol_v1) V3 (accessol_v3)
Preimage SHA-256("accessol:" + chain + ":" + token + ":" + threshold + ":" + cid) SHA-256("accessol_v3:" + chain + ":" + token + ":" + threshold + ":" + effectiveEpoch)
Scope One key per CID One key per (chain, token, threshold, epoch)
Balance check Once per request Once per epoch, then cached
Batch N derivations per batch (up to 20) One derivation, replicated across N CIDs
Cache key (chain, token, threshold, cid, wallet) (chain, token, threshold, epoch, wallet)

The shift from V1 to V3 is motivated by a concrete cost problem. A collection of 500 items under V1 requires 500 separate VetKD derivations and — in the worst case — 500 separate eth_call balance checks. Under V3, the same collection requires one derivation and one balance check per epoch. The entire corpus unlocks with a single round trip.

The epoch

An epoch is floor(unix_timestamp / 2_592_000) — exactly 30 days. The canister exposes the current epoch via getCurrentEpoch (a query call).

When the epoch rolls, all cached approvals expire. A reader who held balance in the previous epoch but has since transferred their tokens cannot derive in the new epoch. This is the revocation mechanism: it is not instantaneous, but it is automatic and requires no action from the publisher.

A future epoch in a request is rejected with #InvalidEpoch before any effects are applied. This prevents a reader from pre-fetching keys for a period in which their balance may change.

The zero-threshold case

If a gate is configured with threshold zero, the effectiveEpoch collapses to 0 regardless of the actual time. This means:

  • The derivation is time-invariant — the same key is derived forever.
  • No balance check is performed (there is nothing to check).
  • The request must still carry a valid EIP-712 signature and a valid epoch.

This mode is useful for archives that want signature-based authentication without token gating — public collections that still want to know who is reading, even if anyone may read.

Transport encryption

The derived key never travels in the clear. The caller includes a transportPublicKey in the EIP-712 request. The canister encrypts the VetKD output against that key before returning it. Only the holder of the corresponding transport secret — which lives solely in the client’s memory — can unwrap the derived material.

The unwrap produces an AES key. The client uses this to decrypt ciphertext fetched from Filecoin/IPFS. At no point does the canister, the EVM chain, or the storage layer see the plaintext key or the plaintext content.

Preimage anatomy

The V3 preimage deserves close reading:

SHA-256("accessol_v3:" + chain + ":" + token + ":" + threshold + ":" + effectiveEpoch)

Each segment binds a specific dimension:

  • chain — prevents a balance on Base from unlocking content gated on Ethereum.
  • token — prevents a balance of token A from unlocking content gated by token B.
  • threshold — prevents a reader holding 1 token from deriving a key meant for holders of 1000.
  • effectiveEpoch — rotates the key monthly, enabling passive revocation.

If any dimension changes, the preimage changes, the derivation changes, and a different key emerges. There is no administrative override.