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.
Listener and public URL
Section titled “Listener and public URL”| 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.
Inbound traffic
Section titled “Inbound traffic”| 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. |
Between nodes
Section titled “Between nodes”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.
Outbound traffic
Section titled “Outbound traffic”| 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. |
Browsers and CORS
Section titled “Browsers and CORS”--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.
App frontends
Section titled “App frontends”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
GETandHEAD./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.jsonlists 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-srcadds 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
Danswers404, never a node route. - On this cluster each package is also at
{cluster_label}.D(calimero_node::app_host::cluster_labelof--clusterand 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 answers404. - Each deployment has its own host too,
{dlabel}.D(calimero_node::app_host::deployment_labelof 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 ashost. - 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’sHostis that name (port 443, or none; onmerod localaname.localhost:portorigin). It is a registered origin, under its own rp-id; see Registered origins.GET /ops/served-originslists them for whatever orders their certificates and routes them (Calimero Cloud’s mdma). - DNS: a wildcard record
*.Dpointing at the cluster, and a TLS certificate for it on the front; for each custom domain, its own record and certificate.merod localuseslocalhost, so frontends are athttp://{label}.localhost:2501/.
Ports at a glance
Section titled “Ports at a glance”| 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. |