macula (macula v4.2.8)
View SourceMacula SDK — Public API for mesh applications.
This is the main entry point for applications using the Macula SDK.
Apps connect via connect/2, which returns a macula_client pool that internally wraps N peering links to N stations. publish/4,5, subscribe/4,5, unsubscribe/2, call/5, advertise/5, unadvertise/3, call_stream/5, advertise_stream/5, and unadvertise_stream/3 route through the pool with realm-per-call semantics. See macula_pubsub for the slice module of the publish/subscribe surface.
LOCAL streaming (call_stream/2,3, open_stream/3,4, advertise_stream/2,3, unadvertise_stream/1) dispatches in-process via macula_stream_local — for unit tests and same-BEAM pairs.
Erlang distribution over the mesh ships via join_mesh/1 (V2 pool carrier) or join_dist_relay/1 (dedicated dist relay). See macula_dist_relay / macula_dist_system.
Summary
Functions
Abort the stream with an error frame.
Advertise a procedure handler on a V2 pool. Fans out to every healthy link and stores in pool state for replay on link respawn. See macula_client:advertise/4.
Advertise a LOCAL in-process streaming procedure (default: server_stream).
Advertise a LOCAL in-process streaming procedure with mode.
Advertise a streaming procedure on a V2 pool. Fans out to every healthy link and stores in pool state for replay on link respawn. See macula_client:advertise_stream/5.
Wait for the terminal reply (client-stream / bidi).
Issue a CALL frame against a V2 pool. First-success across the pool's healthy links. See macula_client:call/5.
Open a LOCAL in-process server-stream call. Used for unit tests and same-BEAM dispatch via macula_stream_local.
Open a LOCAL in-process server-stream call with options.
Open a streaming RPC against a V2 pool. Picks the first currently-healthy link and opens the stream there; the returned stream is sticky-to-link (errors with peer_down if the link dies; caller re-opens). See macula_client:call_stream/5.
OTP child spec to drop a V2 pool into a caller's supervision tree.
Stop a V2 pool. Every subscriber receives a final {macula_event_gone, SubRef, pool_closed} message.
Half-close the write side; recv still drains.
Connect to the Macula relay mesh and return a pool handle.
Ensure this node is running in distributed mode.
Fetch a record from the mesh DHT by its macula_record:storage_key/1.
Return every record of a given type currently visible from the pool's connected stations.
Fetch the bytes for a previously-stored MCID. Returns {error, not_found} if no provider in the pool's reach holds a copy. The returned binary is BLAKE3-verified by the relay before it leaves the store, so the caller does not need to re-verify.
Get the Erlang cluster cookie.
Enable Erlang distribution over a dedicated dist relay (macula-io/macula-dist-relay).
Join the Macula relay mesh with Erlang distribution.
Subscribe to node up/down events.
Open a LOCAL in-process client-stream or bidi call. Used for unit tests and same-BEAM dispatch via macula_stream_local.
Open a LOCAL in-process stream with explicit mode.
Publish to (Realm, Topic) on Pool. Equivalent to publish/5 with empty opts.
Publish to (Realm, Topic) on Pool with options. See macula_pubsub:publish/5 for honored opts.
Store Bytes in the relay's content store and return its MCID (Macula Content ID — 34 bytes: codec, algo (BLAKE3 = 16#55), then the 32-byte BLAKE3 hash). The blob is sent as a single block; on receipt the relay verifies the payload's BLAKE3 matches the MCID before accepting.
Store a signed record in the mesh DHT via a V2 pool.
Receive the next chunk (blocks).
Send a binary chunk on the stream.
Send a chunk with explicit encoding.
Set the Erlang cluster cookie.
Server-side: emit the terminal reply value.
Aggregate health snapshot of a V2 pool. Suitable for /health or /status endpoints; not for hot-loop polling. See macula_client:status/1 for the full shape.
Subscribe Subscriber to (Realm, Topic) on Pool. Equivalent to subscribe/5 with empty opts.
Subscribe Subscriber to (Realm, Topic) on Pool with options. See macula_pubsub:subscribe/5.
Subscribe with a callback function. The SDK spawns a small receiver process internally and invokes the callback once per inbound event. See macula_pubsub:subscribe_callback/4.
Subscribe to live record-stored events filtered by type.
Stop advertising a procedure on a V2 pool.
Stop advertising a LOCAL streaming procedure.
Stop advertising a streaming procedure on a V2 pool.
Unsubscribe from node up/down events.
Drop a pool subscription. Idempotent.
Cancel a subscribe_records/3 subscription.
Types
-type mcid() :: <<_:272>>.
-type pool() :: macula_client:pool().
-type procedure() :: binary().
-type realm() :: <<_:256>>.
32-byte realm tag.
-type record() :: macula_record:record().
-type record_key() :: <<_:256>>.
DHT storage key — macula_record:storage_key/1 output.
-type record_type() :: macula_record:type_tag().
-type stream() :: pid().
-type stream_mode() :: server_stream | client_stream | bidi.
-type topic() :: binary().
Functions
Abort the stream with an error frame.
-spec advertise(pool(), realm(), procedure(), macula_client:handler(), map()) -> ok | {error, term()}.
Advertise a procedure handler on a V2 pool. Fans out to every healthy link and stores in pool state for replay on link respawn. See macula_client:advertise/4.
-spec advertise_stream(procedure(), stream_handler()) -> ok | {error, term()}.
Advertise a LOCAL in-process streaming procedure (default: server_stream).
-spec advertise_stream(procedure(), stream_mode(), stream_handler()) -> ok | {error, term()}.
Advertise a LOCAL in-process streaming procedure with mode.
-spec advertise_stream(pool(), realm(), procedure(), stream_mode(), stream_handler()) -> ok | {error, term()}.
Advertise a streaming procedure on a V2 pool. Fans out to every healthy link and stores in pool state for replay on link respawn. See macula_client:advertise_stream/5.
Wait for the terminal reply (client-stream / bidi).
Issue a CALL frame against a V2 pool. First-success across the pool's healthy links. See macula_client:call/5.
Open a LOCAL in-process server-stream call. Used for unit tests and same-BEAM dispatch via macula_stream_local.
Open a LOCAL in-process server-stream call with options.
Open a streaming RPC against a V2 pool. Picks the first currently-healthy link and opens the stream there; the returned stream is sticky-to-link (errors with peer_down if the link dies; caller re-opens). See macula_client:call_stream/5.
-spec child_spec(term(), [macula_client:seed()], macula_client:opts()) -> supervisor:child_spec().
OTP child spec to drop a V2 pool into a caller's supervision tree.
-spec close(pool()) -> ok.
Stop a V2 pool. Every subscriber receives a final {macula_event_gone, SubRef, pool_closed} message.
-spec close_send(stream()) -> ok.
Half-close the write side; recv still drains.
-spec close_stream(stream()) -> ok.
Close a V1 stream (both sides). Renamed from close/1 in 3.11.0 because close/1 now refers to the V2 pool surface.
-spec connect([macula_client:seed()], macula_client:opts()) -> {ok, pool()} | {error, term()}.
Connect to the Macula relay mesh and return a pool handle.
Seeds is a list of relay endpoints (URL binaries/strings or #{host, port} maps). The pool spawns one peering link per seed and routes ops with replication, replay, and event dedup. Returns immediately; link handshakes complete asynchronously.
Honored opts (full reference: macula_client:opts()):
identity— pool's Ed25519 keypair; auto-generated if absent.replication_factor— links per PUBLISH (default 1).capabilities— per-link bitfield (default 0).alpn— QUIC ALPN list (default[<<"macula">>]).connect_timeout_ms— per-link CONNECT/HELLO deadline (default 30_000).dedup_window_ms,dedup_sweep_ms— inbound-EVENT dedup tunables.
Legacy opts silently dropped (with a one-shot logger:notice): relays (use the Seeds positional argument), realm (V2 is realm-per-call), site (no V2 analog), connections (one link per seed; add more seeds to grow the pool).
See macula_client for the canonical pool implementation and macula_pubsub for the slice module.
-spec ensure_distributed() -> ok | {error, term()}.
Ensure this node is running in distributed mode.
-spec find_record(pool(), record_key()) -> {ok, record()} | {error, not_found | term()}.
Fetch a record from the mesh DHT by its macula_record:storage_key/1.
Returns {error, not_found} when no record exists at the key. The returned record's signature should be verified via macula_record:verify/1 before its payload is trusted.
-spec find_records_by_type(pool(), record_type()) -> {ok, [record()]} | {error, term()}.
Return every record of a given type currently visible from the pool's connected stations.
Coverage depends on each station's view of the DHT — a single station sees its local replicas plus whatever its peers have gossiped. Aggregating across the full mesh requires querying multiple stations and deduplicating by record key.
Fetch the bytes for a previously-stored MCID. Returns {error, not_found} if no provider in the pool's reach holds a copy. The returned binary is BLAKE3-verified by the relay before it leaves the store, so the caller does not need to re-verify.
-spec get_cookie() -> atom().
Get the Erlang cluster cookie.
Enable Erlang distribution over a dedicated dist relay (macula-io/macula-dist-relay).
Different from join_mesh/1: - Connects to a dist relay (port 4434, ALPN macula-dist), NOT the pub/sub station mesh - No mesh_client, no pub/sub subscriptions — only dist traffic - Uses raw QUIC stream routing with no MessagePack overhead
Options: - url (required): <<"quic://relay.example.com:4434">>
After this returns ok, standard OTP distribution (rpc:call/4, gen_server:call/3 across nodes, pg groups, etc.) works across firewalls via the dist relay.
Join the Macula relay mesh with Erlang distribution.
After calling this, standard OTP distribution works across firewalls. Opts takes:
relays(required) — list of seed URLs for the V2 pool.identity— V2 pool'smacula_identity:key_pair(). Default: auto-generated.
Internally builds a V2 macula_client:pool() and registers it with macula_dist_relay as the carrier for _dist.tunnel.* traffic. Dist tunnel frames travel under the all-zeros realm (protocol-internal infrastructure, not bound to any user realm).
-spec monitor_nodes() -> ok.
Subscribe to node up/down events.
Open a LOCAL in-process client-stream or bidi call. Used for unit tests and same-BEAM dispatch via macula_stream_local.
Open a LOCAL in-process stream with explicit mode.
Publish to (Realm, Topic) on Pool. Equivalent to publish/5 with empty opts.
Publish to (Realm, Topic) on Pool with options. See macula_pubsub:publish/5 for honored opts.
Store Bytes in the relay's content store and return its MCID (Macula Content ID — 34 bytes: codec, algo (BLAKE3 = 16#55), then the 32-byte BLAKE3 hash). The blob is sent as a single block; on receipt the relay verifies the payload's BLAKE3 matches the MCID before accepting.
This is the v4.2.7 minimum-viable shape — single-block per blob, no client-side chunking. A subsequent release will add chunked manifests so blobs larger than the per-call payload budget can be transferred via parallel _content.put_block calls. For blobs in the kilobyte-to-low-megabyte range a single block is sufficient (relay default chunk size is 256 KiB; oversized payloads will surface as a CALL-deadline timeout rather than silent truncation).
Store a signed record in the mesh DHT via a V2 pool.
Build the record via the typed constructors in macula_record (node_record/3,4, content_announcement/3,4, tombstone/3,4, realm_directory/3,4, procedure_advertisement/3,4, etc.) then sign it with macula_record:sign/2. The relay validates the signature on receipt; an invalid signature returns {error, bad_signature}. Successful stores propagate to the K-nearest peers in the DHT under the record's macula_record:storage_key/1.
Receive the next chunk (blocks).
Send a binary chunk on the stream.
Send a chunk with explicit encoding.
Set the Erlang cluster cookie.
Server-side: emit the terminal reply value.
-spec status(pool()) -> {ok, macula_client:status()}.
Aggregate health snapshot of a V2 pool. Suitable for /health or /status endpoints; not for hot-loop polling. See macula_client:status/1 for the full shape.
Subscribe Subscriber to (Realm, Topic) on Pool. Equivalent to subscribe/5 with empty opts.
Subscribe Subscriber to (Realm, Topic) on Pool with options. See macula_pubsub:subscribe/5.
-spec subscribe_callback(pool(), realm(), topic(), macula_pubsub:callback()) -> {ok, reference()} | {error, term()}.
Subscribe with a callback function. The SDK spawns a small receiver process internally and invokes the callback once per inbound event. See macula_pubsub:subscribe_callback/4.
-spec subscribe_records(pool(), record_type(), fun((record()) -> any())) -> {ok, reference()} | {error, term()}.
Subscribe to live record-stored events filtered by type.
The callback receives each newly-stored record of the given type. Returns a subscription reference for unsubscribe_records/2. Topic shape is _dht.records.<type>.stored, rendered with the type tag as a decimal integer for log friendliness.
Stop advertising a procedure on a V2 pool.
-spec unadvertise_stream(procedure()) -> ok.
Stop advertising a LOCAL streaming procedure.
Stop advertising a streaming procedure on a V2 pool.
-spec unmonitor_nodes() -> ok.
Unsubscribe from node up/down events.
Drop a pool subscription. Idempotent.
Cancel a subscribe_records/3 subscription.