A Plug that speaks Hrana over HTTP (versions 1, 2, and 3) and upgrades
WebSocket requests to Filo.Socket (Hrana over WebSocket). Mount it in any Plug
or Phoenix app to accept libSQL clients (django-libsql over ws, libsql-client,
libsql-experimental, the libSQL SDKs).
Routes
GET /v1·/v2·/v3·/v3-protobuf— protocol-support checks; reply200.POST /v2/pipeline·/v3/pipeline— run a pipeline of stream requests (JSON).POST /v3-protobuf/pipeline— same, Protobuf-encoded (application/x-protobuf).POST /v3/cursor— run a batch, streaming the result (Hrana 3, JSON).POST /v3-protobuf/cursor— same, length-delimited Protobuf frames.POST /v1/execute·/v1/batch— stateless Hrana 1 legacy API.- A WebSocket upgrade (any path) is handed to
Filo.Socket.
Configuration (init/1)
:executor(required) — theFilo.Executormodule backing each stream.:streams(required) — the registered name of aFilo.Streamssupervisor the host keeps in its supervision tree.:key(required) — the baton signing key (Filo.Baton.new_key/0). Keep it stable for the life of the server; rotating it invalidates live batons.:open_arg— host context forexecutor.open/1. Either a term used as-is or a 1-arity function of thePlug.Conn(e.g. to pick a database from the request). Defaultnil.:authorize— optional host auth callback,fun(open_arg, token)→:ok | {:ok, context} | {:error, %Filo.Error{}}.tokenis the request'sAuthorization: Bearercredential (HTTP), or — on the WebSocket binding — thejwtthe client sent in its Hranahello(libSQL'sauthTokentravels there, not in an upgrade header), falling back to the upgrade request's bearer token;nilwhen the client sent neither. Checked once per stream open (HTTP pipeline/cursor without a baton, each stateless v1 request, the WS hello) — a baton resumes a stream that was already authorized when opened. Returning{:ok, context}threadscontext(any host-opaque term — the verified scope, a tenant id) toexecutor.open/2for every stream this connection opens; a bare:okthreadsnil. A refusal is surfaced with the error'sstatus(default 401), or as a fatalhello_erroron WebSocket. Defaultnil(no auth — the host trusts the network).:base_url— value returned in each response'sbase_url. Defaultnil.:idle_timeout— per-stream inactivity timeout in ms. Default usesFilo.Stream's.
Pipeline flow
A request with no baton opens a new stream; a request with a baton resumes
the stream it names. Each response carries a fresh baton for the next request
(or null once the stream is closed). A reused, forged, or malformed baton, or
a baton for a stream that has been closed or expired, is rejected with 400.