All notable changes to this project are documented here. The format follows Keep a Changelog, and the project adheres to Semantic Versioning.
A release that raises the database schema version says so here and names the new number, because
that is the one thing you have to act on: it means writing a migration of your own that calls
Ithibati.Migration.up(from: <old>, version: <new>).
Unreleased
0.1.0 - 2026-09-15
First release. Ithibati answers who someone is and how they prove it, and nothing about what their account may then do.
Added
- Passkey registration and authentication (WebAuthn, through
wax_), including the first account on an empty instance, and enrolling further credentials on an account that already has one. - Single-use recovery codes, which refill themselves when the last one is spent.
- Revocable server-side sessions. The cookie carries the secret, the row carries its sha256,
and signing out revokes the row.
config :ithibati, session_validity:says how long one lasts, and defaults to sixty days. - Invitations, optional: Ithibati owns the token, its expiry and the redemption; the application
owns the table and whatever the invitation grants.
Ithibati.Migration.invitation_columns/1writes the four columns it needs into acreate tableof yours, andIthibati.Migration.invitation_index/1the unique index, for an application turning invitations on after the migration has already run. The migration checks the columns and their types either way — atoken_hashwritten:stringis refused at migrate time rather than at the first invitation. - Schema macros for the account and the invitation table, so the application keeps both.
Ithibati.Migration, called from a migration of your own rather than copied from a template.- An optional Phoenix layer: the ceremony routes, a gate that denies by default, and a LiveView hook. Phoenix, LiveView and Plug are optional dependencies, taken or left together.
mix ithibati.doctor, twelve checks on a setup, runnable from your own gate. Two of them catch what compiles and migrates anyway: an invitation table whosetoken_hashhas no unique index, and a handler missing one of the four callbacks.- Signing in with a recovery code: a fifth route,
POST /recovery, ending in arecovered/3callback on your handler. It is separate fromauthenticate/2because it carries the fresh batch a spent last code produces, which is the only copy of it there will ever be. - Three Credo checks a consuming project can switch on.
- The migration refuses a table that is missing the column it indexes — your identifier column,
or an invitation table's
token_hash— and says where the column belongs, rather than failing with Postgres'sundefined_column.