Packages
macula
0.8.24
7.1.0
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_realm.erl
%%%-------------------------------------------------------------------
%%% @doc
%%% Realm management and validation.
%%% Realms provide logical isolation boundaries in the mesh.
%%% Realm names follow reverse DNS notation (e.g., "org.example.mesh").
%%% @end
%%%-------------------------------------------------------------------
-module(macula_realm).
%% API
-export([
id/1,
validate/1,
normalize/1,
equals/2,
namespace/1,
to_binary/1,
from_binary/1
]).
%% Types
-type realm_name() :: binary().
-type realm_id() :: binary(). % 32-byte SHA-256 hash
-export_type([realm_name/0, realm_id/0]).
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Generate deterministic realm ID from name.
%% Uses SHA-256 hash for 256-bit realm IDs.
-spec id(realm_name()) -> realm_id().
id(RealmName) when is_binary(RealmName) ->
crypto:hash(sha256, RealmName).
%% @doc Validate realm name format.
%% Rules:
%% - Reverse DNS notation (org.example.mesh)
%% - Lowercase alphanumeric, dots, hyphens, underscores
%% - No leading/trailing dots
%% - No consecutive dots
-spec validate(realm_name()) -> ok | {error, term()}.
validate(<<>>) ->
{error, empty_realm};
validate(RealmName) when is_binary(RealmName) ->
%% Check for leading/trailing dots
case binary:first(RealmName) of
$. -> {error, invalid_realm};
_ ->
case binary:last(RealmName) of
$. -> {error, invalid_realm};
_ ->
%% Check for double dots and invalid characters
validate_segments(RealmName)
end
end.
%% @doc Normalize realm name (lowercase, trim).
-spec normalize(realm_name()) -> realm_name().
normalize(RealmName) when is_binary(RealmName) ->
%% Trim whitespace
Trimmed = string:trim(RealmName),
%% Convert to lowercase
string:lowercase(Trimmed).
%% @doc Check if two realm names are equal.
-spec equals(realm_name(), realm_name()) -> boolean().
equals(Realm1, Realm2) ->
Realm1 =:= Realm2.
%% @doc Extract namespace (top-level domain) from realm name.
%% Example: "org.example.mesh" -> "org"
-spec namespace(realm_name()) -> binary().
namespace(<<>>) ->
<<>>;
namespace(RealmName) when is_binary(RealmName) ->
case binary:split(RealmName, <<".">>) of
[Namespace | _] -> Namespace
end.
%% @doc Encode realm name to binary.
-spec to_binary(realm_name()) -> binary().
to_binary(RealmName) when is_binary(RealmName) ->
%% Length prefix + realm name
Len = byte_size(RealmName),
<<Len:16, RealmName/binary>>.
%% @doc Decode realm name from binary.
-spec from_binary(binary()) -> {ok, realm_name()} | {error, term()}.
from_binary(<<Len:16, RealmName:Len/binary>>) ->
case validate(RealmName) of
ok -> {ok, RealmName};
{error, Reason} -> {error, Reason}
end;
from_binary(_) ->
{error, invalid_binary}.
%%%===================================================================
%%% Internal Functions
%%%===================================================================
%% @doc Validate realm segments.
-spec validate_segments(binary()) -> ok | {error, invalid_realm}.
validate_segments(RealmName) ->
Segments = binary:split(RealmName, <<".">>, [global]),
case validate_segment_list(Segments) of
ok -> ok;
error -> {error, invalid_realm}
end.
%% @doc Validate list of segments.
-spec validate_segment_list([binary()]) -> ok | error.
validate_segment_list([]) ->
ok;
validate_segment_list([<<>> | _Rest]) ->
%% Empty segment (double dot)
error;
validate_segment_list([Segment | Rest]) ->
case validate_segment_chars(Segment) of
ok -> validate_segment_list(Rest);
error -> error
end.
%% @doc Validate characters in a single segment.
-spec validate_segment_chars(binary()) -> ok | error.
validate_segment_chars(<<>>) ->
ok;
validate_segment_chars(<<Char, Rest/binary>>) ->
case is_valid_char(Char) of
true -> validate_segment_chars(Rest);
false -> error
end.
%% @doc Check if character is valid in realm name.
-spec is_valid_char(byte()) -> boolean().
is_valid_char(Char) when Char >= $a, Char =< $z -> true; % lowercase
is_valid_char(Char) when Char >= $A, Char =< $Z -> true; % uppercase
is_valid_char(Char) when Char >= $0, Char =< $9 -> true; % digits
is_valid_char($-) -> true; % hyphen
is_valid_char($_) -> true; % underscore
is_valid_char(_) -> false.