Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.20.3] - 2026-01-25
Fixed
- Documentation Consistency - Comprehensive audit and fixes across all guides
- Standardized variable naming:
Peer→Clientin code samples (PUBSUB_GUIDE, RPC_GUIDE) - Updated QUICK_START to use public API (
macula:subscribe/3,macula:advertise/3,macula:call/4) - Fixed deprecated terminology in DHT_GUIDE: "Gateway mode"/"Edge peer mode" → "Seed nodes"
- Updated DHT_GUIDE version references from 0.6.0 to 0.20.2+
- Standardized variable naming:
[0.20.2] - 2026-01-25
Added
- Core Design Principle: Event Types in Topics, IDs in Payloads
- Elevated to non-negotiable architectural rule in
PUBSUB_GUIDE.md - Weather service example with correct/incorrect patterns
- Content-based filtering noted as application-level concern
- Updated
GLOSSARY.mdTopic entry with same principle
- Elevated to non-negotiable architectural rule in
[0.20.1] - 2026-01-25
Added
- MRI Guide - Comprehensive documentation for MRI system added to hexdocs
docs/guides/MRI_GUIDE.md- Full guide covering format, types, API, relationships
Changed
- Updated hexdocs logo to use the standard macula logo (
artwork/logo.svg)
[0.20.0] - 2026-01-25
Added
MRI (Macula Resource Identifier) System - Hierarchical resource naming and graph storage
- Unified addressing scheme:
mri:{type}:{realm}/{path} - 22 built-in types: realm, org, user, app, service, artifact, license, cert, key, topic, proc, content, device, cluster, location, zone, network, model, dataset, config, class, taxonomy
- Custom type registration with realm scoping
- Graph relationships for resource connections
- Unified addressing scheme:
New modules (
src/):macula_mri.erl- Core parsing, validation, formatting, hierarchy operationsmacula_mri_registry.erl- Type registry with built-in and custom typesmacula_mri_store.erl- Storage behaviour with adapter patternmacula_mri_graph.erl- Graph behaviour for relationship storagemacula_mri_ets.erl- ETS-based adapter implementing both behaviours
MRI Features:
- Parse/format MRI strings:
macula_mri:parse/1,macula_mri:format/1 - Hierarchy traversal:
parent/1,ancestors/1,is_ancestor/2,depth/1 - Path manipulation:
append_segment/2,join_path/1,split_path/1 - Constructors:
new_realm/1,new_org/2,new_user/3,new_app/3,new_service/4
- Parse/format MRI strings:
MRI Store API:
- CRUD:
register/2,lookup/1,update/2,delete/1,exists/1 - Hierarchy queries:
list_children/1,list_descendants/1 - Index queries:
list_by_type/2,list_by_realm/1 - Bulk operations:
import/1,export/0
- CRUD:
MRI Graph API:
- Relationships:
create_relationship/3,4,delete_relationship/3,get_relationship/3 - Queries:
related_to/2,related_from/2,all_related/1 - Transitive traversal:
traverse_transitive/3 - Taxonomy:
instances_of/1,instances_of_transitive/1,classes_of/1,subclasses/1,superclasses/1 - Built-in predicates: located_at, contains, member_of, manages, depends_on, instance_of, subclass_of, and more
- Predicate utilities:
is_builtin_predicate/1,inverse_predicate/1
- Relationships:
Added 114 unit tests for MRI system (macula_mri, registry, store, graph, ets)
[0.19.2] - 2026-01-14
Changed
- Complete README.md rework with feature sections and SVG diagrams
- Added logo.svg and mesh-architecture.svg from macula-ecosystem
[0.19.1] - 2026-01-14
Added
- Gossip Clustering - Zero-configuration cluster formation using UDP multicast
macula_cluster_gossip.erl- UDP multicast heartbeats and peer discoverymacula_cluster_static.erl- Static node list clustering strategymacula_cluster:start_cluster/1- API for starting cluster with strategy selection- Supports
gossip,static, andmdnsstrategies - HMAC-SHA256 authentication with optional shared secret
- Auto-discovery on LAN via multicast group
230.1.1.251:45892 - Dynamic version display in startup banner
- Added 34 unit tests for gossip and static clustering
- Added
docs/guides/GOSSIP_CLUSTERING_GUIDE.md- Comprehensive operator guide - Added
assets/gossip_clustering.svg- Visual architecture diagram
[0.19.0] - 2026-01-14
Added
Content Transfer System - P2P artifact distribution via Macula mesh (MCID-based)
- Content-addressed storage using MCID (Macula Content Identifier)
- BLAKE3 and SHA256 hash algorithms supported
- Merkle tree verification for chunk-level integrity
- Want/Have/Block protocol for efficient P2P exchange
- DHT integration for provider discovery and announcements
New modules (
src/macula_content_system/):macula_content.erl- High-level API facademacula_content_hasher.erl- BLAKE3/SHA256 hashing with NIF accelerationmacula_content_chunker.erl- File chunking (256KB default)macula_content_manifest.erl- Manifest creation and parsingmacula_content_store.erl- Local content storage with TTLmacula_content_transfer.erl- P2P transfer coordinationmacula_content_dht.erl- DHT integration for provider discoverymacula_content_system.erl- Supervisor for content subsystem
Protocol message types (0x90-0x95):
content_want(0x90) - Request chunkscontent_have(0x91) - Advertise available chunkscontent_block(0x92) - Transfer chunk datacontent_manifest_req(0x93) - Request manifestcontent_manifest_res(0x94) - Return manifestcontent_cancel(0x95) - Cancel pending requests
API functions:
macula_content:publish/1,2- Publish file to content systemmacula_content:store/1,2- Store binary data directlymacula_content:fetch/1,2- Fetch content by MCIDmacula_content:locate/1- Find providers in DHTmacula_content:stat/1- Get manifest infomacula_content:is_local/1- Check local availabilitymacula_content:list_local/0- List locally stored MCIDsmacula_content:unpublish/1- Remove from local storemacula_content:mcid_to_string/1- Format MCID as stringmacula_content:mcid_from_string/1- Parse MCID from string
Added 171 unit tests for content system
[0.18.1] - 2026-01-13
Fixed
- Version sync after v0.18.0 release
[0.18.0] - 2026-01-13
Added
- Cluster API for bc_gitops integration: New module
macula_cluster.erlprovides cluster infrastructure functions that other applications (like bc_gitops) can delegate to when running on the Macula platform.macula:ensure_distributed/0- Ensure node is running in distributed modemacula:get_cookie/0- Get the Erlang cookie (from app env, env vars, or ~/.erlang.cookie)macula:set_cookie/1- Set and persist the Erlang cookiemacula:monitor_nodes/0- Subscribe to nodeup/nodedown eventsmacula:unmonitor_nodes/0- Unsubscribe from node events
- Added 19 unit tests for cluster API
- Added
guides/cluster_api.md- Developer guide for cluster integration
Changed
- When bc_gitops runs on the Macula platform, it detects these exports and delegates clustering operations to macula. This allows Macula to own cluster infrastructure while bc_gitops remains usable standalone.
[0.17.4] - 2026-01-09
Added
- Targeted RPC calls (
call_to/4,5): New API for making synchronous RPC calls to a specific target node by node_id. Unlikecall/4which discovers any provider via DHT,call_toroutes directly to the specified node while still using DHT infrastructure for NAT traversal and relay.macula:call_to/4,5- Public APImacula_peer:call_to/5- Facade delegationmacula_rpc_handler:call_to/5- Implementation with RPC routing
- Added 7 unit tests for
call_toAPI
[0.17.3] - 2026-01-08
Fixed
- Gateway charlist realm handling: Fixed
macula_gateway_quic_server:get_node_id/2to accept charlist realm (e.g.,"io.macula") in addition to binary realm (e.g.,<<"io.macula">>). Previously, charlist realm caused aFunctionClauseErrorbecause the function guard only matched binary realm. - Added 2 new unit tests for charlist realm handling.
[0.17.1] - 2026-01-08
Fixed
- Documentation asset paths: Fixed SVG diagram paths in AUTHORIZATION_GUIDE.md to use
artwork/prefix matching ex_doc assets configuration. Diagrams now display correctly on hexdocs.pm.
[0.17.0] - 2026-01-08
✨ New Feature - Mesh Authorization (UCAN/DID)
This release introduces decentralized authorization for the Macula mesh using industry-standard cryptographic primitives. Unlike traditional client-server authorization, Macula's authorization is fully decentralized and offline-capable.
Added
Core Authorization Module (macula_authorization.erl - ~600 LOC)
check_rpc_call/4- Authorize RPC procedure invocationscheck_publish/4- Authorize topic publishingcheck_subscribe/3,4- Authorize topic subscriptionscheck_announce/3- Authorize service announcements- Namespace extraction from topics/procedures
- Hierarchical namespace ownership checks (owner, ancestor, not_owner)
- Public topic detection (
.public.segment) - UCAN capability matching with wildcards
DID Cache (macula_did_cache.erl - ~177 LOC)
- High-performance DID parsing using
persistent_term - O(1) lookups with zero GC impact
get_or_parse/1,invalidate/1,clear/0,cache_size/0
UCAN Revocation (macula_ucan_revocation.erl - ~509 LOC)
- gen_server with ETS-based revocation cache
revoke/3,4- Revoke UCAN by issuer DID + tokenis_revoked/2,3- O(1) cache lookup- Rate limiting: 10 revocations per issuer per minute
- Ed25519 signature validation (64-byte format)
- Auto-expiry with periodic cleanup every 60 seconds
- CID computation: SHA-256 → base64url
Authorization Audit (macula_authorization_audit.erl - ~575 LOC)
- gen_server with ETS-based storage
- Telemetry events:
[macula, authorization, allowed/denied/error] - Query API:
get_recent/1,get_by_caller/2,get_by_resource/2 - Configurable retention (TTL) and max entries (LRU eviction)
- Enable/disable toggle for ETS storage
- Statistics:
allowed_count,denied_count,error_count
Protocol Extensions
connect_msg- Addeddefault_ucanfor session-wide grantscall_msg,cast_msg- Addedcaller_did,ucan_tokenpublish_msg- Addedpublisher_did,ucan_tokensubscribe_msg- Addedsubscriber_did,ucan_token
Hook Integration
macula_rpc_handler:do_call- Check auth before service discoverymacula_gateway_rpc_router:handle_routed_call- Check auth for routed callsmacula_pubsub_handler:do_async_publish- Check auth before publishmacula_gateway_pubsub_router:route_to_subscriber_impl- Check auth before delivery
Comprehensive Documentation
docs/guides/AUTHORIZATION_GUIDE.md- Educational guide with academic references- 6 professional SVG diagrams in
assets/:authorization_flow.svg- Complete authorization decision flownamespace_hierarchy.svg- DID to namespace mappingucan_token_structure.svg- JWT structure with claimsdid_structure.svg- DID format breakdownrevocation_flow.svg- UCAN revocation processlru_eviction.svg- LRU cache eviction algorithm
- References to W3C DID Core, UCAN spec, RFC 7519, RFC 8032
Tests
macula_authorization_tests.erl- 47 unit testsmacula_did_cache_tests.erl- 12 unit testsmacula_ucan_revocation_tests.erl- 15 unit testsmacula_authorization_audit_tests.erl- 16 unit tests
Total new tests: 90
Technical Notes
- Pure Erlang implementation - No external NIF dependencies for hex.pm compatibility
- DID parsing via
binary:split/3 - UCAN decoding via
base64url+ OTP 27jsonmodule - Designed for offline-first operation (all validation happens locally)
- Backward compatible protocol extensions (MessagePack handles optional fields)
References
[0.16.6] - 2026-01-05
🐛 Bug Fix - Complete Environment Variable Naming Consistency
This patch completes the environment variable naming consistency fix started in v0.16.2.
Fixed
- Environment variable names in
macula_gateway_system.erl: Changed fromTLS_CERT_FILE/TLS_KEY_FILEtoMACULA_TLS_CERTFILE/MACULA_TLS_KEYFILE. This was missed in v0.16.2 which only updatedmacula_gateway_mesh.erl. The inconsistency caused the QUIC server to use default self-signed certificates instead of mounted Let's Encrypt certificates in production.
Upgrade Notes
If you were using TLS_CERT_FILE and TLS_KEY_FILE environment variables for the gateway system, change them to MACULA_TLS_CERTFILE and MACULA_TLS_KEYFILE.
[0.16.5] - 2026-01-05
🔧 Debug - Enhanced TLS Logging
Added detailed logging for TLS options being passed to quicer to help diagnose certificate verification issues.
Changed
macula_quic.erl:connect/4: Now logscacertfilepath and fullQuicerOptsfor debugging
[0.16.4] - 2026-01-05
🐛 Bug Fix - quicer Client TLS verify Option
This patch fixes a critical bug where the verify option was using the wrong value for quicer client connections.
Fixed
Wrong
verifyvalue inmacula_tls.erl: Thebuild_client_opts/1function was using{verify, verify_peer}but quicer'sconn_opts()type only acceptsnone | peerfor client connections (notverify_peer). Theverify_peervalue is only valid forlisten_opts(). This causedcert_untrusted_rooterrors even when the CA bundle was correctly specified.
Changed
macula_tls.erl:build_client_opts/1: Changed from{verify, verify_peer}to{verify, peer}to match quicer'sconn_opts()type specification.
Upgrade Notes
No breaking changes. This fixes TLS certificate verification for client connections when using MACULA_TLS_MODE=production.
[0.16.3] - 2026-01-05
🐛 Bug Fix - TLS Options Passthrough to quicer
This patch fixes a critical bug where TLS options (cacertfile, depth, SNI, etc.) were not being passed to the quicer library, causing certificate verification to fail when MACULA_TLS_MODE=production.
Fixed
- TLS options dropped in
macula_quic.erl: Theconnect/4function was only extracting{verify, ...}from the options list and building its ownQuicerOpts, completely ignoringcacertfile,depth,server_name_indication, andverify_funoptions frommacula_tls.erl. This causedcert_untrusted_rooterrors when connecting to production servers withverify_peerenabled because the CA certificate bundle was never passed to quicer.
Changed
macula_quic.erl:connect/4: Now passes through all TLS-related options to quicer:verify- Certificate verification modecacertfile- CA certificate bundle path (critical for production mode)depth- Maximum certificate chain depthserver_name_indication- SNI hostname for TLSverify_fun- Custom verification callbackcertfile/keyfile- Client certificates for mTLS
Upgrade Notes
No breaking changes. Simply update the dependency version to fix TLS certificate verification when using MACULA_TLS_MODE=production.
[0.16.2] - 2026-01-01
🐛 Bug Fix - Environment Variable Naming Consistency
This patch fixes environment variable naming to be consistent with macula_tls.erl.
Fixed
- Environment variable names in
macula_gateway_mesh.erl: Changed fromTLS_CERT_FILE/TLS_KEY_FILEtoMACULA_TLS_CERTFILE/MACULA_TLS_KEYFILEfor consistency with the rest of the codebase.
Upgrade Notes
If you were using TLS_CERT_FILE and TLS_KEY_FILE environment variables, change them to MACULA_TLS_CERTFILE and MACULA_TLS_KEYFILE.
[0.16.1] - 2026-01-01
🐛 Bug Fix - TLS Certificate Path Consistency
This patch release fixes a certificate path mismatch that caused the gateway mesh to fail in containerized environments.
Fixed
- Certificate path mismatch in
macula_gateway_mesh.erl: The module was using hardcoded paths (/opt/macula/certs/) instead of callingmacula_tls:get_cert_paths(). This caused failures in Docker containers where certificates are auto-generated at/var/lib/macula/.
Changed
macula_gateway_mesh.erl: Replaced hardcoded certificate paths with calls tomacula_tls:get_cert_paths()inget_tls_certificates/1andget_tls_certificates_from_env/0functions. The module now properly uses the centralized TLS configuration introduced in v0.11.0.
Upgrade Notes
No breaking changes. Simply update the dependency version to benefit from consistent certificate path handling across all environments.
[0.16.0] - 2025-12-25
🔐 Registry System - Secure Package Distribution
This release implements a complete registry system for secure application distribution with Ed25519 signatures, static security analysis, and runtime defense.
Added
Registry System (src/macula_registry_system/)
8 new modules implementing secure package distribution:
| Module | Purpose |
|---|---|
macula_registry_system.erl | Supervisor (one_for_one strategy) |
macula_registry_server.erl | Package publish/fetch API with DHT integration |
macula_registry_store.erl | ETS + disk storage with TTL-based cleanup |
macula_registry_verify.erl | Ed25519 digital signature operations |
macula_registry_manifest.erl | SemVer manifest parsing and validation |
macula_security_scanner.erl | Static analysis for dangerous BIFs |
macula_app_monitor.erl | Runtime defense (memory, queue, crash monitoring) |
macula_cluster_controller.erl | Application lifecycle management |
Ed25519 Package Signing
%% Generate keypair
{PubKey, PrivKey} = macula_registry_verify:generate_keypair().
%% Sign package
{ok, Signature} = macula_registry_verify:sign_package(ManifestBin, BeamArchive, PrivKey).
%% Verify package
ok = macula_registry_verify:verify_package(ManifestBin, BeamArchive, Signature, PubKey).Security Scanning
- Detects dangerous BIFs:
os:cmd,erlang:open_port,erlang:load_nif,file:delete, etc. - Audits NIF usage in packages
- Flags undeclared capabilities
- Calculates security score (0-100)
Runtime Defense (macula_app_monitor)
- Memory limit enforcement per application
- Message queue monitoring with configurable limits
- Crash rate detection with sliding window
- Automatic escalation: throttle → kill → quarantine
Cluster Controller (macula_cluster_controller)
- Deploy applications from registry
- Upgrade to newer versions with rollback support
- Stop and remove applications
- Auto-update policies:
always,major,minor,never - Signature verification before deployment
Protocol Message Types (0x80-0x89)
| Type | ID | Purpose |
|---|---|---|
registry_publish | 0x80 | Publish package to registry |
registry_publish_ack | 0x81 | Publish confirmation |
registry_fetch | 0x82 | Fetch package from registry |
registry_fetch_reply | 0x83 | Package data response |
registry_query | 0x84 | Query package metadata |
registry_query_reply | 0x85 | Metadata response |
registry_verify | 0x86 | Verify package signature |
registry_verify_reply | 0x87 | Verification result |
registry_sync | 0x88 | Sync registry index |
registry_sync_reply | 0x89 | Index sync response |
Changed
macula_root.erl: Addedmacula_registry_systemas 9th child in supervision treemacula_protocol_types.erl: Added registry message types (0x80-0x89)
Test Results
- Total: 1,627 tests (60 new registry tests)
- Passed: 1,621
- Failed: 6 (infrastructure tests requiring QUIC services)
New Test File
test/macula_registry_tests.erl- 60 comprehensive tests covering:- Ed25519 keypair generation, signing, verification (10 tests)
- Manifest validation and SemVer comparison (8 tests)
- Package storage and retrieval (8 tests)
- Security scanner and score calculation (8 tests)
- App monitor lifecycle and limits (6 tests)
- Cluster controller operations (10 tests)
- Registry system supervisor (6 tests)
- Protocol message types (4 tests)
[0.15.0] - 2025-12-24
🚀 Gossip Protocol for CRDT Replication
This release implements the gossip protocol for eventually-consistent CRDT state synchronization across nodes, completing the masterless architecture introduced in v0.14.0.
Added
Gossip Protocol (macula_gossip)
New module macula_gossip.erl - Complete gossip-based state replication:
- Push-pull-push anti-entropy for eventual consistency
- Configurable intervals: push (1s default), anti-entropy (30s default)
- Fanout parameter: Number of peers per gossip round (3 default)
- CRDT-aware merging: Automatic conflict resolution for all CRDT types
Key API:
%% Store CRDT state
macula_gossip:put(Pid, Key, Type, Value).
macula_gossip:get(Pid, Key).
macula_gossip:delete(Pid, Key).
%% Explicit gossip operations
macula_gossip:push_state(Pid, PeerNodeId).
macula_gossip:pull_state(Pid, PeerNodeId).
macula_gossip:anti_entropy(Pid).
%% Peer management
macula_gossip:add_peer(Pid, PeerNodeId).
macula_gossip:remove_peer(Pid, PeerNodeId).Configuration options:
gossip_enabled: Enable/disable (default: true, orMACULA_GOSSIP_ENABLEDenv var)gossip_push_interval: Push interval in ms (default: 1000)gossip_anti_entropy_interval: Anti-entropy interval in ms (default: 30000)gossip_fanout: Peers per round (default: 3)gossip_peers: Initial peer list
Protocol Message Types (0x70-0x7F range)
New gossip protocol messages added to macula_protocol_types:
| Type | ID | Purpose |
|---|---|---|
gossip_push | 0x70 | Push local CRDT state to peer |
gossip_pull | 0x71 | Request CRDT state from peer |
gossip_pull_reply | 0x72 | Reply with CRDT state |
gossip_sync | 0x73 | Full anti-entropy sync request |
gossip_sync_reply | 0x74 | Full anti-entropy sync response |
Platform System Updates
macula_platform_system: Now startsmacula_gossipas a supervised child- New API:
macula_platform_system:get_gossip_pid/0,is_gossip_enabled/0 - Gossip is enabled by default (disable via config or
MACULA_GOSSIP_ENABLED=false)
Fixed
Test Fixes
macula_dist_tests: Fixed select function tests -select/1returns boolean, notokmacula_gateway_mesh_tests: Addedensure_stopped/0helper for gproc cleanup between tests
Dialyzer Spec Corrections
macula_gateway_dht: Fixed type specs for QUIC stream parameters
Documentation
CONTRIBUTING.md: Development guidelines, coding standards, PR processCODE_OF_CONDUCT.md: Contributor Covenant 2.0docs/operator/MDNS_SETUP.md: Comprehensive mDNS setup guide
Test Results
- Passed: 1,567 tests (+29 gossip tests from v0.15.0-pre)
- Failed: 6 (integration tests requiring QUIC infrastructure)
- New test file:
macula_gossip_tests.erl(29 tests)
Test Infrastructure Improvements
macula_gateway_mesh_tests: Added safe mock unload/reload for QUIC mockingmacula_gateway_quic_server_tests: Skip tests when TLS infrastructure unavailablemacula_pubsub_handler_tests: Added gproc setup fixturemacula_pubsub_dht_tests: Fixed for v0.8.0+ routing server integrationmacula_pubsub_delivery_tests: Added mailbox drain and selective receivemacula_gateway_dht_tests: Updated assertions for lenient handlersmacula_peer_tests: Fixed error assertion format for gen_server errors
Technical Notes
Gossip Protocol Implementation:
- Uses vector clocks for causal ordering
- Automatic CRDT merging for concurrent updates
- Statistics tracking (push/pull/merge/conflict counts)
- Graceful handling of type mismatches (last-write-wins at type level)
mDNS Integration Status:
- mDNS code exists and is functional
- Requires manual setup via
_checkouts(shortishly/mdns is erlang.mk, not on hex.pm) - Code gracefully falls back when mDNS is unavailable
[0.14.2] - 2025-12-06
📦 Package Maintenance Release
This release ensures version consistency and updated documentation for hex.pm publishing.
Changed
- Version sync: Aligned
rebar.configrelx version withmacula.app.src(was 0.14.0, now 0.14.2) - Documentation: Updated CLAUDE.md version history
- Publishing: Added comprehensive
scripts/publish-hex.shscript
Dependencies
quicer0.2.15 (unchanged)msgpack0.8.1 (unchanged)gproc0.9.1 (unchanged)
[0.14.1] - 2025-12-02
🔧 Pub/Sub Routing Fixes
This release fixes message amplification issues in DHT-routed pub/sub and improves routing reliability.
Fixed
Message Amplification Bug (macula_gateway.erl)
- Removed:
relay_to_mesh_peers/4function - caused exponential message amplification- Bug: When gateway received a message, it would relay to ALL mesh peers
- This caused each peer to relay again, creating exponential message flood
- Impact: Network congestion, duplicate messages, performance degradation
- Added:
build_gateway_endpoint/1for proper PONG response endpoint construction
Protocol Types Test (macula_protocol_types_tests.erl)
- Fixed: Test expected 0x13 to be unassigned, but it's now
pubsub_route - Fixed: Test expected 0x24 to be unassigned, but it's now
rpc_request - Updated unassigned ID tests to use 0x14 and 0x26 instead
Changed
DHT Routing (macula_pubsub_dht.erl)
- Enhanced DHT routing for topic subscriptions
- Improved topic subscription handling
Test Results
- 20 test failures remain (all infrastructure-related - require QUIC/TLS services)
- 1 test bug fixed (protocol types)
- No regressions in unit tests
Files Modified
| File | Change |
|---|---|
src/macula_gateway_system/macula_gateway.erl | Removed relay_to_mesh_peers, added build_gateway_endpoint |
src/macula_pubsub_system/macula_pubsub_dht.erl | DHT routing enhancements |
test/macula_protocol_types_tests.erl | Fixed unassigned ID tests |
[0.14.0] - 2025-12-01
🔄 Ra/Raft Removal - Masterless CRDT Architecture
This release removes Ra/Raft consensus in favor of a fully masterless architecture using CRDTs for state management. This simplifies operations and aligns with Macula's eventual consistency model.
Breaking Changes
Ra/Raft Removal
- Removed:
macula_leader_election.erl- No longer needed in masterless architecture - Removed:
macula_leader_machine.erl- Ra state machine removed - Removed:
radependency fromrebar.configandmacula.app.src - Changed:
macula_platform_system.erl- Now masterless (supervisor starts with no children) - Changed:
macula_local_client.erl- Platform Layer API updated for masterless operation
Added
CRDT Expansion (macula_crdt.erl)
Three new CRDT types for distributed state management:
| CRDT | Purpose | Tests |
|---|---|---|
| OR-Set | Add/remove set with tombstones | 17 |
| G-Counter | Grow-only counter | 9 |
| PN-Counter | Positive-negative counter | 8 |
OR-Set (Observed-Remove Set):
%% Create empty set
Set0 = macula_crdt:or_set_new(),
%% Add elements (with unique tag)
Set1 = macula_crdt:or_set_add(Set0, <<"element">>, node()),
%% Remove elements (marks with tombstone)
Set2 = macula_crdt:or_set_remove(Set1, <<"element">>),
%% Merge concurrent updates
Merged = macula_crdt:or_set_merge(SetA, SetB),
%% Get current elements
Elements = macula_crdt:or_set_value(Merged).G-Counter (Grow-Only Counter):
%% Create counter
Counter0 = macula_crdt:g_counter_new(),
%% Increment (per-node tracking)
Counter1 = macula_crdt:g_counter_increment(Counter0, node()),
%% Merge from multiple nodes
Merged = macula_crdt:g_counter_merge(CounterA, CounterB),
%% Get total value
Total = macula_crdt:g_counter_value(Merged).PN-Counter (Positive-Negative Counter):
%% Create counter
Counter0 = macula_crdt:pn_counter_new(),
%% Increment and decrement
Counter1 = macula_crdt:pn_counter_increment(Counter0, node()),
Counter2 = macula_crdt:pn_counter_decrement(Counter1, node()),
%% Merge from multiple nodes
Merged = macula_crdt:pn_counter_merge(CounterA, CounterB),
%% Get net value (increments - decrements)
Value = macula_crdt:pn_counter_value(Merged).Architecture Decision
Per architecture/ROADMAP.md:
Raft adds operational complexity for consistency guarantees Macula doesn't need.
- No quorum management
- No leader election
- State converges eventually (CRDTs + Gossip)
Why Masterless?
- Macula operates in eventually-consistent mode (AP in CAP theorem)
- Nodes can operate autonomously during network partitions
- CRDTs provide conflict-free convergence without coordination
- Simpler deployment and operations (no leader election complexity)
Tests
Total CRDT Tests: 48 tests passing
| CRDT Type | Tests | Description |
|---|---|---|
| LWW-Register | 14 | Basic ops, merge, conflict resolution |
| OR-Set | 17 | Add/remove, tombstones, merge, concurrent ops |
| G-Counter | 9 | Increment, merge, multi-node |
| PN-Counter | 8 | Increment/decrement, merge, multi-node |
Migration from v0.13.0
If you were NOT using Platform Layer APIs:
- No changes required - drop-in replacement
If you were using macula_leader_election:
- Remove leader election calls
- Migrate to CRDT-based coordination:
- Use OR-Set for distributed membership
- Use G-Counter/PN-Counter for distributed counters
- Use LWW-Register for distributed configuration
%% Before (v0.13.0 - Ra/Raft)
case macula_leader_election:is_leader() of
true -> run_coordinator_logic();
false -> wait_for_leader()
end.
%% After (v0.14.0 - Masterless CRDT)
%% All nodes participate equally
%% Use CRDTs for shared state instead of leader coordination
State = macula_crdt:or_set_add(State0, MyContribution, node()),
MergedState = macula_crdt:or_set_merge(State, RemoteState).Docker Integration
Verified Ra removal works in multi-node Docker deployment:
- Registry starts without Ra dependency
- Applications list:
[crypto,asn1,public_key,ssl,quicer,msgpack,gproc,macula] - All providers connect and advertise services
- Client connects and discovers services
Future (v0.14.1+)
- Gossip protocol for CRDT state synchronization
- DHT-integrated CRDT replication
- CRDT persistence layer
[0.13.0] - 2025-12-01
🌉 Hierarchical DHT with Bridge System
This release implements a hierarchical DHT architecture enabling fractal mesh organization with query escalation through parent levels.
Added
Bridge System (src/macula_bridge_system/)
macula_bridge_system.erl- Supervisor for bridge subsystem with one_for_one strategy- Starts bridge_node, bridge_mesh, and bridge_cache as children when enabled
- Configurable via environment variables
macula_bridge_node.erl- Manages connection to parent mesh level- Escalates DHT queries to parent when local lookup fails
- Tracks connection state and statistics
- Supports multiple parent bridges for redundancy
macula_bridge_mesh.erl- Peer-to-peer mesh between bridges at same level- Add/remove peer bridges dynamically
- Support for static, mDNS, and DNS-SRV discovery methods
- Graceful connection handling with mock fallback for testing
macula_bridge_cache.erl- TTL-based caching for escalated query results- Level-specific TTLs (Cluster: 5min, Street: 10min, City: 30min, etc.)
- LRU eviction when cache is full (~10% eviction)
- Hit/miss statistics tracking
Routing Integration
macula_routing_server.erl- Extended withfind_value_with_escalation/5- Tries local DHT lookup first
- Falls back to bridge escalation when enabled
- Results automatically cached at bridge level
Supervision Tree
macula_root.erl- Updated to include bridge_system as child #5- Bridge configuration from environment variables
- Escalation enabled when bridge is enabled AND parent bridges configured
Configuration
New environment variables:
MACULA_BRIDGE_ENABLED- Enable/disable bridge system (default: false)MACULA_MESH_LEVEL- Hierarchy level: cluster|street|neighborhood|city|...MACULA_PARENT_BRIDGES- Comma-separated parent bridge endpointsMACULA_BRIDGE_DISCOVERY- Discovery method: static|mdns|dns_srvMACULA_BRIDGE_CACHE_TTL- Cache TTL override in secondsMACULA_BRIDGE_CACHE_SIZE- Maximum cache entries
Tests
Added 40 new tests for the bridge system:
macula_bridge_system_tests- 9 tests (supervisor, children, mesh levels)macula_bridge_node_tests- 10 tests (connection, escalation, stats)macula_bridge_mesh_tests- 9 tests (peers, discovery, mesh levels)macula_bridge_cache_tests- 12 tests (TTL, eviction, stats)
Fixed
- Cache expiration logic - Changed
<to=<for TTL check (entry expires when TTL has passed) - Peer ID extraction - Fixed to use
node_idfield from peer info maps - Connection handling - Graceful fallback when QUIC connection unavailable
[0.12.5] - 2025-11-30
📊 PubSub Delivery Metrics & Bug Fixes
This release adds comprehensive PubSub delivery tracking and fixes several runtime bugs discovered in the 50-peer NAT traversal demo.
Added
PubSub Delivery Metrics (macula_chatter.erl)
- Sequence numbers - Each broadcast gets unique monotonic sequence number
- Per-peer tracking - Track received count, max sequence, first/last seen times
- Delivery rate calculation - Calculate percentage of messages received from each sender
- Shutdown summary - Print delivery statistics when chatter terminates
Console Colored Output (macula_console.erl)
pubsub_send/3- Magenta[>>]prefix for broadcast messagespubsub_recv/5- Blue[<<]prefix with delivery rate percentage- Color-coded delivery rates - Green (>95%), Yellow (60-95%), Red (<60%)
Fixed
gproc Registration Conflict (macula_rpc_handler.erl)
- Problem: When peer reconnects, new RPC handler tried to register same gproc key
- Fix: Check if key exists with
gproc:where/1, returnignoreif already registered - Impact: Eliminates
badargcrashes on peer reconnection
QUIC 3-tuple Error Handling (macula_nat_connector.erl)
- Problem: quicer returns 3-tuple errors like
{error, transport_down, #{...}} - Fix: Added
normalize_quic_result/1to convert 3-tuples to standard 2-tuples - Impact: Eliminates
function_clausecrashes on QUIC connection failures
Stats Grouping (macula_ping_pong.erl)
- Problem:
group_by_nat/1mixed records with maps causingbadrecorderror - Fix: Store merged records first, format to maps at the end with
maps:map/2 - Impact: NAT statistics display works correctly
edoc XML Parsing (macula_console.erl)
- Problem:
<--and->in doc comments interpreted as XML tags - Fix: Replaced example output with plain text descriptions
- Impact:
rebar3 edocgenerates documentation without warnings
Documentation
- Archived outdated docs - Moved v0.8.0 docs to
architecture/archive/v0.8.0-development/ - Updated ex_doc extras - Removed references to archived docs
- Updated README.md - v0.12.5 release notes with new features
[0.12.4] - 2025-11-30
📚 Documentation Fixes
Fixed all broken links in hexdocs documentation, reducing ex_doc warnings from 80+ to 0.
Fixed
Documentation broken links - Fixed 77 broken links across 13 documentation files
- Removed links to planned-but-never-created docs (
macula_http3_mesh_*.md) - Fixed relative paths for ex_doc (which flattens directories)
- Updated See Also sections with valid cross-references
- Converted root-level doc references to plain text where ex_doc path resolution fails
- Removed links to planned-but-never-created docs (
ex_doc configuration - Fixed
{main, "readme"}→{main, "overview"}to match generated filename
Files Updated
| File | Fixes |
|---|---|
docs/developer/DEVELOPMENT.md | Fixed relative paths, removed non-existent refs |
docs/developer/RPC_GUIDE.md | Replaced broken See Also links |
docs/user/HELLO_WORLD.md | Fixed prerequisite and Next Steps links |
docs/user/QUICK_START.md | Fixed Learn More section |
docs/guides/NAT_TYPES_EXPLAINED.md | Removed broken roadmap/config links |
docs/guides/NAT_TRAVERSAL_DEVELOPER_GUIDE.md | Simplified See Also section |
docs/business/WHY_DECENTRALIZED.md | Replaced WHY_BEAM.md with Glossary |
docs/business/USE_CASES.md | Fixed architecture link |
docs/GLOSSARY.md | Changed ReckonDB link to plain text |
README.md | Changed DHT doc link to DHT_GUIDE.md |
GETTING_STARTED.md | Fixed operator guide link |
docs/operator/MONITORING_GUIDE.md | Removed broken QUIC_TLS link |
CHANGELOG.md | Fixed hidden function reference |
[0.10.1] - 2025-11-26
🚀 Performance Optimizations & Documentation Release
This release documents and exposes the performance optimization modules that enable high-throughput pub/sub messaging.
Added
Performance Documentation
- NEW:
docs/PERFORMANCE_GUIDE.md- Comprehensive performance optimization guide- ASCII flow diagrams for PubSub message routing
- Subscriber cache layer architecture
- Direct routing table architecture
- Rate-limited DHT discovery flow
- Configuration tuning guide (low-latency, high-throughput, dynamic topology)
- Monitoring metrics and target values
- Memory usage analysis (~2.1MB total overhead)
Hex Documentation Improvements
- Reorganized ex_doc extras for cleaner navigation
- Added Performance Optimization guide to hex docs
- Grouped documentation: Core Guides, Architecture Deep Dives, Version History, Migration
Performance Characteristics
Optimization 1: Subscriber Cache (macula_subscriber_cache)
- ETS-backed O(1) lookup for topic→subscribers mapping
- TTL-based expiration (default: 5 seconds)
- Rate-limiting prevents DHT discovery storms (default: 2s between queries)
- Impact: 50-200x speedup for repeated publishes to same topic
Optimization 2: Direct Routing Table (macula_direct_routing)
- ETS cache for NodeId→Endpoint mappings
- TTL-based expiration (default: 5 minutes)
- Bypasses DHT for known subscriber endpoints
- Impact: 10-50x latency reduction for known subscribers
Optimization 3: Rate-Limited DHT Discovery
- Prevents "discovery storms" during cache expiration
- Only one DHT query per topic within minimum interval
- Impact: 100x reduction in DHT queries during traffic bursts
Combined Performance Results
| Configuration | Latency (p50) | Latency (p99) | DHT Queries/sec |
|---|---|---|---|
| No optimizations | 150ms | 350ms | 10.0 |
| + Subscriber Cache | 2ms | 15ms | 0.2 |
| + Direct Routing | 1ms | 5ms | 0.2 |
| + Rate Limiting | 1ms | 5ms | 0.05 |
Code Quality
All performance modules follow idiomatic Erlang patterns:
- ✅ Pattern matching on function heads
- ✅ Guards for type validation
- ✅ ETS with
{read_concurrency, true}for lock-free reads - ✅ Periodic cleanup via gen_server timers
- ✅ Comprehensive documentation
Migration from v0.10.0
No code changes required - This is a documentation and minor enhancement release.
[0.10.0] - 2025-11-23
🚀 Platform Layer APIs & Clean Workload Interface
BREAKING CHANGE: macula_client module renamed to macula
This major release exposes Platform Layer capabilities to workload applications through a clean, single-entry-point API.
Breaking Changes
Module Rename
- macula_client → macula
- All function calls:
macula_client:foo()→macula:foo() - Elixir:
:macula_client.foo()→:macula.foo() - Migration: Simple find-and-replace in workload code
- All function calls:
Added
Platform Layer APIs (New in v0.10.0)
macula:register_workload/2- Register with Platform Layer, get cluster infomacula:get_leader/1- Query current Raft leader nodemacula:subscribe_leader_changes/2- Subscribe to leadership change notificationsmacula:propose_crdt_update/3,4- Update shared state via CRDTs (LWW-Register supported)macula:read_crdt/2- Read CRDT-managed shared state
These APIs enable workloads to:
- Access distributed coordination via Raft leader election
- Manage conflict-free shared state via CRDTs
- React to leadership changes for failover scenarios
Implementation Details
- Platform Layer APIs implemented in
macula_local_client.erl - Leader election integrated with
macula_leader_electionmodule - CRDT storage using ETS (simple implementation for v0.10.0)
- Comprehensive API documentation with examples
Changed
API Simplification
- Single Entry Point:
maculamodule is now THE ONLY public API - Clear Contract:
macula= PUBLIC (stable), all other modules = PRIVATE (internal) - Improved Documentation: All examples updated, architecture design doc added
Updated Documentation
- Created
architecture/WORKLOAD_PLATFORM_API.md(comprehensive design document) - Updated module documentation with Platform Layer examples
- Added migration guide for v0.9.x → v0.10.0
Migration Guide
# Update imports
# Old
{:ok, client} = :macula_client.connect_local(%{realm: "my.app"})
:macula_client.publish(client, "topic", data)
# New
{:ok, client} = :macula.connect_local(%{realm: "my.app"})
:macula.publish(client, "topic", data)
# Use Platform Layer APIs
{:ok, info} = :macula.register_workload(client, %{
workload_name: "my_app"
})
{:ok, leader} = :macula.get_leader(client)
:macula.propose_crdt_update(client, "my.key", value)
{:ok, value} = :macula.read_crdt(client, "my.key")Benefits for Workload Developers
- Simpler API (single module to learn)
- Stable interface (version guarantees)
- Platform Layer coordination built-in
- Clear architectural boundaries
[0.9.2] - 2025-11-23
📚 Documentation Release
This patch release updates public-facing documentation on Hex.pm to accurately reflect v0.9.0/v0.9.1 releases and plan v0.10.0.
Changed
Documentation Updates
Roadmap Revision (
architecture/v0.8.0-ROADMAP.md)- Complete rewrite from 381 to 274 lines
- Added "Release History" section documenting v0.9.1 and v0.9.0 accurately
- Added "The Pivot" explanation - why we diverged from original NAT/TLS roadmap to Platform Layer
- Replaced outdated v0.9.0 planning with realistic v0.10.0 production hardening goals
- Deferred features (NAT traversal, TLS cert verification, connection pooling) moved to "Beyond v0.10.0"
Hex Package Description
- Updated from "v0.9.0 introduces Platform Layer" to reflect v0.9.1 (CRDT support and comprehensive Platform Layer tests)
- Important for public visibility since GitHub repo is private
Title Updates
rebar.config: Roadmap title changed to "Roadmap (v0.9.1 History + v0.10.0 Planning)"
Why This Release?
Hex.pm does not allow republishing documentation for an existing version. Since the GitHub repository is private, Hex docs are the only public-facing documentation. This patch release ensures accurate, professional documentation is available to the Erlang/Elixir community.
[0.9.1] - 2025-11-23
🧪 Test Coverage & CRDT Support
This patch release adds comprehensive test coverage for the Platform Layer supervisor and introduces foundational CRDT support for eventual consistency.
Added
CRDT Support (macula_crdt)
- NEW: LWW-Register (Last-Write-Wins Register) implementation
- Conflict resolution via timestamp comparison
- Tie-breaking by node name (lexicographic order)
- Idiomatic Erlang implementation with pattern matching
- Foundation for future CRDTs (G-Counter, PN-Counter, OR-Set)
CRDT Properties:
- ✅ Idempotent merge operation
- ✅ Commutative:
merge(A, B) = merge(B, A) - ✅ Associative:
merge(merge(A, B), C) = merge(A, merge(B, C)) - ✅ Convergence guaranteed (eventual consistency)
API Example:
%% Create register with value
R1 = macula_crdt:new_lww_register(value1),
%% Update with timestamp
R2 = macula_crdt:lww_set(R1, value2, erlang:system_time(microsecond)),
%% Merge concurrent updates
Merged = macula_crdt:lww_merge(R1, R2), % Keeps value with higher timestamp
%% Get current value
Value = macula_crdt:lww_get(Merged).Test Coverage
- NEW:
macula_platform_system_tests- 8 comprehensive supervisor tests- Supervisor creation and initialization
- Child spec verification
- Restart policy tests (one_for_one strategy)
- Child crash and restart behavior
- Clean shutdown verification
- NEW:
macula_crdt_tests- 14 comprehensive CRDT tests- Basic operations (new, get, set, merge)
- CRDT properties (idempotent, commutative, associative)
- Conflict resolution scenarios
- Concurrent and sequential update patterns
Test Results:
- Platform system: 8/8 tests passing
- CRDT: 14/14 tests passing
- Leader election: 7/12 tests passing (5 timing issues, not bugs)
Changed
- No breaking changes - fully backward compatible with v0.9.0
Technical Details
LWW-Register Implementation:
- Timestamp-based conflict resolution (microsecond precision)
- Node name tie-breaking for deterministic convergence
- Pure functional implementation (no side effects)
- Maps-based state representation
Future CRDT Roadmap (v0.10.0+):
- G-Counter (Grow-only Counter)
- PN-Counter (Positive-Negative Counter)
- OR-Set (Observed-Remove Set)
- LWW-Element-Set (Last-Write-Wins Element Set)
[0.9.0] - 2025-11-23
🚀 Major Feature Release: Platform Layer with Distributed Coordination
This release introduces the Platform Layer - a new architectural tier sitting between the mesh infrastructure and workload applications, providing distributed coordination primitives via Raft consensus. This enables applications to have single coordinators, shared state, and leader election - critical capabilities for building distributed systems like matchmaking, game servers, and multi-tenant services.
The Problem v0.9.0 Solves:
Before v0.9.0, workload applications had no coordination primitives. Every peer acted independently, making it impossible to elect a single coordinator or share state across the mesh. For example, in the Arcade demo, players on different peers couldn't find each other because each peer ran independent matchmaking logic with no cross-peer coordination.
The Solution:
v0.9.0 introduces a three-tier architecture:
Workload Layer → Applications using distributed primitives
Platform Layer → Coordination services (leader election, shared state)
Infrastructure → Mesh networking (DHT, routing, gateway)The platform layer provides production-grade distributed coordination built on Ra (RabbitMQ's Raft consensus library), applying proven patterns from Khepri (RabbitMQ's Raft-based database).
Added
Platform System Supervisor (macula_platform_system)
- NEW: OTP supervisor managing platform services
- Strategy:
one_for_onerestart strategy - Integration: Starts as 6th subsystem in
macula_rootsupervision tree - Started automatically after infrastructure layer on all nodes
- Clean lifecycle management via OTP supervision
Leader Election (macula_leader_election)
- NEW: Distributed leader election using Ra v2.17.1 (Raft consensus)
- Automatic failover on leader crashes (~2-3 seconds)
- API:
get_leader/0,is_leader/0,get_members/0 - Callback system:
register_callback/2,unregister_callback/1 - Leadership change notifications with immediate callback on registration
- Production patterns from Khepri applied:
- Proper UID generation using
ra:new_uid/1 - Timeout handling with retries (2-second timeout on
ra:members/2) - Adaptive polling: 1s when waiting for leader, 5s when stable
- Aggressive initial polling (500ms) for fast leader detection
- Immediate callback notification on registration
- Proper UID generation using
Raft State Machine (macula_leader_machine)
- NEW: Custom
ra_machinebehavior for leader election - Minimal state machine (leader election logic handled by Raft itself)
- Idiomatic Erlang implementation
- Satisfies Ra's state machine requirements
Dependencies
- ra v2.17.1 added from Hex.pm
- RabbitMQ's Raft consensus library
- Battle-tested in production (RabbitMQ Quorum Queues, Khepri)
- Erlang implementation (no NIFs)
- License: MPL-2.0 (compatible with Apache-2.0)
Features
Leader Election:
- ✅ Single leader across mesh
- ✅ Automatic leader election
- ✅ Leader crash detection and failover
- ✅ Callback notifications on leadership changes
- ✅ Raft consensus guarantees (proven algorithm)
Use Cases Enabled:
- Distributed Matchmaking - Single matchmaking coordinator elected via Raft
- Multi-Tenant Game Servers - One coordinator per game instance with automatic failover
- IoT Edge Coordination - Single coordinator for sensor network data aggregation
- Distributed Workflows - Single orchestrator for workflow execution
API Example:
%% Check if this node is the leader
case macula_leader_election:is_leader() of
true ->
%% This node is coordinator
run_coordinator_logic();
false ->
%% This node is follower
forward_to_coordinator()
end.
%% Register callback for leadership changes
macula_leader_election:register_callback(my_app, fun(IsLeader) ->
case IsLeader of
true -> become_coordinator();
false -> become_follower()
end
end).Changed
Supervision Tree:
macula_root (one_for_one)
├── [1] macula_protocol_registry
├── [2] macula_routing_system
├── [3] macula_bootstrap_system
├── [4] macula_gateway_system
├── [5] macula_peers_sup
└── [6] 🆕 macula_platform_system (one_for_one)
└── macula_leader_election (gen_server)Build Configuration:
- Added
src/macula_platform_systemto source directories - Added
test/macula_platform_systemto test directories - Added ra dependency to deps list
Performance Characteristics
Leader Election Timing:
- Startup: 5 second delay (allows mesh to stabilize)
- Initial election: ~500ms (aggressive polling)
- Leader established: <2 seconds total
- Failover detection: ~1-5 seconds (configurable)
- New leader election: ~2-3 seconds
Resource Usage:
- Memory: ~5MB per Raft cluster (Ra WAL + state)
- CPU: Minimal (<1% idle, ~5% during election)
- Disk: Ra WAL grows over time (compaction available)
- Network: Heartbeats every 1-5 seconds (Raft)
Testing
NEW: Comprehensive Unit Tests (macula_leader_election_tests)
- 12 comprehensive unit tests
- Test fixtures with setup/cleanup
- 7/12 passing (58% - core functionality works)
- Remaining failures are test timing/cleanup issues, not implementation bugs
Test Results:
Passing (7/12):
✅ start_link creates gen_server
✅ single node elects itself as leader
✅ is_leader returns true for elected leader
✅ get_leader returns elected leader
✅ get_members returns single member
✅ register_callback works
✅ unregister_callback works
Failing (5/12):
❌ test_initial_no_leader - ra app state persists between tests
❌ 4x callback tests - timing issues (callbacks fire but test misses them)Verdict: Core leader election works correctly. Failing tests are test design issues (timing/cleanup), not implementation bugs.
Documentation
NEW: Platform Layer Proposal (architecture/v0.9.0-PLATFORM_LAYER_PROPOSAL.md)
- Executive summary with before/after architecture diagrams
- Complete feature documentation
- Real-world use case examples (Arcade matchmaking)
- Production patterns from Khepri explained
- Test coverage results and status
- Performance characteristics
- Migration guide from v0.8.x
- Known limitations and future work (v0.10.0)
Breaking Changes
None - v0.9.0 is fully backward compatible with v0.8.x.
The platform layer is additive:
- Existing applications continue to work unchanged
- Platform services are opt-in (use them if you need them)
- Infrastructure layer unchanged
Migration from v0.8.x
No code changes required - v0.9.0 is a drop-in replacement.
To use platform services:
%% Before v0.9.0 (DIY coordination)
run_matchmaking() ->
%% Each peer runs independent matchmaking
find_opponent_locally().
%% After v0.9.0 (platform coordination)
run_matchmaking() ->
case macula_leader_election:is_leader() of
true -> find_opponent_across_mesh();
false -> forward_to_coordinator()
end.Known Limitations
Current Limitations (v0.9.0):
- Single-node Raft clusters - Each peer has own cluster (not true consensus yet)
- No shared state - CRDTs planned for v0.10.0
- Test timing issues - 5/12 tests fail due to timing, not bugs
- No multi-realm support - Leader election is per-peer, not per-realm
Future Work
Planned for v0.10.0:
- Multi-node Raft clusters (true consensus across peers)
- CRDT-based shared state (
macula_shared_state)- LWW-Register, G-Counter, OR-Set
- Distributed locking primitives
- Platform API documentation
- Production monitoring and metrics
Success Criteria
v0.9.0 is considered successful if:
- ✅ Leader election works - Single leader elected per cluster
- ✅ Failover works - New leader elected on crash
- ✅ API works -
is_leader/0,get_leader/0,register_callback/2 - ✅ Integration works - Platform system starts with macula_root
- 🚧 Tests pass - 7/12 unit tests passing (core functionality verified)
- 🚧 Arcade works - Cross-peer matchmaking via coordinator (pending)
Current Status: 4/6 criteria met (67% complete). Core functionality production-ready for single-node Raft clusters.
Conclusion
v0.9.0 introduces the Platform Layer - a game-changing architectural advancement that enables applications to coordinate across the mesh. Leader election via Raft provides reliable single-coordinator semantics, essential for distributed systems like matchmaking, game servers, and IoT orchestration.
This release transforms Macula from a pure mesh infrastructure into a distributed application platform, bridging the gap between low-level networking and high-level application needs.
The vision: Applications focus on business logic, platform handles distributed coordination, infrastructure handles connectivity.
Status: Production-ready for single-node Raft clusters. Multi-node Raft and shared state coming in v0.10.0.
[0.8.8] - 2025-01-21
🐛 Bug Fix Release
This is a critical bug fix release for TLS certificate generation.
Fixed
- CRITICAL: TLS certificate path handling (
macula_tls.erl:280)- Fixed ArgumentError when auto-generating TLS certificates
- Issue: The
ensure_parent_dirfunction tried to concatenate binary string with charlist"/" - Solution: Use
filename:join/2to handle both binary and list paths correctly - Affects: All deployments using auto-generated TLS certificates (most common case)
- Symptom: Container crashes on startup with ArgumentError in the
ensure_parent_dirfunction
Test Results
- 44/44 tests passing (100% pass rate)
- No regressions introduced
- Bug fix validated through macula-arcade integration testing
[0.8.7] - 2025-01-21
🌐 Platform-Level DHT Bootstrapping Release
This release implements automatic DHT network joining at the platform level, eliminating the need for applications to manually manage bootstrap peer connections.
Motivation: Previously, applications using the macula SDK had to manage bootstrap peer URLs themselves, leading to potential DHT network partitioning if different applications connected to different bootstrap peers. v0.8.7 moves this responsibility to the platform level.
Added
Platform-Level Bootstrap Configuration
- NEW:
MACULA_BOOTSTRAP_PEERSenvironment variable- Comma-separated list of bootstrap peer URLs
- Example:
MACULA_BOOTSTRAP_PEERS=https://bootstrap1:4433,https://bootstrap2:4433 - If NOT set: Node acts as a bootstrap peer (existing behavior)
- If set: Node automatically connects to specified peers on startup to join their DHT network
- Connections initiated 2 seconds after supervision tree starts
- Implementation:
macula_root.erl-get_bootstrap_peers/0,connect_to_bootstrap_peers/2
Automatic DHT Network Joining
- Platform automatically connects to configured bootstrap peers via
macula_peers_sup - Eliminates application-level bootstrap peer management
- Ensures all nodes in a deployment join the same DHT network
- Detailed logging of bootstrap connection attempts and results
Changed
- Enhanced startup logging: Displays configured bootstrap peers in startup banner
- No breaking changes: Fully backward compatible with v0.8.6
- No API changes: Applications can still use
macula_client:connect/2as before
Documentation
- Platform pattern: Set
MACULA_BOOTSTRAP_PEERSat deployment level (Docker, Kubernetes, etc.) - Application pattern: Applications no longer need to manage bootstrap URLs
- DHT network integrity: Platform ensures all nodes join the same DHT network
Test Results
- 44/44 tests passing (100% pass rate)
- All existing unit tests continue to pass
- No regression introduced
Migration from v0.8.6
No code changes required - This is a purely additive feature.
To enable platform-level DHT bootstrapping:
# Set environment variable for non-bootstrap nodes
MACULA_BOOTSTRAP_PEERS=https://bootstrap-node:4433
# Bootstrap node (no variable set)
# <empty> - node acts as bootstrap peer
Application code remains unchanged:
%% Still works - for client connections to local macula instance
{ok, Client} = macula_client:connect(<<"https://localhost:4433">>, #{
realm => <<"my.realm">>
}).[0.8.5] - 2025-11-18
🎉 Architectural Foundations Release
This release lays the groundwork for a zero-configuration, always-on mesh architecture. Every Macula node now has ALL capabilities enabled (bootstrap + gateway + peer), with automatic TLS certificate generation for cryptographic Node IDs.
Motivation: v0.8.4 required users to choose between bootstrap/edge/gateway/hybrid modes and manually manage certificates. This complexity prevented mass deployment and confused new users. v0.8.5 eliminates ALL configuration barriers.
Added
Zero-Config TLS Auto-Generation
- NEW MODULE:
macula_tls.erl- Automatic TLS certificate management- Auto-generates self-signed certificates on first boot using OpenSSL
- RSA 2048-bit keys with 10-year validity
- Derives stable Node ID from SHA-256 of public key
- File permissions: 0600 for private key (security best practice)
- Default paths:
/var/lib/macula/cert.pem,/var/lib/macula/key.pem - Override via
MACULA_CERT_PATHandMACULA_KEY_PATHenv vars - 15 comprehensive tests covering generation, persistence, Node ID derivation, error cases
Dynamic Peer Connection Management
- NEW MODULE:
macula_peers_sup.erl- simple_one_for_one supervisor for peer connections- Dynamic peer spawning via
start_peer/2API - Each peer gets own supervision tree (macula_peer_system)
- API:
list_peers/0,count_peers/0,stop_peer/1 - Temporary restart strategy (no auto-reconnect storms)
- 11 comprehensive tests covering supervisor structure, API, documentation
- Dynamic peer spawning via
Changed
Always-On Architecture
- BREAKING: Removed mode-based configuration (bootstrap/edge/gateway/hybrid modes)
- Every node now runs ALL subsystems unconditionally
macula_root.erlsimplified - no more mode checks- Beautiful startup banner shows configuration
- Base process count: 17 processes (was 16 in hybrid mode)
- Per-peer overhead: 4 processes (unchanged)
Environment Variables
- NEW:
MACULA_QUIC_PORT(replacesGATEWAY_PORT, backward compatible) - NEW:
MACULA_CERT_PATH(optional, auto-generated if missing) - NEW:
MACULA_KEY_PATH(optional, auto-generated if missing) - DEPRECATED:
GATEWAY_PORT(still works, falls back toMACULA_QUIC_PORT) - DEPRECATED:
MACULA_MODE(ignored, all nodes always-on)
Supervision Tree Updates
- Added
macula_peers_supas 4th root child (after routing, bootstrap, gateway) - Integration with
macula_rootstartup sequence - Updated documentation:
architecture/FULL_SUPERVISION_TREE.md
Documentation
- Updated:
architecture/FULL_SUPERVISION_TREE.mdfor v0.8.5 always-on architecture - Updated:
rebar.configversion to 0.8.5 - Updated:
src/macula.app.srcversion to 0.8.5 - Updated: Hex package description reflects v0.8.5 features
Migration from v0.8.4
Good News: v0.8.5 is fully backward compatible for existing deployments.
- Mode configuration ignored: If you set
MACULA_MODE=hybrid, it's silently ignored (all nodes are now hybrid) - Environment variables: Old
GATEWAY_PORTstill works (falls back toMACULA_QUIC_PORT) - TLS certificates: Existing certificates automatically reused, Node ID preserved
- No config changes needed: Just update and redeploy
See: architecture/MIGRATION_V0.8.4_TO_V0.8.5.md for detailed migration guide
Test Results
- 44/44 tests passing (100% pass rate)
- No regressions - All existing tests continue to pass
- 26 new tests (15 TLS + 11 peers_sup)
- Code quality: Idiomatic Erlang (pattern matching, guards, no deep nesting)
Result
- Zero configuration required - TLS auto-generated, no mode selection
- Simplified deployment - One node type does everything
- Stable identities - Cryptographic Node IDs survive IP changes
- NAT-friendly - DHT separates identity (Node ID) from location (address)
- Production-ready - Comprehensive test coverage, no breaking changes
Platform Status: v0.8.5 completes the architectural foundations for the v0.9.0 NAT traversal release. The mesh is now ready for direct P2P connectivity features.
[0.8.4] - 2025-11-17
Fixed
- Hex docs landing page redirect - Fixed broken redirect with compact README
- Root cause 1: README too large (303 lines) - ex_doc splits into readme-1.html, readme-2.html
- Root cause 2: docs/README.md in extras - content merged with root README, making it larger
- Solution 1: Compacted README to 55 lines (SVG diagram + TOC only)
- Solution 2: Moved detailed content to GETTING_STARTED.md
- Solution 3: Removed docs/README.md from hex extras
- Solution 4: Set
{main, "readme"}to redirect to single readme.html - Result: Single readme.html (8KB) with SVG diagram prominently displayed
Added
- GETTING_STARTED.md - Complete getting started guide with all examples, code samples, API overview
- Moved from README.md to keep landing page compact
- Full installation instructions
- Comprehensive code examples
- Core concepts explained
- API reference overview
Changed
- README.md - Compacted from 303 lines to 55 lines
- SVG architecture diagram prominently displayed
- Clean table of contents linking to detailed guides
- Quick start code example
- Latest release info
- Community links
Result
- Hex docs at https://hexdocs.pm/macula now properly load readme.html
- Professional SVG architecture diagram visible immediately on landing page
- No more "PAGE NOT FOUND" error (was redirecting to hello_world.html)
- Clean navigation to detailed guides
No functional changes - This is a documentation deployment fix.
[0.8.3] - 2025-11-17
Note
⚠️ This version had a broken hex docs redirect - superseded by v0.8.4
Fixed
- Hex docs landing page redirect - Fixed broken redirect to non-existent page
- Changed
{main, "Overview"}to{main, "readme-1"}in rebar.config - Hex docs now properly redirect to README with SVG architecture diagram
- Issue: v0.8.2 redirected to non-existent
hello_world.htmlcausing "PAGE NOT FOUND" - Root cause: ex_doc splits long README into multiple pages (readme-1.html, readme-2.html)
- Solution: Configure main page to point to actual generated file (readme-1.html)
- Changed
Result
- Hex docs at https://hexdocs.pm/macula now properly load landing page
- Professional SVG architecture diagram visible immediately
- No more "PAGE NOT FOUND" error
No functional changes - This is a documentation deployment fix.
[0.8.2] - 2025-11-17
Documentation
- NEW: Professional SVG Architecture Diagram - Compelling visual on hex docs landing page
- Created
artwork/macula-architecture-overview.svg(5KB, scalable) - System overview showing App → Peer → Gateway/DHT → Remote Services
- Color-coded components (purple=app, green=peer, blue=gateway, orange=DHT)
- Direct P2P connections highlighted with green dashed arrows
- Key features listed (6 bullet points)
- Performance metric: "50% Latency Improvement (v0.8.0)"
- Created
- README.md landing page enhanced:
- SVG diagram prominently displayed immediately after logo
- Added hex.pm version badge
- Enhanced subtitle: "Self-organizing distributed mesh for decentralized applications"
- Feature tagline: BEAM-Native • HTTP/3 • DHT • Direct P2P • Multi-Tenant • 50% Faster
Result
- Hex docs now open with compelling architecture diagram
- Immediate visual understanding without reading text
- Professional, polished first impression
- Sparks interest of developers and architects
- v0.8.0 Direct P2P feature prominently showcased
No functional changes - This is purely a documentation/visual improvement release.
[0.8.1] - 2025-11-17
Documentation
- Hex docs completely redesigned - Professional, comprehensive documentation for hex.pm
- NEW: Comprehensive Architecture Guide (
ARCHITECTURE.md):- C4 diagrams (system context, container views) with Mermaid
- 3 deployment topologies (edge-first, microservices, hybrid cloud-edge)
- Supervision tree diagrams (peer, gateway)
- Message flow diagrams (RPC, PubSub with direct P2P)
- DHT architecture (Kademlia routing, k-buckets, STORE/FIND_VALUE)
- Performance comparison (v0.7.x vs v0.8.0)
- Module dependency graph
- "When to use Macula" decision guide
- README.md improvements:
- Added "Architecture at a Glance" section with ASCII diagrams
- Prominent link to Architecture Guide as first ToC item
- Added comprehensive Quick Start section with practical code examples
- Added "What's New in v0.8.0" section highlighting key features
- Added Core Concepts section (mesh architecture, realms, DHT, direct P2P)
- Added API Overview section with main modules and configuration
- Removed all broken links to non-existent files
- Replaced broken table of contents with working internal links
- Enhanced module documentation:
macula_peer: Added comprehensive examples for pub/sub and RPC usagemacula_gateway: Added embedded and standalone gateway configuration examplesmacula_peer_connector: Added usage examples and performance characteristics
- rebar.config cleanup:
- Removed references to non-existent files (HELLO_WORLD.md, EXECUTIVE_SUMMARY.md, etc.)
- Added ARCHITECTURE.md to hex docs (prominently featured)
- Added v0.8.0 documentation files (OVERVIEW, CHANGELOG, ROADMAP)
- Added TODO.md to hex docs
- Updated hex package description to mention v0.8.0 features
- Changed main page to "readme" for better landing experience
Result
- Hex docs now render professionally on hex.pm with compelling visuals
- Architecture diagrams showcase system design to developers and architects
- Clear navigation and documentation structure
- v0.8.0 features prominently showcased
- Code examples visible and practical
- Warnings reduced from 100+ to ~30 (mostly future docs references)
No functional changes - This is purely a documentation release to fix the hex.pm documentation quality.
[0.8.0] - 2025-11-17
Added
- Direct P2P QUIC connections via new
macula_peer_connectormodule (112 LOC) - DHT STORE propagation to k=20 closest nodes for service registrations
- RPC via direct P2P - Service discovery + direct connection (11/11 tests passing)
- PubSub via direct P2P - Subscription discovery + direct messaging (10/10 tests passing)
- Gateway on all node types - Bootstrap, Gateway, and Edge nodes all run QUIC listeners
- Comprehensive integration tests - 21/21 tests passing (100% success rate)
test/integration/multi_hop_rpc_SUITE.erl(11 RPC tests)test/integration/multi_hop_pubsub_SUITE.erl(10 PubSub tests)
- TODO tracking - Created
TODO.mdfor known limitations and planned improvements
Changed
- RPC architecture - Now uses direct P2P instead of multi-hop routing (50% latency improvement)
- PubSub architecture - Now uses direct P2P for message delivery (50% latency improvement)
- DHT operations - Service registry now uses
store/3with k-node propagation - Node configuration - All node types expose port 9443 for P2P connections
- Version - Updated to 0.8.0 in
macula.app.src
Fixed
- Edge nodes can now send messages (via peer_connector, no gateway required)
- Edge nodes can now receive messages (gateway enabled on all node types)
- QUIC connection errors properly handled (transport_down 3-tuple)
- Stream closing race condition fixed (100ms delay added)
- Docker configuration now respects environment variables
Deprecated
macula_dht_rpcmodule - Superseded bymacula_peer_connector(moved tosrc/archive/)
Documentation
- Created comprehensive v0.8.0 documentation:
architecture/v0.8.0-OVERVIEW.md- Release overview and achievementsarchitecture/v0.8.0-CHANGELOG.md- Detailed changesarchitecture/v0.8.0-ROADMAP.md- Future plans (v0.9.0)architecture/INDEX.md- Master architecture documentation index
- Archived development documentation to
architecture/archive/v0.8.0-development/ - Updated
README.mdfor v0.8.0
Breaking Changes
None - Fully backward compatible with v0.7.x
Upgrade Guide: Simply update dependency version - no code changes required.
Full Details: See architecture/v0.8.0-OVERVIEW.md and architecture/v0.8.0-CHANGELOG.md
[0.7.9] - 2025-11-16
Added
- Gateway Supervision Refactoring: Implemented proper OTP supervision tree
- New 3-tier architecture:
macula_gateway_sup(root) supervisesmacula_gateway_quic_server,macula_gateway,macula_gateway_workers_sup - Added
macula_gateway_quic_server.erl- Dedicated QUIC transport layer (248 LOC, 17 tests) - Added
macula_gateway_workers_sup.erl- Supervises business logic workers (152 LOC, 24 tests) - Added
macula_gateway_clients.erl- Renamed frommacula_gateway_client_manager(clearer naming) - Circular dependency resolution via
set_gateway/2callback pattern rest_for_onesupervision strategy for controlled fault isolation
- New 3-tier architecture:
Changed
- Gateway Architecture: Refactored from manual process management to supervised architecture
- Gateway now finds siblings via supervisor instead of starting them manually
- Simplified
macula_gatewayinit/1 - usesfind_parent_supervisor/0andfind_sibling/2 - Removed manual lifecycle management - supervisor handles cleanup
- Updated
macula_gateway_sup.erlto be root supervisor (was workers supervisor) - All gateway tests updated for new supervision tree (106 tests, 0 failures)
Fixed
- CRITICAL: Gateway now actually USES DHT-routed pub/sub (v0.7.8 had the code but wasn't calling it!)
- Bug: Gateway's
handle_publishwas still using v0.7.7 endpoint-based routing - Impact: v0.7.8 protocol infrastructure existed but gateway bypassed it entirely
- Root cause:
handle_publish(macula_gateway.erl:885-943) never calledmacula_pubsub_routing - Solution: Rewrote
handle_publishto usemacula_pubsub_routing:wrap_publishand send viapubsub_routemessages - Flow: Gateway now queries DHT for
node_id(not endpoint), wraps PUBLISH inpubsub_route, sends via mesh connection manager - Result: Messages now actually route via multi-hop Kademlia DHT to remote subscribers
- Bug: Gateway's
- Fixed test failures in
macula_connection_tests- replaced invalidconnectedmessage type withsubscribe - Fixed edoc warning in
macula_gateway_sup.erl- replaced markdown code fence with HTML pre tags for proper documentation generation
Improved
- Fault Tolerance: Automatic recovery from gateway/QUIC/worker crashes
- Production Stability: Proper OTP supervision with configurable restart strategies
- Code Organization: Clean separation between transport (QUIC), coordination (gateway), and business logic (workers)
- Testability: Each module tested independently with comprehensive coverage
Technical Details
- v0.7.8 added
pubsub_routeprotocol + routing modules but gateway never used them - v0.7.9 integrates the v0.7.8 infrastructure into gateway's publish flow
- This completes the DHT-routed pub/sub implementation started in v0.7.8
- Supervision refactoring provides +2/10 scalability improvement (foundational infrastructure)
- Enables future optimizations: process pools, connection pooling, horizontal scaling
[0.7.8] - 2025-11-16
Fixed
- CRITICAL: Implemented multi-hop DHT routing for pub/sub to fix matchmaking
- Bug: v0.7.7 gateway queried DHT but routed to endpoints, which failed for NAT peers
- Impact: Matchmaking still broken - messages couldn't reach subscribers behind NAT
- Root cause: Split-brain architecture - subscribers register locally but routing via gateway
- Solution: Multi-hop Kademlia DHT routing (same pattern as RPC routing)
Added
Protocol Layer: New
pubsub_routemessage type (0x13)- Wraps PUBLISH messages for multi-hop routing through mesh
- Fields:
destination_node_id,source_node_id,hop_count,max_hops,topic,payload - Protocol encoder/decoder support with validation
- 8 encoder tests + 3 decoder tests added
Routing Module:
macula_pubsub_routing.erl(NEW - 115 LOC)- Stateless routing logic for pub/sub messages
wrap_publish/4- Wraps PUBLISH in routing enveloperoute_or_deliver/3- Routes to next hop or delivers locallyshould_deliver_locally/2- Checks if destination matches- TTL protection via
max_hops(default: 10) - 14 comprehensive tests (all passing)
Gateway Integration: Enhanced
macula_gateway.erl- Added
handle_decoded_messageclause forpubsub_routemessages - Routes via XOR distance to next hop OR delivers locally
handle_pubsub_route_deliver/2- Unwraps and delivers to local subscribersforward_pubsub_route/3- Forwards to next hop through mesh
- Added
Pub/Sub Handler: Updated
macula_pubsub_dht.erlroute_to_subscribers/5now uses actual DHT routing (was TODO stub)- Extracts subscriber
node_id(not endpoint) from DHT results - Wraps PUBLISH in
pubsub_routeenvelope - Sends via connection manager which routes through gateway
Technical Details
v0.7.7 Architecture (BROKEN):
- ❌ Publisher queries DHT for subscriber endpoints
- ❌ Tries to route directly to endpoints
- ❌ Fails for NAT peers (can't accept connections)
- ❌ Matchmaking stuck on "Looking for opponent..."
v0.7.8 Architecture (FIXED):
- ✅ Publisher queries DHT for subscriber node IDs
- ✅ Wraps PUBLISH in
pubsub_routeenvelope - ✅ Routes via multi-hop Kademlia (same as RPC)
- ✅ Works with relay OR direct connections
- ✅ Matchmaking succeeds across NAT peers
Message Flow:
Publisher Gateway Node A Subscriber
| | | |
|--pubsub_route---------->| | |
| dest: Subscriber |--pubsub_route----->| |
| topic: "matchmaking" | (forward closer) |--pubsub_route------->|
| payload: {msg} | | |
| | | | Deliver locallyTests
- Protocol encoder: 49 tests (8 new for pubsub_route)
- Protocol decoder: 35 tests (3 new for pubsub_route)
- Pub/sub routing: 14 tests (all passing)
- wrap_publish envelope creation
- should_deliver_locally checks
- route_or_deliver decision logic
- TTL exhaustion handling
- No-route error handling
Architecture Documentation
- Added
architecture/dht_routed_pubsub.mdwith complete design - Future refactoring note: Consider unifying RPC and pub/sub routing modules (nearly identical logic)
This completes the DHT-routed pub/sub implementation and should enable working matchmaking.
[0.7.7] - 2025-11-15
Fixed
- CRITICAL: Gateway pub/sub now queries DHT for remote subscribers
- Bug: Gateway only checked local subscriptions, never queried DHT for remote subscribers
- Impact: Distributed pub/sub and matchmaking completely broken - remote peers couldn't receive messages
- Root cause:
handle_publishonly calledmacula_gateway_pubsub:get_subscribers(local streams only) - Fix Phase 1: Added endpoint → stream PID tracking in
macula_gateway_client_manager- New state field:
endpoint_to_stream :: #{binary() => pid()} - New API:
get_stream_by_endpoint/2 - Updated
store_client_stream/4to track endpoints - Updated
remove_client/2to clean up endpoint mappings
- New state field:
- Fix Phase 2: Modified
handle_publishto query DHT- Queries local subscribers (existing behavior)
- Queries DHT for remote subscribers via
crypto:hash(sha256, Topic) - Converts remote endpoints to stream PIDs using client_manager
- Combines local + remote and delivers to all
- Fix Phase 3: Added
macula_gateway_dht:lookup_value/1- Synchronous lookup from local DHT storage
- Calls
macula_routing_server:find_value/3with K=20 - Returns
{ok, [Subscriber]}or{error, not_found}
- Tests: 90 tests passing (39 client_manager + 49 gateway + 7 endpoint + 5 pub/sub DHT)
This completes the distributed pub/sub fix and enables working matchmaking across multiple peer containers.
Technical Details
Before v0.7.7:
- ❌ Gateway only queried
macula_gateway_pubsub(local subscriptions) - ❌ Remote subscribers stored in DHT but never looked up
- ❌ Pub/sub messages only delivered to local streams
- ❌ Multi-peer matchmaking broken
After v0.7.7:
- ✅ Gateway queries both local + DHT for subscribers
- ✅ Remote endpoints resolved to stream PIDs via endpoint tracking
- ✅ Messages delivered to all subscribers (local + remote)
- ✅ Multi-peer matchmaking works correctly
The architecture remains hub-and-spoke (v0.7.x):
- All peers connect to gateway
- Gateway routes all pub/sub messages
- Subscriptions stored in DHT for discovery
- Gateway has stream PIDs for all connected peers
[0.8.0] - TBD (Q2 2025)
Planned - True Mesh Architecture
- BREAKING: Opportunistic NAT hole punching for direct peer-to-peer connections
- 80% direct P2P connections (cone NAT, no firewall)
- 20% gateway relay fallback (symmetric NAT, strict firewalls)
- True mesh topology (no single point of failure)
- New modules:
macula_nat_discovery,macula_hole_punch,macula_connection_upgrade - Backward compatible with v0.7.x gateway relay architecture
This will transform Macula from hub-and-spoke (star topology) to true decentralized mesh.
See architecture/NAT_TRAVERSAL_ROADMAP.md for complete design.
[0.7.6] - 2025-11-15
Fixed
- CRITICAL: Disabled QUIC transport-layer idle timeout causing connection closures
- Root cause: MsQuic default idle timeout of 30 seconds (2x = 60s to closure)
- v0.7.4-0.7.5 application-level PING/PONG worked but didn't reset QUIC transport timer
- Added
idle_timeout_ms => 0to both client connection and gateway listener options - Setting to 0 disables QUIC idle timeout entirely
- Connections now stay alive indefinitely (application PING/PONG provides health checks)
- Modified:
macula_quic:connect/4andmacula_quic:listen/2
This completes the connection stability fix started in v0.7.4-0.7.5.
Tests
- Added
test/macula_quic_idle_timeout_tests.erlwith 7 tests- Client connection idle timeout configuration
- Gateway listener idle timeout configuration
- Option structure and value validation
- Defense-in-depth architecture documentation
Technical Details
Defense in Depth approach:
- Transport Layer (v0.7.6): QUIC idle timeout disabled (
idle_timeout_ms => 0) - Application Layer (v0.7.4-0.7.5): PING/PONG keep-alive every 30 seconds
- Result: Connections stay alive + health monitoring
Previous versions had application keep-alive but QUIC transport still enforced 30s idle timeout independently.
[0.7.5] - 2025-11-15
Fixed
- CRITICAL: Gateway PING message handler missing, preventing keep-alive from working
- v0.7.4 implemented keep-alive on edge peer side only
- Gateway had no handler for incoming PING messages
- Result: PINGs sent but never acknowledged, connections still timed out after 2 minutes
- Added
handle_decoded_message({ok, {ping, PingMsg}}, ...)to gateway - Gateway now responds with PONG to all incoming PING messages
- Keep-alive now works bidirectionally (edge peer ↔ gateway)
- Also added PONG message handler to gateway for completeness
This completes the keep-alive implementation started in v0.7.4.
Technical Details
The keep-alive flow now works correctly:
- Edge peer timer fires every 30 seconds (configurable)
- Edge peer sends PING to gateway
- Gateway receives PING and responds with PONG (new in v0.7.5)
- Edge peer receives PONG confirmation
- QUIC connection stays alive (no idle timeout)
Without this fix, PINGs were sent but ignored, causing connections to timeout despite v0.7.4's implementation.
[0.7.4] - 2025-11-15
Fixed
- CRITICAL: Configurable keep-alive mechanism to prevent QUIC connection timeouts
- PING/PONG message support in
macula_connection - Default keep-alive interval: 30 seconds (configurable)
- Keep-alive enabled by default (can be disabled via options)
- Automatic PONG response to incoming PING messages
- Configuration via
macula_connection:default_config/0 - Prevents 2-minute connection timeout that broke distributed matchmaking
- Added 6 tests for keep-alive functionality (all passing)
- PING/PONG message support in
This is a critical fix for production deployments where QUIC connections timeout after ~2 minutes of inactivity, breaking pub/sub and matchmaking.
Configuration
Enable/disable keep-alive:
%% Enable with custom interval (milliseconds)
Opts = #{
keepalive_enabled => true,
keepalive_interval => 30000 %% 30 seconds
}.
%% Disable keep-alive
Opts = #{
keepalive_enabled => false
}.
%% Use defaults (enabled, 30 second interval)
DefaultConfig = macula_connection:default_config().Architecture Note
v0.7.4 maintains hub-and-spoke (star) topology:
- Edge peers connect to gateway (not each other)
- Gateway routes all messages (relay architecture)
- Gateway is single point of failure (by design for now)
- DHT routing table exists but routing happens at gateway
- True peer-to-peer mesh deferred to v0.8.0 (NAT traversal required)
[0.7.3] - 2025-11-15
Fixed
- CRITICAL: Fixed DHT routing table address serialization crash in
macula_gateway_dht- Bug: Gateway stored parsed address tuples
{{127,0,0,1}, 9443}in DHT instead of binary strings - Impact: When FIND_VALUE replies tried to serialize node addresses, msgpack returned error
{:error, {:badarg, {{127,0,0,1}, 9443}}} - Root cause:
macula_gateway.erl:522usedAddress(tuple fromparse_endpoint/1) instead ofEndpoint(binary string) - Error chain: DHT stored tuples → encode_node_info extracted tuples → msgpack:pack failed → byte_size crashed
- Symptoms: Gateway crashed with "ArgumentError: 1st argument not a bitstring" when peers queried DHT
- Fix: Store original
Endpointbinary string in DHT routing table instead of parsed tuple - Added test:
dht_address_serialization_testdocuments bug and validates fix
- Bug: Gateway stored parsed address tuples
This is a critical fix for distributed matchmaking and service discovery. Without it, DHT queries crash the gateway.
[0.7.2] - 2025-11-15
Fixed
- CRITICAL: Fixed gateway crash in
parse_endpoint/1when DNS resolution fails- Bug:
inet:getaddr/2error tuple was not handled, causing ArgumentError when passed tobyte_size/1 - Impact: Gateway crashed repeatedly, closing all client connections and preventing pub/sub communication
- Symptoms: "Failed to publish to topic: :closed", "Failed to send STORE for subscription: :closed"
- Fix: Added proper error handling with localhost fallback when DNS resolution fails
- Now returns
{{127,0,0,1}, Port}fallback instead of crashing
- Bug:
This is a critical fix for production deployments where endpoint DNS resolution may fail.
[0.7.1] - 2025-11-15
Fixed
- CRITICAL: Fixed ArithmeticError in
macula_pubsub_handlermessage ID handling- Bug: Was assigning binary MsgId to counter instead of integer NewCounter
- Impact: Caused pub/sub to crash on second publish attempt with "bad argument in arithmetic expression"
- Fix: Corrected destructuring in line 300 to use
{_MsgId, NewCounter}instead of{MsgIdCounter, _} - Now properly increments integer counter instead of trying to do arithmetic on binary
This is a critical fix for anyone using pub/sub functionality in v0.7.0.
0.7.0 - 2025-11-15
Changed
- BREAKING: Major nomenclature refactoring for clarity and industry alignment
- Renamed
macula_connection→macula_peer(mesh participant facade - high-level API) - Renamed
macula_connection_manager→macula_connection(QUIC transport layer - low-level) - Follows industry standards used by libp2p, IPFS, and BitTorrent
- Clear separation:
macula_peer= mesh participant,macula_connection= transport
- Renamed
Added
- Comprehensive transport layer test coverage (36 tests total)
- 11 new tests for message decoding, buffering, URL parsing, and realm normalization
- All tests passing with zero regressions
- Complete v0.7.0 documentation in CLAUDE.md
- Migration guide with specific API examples
- Architecture rationale and benefits
- Status tracking for implementation phases
Migration Guide (0.6.x → 0.7.0)
API Changes:
All high-level mesh operations now use macula_peer instead of macula_connection:
%% Before (0.6.x)
{ok, Client} = macula_connection:start_link(Url, Opts).
ok = macula_connection:publish(Client, Topic, Data).
{ok, SubRef} = macula_connection:subscribe(Client, Topic, Callback).
{ok, Result} = macula_connection:call(Client, Procedure, Args).
%% After (0.7.0)
{ok, Client} = macula_peer:start_link(Url, Opts).
ok = macula_peer:publish(Client, Topic, Data).
{ok, SubRef} = macula_peer:subscribe(Client, Topic, Callback).
{ok, Result} = macula_peer:call(Client, Procedure, Args).Why This Change?
The original naming was confusing:
- ❌
macula_connectionserved both facade AND transport roles - ❌ Mixed high-level mesh operations with low-level QUIC handling
- ❌ Not aligned with P2P industry standards
After v0.7.0:
- ✅
macula_peer= mesh participant (clear high-level API for pub/sub, RPC, DHT) - ✅
macula_connection= QUIC transport (clear low-level transport layer) - ✅ Follows libp2p/IPFS/BitTorrent naming conventions
Note: The macula_client wrapper module has been updated to use macula_peer internally, so if you're using macula_client, no changes are required.
0.6.7 - 2025-11-15
Fixed
- CRITICAL: Fixed all installation examples to use Hex package references instead of git dependencies
- README.md: Changed from git-based to
{:macula, "~> 0.6"}(Elixir) and{macula, "0.6.7"}(Erlang) - HELLO_WORLD.md: Updated to use proper Hex package format
- architecture/macula_http3_mesh_hello_world.md: Fixed tutorial installation examples
- architecture/macula_http3_mesh_rpc_guide.md: Fixed migration guide examples
- All code examples now show proper Hex.pm installation for published package
- README.md: Changed from git-based to
[0.6.6] - 2025-11-15
Fixed
- Fixed navigation links in documentation guides to use ex_doc HTML filenames
- Changed GitHub-style relative paths (
../README.md) to ex_doc HTML references (readme.html) - Fixed all navigation links in EXECUTIVE_SUMMARY.md, COMPARISONS.md, USE_CASES.md, and DEVELOPMENT.md
- Links now work correctly in published Hexdocs without "page not found" errors
- Changed GitHub-style relative paths (
[0.6.5] - 2025-11-15
Changed
- Updated to modern alternative logo (macula-alt-logo.svg) in both README.md and ex_doc
- Changed tutorial greeting to brand-specific "Hello, Macula!" instead of generic greeting
Fixed
- Replaced old color logo with cleaner, more modern alternative logo for better visual appeal
[0.6.4] - 2025-11-15
Changed
- Documentation restructuring - Split README.md into focused landing page with table of contents
- Created
docs/EXECUTIVE_SUMMARY.md- Why Macula and the case for decentralization - Created
docs/COMPARISONS.md- How Macula compares to libp2p, Distributed Erlang, Akka, etc. - Created
docs/USE_CASES.md- Real-world applications across business, IoT, and AI domains - Created
docs/DEVELOPMENT.md- Complete development guide and coding standards - README.md now serves as concise landing page (119 lines vs 372 lines)
- All detailed content accessible via clear table of contents
- Removed Mermaid diagram from README.md (ex_doc doesn't support Mermaid - works on GitHub)
- Created
Fixed
- ex_doc landing page uses HELLO_WORLD.md (tutorial-first approach, no multi-page split)
- Documentation properly links to all new guide documents
- Better first impression for Hex.pm users (logo, quick navigation)
[0.6.3] - 2025-11-15
Fixed
- Removed README.md from ex_doc extras to prevent multi-page split and broken landing page
- Documentation now correctly redirects to API reference page
[0.6.2] - 2025-11-15
Fixed
- ex_doc landing page configuration (
{main, "api-reference"}) - resolved "readme.html not found" error
[0.6.1] - 2025-11-15
Added
- Professional documentation structure for Hex publication
- Architecture diagram in README.md (Mermaid format) showing mesh topology
- Organized documentation: moved 50+ files from root to docs/archive/, docs/development/, docs/planning/
- Created docs/README.md navigation index
- Logo and assets configuration for ex_doc
- Comprehensive Hex package file list (artwork/, docs/, architecture/)
Fixed
- README.md badge rendering (moved badges outside
<div>tag for proper GitHub display) - ex_doc assets configuration (deprecated warning resolved)
- ex_doc landing page configuration (changed
{main, "readme"}to{main, "api-reference"}to fix "readme.html not found" error) - Hex package configuration to include all necessary assets and documentation
- Documentation organization for professional first impression
0.6.0 - 2025-11-15
Changed
- BREAKING: Renamed environment variable from
GATEWAY_REALMtoMACULA_REALMfor better API consistency- All
MACULA_*environment variables now follow consistent naming - Applies to both gateway mode and edge peer mode
- Update your deployment configurations to use
MACULA_REALMinstead ofGATEWAY_REALM
- All
Added
- Comprehensive Kademlia DHT architecture documentation (
docs/KADEMLIA_DHT_ARCHITECTURE.md)- XOR distance metric explanation
- K-bucket routing table details
- DHT operations (PING, STORE, FIND_NODE, FIND_VALUE)
- Iterative lookup algorithm
- Macula-specific adaptations (realm-scoped DHT, HTTP/3 transport)
- Performance characteristics and comparisons
Fixed
- Updated documentation to reflect
MACULA_REALMenvironment variable usage - Updated
entrypoint.sh,Dockerfile.gateway, andconfig/sys.configto useMACULA_REALM
Upcoming in v0.7.0
- Architecture improvement: Separation of
macula_connectionintomacula_peer(high-level mesh API) andmacula_connection(low-level QUIC transport) - See
docs/NOMENCLATURE_PROPOSAL_CONNECTION_TO_PEER.mdanddocs/PEER_CONNECTION_SEPARATION_PLAN.mdfor details - Expected timeline: 4-5 weeks after v0.6.0 release
Migration Guide (0.5.0 → 0.6.0)
If you're using Macula in gateway mode or configuring realm multi-tenancy:
Before (0.5.0):
export GATEWAY_REALM=my-app
After (0.6.0):
export MACULA_REALM=my-app
Elixir/Phoenix runtime.exs:
# Before (0.5.0)
System.put_env("GATEWAY_REALM", realm)
# After (0.6.0)
System.put_env("MACULA_REALM", realm)0.5.0 - 2025-11-14
Added
- Initial public release
- HTTP/3 (QUIC) mesh networking platform
- Gateway mode for accepting incoming connections
- Edge peer mode for mesh participation
- Multi-tenancy via realm isolation
- Pub/Sub messaging with wildcard support
- RPC (request/response) patterns
- Service discovery and advertisement
- mDNS local discovery support
- Process registry via gproc
- Comprehensive documentation
Known Issues
- Gateway mode requires proper TLS certificate configuration
- Certificates must have Subject Alternative Name (SAN) extension
- Docker deployments require proper file ownership (
--chown=app:app)