All notable changes to logdb_client are documented here.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. While the major version is 0, a minor bump (0.x.0) may break the API; a patch (0.x.y) never does. How a release is made: RELEASING.md.

Unreleased

0.3.0 - 2026-10-10

Everything since 0.2.11, the previous version on hex (0.2.12 and 0.2.13 were never published). The public API is the same — use LogDB.Client.Consumer, LogDB.Client.Supervisor, LogDB.publish/4 and friends — but reply shapes and option checks changed; see Upgrading from 0.2 below.

Added

  • Connection string: logdb[s]://[token@]host[:port][/stream/consumer][?transport=…&node=…&client=…] (LogDB.Client.ConnectionString, spec in docs/connection-string.md). The token may also come as token:; anything set both ways must agree. Default port 4000 (443 with logdbs).
  • LogDB.Error — one error for every transport: {:error, %LogDB.Error{code, message, details}}. It is an exception, so raise error works.
  • Client API 1 (hello): the client names itself (client_name:, default — node or host name) and negotiates the API with the bus; the LogDB console shows which instance is connected.
  • consumer: "stream/name" for every transport — the service is known from the token. The old stream: + consumer_id: pair still works.
  • HTTP delivery: long-poll from a pull consumer (/v1/events/poll), the next batch only after the consumer has handled the previous one.
  • WebSocket is self-sufficient: publish, subscribe, ack/nack/term/progress and ping over one socket; every command waits for the bus's reply (request_id).
  • Confirmations: LogDB.term/3 (never deliver again), LogDB.nack/3 with delay_ms:, LogDB.progress/2 (reset ack_wait during long handling); {:term, st} and {:nack, st, delay_ms: n} replies from handle_event/4.
  • Payload types: the publisher declares _content_type; over erlangd the parser encodes bytes (encode_bytes/1). Parser.Auto decodes by the declared type.
  • LogDB.await_connected/2; init_arg: option for the consumer's own data; the child id is the client's name, so several clients fit in one supervisor.

Changed

  • Reply shapes: commands return `{:ok, result}},subscribe` and
    ping return `:ok}`, on every transport. Errors used to be
    {status, body}, {code, message}, {string, index}, {:exit, r} or bare atoms. HTTP returns the bus's body instead of %Req.Response{}; erlangd publish results have the WS/HTTP shape (%{"result" => [...], "failed" => n}).
  • A single publish the bus refused (no stream, JetStream refusal) is {:error, %LogDB.Error{code: :unavailable}}; over HTTP and WS it used to be {:ok, %{"failed" => 1}}. publish_batch/2 still reports partial refusals as {:ok, %{"failed" => n}}.
  • Options are checked at start: a missing name, token or host, a wrong scheme, an unknown transport or parser, an unknown option (with a hint), an option given twice — the client refuses to start with the reason instead of crash-looping in connect.
  • Hybrid writes over the same WebSocket when the bus speaks API 1; over HTTP otherwise.
  • Parser.Json takes a payload the bus already decoded (WS/HTTP) as is: strings stay strings. It used to re-parse them — "hello" was redelivered forever, a string holding JSON became a map.
  • An undecodable event is terminated (term, visible among dropped events in the console) instead of being redelivered forever.
  • A dynamic subscription (LogDB.subscribe/2,3) is repeated after every reconnect.
  • Publishing reads the transport from an ETS table instead of calling the connection process, so it never waits behind incoming events.
  • Reconnect backs off (1 s, 2 s, 4 s … 30 s) with jitter.

Fixed

  • The consumer's code can no longer bring the client down: raise, throw, exit or a reply outside the contract go to handle_error/2; stray messages are ignored.
  • {:ack, state} with a bus that puts the id in ack_id (before /v1) used to send ack([nil]).
  • A half-open WebSocket is detected: three pings without any frame (15–20 s, plus up to 5 s to close) and the client reconnects. disconnect/1 really closes the socket.
  • A payload that is not valid UTF-8 no longer crashes the WS socket for everyone or raises over HTTP — that call gets {:error, %LogDB.Error{code: :invalid}}.
  • The token no longer ends up in logs (WS disconnect) or in inspect.
  • HTTP single publish keeps id: (dedup); HTTP connect checks the bus and the token.
  • A transport that exits mid-call gives {:error, …} instead of an exit in the caller.

Removed

Upgrading from 0.2

  • Match {:error, _} as before; inside it is now %LogDB.Error{}. Don't match %Req.Response{} or {status, body}.
  • An app that passes options the client doesn't know will not start; move your own data to init_arg: (read it in init/1 as opts[:init_arg]).
  • catch :exit around LogDB.* calls is no longer needed.
  • Tests that fake the client by registering <name>_conn and answering :get_transport must also put {:transport, module, state} into the ETS table <name>_transport.
  • With a bus without hello everything works; Hybrid then writes over HTTP as before.

0.2.11 - 2026-10-08

Fixed

  • WebSocket consumers reconnect after the bus closes the socket normally (close code 1000/1001, e.g. a bus restart on deploy); they used to stay disconnected until the service was restarted.

0.2.10 and earlier

See the git history.