Abyss
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:
- Architecture: supervision, ownership and data flow.
- UDP addressing and lifecycle: clients, memberships, pause and drain.
- Datagram runtime and migration: callbacks, bounds, effects and backends.
- Dispatcher and writer: routing, send capabilities and receipts.
- QUIC service: optional engine and application handlers.
- Operations and development: inspection, telemetry and scoped checks.
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.