Configuration Reference
A node’s configuration is one TOML file, {home}/{node}/config.toml
(--home/CALIMERO_HOME, default ~/.calimero). One struct,
node_config::NodeArgs in crates/merod/src/node_config.rs, is both the file
and the node flags, so every key has a flag and an
environment variable of the same meaning.
Precedence
Section titled “Precedence”For merod run, from strongest to weakest:
- a flag on the command line;
- its environment variable (
MEROD_*, orMERO_TEE_*for the two TEE keys); - the key in
{home}/{node}/config.toml, if--nodeis given and the file exists; - the default, applied when the node starts.
A list key (peers, origins, peer-profiles) given on the command line
replaces the file’s list. merod init writes only what it is given;
merod config merges what it is given into the file. Both write the file with
mode 0600.
Rules of the file
Section titled “Rules of the file”- Keys are kebab-case, as the flags without
--. - Unknown keys are refused, so a typo fails at start instead of being ignored.
- Every key is optional in the file;
node-idandpublic-urlmust be set somewhere beforerunstarts. - Defaults are not written, so a file shows only what someone chose.
| Key | Type | Default | See |
|---|---|---|---|
node-id |
integer | required | This node’s id in the cluster. |
listen |
"ip:port" |
"0.0.0.0:2528" |
Networking |
public-url |
string | required | Networking |
data-dir |
path | {home}/{node}/data |
Data directory |
peers |
list of "id=url" |
[] |
Deployment |
bootstrap |
bool | false |
Deployment |
cluster |
string | "dev" |
Must match on every node. |
dev |
bool | false |
Security |
storage-key |
hex string | none | dev only. |
storage-key-file |
path | none | dev only. |
kms-url |
string | none | Deployment |
kms-identity |
path | none | Deployment |
rp-id |
string | "localhost" |
Authentication |
origins |
list of URLs | ["http://localhost:3000"] |
Authentication |
ops-token |
string | none | Authentication |
ops-token-sha256 |
hex string | none | Authentication |
replicas |
integer | 3 |
How many nodes hold each new app. |
backup-dir |
path | none | Runbooks |
operator-key |
hex string | none | Operator |
tee-release |
string | none (MERO_TEE_VERSION) |
Security |
tee-profile |
string | none (MERO_TEE_PROFILE) |
Security |
peer-profiles |
list | [tee-profile] |
Security |
peer-min-release |
string | "2.3.109" |
Security |
registry-url |
string | none | Registry |
allow-unsigned-bundles |
bool | false (true with dev) |
Raw WASM uploads. |
release-approvals |
"owner" | "operator" |
"owner" |
Governance |
app-upgrades |
"owner" | "operator" |
"owner" |
Governance |
outbound-approvals |
"owner" | "operator" |
"owner" |
Governance |
apps-domain |
string | none | Networking |
settings |
path | none | A file whose [outbound] table replaces this one’s. |
[outbound] |
table | none (no outbound HTTPS) | Outbound |
Example
Section titled “Example”node-id = 1listen = "0.0.0.0:2528"public-url = "https://n1.eu1.example.com/"cluster = "eu1"peers = [ "1=https://n1.eu1.example.com/", "2=https://n2.eu1.example.com/", "3=https://n3.eu1.example.com/",]bootstrap = truekms-url = "https://kms.internal.example.com/"kms-identity = "/data/disk-identity"rp-id = "example.com"origins = ["https://app.example.com"]ops-token-sha256 = "<hex SHA-256 of the ops token>"backup-dir = "/backups"registry-url = "https://registry.example.com/"
[outbound]enabled = truehosts = ["api.example.com"]
[outbound.env_secrets.EXAMPLE_API]env = "EXAMPLE_API_KEY"packages = ["com.example.forms"]signers = ["did:key:z6Mk..."]hosts = ["api.example.com"]tee-release and tee-profile are usually left to the image, which sets
MERO_TEE_VERSION and MERO_TEE_PROFILE.
Data directory
Section titled “Data directory”data-dir holds everything the node persists, sealed under its storage key:
| Path | What |
|---|---|
store/ |
One RocksDB: the Raft logs, votes, state of every group and the sealed blob store (bundles, .mpk files, app blobs). Every value is AES-256-GCM sealed and bound to its column, group and key. |
snapshots/ |
Raft snapshot files, sealed frame by frame. |
A directory that still holds the earlier log.redb file is migrated into
store/ on start, and the old file is renamed log.redb.migrated.
Registry
Section titled “Registry”registry-url is the package registry every node fetches signed releases from
(calimero-app-downloader’s HTTP registry). meroctl app install PACKAGE@VERSION, POST /apps { package, version } for a release the cluster
lacks, and owner upgrade runs install from it. A node that runs a registry
release it does not hold fetches it from the registry, never from a peer, and
refuses bytes whose hash differs from what the log recorded. Without
registry-url, those requests answer 400 NO_REGISTRY. Uploaded .mpk files
(POST /packages) are taken only with --dev: in production every release
comes from the registry.
Outbound
Section titled “Outbound”The [outbound] table (or the [outbound] table of the file settings
names, which replaces it) is the operator’s policy for HTTPS requests apps make
with env::http_request. Without it, every such request fails.
| Key | Meaning |
|---|---|
enabled |
true to allow outbound requests at all. Default false. |
hosts |
DNS names requests may go to; "*" allows any host an installed app’s signed manifest declares. |
env_secrets.NAME |
A secret from an environment variable, used in a request as {{secret:NAME}}: env (the variable, read once at start), packages, signers (did:key:…; both must match) and hosts (where it may be sent). |
sidecar |
Secrets from a loopback service instead: url (a loopback http:// URL) and token_env (the variable holding the bearer token sent to it). Exclusive with env_secrets. |
A request is made only when all of these hold: the operator allows the host
here; the app’s signed .mpk manifest declares a prefix covering the URL; the
prefix was approved for the app’s publisher by whoever holds
outbound approvals; the run writes (a call,
follow-up or timer, never a view or migration); and the URL is https on port
443 to a name that resolves only to public addresses. Secrets are filled in by
the node and redacted from the answer the app sees.