Abyss

release Hex.pm Documentation

Abyss is a supervised Elixir UDP service library. It provides socket ownership, bounded admission, pluggable handlers, broadcast/multicast support and lifecycle control. The core package does not require Rust, TLS or a QUIC engine.

Installation

def deps do
  [
    {:abyss, "~> 0.8.1"}
  ]
end

QUIC applications explicitly install elixir_quic and its engine dependencies. DHCP native users explicitly install the optional, currently unpublished abyss_dhcp_socket package. See the QUIC guide and DHCP migration.

Datagram echo

Use the datagram API for immutable packet values and finite reply/send/timer actions. This example keeps one serialized endpoint state:

defmodule Echo do
  @behaviour Abyss.DatagramHandler

  @impl true
  def init(_options, _context), do: {:ok, 0}

  @impl true
  def handle_datagram(datagram, count) do
    {:ok, count + 1, [{:reply, datagram.payload}]}
  end
end

{:ok, endpoint} = Abyss.Endpoint.start_link(
  datagram_handler: Echo,
  execution: :endpoint,
  port: 1234,
  transport_options: [ip: {127, 0, 0, 1}]
)

{:ok, {address, port}} = Abyss.Endpoint.local_endpoint(endpoint)

From another terminal:

printf 'hello' | nc -4 -u -w1 127.0.0.1 1234

Choose :per_datagram (the default) for a fresh state per input, :endpoint for serialized endpoint state, or :keyed with an explicit route_key for application-defined state partitions. Broadcast and multicast addressing are independent of execution policy. Endpoint state persists for its socket generation; keyed state is subject to idle expiry. Both retain configured work limits.

A reply action uses the original endpoint socket. Send admission and local completion are distinct; neither establishes peer receipt. See migration and backend capabilities and the runtime contract.

Existing handler API

Legacy handlers continue to receive {ip, port, payload} and access the shared socket through their state:

defmodule LegacyEcho do
  use Abyss.Handler

  @impl true
  def handle_data({ip, port, payload}, state) do
    :ok = Abyss.Transport.UDP.send(state.socket, ip, port, payload)
    {:close, state}
  end
end

{:ok, server} = Abyss.start_link(
  handler_module: LegacyEcho,
  port: 1235,
  num_listeners: 1,
  transport_options: [ip: {127, 0, 0, 1}]
)

Each accepted packet gets a separate invocation. {:continue, state} keeps that invocation alive without routing subsequent peer packets to it. Stateful protocols can use the datagram API, or retain an existing dispatcher.

Documentation

The documentation guide is the entry point for:

Source-attributed plans, diagnostics and release evidence are separated in the historical archive. Current guides describe behavior; historical measurements do not imply a universal throughput improvement.

Development

mix deps.get
mix docs --warnings-as-errors
mix format --check-formatted

Use scoped verification commands for the changed area. Independent network and optional-package gates are documented with their fixture prerequisites.

License

MIT.