Current section
Files
Jump to
Current section
Files
lib/layr8/config.ex
defmodule Layr8.Config do
@moduledoc """
Configuration for a Layr8 client.
Fields can be supplied explicitly or resolved from environment variables:
- `LAYR8_NODE_URL` — WebSocket URL of the cloud-node
- `LAYR8_API_KEY` — authentication key
- `LAYR8_AGENT_DID` — agent DID (optional; ephemeral DID created on connect if absent)
HTTP(S) URLs are automatically normalized to WebSocket scheme:
- `https://` → `wss://`
- `http://` → `ws://`
## Example
config = Layr8.Config.resolve!(%{node_url: "wss://node.example.com/plugin_socket/websocket", api_key: "secret"})
"""
@enforce_keys [:node_url, :api_key]
defstruct [:node_url, :api_key, agent_did: ""]
@type t :: %__MODULE__{
node_url: String.t(),
api_key: String.t(),
agent_did: String.t()
}
@doc """
Resolves a config map or struct, filling missing fields from environment variables,
normalizing the URL scheme, and validating required fields.
Raises `Layr8.Error` if required fields are missing.
"""
@spec resolve!(map()) :: t()
def resolve!(cfg \\ %{}) do
node_url = Map.get(cfg, :node_url) || System.get_env("LAYR8_NODE_URL") || ""
api_key = Map.get(cfg, :api_key) || System.get_env("LAYR8_API_KEY") || ""
agent_did = Map.get(cfg, :agent_did) || System.get_env("LAYR8_AGENT_DID") || ""
if node_url == "" do
raise Layr8.Error,
message: "node_url is required (set in config or LAYR8_NODE_URL env)"
end
normalized_url = normalize_url(node_url)
if api_key == "" do
raise Layr8.Error,
message: "api_key is required (set in config or LAYR8_API_KEY env)"
end
%__MODULE__{
node_url: normalized_url,
api_key: api_key,
agent_did: agent_did
}
end
@doc """
Derives the REST API base URL from a WebSocket URL.
iex> Layr8.Config.rest_url_from_websocket("wss://node.example.com/plugin_socket/websocket")
"https://node.example.com"
iex> Layr8.Config.rest_url_from_websocket("ws://localhost:4000/plugin_socket/websocket")
"http://localhost:4000"
"""
@spec rest_url_from_websocket(String.t()) :: String.t()
def rest_url_from_websocket(ws_url) do
uri = URI.parse(ws_url)
scheme =
case uri.scheme do
"wss" -> "https"
_ -> "http"
end
host_part =
if uri.port && uri.port not in [80, 443] do
"#{uri.host}:#{uri.port}"
else
uri.host
end
"#{scheme}://#{host_part}"
end
@doc false
@spec normalize_url(String.t()) :: String.t()
def normalize_url(url) do
cond do
String.starts_with?(url, "https://") ->
"wss://" <> String.slice(url, String.length("https://")..-1//1)
String.starts_with?(url, "http://") ->
"ws://" <> String.slice(url, String.length("http://")..-1//1)
true ->
url
end
end
end