Packages

Typed distributed messaging for Gleam on the BEAM.

Retired package: Deprecated - The project needs to be redesigned around a much smaller and clearer core.

Current section

Files

Jump to
distribute src distribute@crypto.erl
Raw

src/distribute@crypto.erl

-module(distribute@crypto).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/distribute/crypto.gleam").
-export([default_options/1, development_options/1, child_spec_with_options/1, child_spec_with_adapter/2, start_link/1, start_link_with_adapter/2, get_handle_by_name/1, child_spec/0, get_handle/0, shutdown/0, handshake_start/3, handshake_continue/2, secure_context/1, encrypt/2, decrypt/2, rekey/1, health/0, metrics/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Crypto facade - singleton per node.\n"
"\n"
" This module provides the public API for cryptographic operations\n"
" in the distribute cluster. It follows the same facade pattern as\n"
" transport and discovery modules.\n"
"\n"
" ## Architecture\n"
"\n"
" ```\n"
" ┌─────────────────────────────────────────────────────────┐\n"
" │ crypto.gleam │\n"
" │ (Facade/API) │\n"
" └─────────────────────────────────────────────────────────┘\n"
" │\n"
" ▼\n"
" ┌─────────────────────────────────────────────────────────┐\n"
" │ CryptoAdapter │\n"
" │ (Behaviour contract) │\n"
" └─────────────────────────────────────────────────────────┘\n"
" ▲ ▲\n"
" │ │\n"
" ┌─────────────┴────────┐ ┌──────┴───────────┐\n"
" │ noop_adapter │ │ (future: TLS, │\n"
" │ (development) │ │ NaCl, etc.) │\n"
" └──────────────────────┘ └──────────────────┘\n"
" ```\n"
"\n"
" ## Usage\n"
"\n"
" ```gleam\n"
" import distribute/crypto\n"
" import gleam/option.{Some}\n"
"\n"
" // Start crypto layer (typically done in node_builder)\n"
" let assert Ok(handle) = crypto.start_link(crypto.default_options(\"my_crypto\"))\n"
"\n"
" // Initiate handshake with remote node\n"
" let local = \"node_a@host\"\n"
" let remote = \"node_b@host\"\n"
" let assert Ok(result) = crypto.handshake_start(local, remote, option.None)\n"
"\n"
" // Encrypt message\n"
" let assert Some(ctx) = crypto.secure_context(remote)\n"
" let assert Ok(ciphertext) = crypto.encrypt(ctx, <<\"hello\">>)\n"
" ```\n"
"\n"
" ## Lifecycle\n"
"\n"
" 1. Call `start_link()` or use `child_spec()` with a supervisor\n"
" 2. Provider initializes and registers with registry\n"
" 3. Use `handshake_start/3` when connecting to remote nodes\n"
" 4. Use `encrypt/2` and `decrypt/2` for secure messaging\n"
" 5. Call `shutdown()` for graceful cleanup\n"
).
-file("src/distribute/crypto.gleam", 78).
?DOC(" Create default provider options.\n").
-spec default_options(binary()) -> distribute@crypto@types:provider_options().
default_options(Name) ->
distribute@crypto@adapter:default_options(Name).
-file("src/distribute/crypto.gleam", 83).
?DOC(" Create development options with debug logging.\n").
-spec development_options(binary()) -> distribute@crypto@types:provider_options().
development_options(Name) ->
distribute@crypto@adapter:development_options(Name).
-file("src/distribute/crypto.gleam", 101).
?DOC(" Create a child specification with custom options.\n").
-spec child_spec_with_options(distribute@crypto@types:provider_options()) -> gleam@otp@supervision:child_specification(distribute@crypto@types:provider_handle()).
child_spec_with_options(Options) ->
distribute@crypto@noop_adapter:child_spec(Options).
-file("src/distribute/crypto.gleam", 109).
?DOC(
" Create a child specification with a custom adapter.\n"
" Note: For custom adapters, use the adapter's own child_spec function.\n"
).
-spec child_spec_with_adapter(
distribute@crypto@adapter:crypto_adapter(),
distribute@crypto@types:provider_options()
) -> gleam@otp@supervision:child_specification(distribute@crypto@types:provider_handle()).
child_spec_with_adapter(_, Options) ->
distribute@crypto@noop_adapter:child_spec(Options).
-file("src/distribute/crypto.gleam", 325).
?DOC(
" Get the current adapter.\n"
" For now, always returns noop_adapter.\n"
" In future, this will be configurable.\n"
).
-spec get_current_adapter() -> distribute@crypto@adapter:crypto_adapter().
get_current_adapter() ->
distribute@crypto@noop_adapter:new().
-file("src/distribute/crypto.gleam", 302).
-spec store_handle(binary(), distribute@crypto@types:provider_handle()) -> {ok,
nil} |
{error, nil}.
store_handle(Name, Handle) ->
Key = <<"crypto_handle_"/utf8, Name/binary>>,
State = distribute@crypto@types:handle_state(Handle),
crypto_provider_ffi:put_persistent_term(Key, State),
{ok, nil}.
-file("src/distribute/crypto.gleam", 132).
-spec do_start_link(
distribute@crypto@adapter:crypto_adapter(),
distribute@crypto@types:provider_options()
) -> {ok, distribute@crypto@types:provider_handle()} |
{error, distribute@crypto@types:crypto_error()}.
do_start_link(Provider, Options) ->
case (erlang:element(2, Provider))(Options) of
{ok, Handle} ->
_ = store_handle(erlang:element(2, Options), Handle),
{ok, Handle};
{error, Err} ->
{error, Err}
end.
-file("src/distribute/crypto.gleam", 118).
?DOC(" Start the crypto provider with default settings.\n").
-spec start_link(distribute@crypto@types:provider_options()) -> {ok,
distribute@crypto@types:provider_handle()} |
{error, distribute@crypto@types:crypto_error()}.
start_link(Options) ->
do_start_link(distribute@crypto@noop_adapter:new(), Options).
-file("src/distribute/crypto.gleam", 125).
?DOC(" Start the crypto provider with a custom adapter.\n").
-spec start_link_with_adapter(
distribute@crypto@adapter:crypto_adapter(),
distribute@crypto@types:provider_options()
) -> {ok, distribute@crypto@types:provider_handle()} |
{error, distribute@crypto@types:crypto_error()}.
start_link_with_adapter(Provider, Options) ->
do_start_link(Provider, Options).
-file("src/distribute/crypto.gleam", 310).
-spec get_stored_handle(binary()) -> {ok,
distribute@crypto@types:provider_handle()} |
{error, nil}.
get_stored_handle(Name) ->
Key = <<"crypto_handle_"/utf8, Name/binary>>,
case crypto_provider_ffi:get_persistent_term(Key) of
{ok, State} ->
{ok, distribute@crypto@types:new_handle(Name, State)};
{error, _} ->
{error, nil}
end.
-file("src/distribute/crypto.gleam", 294).
?DOC(" Get a handle by name.\n").
-spec get_handle_by_name(binary()) -> {ok,
distribute@crypto@types:provider_handle()} |
{error, nil}.
get_handle_by_name(Name) ->
get_stored_handle(Name).
-file("src/distribute/crypto.gleam", 95).
?DOC(
" Create a child specification for OTP supervision.\n"
"\n"
" Uses the noop adapter by default. For production, you should\n"
" use a proper crypto adapter.\n"
).
-spec child_spec() -> gleam@otp@supervision:child_specification(distribute@crypto@types:provider_handle()).
child_spec() ->
Options = default_options(<<"distribute_crypto"/utf8>>),
distribute@crypto@noop_adapter:child_spec(Options).
-file("src/distribute/crypto.gleam", 289).
?DOC(" Get the current provider handle.\n").
-spec get_handle() -> {ok, distribute@crypto@types:provider_handle()} |
{error, nil}.
get_handle() ->
get_stored_handle(<<"distribute_crypto"/utf8>>).
-file("src/distribute/crypto.gleam", 147).
?DOC(" Shutdown the crypto provider.\n").
-spec shutdown() -> {ok, nil} | {error, distribute@crypto@types:crypto_error()}.
shutdown() ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(3, Adapter))(Handle);
{error, _} ->
{error, not_initialized}
end.
-file("src/distribute/crypto.gleam", 166).
?DOC(
" Start a cryptographic handshake with a remote node.\n"
"\n"
" Returns either:\n"
" - `InProgress(state, message)` - Send message to remote, await response\n"
" - `Established(context)` - Handshake complete (e.g., noop adapter)\n"
).
-spec handshake_start(
binary(),
binary(),
gleam@option:option(distribute@crypto@types:handshake_message())
) -> {ok, distribute@crypto@types:handshake_result()} |
{error, distribute@crypto@types:crypto_error()}.
handshake_start(Local, Remote, Initial) ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(4, Adapter))(Handle, Local, Remote, Initial);
{error, _} ->
{error, not_initialized}
end.
-file("src/distribute/crypto.gleam", 181).
?DOC(" Continue a handshake with a message from the remote node.\n").
-spec handshake_continue(
distribute@crypto@types:handshake_state(),
distribute@crypto@types:handshake_message()
) -> {ok, distribute@crypto@types:handshake_result()} |
{error, distribute@crypto@types:crypto_error()}.
handshake_continue(State, Message) ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(5, Adapter))(Handle, State, Message);
{error, _} ->
{error, not_initialized}
end.
-file("src/distribute/crypto.gleam", 199).
?DOC(" Get the secure context for a node (if handshake completed).\n").
-spec secure_context(binary()) -> gleam@option:option(distribute@crypto@types:secure_context()).
secure_context(Node) ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(6, Adapter))(Handle, Node);
{error, _} ->
none
end.
-file("src/distribute/crypto.gleam", 214).
?DOC(" Encrypt a message using the secure context.\n").
-spec encrypt(distribute@crypto@types:secure_context(), bitstring()) -> {ok,
bitstring()} |
{error, distribute@crypto@types:crypto_error()}.
encrypt(Ctx, Plaintext) ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(7, Adapter))(Handle, Ctx, Plaintext);
{error, _} ->
{error, not_initialized}
end.
-file("src/distribute/crypto.gleam", 228).
?DOC(" Decrypt a message using the secure context.\n").
-spec decrypt(distribute@crypto@types:secure_context(), bitstring()) -> {ok,
bitstring()} |
{error, distribute@crypto@types:crypto_error()}.
decrypt(Ctx, Ciphertext) ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(8, Adapter))(Handle, Ctx, Ciphertext);
{error, _} ->
{error, not_initialized}
end.
-file("src/distribute/crypto.gleam", 248).
?DOC(
" Trigger a rekey operation for a node.\n"
"\n"
" This rotates the session key while maintaining the connection.\n"
).
-spec rekey(binary()) -> {ok, nil} |
{error, distribute@crypto@types:crypto_error()}.
rekey(Node) ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(9, Adapter))(Handle, Node);
{error, _} ->
{error, not_initialized}
end.
-file("src/distribute/crypto.gleam", 263).
?DOC(" Get the health status of the crypto provider.\n").
-spec health() -> distribute@crypto@types:health_status().
health() ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(10, Adapter))(Handle);
{error, _} ->
{down, <<"Not initialized"/utf8>>}
end.
-file("src/distribute/crypto.gleam", 274).
?DOC(" Get metrics from the crypto provider.\n").
-spec metrics() -> distribute@crypto@types:crypto_metrics().
metrics() ->
case get_handle() of
{ok, Handle} ->
Adapter = get_current_adapter(),
(erlang:element(11, Adapter))(Handle);
{error, _} ->
distribute@crypto@types:empty_metrics()
end.