Skip to content

Networking

A node has one network surface: an HTTP listener. Clients and peers both reach it at the node’s public URL, and everything except /health and /attestation travels inside the sealed channel. There is no peer discovery, relay or NAT traversal: nodes of a cluster are servers that know each other’s URLs from the system group.

Setting Default Meaning
--listen 0.0.0.0:2528 The socket the API is served on (plain HTTP).
--public-url required The base URL clients and peers use for this node.

--public-url is recorded in the fleet directory and used by every other node to reach this one: Raft traffic, forwarded calls and reads, blob transfers. It must reach this node specifically, not a balancer that may pick another node. Use a trailing slash (https://n1.example.com/) as merod local does.

From To What
Clients any node The client API (/auth, /packages, /apps, /orgs, …), sealed. Any node answers; writes and strong reads are forwarded to the leader of the group concerned.
Peers every node /internal/*, sealed and carrying the cluster secret.
The operator any node /ops/*, sealed, with the ops token. Replica moves must reach the app group’s leader; others answer 421 NOT_LEADER with its URL.
Anyone any node /health and /attestation, in the clear.

A node talks to a peer only through a sealed channel opened after it judged the peer’s quote the way a client would: in TDX mode the quote must match a profile in --peer-profiles (default: its own MERO_TEE_PROFILE) of the signed mero-tee release the peer names, at least --peer-min-release. On top of that, every internal request carries the cluster secret, an HMAC of the storage key and --cluster, so a genuine TD of another cluster is refused too.

Forwarded calls and reads wait at most 5 seconds for a leader before retrying at whoever leads then. An app’s data is sent only to peers whose attested release the app’s owner (or the operator, per governance) approved.

To When
Peers’ --public-url Always.
mero-kms (--kms-url) At start, to fetch the storage key.
GitHub release assets (Sigstore-signed published-mrtds.json and KMS policies) To verify the KMS and peers’ quotes against signed mero-tee releases.
Intel DCAP collateral To verify TDX quotes.
The package registry (--registry-url) On installs and when a node needs a registry release it does not hold.
App-declared HTTPS hosts Only with an [outbound] policy, on port 443 to public addresses, for approved prefixes. See Configuration.

--origins are both the origins passkey ceremonies may come from and the CORS origins. The CORS layer allows GET, POST and DELETE, the Content-Type and Authorization headers, exposes Retry-After, and caches preflights for 10 minutes. Origins of package frontends and deployment hosts under --apps-domain, and origins packages’ managers registered (served custom domains included), are allowed as well.

With --apps-domain D, a request whose host is {label}.D is served before any node route: label is a 52-character base32 name derived from the package (calimero_node::app_host::label), and the files come from the frontend inside the package’s signed .mpk, verified against the publisher’s signature before a byte is served.

  • Only GET and HEAD. / redirects to the newest release’s entry, /{version}/; /{version}/{path} serves a file, or the entry for an HTML request to a path with no file (a single-page app’s routes survive a reload). /app/{app}/ redirects to /{version}/ of the release that instance runs, and /d/{deployment}/ to /{version}/?deployment={id} of the release that deployment offers, whatever newer release / points at. /.well-known/calimero.json lists the package, its releases, the deployments offering it and the cluster’s API URLs for the page to connect to.
  • Responses carry a strict CSP whose connect-src adds the cluster’s API origins and the attestation mirror, https://cloud.calimero.network, where the page’s client fetches the release’s signed measurements and Intel’s collateral to verify the nodes.
  • An unknown label under D answers 404, never a node route.
  • On this cluster each package is also at {cluster_label}.D (calimero_node::app_host::cluster_label of --cluster and the package): the same site and the same package origin, for a front that routes by hostname to the cluster; see Cluster hosts. Another cluster’s label answers 404.
  • Each deployment has its own host too, {dlabel}.D (calimero_node::app_host::deployment_label of the deployment id), serving what /d/{deployment}/ serves, as its own origin of the package; see Deployment hosts. A front that routes by hostname sends each deployment’s host to the cluster holding it; the deployment API returns it as host.
  • A custom domain a package’s manager registered as a served origin (POST /package-origins/{package} { origin, served: true }) is served the same site when a request’s Host is that name (port 443, or none; on merod local a name.localhost:port origin). It is a registered origin, under its own rp-id; see Registered origins. GET /ops/served-origins lists them for whatever orders their certificates and routes them (Calimero Cloud’s mdma).
  • DNS: a wildcard record *.D pointing at the cluster, and a TLS certificate for it on the front; for each custom domain, its own record and certificate. merod local uses localhost, so frontends are at http://{label}.localhost:2501/.
Port Used by
2528 The default --listen port.
2501–2503 merod local (--base-port 2500, nodes 1–3).
2428 Exposed by the Dockerfile, used by nothing.