HavenAn archive network for communitiesApplication layer · L7

CodexThe Rule SetII.03

The entity shape

Every surface reads and writes the same container — Ident32 keys, typed attributes, a payload, and a content type — enforced at the precompile, not by convention.

The container

An entity is the atomic unit of the Arkiv state trie. It is not a row in a database; it is an account in an Ethereum-compatible state, stored at the precompile address 0x4400000000000000000000000000000000000044. The shape is enforced in the precompile itself — a malformed entity is rejected at execution time, not at indexing time.

The container has five components:

struct Operation {
  uint8   operationType;   // 1=CREATE, 2=UPDATE, 3=EXTEND, 4=TRANSFER, 5=DELETE, 6=EXPIRE
  bytes32 entityKey;       // Ident32
  bytes   payload;         // application/json, base64
  Mime128 contentType;     // 4×bytes32, 128-byte MIME, validated
  Attribute[] attributes;  // searchable typed fields
  BlockNumber32 btl;       // blocks-to-live (uint32)
  address newOwner;        // used only by TRANSFER
}

Entities are written atomically via execute(Operation[]). The precompile processes the entire batch or reverts — there is no partial commit.

Primitive types

Three user-defined value types enforce constraints at the precompile level:

Type Definition Constraint
Ident32 bytes32 ≤32 bytes, lowercase a–z0–9_./\-, left-aligned, validated byte-by-byte
Mime128 bytes32[4] 128-byte content-type string, zero-padded, validated
BlockNumber32 uint32 Distinct type for TTL and expiry fields

An Ident32 key that contains an uppercase letter, a space, or an emoji will be rejected with Ident32InvalidByte(position, value). This is not a convention; it is a revert.

Attributes

Each attribute carries a name (an Ident32), a value type, and a four-word value:

Code Name Semantics
1 ATTR_UINT value[0] only; remaining words must be zero
2 ATTR_STRING 128-byte string, zero-padded, no embedded null after content
3 ATTR_ENTITY_KEY value[0] only; references another entity by key

The precompile validates the encoding: AttributeValueMalformed and AttributeStringInvalidByte catch any deviation. This means the query layer can trust that every attribute it reads conforms to its declared type without defensive parsing.

Media specialisation

For media entities, Haven defines a standardised set of attribute keys. These are conventions over the container — the precompile does not mandate them, but all five surfaces agree to use them:

Key Type Required Meaning
title STRING Yes Display title, ≤128 bytes
is_encrypted UINT Yes 1 if encrypted, 0 or absent if clear
duration UINT Recommended Seconds
creator_handle STRING Recommended Lowercased handle
encrypted_cid STRING If encrypted Filecoin CID of the ciphertext

The payload field carries the extended metadata as base64-encoded JSON — fields like encryption_metadata, codec_variants, segment_metadata, and thumbnail_cid that are too large or too complex for on-chain attributes.

Why the shape is shared, not the code

ADR-001 mandates that no two surfaces share a library. The entity shape is therefore defined once in EntityRegistry.sol and consumed verbatim by each surface in its own language: Rust in arkiv-entitydb, TypeScript in haven-dapp/src/types/arkiv.ts, Python in haven-cli/media, Kotlin in haven-mobile. There is no generated binding and no shared package. The precompile is the authority; the surface implementations are transcriptions.

This costs duplication. It buys the guarantee that a canister upgrade, a mobile release, and a CLI patch can each ship without coordinating a shared dependency version.