Skip to content

Configuring HyperBEAM

A beginner's guide to node and message configuration


What You'll Learn

By the end of this tutorial, you'll understand:

  1. Options — Hierarchical configuration with local and global settings
  2. Feature Flags — Compile-time features enabled at build time
  3. Private State — Non-serialized data for temporary storage
  4. Paths — Request routing and cryptographic message history

No prior HyperBEAM knowledge required. Basic Erlang helps, but we'll explain as we go.


The Big Picture

HyperBEAM's configuration system flows through multiple layers, from compile-time flags to runtime options to per-message overrides. Understanding this hierarchy is essential for building and operating nodes.

Here's the mental model:

Compile-Time → Node Config → Message Options → Private State
     ↓              ↓              ↓                ↓
  Features      Defaults      Overrides         Temporary

Think of it like a restaurant:

  • Feature Flags = Kitchen equipment (what's installed)
  • Node Config = House rules (default behavior)
  • Message Options = Customer preferences (per-request overrides)
  • Private State = Kitchen notes (internal, never served)

Let's build each piece.


Part 1: Options System

📖 Reference: hb_opts

The options system manages configuration at three levels:

  • Global — Application-wide defaults
  • Local — Per-message overrides in Opts maps
  • Environment — OS environment variable overrides

Getting Configuration Values

%% Get a global option
Port = hb_opts:get(port).
 
%% Get with a default fallback
Gateway = hb_opts:get(gateway, <<"https://arweave.net">>).
 
%% Get with local override
Opts = #{gateway => <<"https://custom.gateway">>},
Gateway = hb_opts:get(gateway, <<"default">>, Opts).
%% => <<"https://custom.gateway">>

Precedence Rules

Local options override global by default:

%% Global setting
application:set_env(hb, mode, production).
 
%% Local override wins
Opts = #{mode => debug},
hb_opts:get(mode, undefined, Opts).
%% => debug

Force global to win with prefer:

%% Prefer global value
Opts = #{mode => debug, prefer => global},
hb_opts:get(mode, undefined, Opts).
%% => production (from global)

Loading Configuration Files

HyperBEAM supports two configuration formats:

Flat Format (.flat):
port: 8734
host: https://ao.computer
gateway: https://arweave.net
http-client: gun
await-inprogress: false
JSON Format (.json):
{
  "port": 8734,
  "host": "https://ao.computer",
  "gateway": "https://arweave.net",
  "http_client": "gun",
  "await_inprogress": false,
  "store": [
    {
      "name": "cache-mainnet/lmdb",
      "store-module": "hb_store_lmdb"
    }
  ]
}

Load either format:

%% Load flat config
{ok, Config} = hb_opts:load("config.flat").
 
%% Load JSON config
{ok, Config} = hb_opts:load("config.json").
 
%% Merge with defaults
NodeOpts = hb_maps:merge(hb_opts:default_message(), Config).

Default Configuration

Get the full default configuration:

Defaults = hb_opts:default_message().
%% Returns: #{
%%   http_client => gun,
%%   scheduling_mode => local_confirmation,
%%   compute_mode => lazy,
%%   gateway => <<"https://arweave.net">>,
%%   bundler_ans104 => <<"https://up.arweave.net:443">>,
%%   priv_key_location => <<"hyperbeam-key.json">>,
%%   ...
%% }

Include environment variable overrides:

Defaults = hb_opts:default_message_with_env().
%% Includes HB_PORT, HB_MODE, HB_PRINT, etc.

Environment Variables

VariableTypeDefaultDescription
HB_KEYstringhyperbeam-key.jsonPrivate key file location
HB_CONFIGstringconfig.flatConfiguration file path
HB_PORTinteger8734HTTP server port
HB_MODEatom-Operating mode (prod/debug)
HB_PRINTstring-Debug print topics

Quick Reference: Options Functions

FunctionWhat it does
hb_opts:get(Key)Get global option
hb_opts:get(Key, Default)Get with fallback
hb_opts:get(Key, Default, Opts)Get with local override
hb_opts:load(Path)Load config from file
hb_opts:default_message()Get all defaults
hb_opts:default_message_with_env()Defaults + env vars

Part 2: Feature Flags

📖 Reference: hb_features

Feature flags are compile-time switches that enable optional functionality. They're set during build and cannot be changed at runtime.

Checking Features

%% Check a specific feature
case hb_features:enabled(http3) of
    true -> start_http3_server();
    false -> start_http2_server()
end.
 
%% Direct function call
true = hb_features:rocksdb().
false = hb_features:http3().

