Thanks for helping build Filo. This is the practical "get set up and land a change"
guide. Filo is a Hrana (libSQL) protocol server and client for Elixir; the
architecture notes live in AGENTS.md.
Prerequisites
- Elixir ~> 1.20 on OTP 27, 28, or 29 (what CI runs).
- A C toolchain —
exqliteis a test-only dependency and compiles its bundled SQLite from source (build-essentialon Linux, Xcode Command Line Tools on macOS). - Nothing else. Filo has no database, no service dependencies, and no config to set
up: it ships a
Plugand aWebSockhandler, and the host brings the server.
git clone https://github.com/cwisecarver/filo && cd filo
mix deps.get
mix test
The gate
mix precommit is what has to pass before a commit lands. It runs
compile --warnings-as-errors, deps.unlock --unused, format --check-formatted,
and the test suite:
mix precommit
CI runs exactly this across OTP 27/28/29. If precommit is green locally it should
be green on CI; if it isn't, that difference is itself worth reporting.
Integration tests
The default suite is self-contained. Integration tests drive real libSQL clients
against a live Filo server and need external setup (a Python with libsql-client),
so they are excluded by default:
mix test --include integration
Run these when you touch the wire format, the transports, or stream lifecycle — they are what catch a change that is valid Elixir but invalid Hrana.
What a good change looks like
- Test-first. Filo was built that way and the protocol rewards it: the wire format has exact expectations, and a test is usually shorter than the prose describing the case.
- Protocol changes ship with a test at the wire level, not just at the function boundary. A codec that round-trips in Elixir but emits the wrong JSON shape is still broken.
- Keep the dependency footprint small. Runtime deps are
plug,jason, and thewebsockbehaviour — plusmint, which is optional and only for the client's default transport. A server-only host should never pull in a transport. If a change needs a new runtime dep, that is a design discussion first. - The executor stays engine-agnostic. Filo owns the protocol; the host owns SQL
execution. New
Filo.Executorcallbacks should be in@optional_callbacksunless there is a strong reason, so existing implementations keep working. - Update
CHANGELOG.mdunder an## [Unreleased]heading for anything user-visible.
Versioning
Filo follows Semantic Versioning. While the
version is below 1.0.0, the minor number carries breaking changes as well as
features; patch releases stay backward compatible.
Releases are git tags of the form vX.Y.Z matching @version in mix.exs. That
match matters beyond tidiness: ex_doc builds source links from
source_ref: "v#{@version}", so a tag that doesn't exist at the documented code
produces documentation whose "source" links point at a tree without it.
Reporting a protocol bug
Filo implements someone else's wire protocol, so the most useful bug report includes what the client sent and what it expected back. A failing request/response pair — or the client library and version plus the operation that failed — is worth more than a stack trace, because the question is almost always "what does Hrana actually require here."