Skip to content

Concepts

  • Cluster — A set of merod nodes that share one system group and one storage key. It has a name (--cluster), which is bound into the storage key and into the secret peers present to each other.
  • Node — One merod process inside a TDX VM, with a numeric id (--node-id) and a public URL. It hosts the system group and the app groups placed on it, and serves the API only through the sealed channel.
  • Group — One Raft log with its state machine (calimero-log). Group 1 is the system group; every app instance gets a group of its own, numbered by the system group when the instance is created.
  • App instance (an app, and what meroctl still calls a context) — One running copy of an application: a bundle, an owner, an access rule and its own replicated state. Its id is 16 random bytes in base64url; its context id, what the app reads as env::context_id(), is SHA-256(app_id) in hex. The API takes either.
  • Bundle — One WASM module, named by hex(SHA-256(wasm)). Bundles live in each node’s sealed blob store; the system log records only their hash and size.
  • Package and release — A signed .mpk from a publisher: a package name, a version, one or more services (each a bundle), a frontend, and the outbound URL prefixes it declares. A package’s publisher key is pinned by its first release. See Packages & frontends.
  • Account — A person (32 random bytes, base64url). Its passkeys are its devices; it signs in with one and gets a short session token. Machines act for an account with an API key. See Accounts, passkeys & sessions.
  • Organization and team — An organization has members with a role (guest, member, admin, owner) and nested teams. An app may belong to an organization and be reachable by all its members, or by one team’s. See Organizations, teams & access.
  • Operator — Whoever runs the machines. It holds the ops token, reaches the Ops API, and sees counts and Raft status, never app data.
cluster "eu1"
├── system group (group 1, on every node)
│ ├── nodes 1 → https://n1…, 2 → https://n2…, …
│ ├── packages chat 1.2.0 { signer did:key:z…, services, .mpk hash }
│ ├── bundles hash → { size, uploaded by, release }
│ ├── accounts ada { passkeys, approvals, aliases }
│ ├── credentials API-key hashes, refresh tokens, used decision proofs
│ ├── organizations acme { members, teams, approvals, upgrade runs }
│ └── apps (directory) app-7f… { bundle, owner, org, access, replicas [1,2,3], group 12 }
└── app groups
├── group 12 on nodes 1,2,3 ← app-7f…'s log and state
└── group 13 on nodes 2,3,4 ← another instance
one cluster, many small groupsnode 1node 2node 3node 4node 5system groupgroup 1, every nodevoter ★votervoterlearnerlearnerapp Agroup 12leader ★replicareplicaapp Bgroup 13replicaleader ★replicaapp Cgroup 14replicaleader ★replica… one group per app★ leader · voters decide commits · a learner receives entries, does not votean app's data lives only on its replicas; leaders spread across nodes

The system group is replicated to every node, so any node can answer who an account is and where an app lives. Only its voters decide commits; the rest of the nodes follow it as learners. An app group is replicated only to the instance’s replicas; a request that reaches another node is forwarded to them.

The split follows who has to agree on what.

  • Who may do what is cluster-wide. A passkey registered through one node must sign in through any other; removing a member must hold on every node before the next request. So identity, membership and the app directory are one log every node applies.
  • An app’s state concerns only that app. Its calls are ordered against each other, never against another app’s, so each instance gets its own log and its own leader. Apps on different groups progress in parallel, and an app’s data is held only by the nodes placed to hold it — which is what lets an owner restrict which images may hold it.

The glossary defines every term; the names meroctl still uses (context, namespace, group) are mapped there.