Available Features

FeatureFlagDescription
http3ENABLE_HTTP3HTTP/3 QUIC protocol support
rocksdbENABLE_ROCKSDBRocksDB storage backend
testTESTTest mode (auto-enabled by EUnit)
genesis_wasmENABLE_GENESIS_WASMGenesis WASM module support
eflameENABLE_EFLAMEProfiler support

Listing All Features

Features = hb_features:all().
%% => #{
%%   http3 => false,
%%   rocksdb => true,
%%   test => true,
%%   genesis_wasm => false,
%%   eflame => false
%% }

Enabling Features at Build Time

Via Compiler Flags:
# Single feature
rebar3 compile -D ENABLE_HTTP3
 
# Multiple features
rebar3 compile -D ENABLE_HTTP3 -D ENABLE_ROCKSDB
 
# All optional features
rebar3 compile -D ENABLE_HTTP3 -D ENABLE_ROCKSDB -D ENABLE_GENESIS_WASM -D ENABLE_EFLAME
Via rebar.config:
{erl_opts, [
    {d, 'ENABLE_HTTP3'},
    {d, 'ENABLE_ROCKSDB'},
    {d, 'ENABLE_EFLAME'}
]}.

Feature-Dependent Code

%% Select storage backend based on feature
select_store() ->
    case hb_features:enabled(rocksdb) of
        true -> 
            {hb_store_rocksdb, #{path => "/data/rocksdb"}};
        false ->
            {hb_store_ets, #{}}
    end.
 
%% Conditional profiling
maybe_profile(Fun) ->
    case hb_features:enabled(eflame) of
        true -> eflame:apply(Fun, []);
        false -> Fun()
    end.

Quick Reference: Feature Functions

FunctionWhat it does
hb_features:all()Get all features as map
hb_features:enabled(Feature)Check if feature is on
hb_features:http3()HTTP/3 support
hb_features:rocksdb()RocksDB backend
hb_features:test()Test mode
hb_features:genesis_wasm()Genesis WASM
hb_features:eflame()Profiler support

Part 3: Private State

📖 Reference: hb_private

Private state is data stored in a message's priv field. It is:

  • Not serialized — Never sent over the network
  • Not cached — Excluded from content-addressed storage
  • Temporary — For internal state during execution

Why Private State?

Private state is essential for:

  • Caching expensive computations
  • Storing device execution context
  • Keeping secrets during processing
  • Tracking non-deterministic data

Getting and Setting Private Data

%% Set a private value
Msg1 = #{<<"data">> => <<"public">>},
Msg2 = hb_private:set(Msg1, <<"cache/result">>, 42, #{}).
%% => #{<<"data">> => <<"public">>, <<"priv">> => #{<<"cache">> => #{<<"result">> => 42}}}
 
%% Get a private value
Result = hb_private:get(<<"cache/result">>, Msg2, #{}).
%% => 42
 
%% Get with default
Missing = hb_private:get(<<"missing">>, Msg2, <<"default">>, #{}).
%% => <<"default">>

Deep Path Access

Private state supports AO-Core path resolution:

%% Set deeply nested value
Msg = hb_private:set(#{}, <<"device/state/counter">>, 42, #{}),
 
%% Access via path
Counter = hb_private:get(<<"device/state/counter">>, Msg, #{}).
%% => 42
 
%% Path prefix is auto-removed
Value = hb_private:get(<<"priv/device/state/counter">>, Msg, #{}).
%% => 42 (same result)

Extracting Private Data

Msg = #{
    <<"data">> => <<"public">>,
    <<"priv">> => #{<<"secret">> => <<"hidden">>}
},
 
%% Get the entire private map
Priv = hb_private:from_message(Msg).
%% => #{<<"secret">> => <<"hidden">>}
 
%% No private data returns empty map
Empty = hb_private:from_message(#{<<"data">> => <<"public">>}).
%% => #{}

Merging Private State

When combining messages, private state can be merged:

Msg1 = #{<<"priv">> => #{<<"a">> => 1, <<"shared">> => <<"old">>}},
Msg2 = #{<<"priv">> => #{<<"b">> => 2, <<"shared">> => <<"new">>}},
 
Merged = hb_private:merge(Msg1, Msg2, #{}).
%% Priv => #{<<"a">> => 1, <<"b">> => 2, <<"shared">> => <<"new">>}
%% Msg2 values override Msg1

Cleaning Private Data

Critical: Always remove private data before serialization!

%% Message with secrets
Msg = #{
    <<"data">> => <<"public">>,
    <<"priv">> => #{<<"wallet">> => PrivateKey}
},
 
%% Clean before sending
CleanMsg = hb_private:reset(Msg).
%% => #{<<"data">> => <<"public">>}
 
%% Now safe to serialize
Binary = hb_message:serialize(CleanMsg).

The reset/1 function recursively removes all private fields:

%% Nested private data
Nested = #{
    <<"outer">> => #{
        <<"inner">> => <<"value">>,
        <<"priv">> => <<"hidden">>
    }
},
 
Cleaned = hb_private:reset(Nested).
%% => #{<<"outer">> => #{<<"inner">> => <<"value">>}}

Checking Private Keys

%% Check if a key is private
hb_private:is_private(<<"priv">>).     %% => true
hb_private:is_private(<<"private">>).  %% => true
hb_private:is_private(<<"priv-data">>). %% => true
hb_private:is_private(<<"data">>).     %% => false

Quick Reference: Private Functions

FunctionWhat it does
hb_private:get(Key, Msg, Opts)Get private value
hb_private:get(Key, Msg, Default, Opts)Get with default
hb_private:set(Msg, Key, Value, Opts)Set private value
hb_private:set(Msg, PrivMap, Opts)Merge private map
hb_private:from_message(Msg)Extract private map
hb_private:merge(Msg1, Msg2, Opts)Merge private states
hb_private:reset(Term)Remove all private data
hb_private:is_private(Key)Check if key is private

Part 4: Path Management

📖 Reference: hb_path

Paths serve two purposes in HyperBEAM:

  1. Request Paths — Route messages to handlers
  2. HashPaths — Cryptographic message history

Request Paths

Request paths control message routing:

%% Get first path element
Head = hb_path:hd(#{<<"path">> => [<<"a">>, <<"b">>, <<"c">>]}, #{}).
%% => <<"a">>
 
%% Get remaining path
Tail = hb_path:tl(#{<<"path">> => [<<"a">>, <<"b">>, <<"c">>]}, #{}).
%% => #{<<"path">> => [<<"b">>, <<"c">>]}
 
%% Pop both at once
{Head, Rest} = hb_path:pop_request(#{<<"path">> => [<<"a">>, <<"b">>]}, #{}).
%% => {<<"a">>, #{<<"path">> => [<<"b">>]}}

Building Paths

Push adds to the front (next to execute):

Msg1 = #{<<"path">> => [<<"a">>]},
Msg2 = hb_path:push_request(Msg1, <<"b">>).  %% path: [<<"b">>, <<"a">>]

Queue adds to the back:

Msg1 = #{<<"path">> => [<<"a">>]},
Msg2 = hb_path:queue_request(Msg1, <<"b">>).  %% path: [<<"a">>, <<"b">>]

Path Conversion

%% Binary to list
Parts = hb_path:term_to_path_parts(<<"a/b/c">>).
%% => [<<"a">>, <<"b">>, <<"c">>]
 
%% List to binary
Binary = hb_path:to_binary([<<"a">>, <<"b">>, <<"c">>]).
%% => <<"a/b/c">>
 
%% Normalize (add leading slash)
Normalized = hb_path:normalize(<<"test">>).
%% => <<"/test">>

Path Matching

%% Case-insensitive matching
hb_path:matches(<<"Test">>, <<"test">>).
%% => true
 
%% Regex matching
hb_path:regex_matches(<<"a/b/c">>, <<"a/.*/c">>).
%% => true
 
hb_path:regex_matches(<<"a/anything/c">>, <<"a/.*/c">>).
%% => true

HashPaths

HashPaths create a cryptographic chain of message history:

%% Single message
Msg1 = #{<<"data">> => <<"initial">>},
HP1 = hb_path:hashpath(Msg1, #{}).
%% => <<"vKk...abc">> (43 bytes)
 
%% Apply second message
Msg2 = #{<<"action">> => <<"update">>},
HP2 = hb_path:hashpath(Msg1, Msg2, #{}).
%% => <<"vKk...abc/wLl...def">> (87 bytes)
 
%% Store hashpath for next operation
Msg3 = #{priv => #{<<"hashpath">> => HP2}}.

Verifying HashPaths

%% Build message chain
Msg1 = #{<<"data">> => <<"initial">>},
Msg2 = #{<<"action">> => <<"update">>},
Msg3 = #{priv => #{<<"hashpath">> => hb_path:hashpath(Msg1, Msg2, #{})}},
Msg4 = #{priv => #{<<"hashpath">> => hb_path:hashpath(Msg2, Msg3, #{})}},
 
%% Verify the chain
true = hb_path:verify_hashpath([Msg1, Msg2, Msg3, Msg4], #{}).

Quick Reference: Path Functions

FunctionWhat it does
hb_path:hd(Msg, Opts)Get first path element
hb_path:tl(Msg, Opts)Get remaining path
hb_path:pop_request(Msg, Opts)Get head and tail
hb_path:push_request(Msg, Path)Add to front of path
hb_path:queue_request(Msg, Path)Add to end of path
hb_path:term_to_path_parts(Path)Convert to list
hb_path:to_binary(Path)Convert to binary
hb_path:normalize(Path)Add leading slash
hb_path:matches(Key1, Key2)Case-insensitive match
hb_path:regex_matches(Path, Pattern)Regex match
hb_path:hashpath(Msg, Opts)Get message hashpath
hb_path:hashpath(Msg1, Msg2, Opts)Chain two messages
hb_path:verify_hashpath(Msgs, Opts)Verify chain

Part 5: Putting It Together

Test File

Add this to your src/test/test_hb2.erl:

-module(test_hb2).
-include_lib("eunit/include/eunit.hrl").
-include("include/hb.hrl").
 
%% Run with: rebar3 eunit --module=test_hb2
 
features_test() ->
    %% Get all features
    Features = hb_features:all(),
    ?assert(is_map(Features)),
    ?debugFmt("Features: ~p", [Features]),
    
    %% Check known features exist
    ?assert(maps:is_key(http3, Features)),
    ?assert(maps:is_key(rocksdb, Features)),
    ?assert(maps:is_key(test, Features)),
    ?debugFmt("Feature flags: OK", []),
    
    %% enabled/1 returns boolean
    ?assert(is_boolean(hb_features:enabled(http3))),
    ?assert(is_boolean(hb_features:enabled(rocksdb))),
    ?debugFmt("Feature checks: OK", []),
    
    %% Unknown features return false
    ?assertEqual(false, hb_features:enabled(nonexistent_feature)),
    ?debugFmt("Unknown feature handling: OK", []).
 
options_test() ->
    %% Local options override
    LocalOpts = #{gateway => <<"https://custom.gateway">>},
    Gateway = hb_opts:get(gateway, <<"default">>, LocalOpts),
    ?assertEqual(<<"https://custom.gateway">>, Gateway),
    ?debugFmt("Local override: OK", []),
    
    %% Default fallback
    Missing = hb_opts:get(nonexistent_key, <<"fallback">>, #{}),
    ?assertEqual(<<"fallback">>, Missing),
    ?debugFmt("Default fallback: OK", []),
    
    %% Prefer global
    GlobalOpts = #{mode => local_value, prefer => global},
    ?assertNotEqual(local_value, hb_opts:get(mode, undefined, GlobalOpts)),
    ?debugFmt("Prefer global: OK", []).
 
private_state_test() ->
    %% Create message with private state
    Msg = #{<<"data">> => <<"public">>},
    MsgWithPriv = hb_private:set(Msg, <<"cache/result">>, 42, #{}),
    ?assert(maps:is_key(<<"priv">>, MsgWithPriv)),
    ?debugFmt("Set private: OK", []),
    
    %% Get private value
    Value = hb_private:get(<<"cache/result">>, MsgWithPriv, #{}),
    ?assertEqual(42, Value),
    ?debugFmt("Get private: OK", []),
    
    %% Get with default
    Default = hb_private:get(<<"missing">>, MsgWithPriv, <<"default">>, #{}),
    ?assertEqual(<<"default">>, Default),
    ?debugFmt("Get with default: OK", []),
    
    %% Extract private map
    Priv = hb_private:from_message(MsgWithPriv),
    ?assert(is_map(Priv)),
    ?debugFmt("Extract private: OK", []),
    
    %% Reset removes private
    Cleaned = hb_private:reset(MsgWithPriv),
    ?assertEqual(#{}, hb_private:from_message(Cleaned)),
    ?debugFmt("Reset private: OK", []).
 
private_key_check_test() ->
    %% Check private key detection
    ?assert(hb_private:is_private(<<"priv">>)),
    ?assert(hb_private:is_private(<<"private">>)),
    ?assert(hb_private:is_private(priv)),
    ?assertNot(hb_private:is_private(<<"data">>)),
    ?assertNot(hb_private:is_private(<<"public">>)),
    ?debugFmt("Private key detection: OK", []).
 
path_manipulation_test() ->
    %% Head and tail
    Msg = #{<<"path">> => [<<"a">>, <<"b">>, <<"c">>]},
    ?assertEqual(<<"a">>, hb_path:hd(Msg, #{})),
    ?debugFmt("Path head: OK", []),
    
    Tail = hb_path:tl(Msg, #{}),
    ?assertEqual([<<"b">>, <<"c">>], maps:get(<<"path">>, Tail)),
    ?debugFmt("Path tail: OK", []),
    
    %% Pop request
    {Head, Rest} = hb_path:pop_request(Msg, #{}),
    ?assertEqual(<<"a">>, Head),
    ?assertEqual([<<"b">>, <<"c">>], maps:get(<<"path">>, Rest)),
    ?debugFmt("Pop request: OK", []).
 
path_building_test() ->
    %% Push adds to front (need to start with existing path)
    Msg1 = #{<<"path">> => [<<"a">>]},
    Msg2 = hb_path:push_request(Msg1, <<"b">>),
    Path = maps:get(<<"path">>, Msg2),
    ?assertEqual(<<"b">>, hd(Path)),
    ?debugFmt("Push request: OK", []),
    
    %% Queue adds to back
    Msg3 = #{<<"path">> => [<<"a">>]},
    Msg4 = hb_path:queue_request(Msg3, <<"b">>),
    QPath = maps:get(<<"path">>, Msg4),
    ?assertEqual(<<"a">>, hd(QPath)),
    ?assertEqual([<<"a">>, <<"b">>], QPath),
    ?debugFmt("Queue request: OK", []).
 
path_conversion_test() ->
    %% Binary to parts
    Parts = hb_path:term_to_path_parts(<<"a/b/c">>),
    ?assertEqual([<<"a">>, <<"b">>, <<"c">>], Parts),
    ?debugFmt("Path to parts: OK", []),
    
    %% Parts to binary
    Binary = hb_path:to_binary([<<"a">>, <<"b">>, <<"c">>]),
    ?assertEqual(<<"a/b/c">>, Binary),
    ?debugFmt("Parts to binary: OK", []),
    
    %% Normalize
    Normalized = hb_path:normalize(<<"test">>),
    ?assertEqual(<<"/test">>, Normalized),
    ?debugFmt("Path normalize: OK", []).
 
path_matching_test() ->
    %% Case-insensitive matching
    ?assert(hb_path:matches(<<"Test">>, <<"test">>)),
    ?assert(hb_path:matches(<<"ABC">>, <<"abc">>)),
    ?assertNot(hb_path:matches(<<"test">>, <<"other">>)),
    ?debugFmt("Case-insensitive match: OK", []),
    
    %% Regex matching
    ?assert(hb_path:regex_matches(<<"a/b/c">>, <<"a/.*/c">>)),
    ?assert(hb_path:regex_matches(<<"a/anything/c">>, <<"a/.*/c">>)),
    ?assertNot(hb_path:regex_matches(<<"a/b/c">>, <<"a/.*/d">>)),
    ?debugFmt("Regex match: OK", []).
 
hashpath_test() ->
    %% Single message hashpath
    Msg1 = #{<<"data">> => <<"initial">>},
    HP1 = hb_path:hashpath(Msg1, #{}),
    ?assert(is_binary(HP1)),
    ?assertEqual(43, byte_size(HP1)),
    ?debugFmt("Single hashpath: ~s", [HP1]),
    
    %% Two message hashpath
    Msg2 = #{<<"action">> => <<"update">>},
    HP2 = hb_path:hashpath(Msg1, Msg2, #{}),
    ?assert(is_binary(HP2)),
    ?assertEqual(87, byte_size(HP2)),
    ?debugFmt("Chained hashpath: OK", []).
 
complete_workflow_test() ->
    ?debugFmt("=== Complete Configuration Workflow ===", []),
    
    %% 1. Check features
    Features = hb_features:all(),
    ?debugFmt("1. Available features: ~p", [maps:keys(Features)]),
    
    %% 2. Create message with path
    Msg = #{
        <<"device">> => <<"Process@1.0">>,
        <<"path">> => [<<"init">>, <<"execute">>]
    },
    ?debugFmt("2. Created message with path", []),
    
    %% 3. Add private state
    MsgWithPriv = hb_private:set(Msg, <<"state/counter">>, 0, #{}),
    ?debugFmt("3. Added private state", []),
    
    %% 4. Process path
    {Head, Rest} = hb_path:pop_request(MsgWithPriv, #{}),
    ?assertEqual(<<"init">>, Head),
    ?debugFmt("4. Processing path element: ~s", [Head]),
    
    %% 5. Update private state
    UpdatedMsg = hb_private:set(Rest, <<"state/counter">>, 1, #{}),
    Counter = hb_private:get(<<"state/counter">>, UpdatedMsg, #{}),
    ?assertEqual(1, Counter),
    ?debugFmt("5. Updated counter to: ~p", [Counter]),
    
    %% 6. Clean for serialization
    Cleaned = hb_private:reset(UpdatedMsg),
    ?assertEqual(#{}, hb_private:from_message(Cleaned)),
    ?debugFmt("6. Cleaned private state for serialization", []),
    
    ?debugFmt("=== All tests passed! ===", []).

Run the Tests

rebar3 eunit --module=test_hb2

Common Patterns

Pattern 1: Configuration Loading

%% Load config with fallback
load_config() ->
    ConfigPath = hb_opts:get(config_path, "config.flat"),
    case hb_opts:load(ConfigPath) of
        {ok, Config} -> 
            hb_maps:merge(hb_opts:default_message(), Config);
        {error, _} ->
            hb_opts:default_message()
    end.

Pattern 2: Feature Guards

%% Guard function execution with feature check
with_feature(Feature, Fun) ->
    case hb_features:enabled(Feature) of
        true -> Fun();
        false -> {error, {feature_disabled, Feature}}
    end.
 
%% Usage
Result = with_feature(http3, fun() -> 
    start_http3_listener(Port) 
end).

Pattern 3: Private Cache

%% Cache expensive computation
cached_compute(Msg, Key, ComputeFun, Opts) ->
    CacheKey = <<"cache/", Key/binary>>,
    case hb_private:get(CacheKey, Msg, Opts) of
        not_found ->
            Result = ComputeFun(),
            UpdatedMsg = hb_private:set(Msg, CacheKey, Result, Opts),
            {Result, UpdatedMsg};
        Cached ->
            {Cached, Msg}
    end.

Pattern 4: Clean Serialization

%% Always clean before sending
serialize_message(Msg) ->
    CleanMsg = hb_private:reset(Msg),
    hb_message:serialize(CleanMsg).

What's Next?

You now understand HyperBEAM configuration:

ConceptModuleKey Functions
Optionshb_optsget, load, default_message
Featureshb_featuresall, enabled, feature checks
Private Statehb_privateget, set, reset, from_message
Pathshb_pathhd, tl, push_request, hashpath

Going Further

  1. Storage System — How data is persisted (hb_store)
  2. Message System — Creating and signing messages (hb_message)
  3. AO Protocol — The core execution engine (hb_ao)

Quick Reference Card

📖 Reference: hb_opts | hb_features | hb_private | hb_path

%% === OPTIONS ===
Val = hb_opts:get(key).
Val = hb_opts:get(key, default).
Val = hb_opts:get(key, default, Opts).
{ok, Cfg} = hb_opts:load("config.json").
Defaults = hb_opts:default_message().
 
%% === FEATURES ===
Features = hb_features:all().
Enabled = hb_features:enabled(feature).
true = hb_features:rocksdb().
false = hb_features:http3().
 
%% === PRIVATE STATE ===
Val = hb_private:get(Key, Msg, Opts).
Val = hb_private:get(Key, Msg, Default, Opts).
Msg2 = hb_private:set(Msg, Key, Value, Opts).
Msg2 = hb_private:set(Msg, PrivMap, Opts).
Priv = hb_private:from_message(Msg).
Clean = hb_private:reset(Msg).
true = hb_private:is_private(<<"priv">>).
 
%% === PATHS ===
Head = hb_path:hd(Msg, Opts).
Tail = hb_path:tl(Msg, Opts).
{H, T} = hb_path:pop_request(Msg, Opts).
Msg2 = hb_path:push_request(#{<<"path">> => [<<"a">>]}, <<"b">>).
Msg2 = hb_path:queue_request(#{<<"path">> => [<<"a">>]}, <<"b">>).
Parts = hb_path:term_to_path_parts(<<"a/b/c">>).
Bin = hb_path:to_binary([<<"a">>, <<"b">>]).
true = hb_path:matches(<<"A">>, <<"a">>).
HP = hb_path:hashpath(Msg1, Msg2, Opts).
true = hb_path:verify_hashpath(Msgs, Opts).

Now go configure something permanent!


Resources

HyperBEAM Documentation Related Tutorials