Identities & Keys
Three keys, three jobs
Section titled “Three keys, three jobs”It is easy to conflate “who a node is on the network” with “who authored an operation” — they are different keys with different lifetimes. Calimero uses three distinct kinds of key, and keeping them separate makes the rest of the protocol clear.
Transport identity — naming a node
Section titled “Transport identity — naming a node”Every node has an Ed25519 keypair used only by the network layer. Its public key is the node’s peer id: the address other nodes dial, the identity the encrypted transport authenticates. It says nothing about authority — it answers “which box am I talking to,” not “who wrote this.” A node can serve many namespaces over one transport identity.
Member identity — authoring operations
Section titled “Member identity — authoring operations”Authority lives in the member identity: a single Ed25519 keypair per node, generated on first use and persisted. It is the author on every operation and the key that signs an operation’s id (see Operations & the DAG). Membership, roles, and capabilities (defined in Governance) are expressed in terms of the account this key writes as, not the key itself.
One key, not one per namespace. It is recorded as the node’s device signing key, and a device is one installation rather than one installation per scope. A key that varied by namespace would also have bought nothing: it is published in every namespace’s device binding, so it correlates a node across namespaces exactly as a shared one does.
The executor
Section titled “The executor”A node owns exactly one member identity per context, so the executor — the
key that authors and signs the resulting operation — is resolved, never named.
execute_request (crates/server/src/execute.rs) looks up the node’s owned
identity for the target context and passes it to the runtime; callers cannot
supply one.
Authentication and attribution are therefore separate. When a request arrives
with a verified key, that key gates access via the context-membership check, but
the identity handed to the WASM guest is the node’s own. An application that
inspects executor sees the node, not the caller — a deliberate design, and one
with a known consequence: a context member whose in-application permissions are
lower than the node owner’s can still execute at the higher privilege level.
Scope key — reading a scope
Section titled “Scope key — reading a scope”A scope’s operations are encrypted at rest and on the wire under a symmetric scope key (every scope owns a key). Holding the key is what lets a member read a scope; lacking it, a node sees only opaque ciphertext.
Scope keys are never sent in the clear. They are delivered wrapped to a specific member’s public key using an ECDH exchange between that member key and the sender’s — the KeyDelivery step in Governance’s join flow. Removing a member rotates the scope key and re-wraps the fresh one to everyone who remains, which is how forward secrecy is enforced.
How they compose
Section titled “How they compose”Put together, a single received operation touches all three: it arrives over a connection authenticated by transport identities, its ciphertext is decrypted with the scope key, and its authorship is verified against the author’s member key before anything trusts it. The Receive & Apply Path and Sync show that sequence on the wire.
A fourth role: the bundle signing key
Section titled “A fourth role: the bundle signing key”The three keys above are all node-side — they live on a node and act inside a namespace. There is one more key role that belongs to a different actor entirely: the bundle signing key, an Ed25519 key held by whoever publishes an application.
When an application bundle is built, its manifest is signed with this key, and the
public half is rendered as a did:key signerId — the bundle’s update
authority (crates/node/primitives/src/bundle/signature.rs). That signerId is
what anchors an application’s id across releases: a new
version signed by the same key keeps the same application identity, and only the
holder of that key can publish an update under it.
It is not a node or member key. It identifies an app publisher, not a box on the network or an author inside a context, and it never participates in the transport / member / scope sequence that processes an operation. Keep it separate when reasoning about authority: the first three say “who may write into a context”; the bundle key says “who may ship the code that context runs.”
Next: Encryption — which key encrypts what, and what an observer can still see.