Skip to content

Releases: cwisecarver/filo

v0.2.0 — the Hrana client, the authorize seam, and stream resume

Choose a tag to compare

@cwisecarver cwisecarver released this 29 Jul 05:03

Filo stops being server-only: it now ships a Hrana client built on the same
codec, plus an authorization seam that works on both transports. Everything here is
additive — a host on 0.1.0 upgrades without code changes.

Added

  • Filo.Client — a Hrana client mirroring the server's codec, so there is no
    second implementation of the wire format to drift from. One client is one Hrana
    stream over one owned connection, with baton threading, so a transaction is a
    burst of execute/3 on a held connection. Scope is the Hrana 2/3 HTTP JSON
    pipeline. The HTTP round-trip is a Filo.Client.Transport behaviour; the default
    Filo.Client.Transport.Mint uses the optional :mint dependency, so
    server-only hosts never pull it in.
  • Transparent stream resume. A dropped connection is retried with the same
    baton, resuming the Hrana stream instead of abandoning it. This matters behind a
    load balancer that recycles an idle client keep-alive after N requests — that used
    to kill a held transaction mid-flight.
  • An optional :authorize seam gating every stream open, on both transports.
    It is a Filo callback rather than a plug because WebSocket clients send no upgrade
    header: the credential arrives in the Hrana hello frame, which only Filo sees.
  • Authorize context threaded to Executor.open/2. :authorize may return
    {:ok, context}; Filo threads that host-opaque value into open/2 for every
    stream the connection opens — the HTTP stream that opens in its own process, the
    stateless v1 open, and every WebSocket open_stream after the hello. open/2 is
    in @optional_callbacks, so an executor that only implements open/1 keeps
    working
    and the context is dropped. Not a breaking change.
  • Streams monitor their connection owner and tear down when it dies, so an
    orphaned stream cannot outlive the process that opened it.

Changed

  • Cursor entries batch into ~32 KiB transport chunks instead of one write per
    ndjson entry.
  • Direct-to-iodata JSON row encoding (Filo.Value.encode_json, rows: :json)
    and pre-encoded row fragments on the JSON paths, with iodata carried through to
    the send sites — fewer intermediate binaries per result.
  • Idle-timer cancellation is async, and the host can set a process policy for
    stream processes.

Fixed

  • A malformed WebSocket text frame now closes with 1007 (invalid frame payload
    data) rather than a bare 1000, so a client can tell a protocol error from a normal
    close.

Install

{:filo, "~> 0.2.0"}

Published on Hex: https://hex.pm/packages/filo · Docs: https://hexdocs.pm/filo/0.2.0

v0.1.0 — first release

Choose a tag to compare

@cwisecarver cwisecarver released this 03 Jul 00:12
455f680

The first release of Filo — a Hrana (libSQL) protocol server for Elixir. Speak libSQL's wire protocol from any Plug app, backed by the SQLite engine of your choice.

Highlights

  • Both transports libSQL clients use, from one Plug:
    • HTTP — Hrana 1 (stateless execute/batch), Hrana 2/3 pipelines, and Hrana 3 cursors, in JSON and Protobuf.
    • WebSocket — Hrana 2/3 (and hrana3-protobuf), the path django-libsql uses.
  • Engine-agnostic via the small Filo.Executor behaviour — Filo owns the protocol; you own the SQL.
  • Stateful streams — one connection per stream (transactions/temp tables persist across requests), pinned by signed batons (HTTP) or a client-allocated id (WebSocket).
  • Ships a Plug and a WebSock handler and nothing else — bring your own server (Bandit/Cowboy).

django-libsql (WebSocket) and the libSQL SDKs / libsql-experimental (HTTP) work end to end.

Status: early, built test-first. CI runs the suite on Elixir 1.20 across OTP 27, 28, and 29.