Packages
macula
4.2.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/peering/macula_bolt4.erl
%% @doc BOLT#4-style error taxonomy for CALL failures (Part 4 §6.1).
%%
%% Adapted from Lightning Network's BOLT#4 onion-failure codes, which
%% have proven in adversarial conditions that a small, specific
%% taxonomy prevents retry loops and enables post-mortem. Every CALL
%% ERROR frame carries one of these 16 codes plus the reporting
%% station's signature so downstream hops cannot forge "not my fault".
%%
%% The retry policy column is advisory — the caller's CALL state
%% machine (Phase 4 §6.2 / §6.4) is the actual decision point. Codes
%% are stable across V2 minor versions; new codes append at the next
%% free integer.
%%
%% Reference: plans/PLAN_MACULA_V2_PART4_LIFECYCLE.md §6.1;
%% plans/PLAN_MACULA_V2_PART6_PROTOCOL.md §13.
-module(macula_bolt4).
-export([
code/1,
name/1,
info/1,
table/0,
is_retryable/1
]).
-export_type([code/0, name/0, retry_policy/0, info/0]).
-type code() :: 16#00..16#0F.
-type name() ::
ok
| unknown_next_peer
| temporary_relay_failure
| relay_disabled
| node_not_found_at_target_relay
| target_realm_refused
| loop_detected
| expiry_too_soon
| upstream_congestion
| invalid_path_header
| crypto_puzzle_invalid
| realm_not_authoritative_here
| tombstoned
| payload_too_large
| signature_invalid
| unknown_error.
-type retry_policy() ::
none
| different_path
| same_path_after_backoff
| application
| crypto_drop
| caller_recompute
| caller_recompute_with_lookup
| caller_extends_deadline
| exponential_backoff
| log_and_caution.
-type info() :: #{
code := code(),
name := name(),
retry := retry_policy()
}.
%%=====================================================================
%% Public API
%%=====================================================================
%% @doc The complete 16-entry table. Order is by code.
-spec table() -> [info()].
table() ->
[
#{code => 16#00, name => ok,
retry => none},
#{code => 16#01, name => unknown_next_peer,
retry => different_path},
#{code => 16#02, name => temporary_relay_failure,
retry => same_path_after_backoff},
#{code => 16#03, name => relay_disabled,
retry => different_path},
#{code => 16#04, name => node_not_found_at_target_relay,
retry => caller_recompute_with_lookup},
#{code => 16#05, name => target_realm_refused,
retry => application},
#{code => 16#06, name => loop_detected,
retry => caller_recompute},
#{code => 16#07, name => expiry_too_soon,
retry => caller_extends_deadline},
#{code => 16#08, name => upstream_congestion,
retry => exponential_backoff},
#{code => 16#09, name => invalid_path_header,
retry => caller_recompute},
#{code => 16#0A, name => crypto_puzzle_invalid,
retry => crypto_drop},
#{code => 16#0B, name => realm_not_authoritative_here,
retry => caller_recompute_with_lookup},
#{code => 16#0C, name => tombstoned,
retry => application},
#{code => 16#0D, name => payload_too_large,
retry => application},
#{code => 16#0E, name => signature_invalid,
retry => crypto_drop},
#{code => 16#0F, name => unknown_error,
retry => log_and_caution}
].
%% @doc Resolve a name to its integer code.
-spec code(name()) -> code().
code(Name) ->
pluck(name, Name, code).
%% @doc Resolve an integer code to its symbolic name.
-spec name(code()) -> name().
name(Code) ->
pluck(code, Code, name).
%% @doc Full info entry for a given name or code.
-spec info(name() | code()) -> info().
info(NameOrCode) ->
case lookup(NameOrCode) of
{ok, Entry} -> Entry;
error -> error({bolt4_unknown, NameOrCode})
end.
%% @doc Whether the spec's recommended retry policy permits a retry
%% at all. `none' (success), `application' (handler-level remedy),
%% and `crypto_drop' (security-critical) are all non-retryable.
-spec is_retryable(name() | code()) -> boolean().
is_retryable(NameOrCode) ->
classify_retry(maps:get(retry, info(NameOrCode))).
%%=====================================================================
%% Internal
%%=====================================================================
-spec pluck(atom(), term(), atom()) -> term().
pluck(LookupField, LookupValue, ResultField) ->
case lookup_by(LookupField, LookupValue) of
{ok, Entry} -> maps:get(ResultField, Entry);
error -> error({bolt4_unknown, LookupField, LookupValue})
end.
-spec lookup(name() | code()) -> {ok, info()} | error.
lookup(N) when is_atom(N) -> lookup_by(name, N);
lookup(N) when is_integer(N) -> lookup_by(code, N).
-spec lookup_by(atom(), term()) -> {ok, info()} | error.
lookup_by(Field, Value) ->
case [E || E <- table(), maps:get(Field, E) =:= Value] of
[E | _] -> {ok, E};
[] -> error
end.
-spec classify_retry(retry_policy()) -> boolean().
classify_retry(none) -> false;
classify_retry(application) -> false;
classify_retry(crypto_drop) -> false;
classify_retry(_) -> true.