The Write Path
The left half of the operation’s life
Section titled “The left half of the operation’s life”The landing page promised to follow one operation from birth to convergence. This chapter is its birth: a request arrives, an application runs, and a signed operation is persisted and broadcast. Every step here was introduced earlier — this is where they connect into a single flow.
Step by step
Section titled “Step by step”-
Request and lock. — A request (HTTP/RPC) names a context and a method. The node takes an execution lock on that context so concurrent writes don’t interleave. Read-only calls take a shared lock and skip everything below — they produce no operation.
-
Execute. — The WASM application runs the method against current state (execution). It produces an outcome: a new root hash and an artifact of committed writes, plus events and any queued cross-calls. If it traps, the lock releases and nothing is written.
-
Build the operation. — The committed writes become data-plane operations in the context scope. Each takes the scope’s current heads as parents, a fresh hybrid timestamp, the executor’s member identity as author, and a content-addressed id, all as defined by the operations model.
-
Sign. — The author signs the id with their member key. The signature binds authorship to content so a peer can verify it before trusting the operation — and so the operation can’t be relabelled in flight.
-
Persist atomically. — In one atomic write the node stores the operation, the updated context metadata (new root hash and heads), and the materialized state. Atomicity matters: a node must never advance its heads without also holding the operation that produced them, or a crash could strand the two out of step.
-
Broadcast. — The signed operation is published to the
context/<id>gossip topic, encrypted under the scope key. Broadcast is fire-and-forget and runs off the request’s critical path — the writer has already durably committed; gossip is how everyone else finds out.
A worked example: send_message("hi")
Section titled “A worked example: send_message("hi")”Follow one real chat write through the six steps. The context’s current head is 0x11aa…02, the executing member’s key is 0x9f4b…21, and the group key hashes to key_id = 0x4d90…7c. (Hashes are abbreviated and illustrative.)
1. execute — WASM appends to the messages collection. outcome.artifact = [ Put(entity=messages["m17"], value=borsh("hi")) ] outcome.events = [ { kind: "MessageSent", data: { len: 2 } } ] outcome.root = 0x6e72…ff (new scope_root after the put)
2. build — parents = [ 0x11aa…02 ] (the current head) hlc = 1750.000123:#0 author = 0x9f4b…21 delta_id = hash(parents ‖ artifact) = 0x3c08…9d
3. sign — delta_signature = Ed25519_sign(author_sk, payload of (b"calimero/delta/1", context_id, delta_id, author_id, governance_position)) The chat entry is Public, so the action carries no per-action signature.
4. persist — one atomic write: { delta, meta(root=0x6e72…ff, heads=[0x3c08…9d]), state }
5. broadcast — SharedKey::from_sk(group_key).encrypt(borsh(artifact), nonce) → ciphertext publish BroadcastMessage::StateDelta { author_id, delta_id, parent_ids, hlc, root_hash=0x6e72…ff, key_id=0x4d90…7c, nonce, artifact=ciphertext, events (plaintext), delta_signature } on TopicHash(context_id)After step 4 the writer’s own state already shows "hi" and its head has advanced to delta_id. The new head is the delta_id — the next op the same node authors will list 0x3c08…9d as its parent. Step 5 is how everyone else finds out; the receive path picks up this exact message from here.
What gets committed before the broadcast
Section titled “What gets committed before the broadcast”The ordering is deliberate: persist, then broadcast. By the time a single byte leaves the node, the operation is already durable locally and the author’s own state already reflects it. If the network is down, or no peer is listening, nothing is lost — the operation sits in the local DAG and will be served to peers later through the sync channel. Convergence never depends on a broadcast being received; it only depends on the operation eventually being available.
Governance writes take the same path
Section titled “Governance writes take the same path”A membership change, a capability grant, or an admin action is built and broadcast exactly like a data write — the only differences are that the payload is a governance variant and the topic is the governance scope’s (group/ or ns/) rather than the context’s. One write path serves all four planes, because they are all just operations.
Where this leads
Section titled “Where this leads”The operation is now in flight. The Receive & Apply Path is the right half of its life: what a receiving node does to verify, decrypt, order, and apply it.