Documentation
What a station and a realm are in macula 12, and what you do with each.
The mesh in one paragraph
A client is your program, using the macula library. It connects to one or more stations and through them calls procedures, publishes and subscribes, and stores and finds signed records. A station is infrastructure: it carries traffic and holds records, and it has no idea what any organisation is. A realm is an authority: it decides which organisations and members exist, and signs the records that say so. A realm is itself a client of stations. It publishes what it signs through them and does not run the mesh.
- client, station and realm each have their own node identity, a key stored once per node
- a client names each station it dials by address and by the node_id that station must prove
- a client that works with a realm's organisations pins that realm's public key when it connects
Stations
What a station does
- accepts QUIC connections from clients and from other stations, on one UDP port (4433 in the published image)
- routes a CALL to the station its provider is connected to, and delivers publications to subscribers across stations
- holds a share of the mesh's DHT: signed records, each verified before it is stored and dropped when it expires
- announces itself with a signed record of its own, so clients and other stations can find it
Records live in memory. A restarted station starts with an empty store and the mesh fills it again as records are republished.
Identity and certificate
A station's identity is a node key in its data directory. It is generated on first start, and its node_id is what clients and other stations pin. The station mints its own TLS certificate from a separate key, self-signed with ML-DSA-87, and renews it itself. There is no certificate authority, no ACME client and no certificate file to provide.
Running one
-
the image is
ghcr.io/macula-io/macula-station, run with Docker or Podman -
it reads one JSON config file.
data_dir,bindandportare required;outbound_peerslists the stations it dials, each withhost,portandexpected_node_id -
a peer without
expected_node_idis never dialled. A station proves who it is; it is never trusted by name -
puzzle_enforcementisoff,log_onlyorenforce: whether a connecting node must hold a node_id that took work to generate. It defaults tooff -
certfileandkeyfileare refused: the station makes its own certificate
A user of the mesh runs no station. You need one station's address and node_id to dial, and the mesh does the rest.
Realms
What a realm does
- it holds a realm signing key, and that key is the root of trust for everything below
- it signs the realm's org directory, which names each organisation's key. An organisation's key in turn signs a procedure delegation naming the nodes allowed to serve its procedures
- it admits members and issues them capability tokens (UCANs) signed with the realm key and bound to the member's node_id
- a node that wants to serve procedures for an organisation claims it, and a human administrator admits it. A claim is never admitted automatically
How it relates to stations
A realm connects to stations as a client does, to seed stations it is configured with, each named by address and node_id. It does not discover stations on its own. What it signs reaches the mesh through those stations, and a realm with no station link publishes nothing until one comes back.
What you do with one
- calling an organisation's procedures: pin the realm's public key when you connect (see below). For the io.macula realm, take it from the realm's key document and check it as shown next
- serving procedures for an organisation: run your service under a stored node identity, claim the organisation through the realm, and wait for an administrator to admit the claim
- running a realm: macula.io runs one. Talk to us before planning your own
Membership is not required to reach the mesh. Stations do not check realm membership when a client connects.
The io.macula realm's key
The realm publishes its public key at realm.macula.io/.well-known/macula-realm.json . This page is a second, independent place for the values it must match. Do not pin a key that does not match them.
- realm
- io.macula
- realm_id
- abb81b5a614b63551b400b810648c0c8a78efad845442630c94b46cc95d2fcd1
- profile
- pq_hybrid
- key_id
- a50fef96e52b4d68ad112ff42b55dc916cd70e5b283280e8cf3cc5e492459cf6
To check the document: base64-decode public_key, compute its key id with macula_node_keys:key_id(RealmKey, pq_hybrid), and compare it, hex-encoded,
with the key_id above. Then pin it:
RealmKey = base64:decode(PublicKeyFromDocument),
<<"a50fef96e52b4d68ad112ff42b55dc916cd70e5b283280e8cf3cc5e492459cf6">> =
binary:encode_hex(macula_node_keys:key_id(RealmKey, pq_hybrid), lowercase),
RealmId = binary:decode_hex(<<"abb81b5a614b63551b400b810648c0c8a78efad845442630c94b46cc95d2fcd1">>),
{ok, Pool} = macula:connect(Seeds, #{realm_trust => #{RealmId => RealmKey}}).
Connecting from code
The macula library, version 12, is the client. It is an Erlang/OTP library on hex and works from Elixir as well.
{macula, "~> 12.0"}
-
macula:connect(Seeds, Opts)starts a pool. Every seed carriesexpected_node_id; a pool will not start without one per seed -
realm_trust => #{RealmId => RealmKey}pins realm keys, and connect is the only place it goes: on a call it is refused. Without the key for a realm, no advertisement of that realm's organisations is trusted, and a call returns{error, {unresolved, no_trusted_advertisement}} -
there is one way to trust a station: its handshake, under the node_id you gave.
verifyin any value andpin_tls_cert => trueare refused -
a node has one identity per program and user account, stored and reused by every pool
on that node, at
<identity_dir>/<identity_name>.<profile>.key.identity_diris the application setting andidentity_nameis given per connect (macula 14; the oldnode_identity_pathsetting is refused). A 13-eraidentity.keyis moved todefault.<profile>.keyon first connect
Cryptography
- node identities, records, tokens and handshakes are signed with ML-DSA-87
-
a node runs one of two profiles:
pq_pure, ML-DSA-87 alone, orpq_hybrid, where every signature outside TLS also carries an RSA-PSS-4096 half and is valid only if both verify -
every node offers only post-quantum hybrid key exchange groups,
SecP384r1MLKEM1024thenSecP256r1MLKEM768, with no classical fallback