Elixir ATProtocol ingestion and sync library.
Agent Guidelines for Drinkup#
Commands#
- Test:
mix test(all),mix test test/path/to/file_test.exs(single file),mix test test/path/to/file_test.exs:42(single test at line) - Format:
mix format(auto-formats all code) - Lint:
mix credo(static analysis),mix credo --strict(strict mode) - Compile:
mix compile - Docs:
mix docs - Type Check:
mix dialyzer(if configured)
Code Style#
- Imports: Use
aliasfor modules (e.g.,alias Drinkup.Firehose.{Event, Options}),requirefor macros (e.g.,require Logger) - Formatting: Elixir 1.18+, auto-formatted via
.formatter.exswithimport_deps: [:typedstruct] - Naming: snake_case for functions/variables, PascalCase for modules,
:lowercase_atomsfor atoms,@behaviour(not@behavior) - Types: Use
@typeand@specfor all functions; use TypedStruct for structs withenforce: truefor required fields - Moduledocs: Public modules need
@moduledoc, public functions need@docwith examples - Error Handling: Return
{:ok, result}or{:error, reason}tuples; usewithfor chaining operations; log errors withLogger.error("#{Exception.format(:error, e, __STACKTRACE__)}") - Pattern Matching: Prefer pattern matching in function heads over conditionals; use guard clauses when appropriate
- OTP: Use
child_spec/1for custom supervisor specs;:gen_statemfor state machines;Task.Supervisorfor concurrent tasks; Registry for named lookups - Tests: Use ExUnit with
use ExUnit.Case; usedoctest Modulefor documentation examples - Dependencies: Core deps include gun (WebSocket), car (CAR format), cbor (encoding), TypedStruct (typed structs), Credo (linting)
Project Structure#
Each namespace (Drinkup.Firehose, Drinkup.Jetstream, Drinkup.Tap) follows a common architecture:
-
Core Modules:
Consumer- Behaviour/macro for handling events;use Namespacewithhandle_event/1implementationEvent- Typed event structs specific to the protocolSocket-:gen_statemWebSocket connection managerOptions(or top-level utility module) - Configuration and runtime utilities
-
Consumer Pattern:
use Namespace, opts...withhandle_event/1callback; consumer module becomes a supervisor
Namespace-Specific Details#
-
Firehose (
Drinkup.Firehose.*): Full AT Protocol firehose- Events:
Commit,Sync,Identity,Account,Info - Additional:
RecordConsumermacro for filtered commit records withhandle_create/1,handle_update/1,handle_delete/1callbacks - Pattern:
use Drinkup.Firehose.RecordConsumer, collections: [~r/app\.bsky\.graph\..+/, "app.bsky.feed.post"]
- Events:
-
Jetstream (
Drinkup.Jetstream.*): Simplified JSON event stream- Events:
Commit,Identity,Account - Config:
:wanted_collections,:wanted_dids,:compress(zstd) - Utility:
Drinkup.Jetstream.update_options/2for dynamic filtering - Semantics: Fire-and-forget (no acks)
- Events:
-
Tap (
Drinkup.Tap.*): HTTP API + WebSocket indexer/backfill service- Events:
Record,Identity - Config:
:host,:admin_password,:disable_acks - Utility:
Drinkup.TapHTTP API functions (add_repos/2,remove_repos/2,get_repo_info/2) - Semantics: Ack/nack - return
:ok/{:ok, _}/nilto ack,{:error, _}to nack (Tap retries)
- Events:
Important Notes#
- Update CHANGELOG.md when adding features, changes, or fixes under
## [Unreleased]with appropriate sections (Added,Changed,Fixed,Deprecated,Removed,Security) - WebSocket States: Socket uses
:disconnected→:connecting_http→:connecting_ws→:connectedflow - Sequence Tracking: Use
Event.valid_seq?/2to validate sequence numbers from firehose