Packages
macula
0.8.9
7.0.0
6.0.0
5.2.2
5.2.1
5.2.0
5.1.0
5.0.0
4.8.0
4.7.1
4.7.0
4.6.0
4.5.0
4.4.10
4.4.9
4.4.8
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.1
4.3.0
4.2.9
4.2.8
4.2.7
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.1
4.1.0
4.0.0
3.16.0
3.15.3
3.15.2
3.15.1
3.14.0
3.13.0
3.12.1
3.12.0
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.9.0
3.8.0
3.7.0
3.5.0
3.4.0
3.3.0
3.2.0
3.1.0
3.0.0
2.1.1
2.1.0
2.0.0
1.5.2
1.5.1
1.4.30
1.4.29
1.4.28
1.4.27
1.4.26
1.4.25
1.4.24
1.4.23
1.4.22
1.4.21
1.4.20
1.4.19
1.4.18
1.4.17
1.4.16
1.4.15
1.4.14
1.4.13
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.0
1.1.0
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.48.6
0.48.5
0.48.4
0.48.3
0.48.2
0.48.1
0.48.0
0.47.1
0.47.0
0.46.3
0.46.1
0.46.0
0.45.3
0.45.2
0.45.1
0.45.0
0.44.2
0.44.1
0.44.0
0.43.3
0.43.2
0.43.1
0.43.0
0.42.9
0.42.8
0.42.7
0.42.6
0.42.5
0.42.4
0.42.3
0.42.2
0.42.1
0.42.0
0.41.1
0.41.0
0.40.1
0.40.0
0.39.9
0.39.8
0.39.7
0.39.6
0.39.5
0.39.4
0.39.3
0.39.2
0.39.1
0.39.0
0.38.8
0.38.7
0.38.6
0.38.5
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.7
0.37.6
0.37.5
0.37.4
0.37.3
0.37.2
0.37.1
0.37.0
0.36.6
0.36.5
0.36.4
0.36.3
0.36.2
0.36.1
0.36.0
0.35.4
0.35.3
0.35.2
0.35.1
0.35.0
0.34.1
0.34.0
0.33.1
0.33.0
0.32.5
0.32.4
0.32.3
0.32.2
0.32.1
0.32.0
0.31.9
0.31.8
0.31.7
0.31.6
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.10
0.30.9
0.30.8
0.30.7
0.30.6
0.30.5
0.30.4
0.30.3
0.30.2
0.30.1
0.30.0
0.29.0
0.28.3
0.28.2
0.28.1
0.28.0
0.27.1
0.27.0
0.26.1
0.26.0
0.25.6
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.6
0.24.5
0.24.4
0.24.3
0.24.2
0.24.1
0.24.0
0.23.3
0.23.2
0.23.1
0.23.0
0.22.12
0.22.11
0.22.10
0.22.9
0.22.8
0.22.7
0.22.6
0.22.5
0.22.4
0.22.3
0.22.2
0.22.1
0.22.0
0.21.7
0.21.6
0.21.5
0.21.4
0.21.2
0.21.1
0.21.0
0.20.25
0.20.24
0.20.23
0.20.22
0.20.21
0.20.20
0.20.19
0.20.18
0.20.17
0.20.16
0.20.15
0.20.14
0.20.13
0.20.12
0.20.11
0.20.10
0.20.9
0.20.8
0.20.7
0.20.6
0.20.5
0.20.3
0.20.2
0.20.1
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.12.6
0.12.5
0.12.3
0.11.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.30
0.7.29
0.7.28
0.7.27
0.7.26
0.7.25
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
Macula HTTP/3 Mesh SDK — connect, subscribe, publish, call, advertise
Current section
Files
Jump to
Current section
Files
src/macula_root.erl
%%%-------------------------------------------------------------------
%% @doc Macula Application Root Supervisor.
%%
%% This is the top-level supervisor for the Macula application.
%% It manages all Macula subsystems in an always-on architecture.
%%
%% Supervision Hierarchy (v0.8.5+):
%% <pre>
%% macula_root (this module - application root)
%% ├── macula_routing_server (core DHT infrastructure - always on)
%% ├── macula_bootstrap_system (bootstrap services - always on)
%% │ ├── macula_bootstrap_server
%% │ ├── macula_bootstrap_registry
%% │ └── macula_bootstrap_health
%% ├── macula_gateway_system (gateway services - always on)
%% │ ├── macula_gateway_health
%% │ ├── macula_gateway_diagnostics
%% │ ├── macula_gateway_quic_server
%% │ ├── macula_gateway
%% │ └── macula_gateway_workers_sup
%% └── macula_peers_sup (dynamic peer connections - always on)
%% </pre>
%%
%% Architecture Philosophy (v0.8.5):
%% - All nodes have ALL capabilities (no mode selection)
%% - Zero configuration required (TLS auto-generated)
%% - Simplified deployment (every node is bootstrap + gateway + peer)
%% - True P2P mesh (nodes connect on-demand based on capability discovery)
%%
%% Naming Convention (v0.7.10+):
%% - _root: Application root supervisor (one per application)
%% - _system: Subsystem root supervisors (gateway, peer, bootstrap, etc.)
%% - _sup: Worker supervisors (workers_sup, peers_sup)
%%
%% @end
%%%-------------------------------------------------------------------
-module(macula_root).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%% sup_flags() = #{strategy => strategy(), % optional
%% intensity => non_neg_integer(), % optional
%% period => pos_integer()} % optional
%% child_spec() = #{id => child_id(), % mandatory
%% start => mfargs(), % mandatory
%% restart => restart(), % optional
%% shutdown => shutdown(), % optional
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
io:format("~n"),
io:format("═══════════════════════════════════════════════════════════════~n"),
io:format(" Starting Macula v0.8.5 (Always-On Architecture)~n"),
io:format(" All capabilities enabled: Bootstrap + Gateway + Peer~n"),
io:format("═══════════════════════════════════════════════════════════════~n"),
io:format("~n"),
%% Ensure TLS certificates exist (auto-generate if missing)
{CertPath, KeyPath} = macula_tls:get_cert_paths(),
{ok, _CertPath, _KeyPath, NodeID} = macula_tls:ensure_cert_exists(CertPath, KeyPath),
io:format("✓ TLS Certificate: ~s~n", [CertPath]),
io:format("✓ Private Key: ~s~n", [KeyPath]),
io:format("✓ Node ID: ~s~n", [NodeID]),
io:format("~n"),
%% Get configuration
Port = get_quic_port(),
Realm = get_realm(),
HealthPort = get_health_port(),
HealthInterval = get_bootstrap_health_interval(),
BootstrapPeers = get_bootstrap_peers(),
io:format("Configuration:~n"),
io:format(" QUIC Port: ~p~n", [Port]),
io:format(" Realm: ~s~n", [Realm]),
io:format(" Health Port: ~p~n", [HealthPort]),
io:format(" Bootstrap Health Interval: ~pms~n", [HealthInterval]),
case BootstrapPeers of
[] -> io:format(" Bootstrap Peers: none (this node is a bootstrap peer)~n");
_ -> io:format(" Bootstrap Peers: ~s~n", [string:join([binary_to_list(P) || P <- BootstrapPeers], ", ")])
end,
io:format("~n"),
SupFlags = #{
strategy => one_for_one,
intensity => 10,
period => 5
},
ChildSpecs = [
%% 1. Core DHT routing (always on)
get_routing_server_spec(NodeID),
%% 2. Bootstrap system (always on)
get_bootstrap_system_spec(Realm, HealthInterval),
%% 3. Gateway system (always on)
get_gateway_system_spec(Port, Realm, HealthPort),
%% 4. Peer connections supervisor (always on)
get_peers_sup_spec()
],
io:format("Starting subsystems:~n"),
io:format(" [1/4] Core DHT Routing~n"),
io:format(" [2/4] Bootstrap System~n"),
io:format(" [3/4] Gateway System~n"),
io:format(" [4/4] Peers Supervisor~n"),
io:format("~n"),
%% Schedule bootstrap peer connections after supervision tree is up
case BootstrapPeers of
[] ->
io:format("No bootstrap peers configured - this node is a bootstrap peer~n~n"),
ok;
_ ->
io:format("Will connect to bootstrap peers after startup: ~p~n~n", [BootstrapPeers]),
spawn(fun() -> connect_to_bootstrap_peers(BootstrapPeers, Realm) end)
end,
{ok, {SupFlags, ChildSpecs}}.
%% internal functions
%% @private
%% @doc Get routing server child spec (core DHT infrastructure).
get_routing_server_spec(NodeID) ->
#{
id => macula_routing_server,
start => {macula_routing_server, start_link, [NodeID, get_routing_config()]},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [macula_routing_server]
}.
%% @private
%% @doc Get bootstrap system child spec.
get_bootstrap_system_spec(Realm, HealthInterval) ->
#{
id => macula_bootstrap_system,
start => {macula_bootstrap_system, start_link, [#{
realm => Realm,
health_check_interval => HealthInterval
}]},
restart => permanent,
shutdown => infinity, % supervisor shutdown
type => supervisor,
modules => [macula_bootstrap_system]
}.
%% @private
%% @doc Get gateway system child spec.
get_gateway_system_spec(Port, Realm, HealthPort) ->
#{
id => macula_gateway_system,
start => {macula_gateway_system, start_link, [[
{port, Port},
{realm, Realm},
{health_port, HealthPort}
]]},
restart => permanent,
shutdown => infinity, % supervisor shutdown
type => supervisor,
modules => [macula_gateway_system]
}.
%% @private
%% @doc Get peers supervisor child spec (simple_one_for_one for dynamic peers).
get_peers_sup_spec() ->
#{
id => macula_peers_sup,
start => {macula_peers_sup, start_link, []},
restart => permanent,
shutdown => infinity, % supervisor shutdown
type => supervisor,
modules => [macula_peers_sup]
}.
%% @private
%% @doc Get QUIC port from environment variable or config.
%% Environment variable renamed from GATEWAY_PORT to MACULA_QUIC_PORT (v0.8.5).
get_quic_port() ->
case os:getenv("MACULA_QUIC_PORT") of
false ->
%% Fallback to old GATEWAY_PORT for backward compatibility
case os:getenv("GATEWAY_PORT") of
false ->
application:get_env(macula, quic_port,
application:get_env(macula, gateway_port, 9443));
PortStr ->
list_to_integer(PortStr)
end;
PortStr ->
list_to_integer(PortStr)
end.
%% @private
%% @doc Get realm from environment variable or config.
get_realm() ->
case os:getenv("MACULA_REALM") of
false ->
application:get_env(macula, realm,
application:get_env(macula, gateway_realm, <<"com.example.realm">>));
RealmStr ->
list_to_binary(RealmStr)
end.
%% @private
%% @doc Get health check port from environment variable or config.
get_health_port() ->
case os:getenv("HEALTH_PORT") of
false ->
application:get_env(macula, health_port, 8080);
PortStr ->
list_to_integer(PortStr)
end.
%% @private
%% @doc Get bootstrap health check interval.
get_bootstrap_health_interval() ->
application:get_env(macula, bootstrap_health_interval, 60000).
%% @private
%% @doc Get DHT routing configuration.
get_routing_config() ->
#{
k => application:get_env(macula, dht_k, 20), %% Kademlia K parameter
alpha => application:get_env(macula, dht_alpha, 3) %% Concurrent queries
}.
%% @private
%% @doc Get bootstrap peer URLs from environment variable.
%% Returns list of binary URLs or empty list if not configured.
%% Environment variable format: "https://peer1:4433,https://peer2:4433"
get_bootstrap_peers() ->
case os:getenv("MACULA_BOOTSTRAP_PEERS") of
false ->
[];
PeersStr ->
%% Split comma-separated URLs and convert to binaries
Urls = string:tokens(PeersStr, ","),
[list_to_binary(string:trim(Url)) || Url <- Urls]
end.
%% @private
%% @doc Connect to bootstrap peers to join their DHT network.
%% Waits briefly for supervision tree to stabilize, then initiates connections.
connect_to_bootstrap_peers(Peers, Realm) ->
%% Wait for supervision tree to fully start
timer:sleep(2000),
io:format("[DHT Bootstrap] Connecting to bootstrap peers...~n"),
lists:foreach(fun(PeerUrl) ->
io:format("[DHT Bootstrap] Connecting to: ~s~n", [PeerUrl]),
%% Start a peer connection via macula_peers_sup
case macula_peers_sup:start_peer(PeerUrl, #{realm => Realm}) of
{ok, PeerPid} ->
io:format("[DHT Bootstrap] Successfully connected to ~s (PID: ~p)~n",
[PeerUrl, PeerPid]);
{error, Reason} ->
io:format("[DHT Bootstrap] Failed to connect to ~s: ~p~n",
[PeerUrl, Reason])
end
end, Peers),
io:format("[DHT Bootstrap] Bootstrap peer connections initiated~n"),
ok.