All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

0.22.0 - 2026-03-19

Added

  • params: option for query string encoding — Pass params: [page: 1, per: 20] to append query parameters to the request URL. Merges with existing query strings. Accepts keyword lists, maps, or lists of two-element tuples (flat key-value only). Can be combined with any body option (json:, form:, body:).

Changed

  • Reframed project positioning as a production reliability layer — Updated README, package description, moduledoc, and roadmap to position HTTPower as a reliability layer for existing HTTP clients (Finch, Req, Tesla) rather than as another HTTP client library. README now leads with a hero code example showing the client pattern with explicit reliability options.

0.21.0 - 2026-03-18

Added

  • json: option for automatic JSON request body encoding — Pass json: %{key: value} to encode the body as JSON, set Content-Type: application/json, and set Accept: application/json in one step. Replaces the previous pattern of manually calling Jason.encode! and setting headers.

  • form: option for automatic form-urlencoded request body encoding — Pass form: %{key: value} to encode the body as URL-encoded form data and set Content-Type: application/x-www-form-urlencoded automatically.

  • raw: option to skip automatic response body decoding — Pass raw: true to receive the raw binary response body, bypassing Content-Type-driven decoding.

  • HTTPower.Codec module for adapter-independent body encoding/decoding — Centralizes request encoding (json:, form:) and Content-Type-driven response decoding (auto-decodes application/json and +json suffix responses). Lives above the adapter layer; called from HTTPower.Client.

  • New error reasons :conflicting_body_options and :json_encode_error:conflicting_body_options is returned when more than one of json:, form:, or body: is provided for the same request. :json_encode_error is returned when json: encoding fails.

Changed

  • Response decoding is now consistent across all adapters — Finch, Req, and Tesla all decode response bodies via HTTPower.Codec, which applies Content-Type-driven JSON decoding. Previously, adapters had divergent behavior (Finch decoded all bodies as JSON; Req used its own decoding; Tesla depended on user middleware).

  • POST requests no longer get a default Content-Type: application/x-www-form-urlencoded — Use the new form: option or set the header explicitly. This default was added in v0.1.0 but conflicted with the new json: and form: encoding options and was inconsistent with other HTTP methods.

Removed

  • Finch adapter no longer blindly decodes all response bodies as JSON — The Finch adapter previously attempted JSON decoding on every response regardless of Content-Type. Decoding is now delegated to HTTPower.Codec and is driven by the Content-Type response header.

  • Req adapter's built-in response decoding is disabledHTTPower.Codec handles all response decoding uniformly. Disabling Req's decoding prevents double-decoding and ensures consistent behavior across adapters.

Breaking Changes

  • POST requests without form: or explicit Content-Type header no longer receive application/x-www-form-urlencoded default — Applications that relied on this default must add form: params or set the header explicitly.

  • Finch adapter returns raw binary bodies for non-JSON responses — Previously, non-JSON responses passed through Finch's blind JSON decode attempt (which would fail silently). Now they are returned as binary unless the Content-Type header indicates JSON.

  • Tesla users with Tesla.Middleware.JSON must remove it — Having both Tesla.Middleware.JSON and HTTPower.Codec in the stack results in double-decoding of JSON responses.

  • Telemetry request.body metadata contains encoded JSON string when json: is used — When using the json: option, the telemetry metadata reflects the encoded JSON string body rather than the original data structure.

Fixed

  • CircuitBreaker.call/3 returns consistent error format — Returns %HTTPower.Error{reason: :service_unavailable} instead of bare :service_unavailable atom, matching handle_request/2 behavior.

  • Remove rescue-for-control-flow in RateLimiterclear_adaptive_state/1 no longer uses rescue ArgumentError -> :ok. :ets.delete/2 on a nonexistent key is a no-op; the rescue was masking potential table-missing errors.

  • Cap backoff exponent at 30 — Prevents wasteful big-integer math in calculate_backoff_delay/2 when max_retries is set to high values. 2^30 (~1 billion ms) exceeds any practical max_delay.

  • Use default GenServer timeout for dedupDedup.deduplicate/2 no longer uses :infinity timeout. The default 5s timeout surfaces a stuck GenServer instead of hanging indefinitely.

  • Guard Logger against non-map headerssanitize_if_enabled(:headers, ...) now handles non-map input gracefully instead of crashing on map_size/1.

Changed

  • Normalize adapter callback URL type to URI.t() — The adapter callback spec now correctly reflects URI.t() instead of String.t(). call_adapter normalizes strings to URI as a safety net. Finch and Req accept URI.t() natively; Tesla converts internally.

  • Cache rate limiter default config at compile timeget_strategy/1 and get_max_wait_time/1 now use @default_config via Application.compile_env instead of calling Application.get_env per request.

  • Move @cleanup_interval to top of Dedup module — Grouped with other module attributes for consistency.

  • Remove no-op pipeline order test — The test in coordination_test.exs asserted nothing and noted the behavior was already covered by dedup bypass tests.

0.20.0 - 2026-03-08

Added

  • Finch adapter unit tests — Added 27 unit tests for HTTPower.Adapter.Finch covering HTTP methods, headers, SSL, proxy, response conversion, error handling, body handling, and conn_opts merging. Finch was the only adapter without unit tests.

Changed

  • Extract shared prepare_headers/2 into HTTPower.Adapter — Consolidated duplicate header preparation logic from Finch, Req, and Test modules into a single public function in the HTTPower.Adapter behaviour module.

  • Cache sanitization field lists in Logger at attach time — Sanitization header and body field lists are now normalized once during HTTPower.Logger.attach/0 and stored in the handler config, eliminating repeated Application.get_env + normalization on every telemetry event.

  • Logger sanitizes once per event — Headers and body are now sanitized once and reused for both structured metadata and log message formatting, eliminating redundant sanitization work.

  • Document HTTPower.new/1 raises on invalid profile — Clarified that the "never raises" principle applies to HTTP operations, not configuration errors. new/1 raises ArgumentError for unknown profiles, which is correct Elixir convention.

  • Bump ExDoc to ~> 0.40 — Enables automatic llms.txt generation for LLM-friendly documentation on HexDocs.

  • Document compile-time config caching in Client — Added moduledoc section explaining that default adapter and middleware settings are cached at compile time and require recompilation to change.

  • Runtime Plug check in HTTPower.Test — Response helpers (json/2, html/2, text/2, transport_error/2) now raise a clear error message if Plug is not installed, instead of crashing with UndefinedFunctionError.

Fixed

  • Dedup pid_to_hash overwrite when process tracks multiple hashes — Changed from a single hash per PID to a MapSet of hashes. Previously, if a process made concurrent requests with different bodies, only the last hash was tracked, leaking earlier entries on process death.

  • max_retries off-by-onemax_retries: 3 now correctly performs 3 retries (4 total attempts). Previously it performed only 2 retries due to a < vs <= comparison error.

  • Req adapter leaks HTTPower-specific options to Req — Expanded the option filter list from 12 to 20 entries, adding :adapter_config, :circuit_breaker, :circuit_breaker_key, :deduplicate, :profile, :rate_limit, :rate_limit_key, and :request_steps.

  • DELETE method ignores request bodyHTTPower.delete/2 now extracts and forwards the :body option, matching the behavior of POST, PUT, and PATCH.

  • Dedup wait telemetry reports actual wait durationwait_time_ms in [:httpower, :dedup, :wait] events now reflects the actual time spent waiting for the original request, instead of always reporting 0.

  • circuit_breaker_key works as top-level request option — Previously only recognized when nested inside circuit_breaker: [circuit_breaker_key: "..."]. Now works as documented: HTTPower.get(url, circuit_breaker_key: "payment_api"). Also fixed in the rate limiter's adaptive rate adjustment.

  • Removed unreachable {:error, {:http_status, ...}} pattern — Dead code in Client.get_request_function/2 telemetry metadata that could never match, since the retry module converts HTTP status errors to {:ok, response}.

  • parse_retry_after/1 normalizes header keys — Now performs case-insensitive header lookup, matching the behavior of parse/2. Previously, mixed-case keys like "Retry-After" would not be found when calling parse_retry_after/1 directly.

0.19.0 - 2026-03-07

Added

  • Dedup abort telemetry event — New [:httpower, :dedup, :abort] telemetry event emitted when the original requester dies or cancels, notifying waiting processes of the abort.

Changed

  • Short-circuit can_do_request? when test_mode is false — Skips mock/plug checks entirely when test mode is disabled, avoiding unnecessary work in production.

  • Removed redundant Logger.debug from rate limiter wait path — The debug log in the wait-and-retry loop was redundant with the telemetry event already emitted.

  • Removed redundant URI.parse in Finch adaptermaybe_add_ssl_options/3 now uses the %URI{} struct directly instead of re-parsing it from a string.

  • Consolidated retry attempt count check — Retry attempt validation is now handled in a single location rather than being duplicated.

  • Set explicit failure_threshold in profiles — Configuration profiles now set failure_threshold explicitly to match their percentage-based intent.

  • Credit card sanitization now uses Luhn validation — The regex pattern finds candidates (13-19 digit sequences), then Luhn checksum filters out non-card numbers like order IDs and timestamps. Reduces false positives by ~90% while maintaining PCI safety. JSON field-name sanitization (e.g., "credit_card": "value") remains unconditional.

Fixed

  • Dedup waiters hanging when original requester dies or request is cancelled — Waiters now receive an abort message and re-issue the request instead of hanging indefinitely.

  • Logger credit card regex now handles all PAN formats — The sanitization regex now matches any 13-19 digit sequence regardless of grouping (AmEx non-standard grouping, old 13-digit Visa, extended 19-digit PANs), not just 4+4+4+N formatted cards.

  • Logger JSON field sanitization now redacts non-string values — Sensitive JSON fields with numeric ("pin": 1234), boolean ("secret": true), or null values are now properly redacted, not just double-quoted string values.

Removed

  • Removed redundant tests — Eliminated ~30 overlapping tests: exact duplicate transport error tests, redundant "no logs when not attached" logger tests, adapter integration tests that only exercised HTTPower.Test interception rather than actual adapter code, and SSL/proxy smoke tests already covered by adapter unit tests.

0.18.0 - 2026-03-06

Added

  • Cross-process mock support in HTTPower.Test — Mocks are now visible to processes spawned from the test (Task.async, Task.async_stream) via $callers chain walking. For pre-existing processes (GenServers, Agents), use the new HTTPower.Test.allow/2. Mock storage moved from process dictionary to ETS for cross-process accessibility.

0.17.0 - 2026-03-06

Added

  • Direct test coverage for HTTPower.Error module - Added unit tests for error struct creation and the error_message/1 helper function.

  • Configurable dedup wait timeout - The dedup wait_timeout option is now user-configurable via the deduplicate keyword list (e.g., deduplicate: [wait_timeout: 30_000]).

Changed

  • Elixir 1.20.0-rc.2 - Updated .tool-versions to Elixir 1.20.0-rc.2.

  • Adaptive rate limiting telemetry only emits on state transitions - The [:httpower, :rate_limit, :adaptive_reduction] telemetry event now only fires when the adaptive multiplier actually changes, reducing noise under sustained circuit breaker states.

  • Tesla header conversion aligned with Finch - Tesla adapter now uses the same prepend+reverse approach as the Finch adapter for header conversion, ensuring consistent header ordering.

  • mix.exs description includes Finch - Updated the package description to mention Finch as the default adapter.

  • Retry documentation improvements - Clarified the distinction between retryable HTTP status codes and transport errors. Documented that {:ok, response} may include 5xx responses after retries are exhausted.

Fixed

  • Elixir 1.14 test failures due to missing telemetry dependency - Added :telemetry as a direct dependency. It was used in 6 source files but only available transitively through optional deps (Finch, Req, Plug), causing 69 test failures on Elixir 1.14 where the telemetry application wasn't auto-started.

  • Req.Test.Ownership not started on Elixir 1.14 - SSL+proxy tests that use Req.Test.stub/2 directly now explicitly start the Req application, fixing 2 test failures on Elixir 1.14.

  • Circuit breaker now records 5xx and 429 responses as failures - Previously, handle_post_request treated any {:ok, response} as a success, including 5xx and 429 responses returned after retry exhaustion. This meant sustained server errors would never trip the circuit breaker. Now responses with status >= 500 or status == 429 are correctly recorded as failures.

  • Rate limiter :wait strategy re-checks tokens after sleeping - Previously, the :wait strategy would sleep and then consume without re-checking availability, which could lead to negative token counts under concurrency. Now it re-checks after waking up.

  • handle_info catch-all in RateLimiter and Dedup GenServers - Added catch-all handle_info/2 clauses to prevent crashes from unexpected messages.

  • Error.reason type spec includes tuple variants - Fixed the typespec to reflect that reason can be a tuple (e.g., {:circuit_breaker, :open}), not just an atom.

  • Removed unreachable error_message/1 catch-all clause - Cleaned up dead code in the Error module.

  • Client uses request.headers instead of re-extracting from opts - Fixed the client to use the already-built request headers rather than re-reading raw options.

0.16.0 - 2026-02-18

Fixed

  • Race condition in RateLimiter.consume/2 - consume/2 previously used two separate GenServer calls (check_rate_limit then consume_token). Under concurrency, multiple processes could pass the check before any consumed, allowing over-consumption and negative token counts. Replaced with a single atomic {:check_and_consume, ...} GenServer call that checks availability and subtracts in one step. Extracted shared refill_bucket/3 helper to reduce duplication between check_rate_limit and check_and_consume.

  • Documented dedup waiter ref-sharing edge case - Added comments explaining the shared reference mechanism in request deduplication and its safe timeout fallback when ETS entries are cleaned up between deduplicate and receive.

  • Req adapter connect_options overwrite - maybe_add_ssl_options and maybe_add_proxy_options now merge into existing :connect_options instead of overwriting. Previously, configuring both SSL and proxy on an HTTPS request would lose the SSL settings.

  • Inconsistent response header format across adapters - Tesla adapter now normalizes response headers to %{"key" => ["value"]} (list values), matching Finch and Req. The Response.t() type spec is updated to %{optional(String.t()) => [String.t()]}.

  • Hardcoded connection: close header removed - Finch and Req adapters no longer inject connection: close on every request. This was defeating HTTP connection pooling. Connection management is now left to the underlying client.

  • URL double-slash with trailing base_url slash - build_url("https://api.com/", "/users") now produces https://api.com/users instead of https://api.com//users.

  • Telemetry circuit_key: "unknown" fallback removed - emit_state_change_event now requires a real circuit key. All call sites updated to pass the actual key.

Added

  • PATCH, HEAD, and OPTIONS HTTP methods - Added HTTPower.patch/2, HTTPower.head/2, and HTTPower.options/2 to the public API, with full client instance support.

  • HTTPower.Config module - Standardized config resolution (request → application env → default) used by CircuitBreaker and RateLimiter, replacing inconsistent ad-hoc patterns.

Changed

  • Plug is now an optional dependency - Changed from only: :test to optional: true so library consumers can use HTTPower.Test without independently adding Plug to their deps.

  • Dedup cleanup interval increased from 1s to 5s - Reduces GenServer message processing overhead. Completed entries with a 500ms TTL sit harmlessly in memory for a few extra seconds before being cleaned up.

  • Updated moduledocs - HTTPower and HTTPower.Adapter moduledocs now correctly describe the adapter-based architecture with Finch as the default.

  • Mint.TransportError decoupled from core - Adapters now unwrap Mint.TransportError structs to plain atoms at the adapter boundary. HTTPower.Error and HTTPower.Retry no longer depend on Mint directly.

  • CircuitBreaker catch-all handle_info/2 - Silently ignores unexpected messages instead of logging GenServer warnings.

  • Generic deep merge for profile options - deep_merge_options now uses Keyword.keyword?/1 instead of a hardcoded key list, automatically deep-merging any nested keyword list config.

0.15.2 - 2026-02-06

Added

  • Retry-After HTTP date format support (RFC 7231) - parse_retry_after/1 now parses IMF-fixdate values (e.g., Wed, 21 Oct 2015 07:28:00 GMT) in addition to integer seconds. Converts the date to seconds-until-that-time for use in retry backoff. Dates in the past are treated as missing. Previously only integer seconds were supported.

Changed

  • CI matrix updated for Elixir 1.19 / OTP 28 - Added Elixir 1.19 and OTP 28 to test matrix, dropped OTP 25. Format, deps, and coverage jobs now run on latest versions.
  • Credo strict compliance - Resolved all 14 credo strict issues: implicit try in adapters, reduced function nesting/complexity, sorted aliases, removed redundant with clause, replaced apply/3 with direct call, and other cleanups.
  • Telemetry warning fix - Replaced anonymous function handlers with module-qualified captures to eliminate :telemetry performance warnings.
  • Code simplification - Consolidated duplicate error_message/1 into Error.message/1, removed dead code, simplified control flow. Net -159 lines.

0.15.1 - 2025-10-19

Changed

  • Relaxed optional dependency version requirements to prevent conflicts
    • Changed Finch from ~> 0.20 to >= 0.19.0 (optional)
    • Changed Req from ~> 0.4.0 to >= 0.4.0 (optional)
    • Changed Tesla from ~> 1.11 to >= 1.10.0 (optional)
    • Prevents version conflicts in consuming applications that need different HTTP client versions
    • For example, apps using Req 0.5+ alongside HTTPower will no longer encounter dependency conflicts
    • Consuming apps should specify their preferred HTTP client versions in their own mix.exs

Technical Details

  • Optional dependencies now use >= instead of ~> to maximize flexibility
  • Minimum version requirements still ensure compatibility with HTTPower's adapter code
  • All 368 tests passing with current dependency versions
  • This change particularly helps when multiple packages in an app have conflicting requirements for the same HTTP client

0.15.0 - 2025-10-19

Fixed

  • Critical: HTTPower.Test mocking now works when HTTPower is installed from Hex
    • Fixed HTTPower.TestInterceptor to use runtime checks instead of compile-time Mix.env() check
    • Test interception now works regardless of how HTTPower was compiled (hex package or local path)
    • Changed from if Mix.env() == :test to runtime Code.ensure_loaded?(HTTPower.Test) and HTTPower.Test.mock_enabled?()
    • Enables HTTPower.Test.stub/1 to work in consuming applications that install HTTPower from Hex
    • Previously, test mocking only worked when HTTPower was compiled locally in test environment

Technical Details

  • TestInterceptor.test_enabled?/0 now checks if HTTPower.Test module is loaded AND mock is enabled at runtime
  • No changes to test mode blocking logic in HTTPower.Client - that continues to work correctly
  • Separation of concerns: test_mode config blocks REAL requests, TestInterceptor enables MOCKED requests
  • All 368 tests passing with zero compilation warnings

0.14.0 - 2025-10-16

Fixed

  • Critical: Conditional compilation for optional adapter dependencies
    • Wrapped all adapter modules (Finch, Req, Tesla) with if Code.ensure_loaded?/1
    • Adapters now only compile when their dependencies are available
    • Fixes compilation errors when using HTTPower with only one adapter installed
    • Enables true optional dependencies - install only what you need
    • Standard Elixir pattern used by Phoenix, Ecto, and other ecosystem libraries

Technical Details

  • Adapter modules check dependency availability at compile time via Code.ensure_loaded?/1
  • When dependency is missing, adapter module is not defined (compilation skipped entirely)
  • Runtime adapter detection (detect_adapter/0) already uses Code.ensure_loaded?/1 - no changes needed
  • All 380 tests passing (368 tests + 12 doctests)
  • Verified compilation with various dependency combinations (Finch only, Req only, all three)

0.13.0 - 2025-10-13

Added

  • Intelligent middleware coordination - Middleware now coordinate intelligently for dramatically improved system capacity
    • Dedup bypasses rate limiting - Cache hits skip rate limiting entirely (5x effective capacity improvement)
      • Middleware reordered: Dedup → RateLimiter → CircuitBreaker
      • Pipeline short-circuits on cache hits with {:halt, response}
      • Telemetry coordination metadata: bypassed_rate_limit measurement + :rate_limit_bypass in metadata
      • Zero coupling - pure pipeline architecture
    • Adaptive rate limiting - Dynamically adjusts rates based on downstream health (prevents thundering herd)
      • When circuit breaker opens (service down) → 10% of normal rate
      • When circuit breaker half-open (recovering) → 50% of normal rate
      • When circuit breaker closed (healthy) → 100% of normal rate
      • Enabled by default with adaptive: true in rate limit config
      • Read-only state queries preserve loose coupling
      • Telemetry events: [:httpower, :rate_limit, :adaptive_reduction]
    • Configuration profiles - Pre-configured settings for common use cases
      • :payment_processing - Conservative settings for payment gateways (Stripe, PayPal)
        • 100 req/min, 30% failure threshold, 5s dedup window
        • Prevents double charges and duplicate orders automatically
      • :high_volume_api - High throughput for public APIs
        • 1000 req/min, 50% failure threshold, 1s dedup window
        • Maximum throughput with dedup providing 5x capacity boost
      • :microservices_mesh - Balanced settings for service-to-service calls
        • 500 req/min, 40% failure threshold, 2s dedup window
        • Prevents cascade failures and retry storms
      • HTTPower.Profiles.list/0 - List all available profiles
      • HTTPower.Profiles.get/1 - Get profile configuration
      • Profiles support option overrides with deep merge (nested options preserved)

Changed

  • Middleware pipeline handling improvements

    • Fixed pipeline short-circuit bug where {:halt, response} was incorrectly processed
    • Pipeline now correctly distinguishes between {:ok, %Request{}} (continue) and {:halt, %Response{}} (short-circuit)
    • Removed obsolete handle_pipeline_result/1 functions (replaced with direct pattern matching)
    • Cleaner error handling for middleware failures
  • Profile configuration with deep merge

    • User options now deep merge with profile defaults for nested keys (:rate_limit, :circuit_breaker, :deduplicate)
    • Example: profile: :high_volume_api, rate_limit: [requests: 2000] preserves per: :minute from profile
    • Prevents accidental override of entire nested config
    • Explicit options always win over profile defaults

Technical Details

  • Coordination patterns
    • Pattern A: Dedup bypasses rate limiting through pipeline ordering (0 lines of coupling code)
    • Pattern B: Adaptive rate limiting via read-only state queries (loose coupling preserved)
    • Deep merge algorithm handles nested keyword lists correctly
  • Pipeline short-circuit fix
    • Changed from with statement to case statement for clearer flow control
    • Direct pattern matching on {:ok, %Request{}} vs {:halt, %Response{}}
    • Post-request handlers only called when HTTP actually executed
  • Test improvements
    • 14 new coordination tests (all passing)
    • Tests use synchronous requests to avoid process dictionary issues
    • Comprehensive coverage of all coordination patterns
  • Performance impact
    • Dedup cache hits: 5x effective capacity (skip rate limiter entirely)
    • Adaptive rate limiting: Prevents thundering herd during recovery
    • Zero overhead for disabled features (compile-time pipeline)
  • All 371 tests passing (14 new coordination tests)
  • Zero compilation warnings
  • Comprehensive telemetry for observability

0.12.0 - 2025-10-13

Added

  • Finch adapter - High-performance HTTP client built on Mint and NimblePool
    • HTTPower.Adapter.Finch - New adapter using Finch HTTP client
    • Performance-focused with explicit connection pooling
    • Built on Mint (same low-level library that powers Req)
    • SSL/TLS support with configurable verification
    • Proxy support (system or custom)
    • Manual JSON decoding with Jason for flexibility
    • Connection pool configuration via Application config
    • Comprehensive test suite (357 tests passing)

Changed

  • Finch is now the default adapter (breaking change for adapter detection order)
    • Adapter detection priority: Finch → Req → Tesla
    • All adapters remain optional - users can choose what to install
    • No breaking changes to existing code (auto-detection still works)
    • Updated error message to recommend Finch first
    • Updated README and documentation to reflect Finch as default
  • Renamed Feature to Middleware for clearer semantics
    • HTTPower.FeatureHTTPower.Middleware (module namespace change)
    • lib/httpower/feature/lib/httpower/middleware/ (directory reorganization)
    • All middleware modules moved: RateLimiter, CircuitBreaker, Dedup
    • Updated all references in code, tests, and documentation
    • Industry-standard terminology (matches Phoenix/Plug, Express, Rails)
    • Zero breaking changes to public API - internal refactoring only
  • Logger tests updated to use Finch adapter
    • Converted from Req.Test to HTTPower.Test (adapter-agnostic)
    • All 51 logger tests now test with Finch adapter
    • Tests demonstrate adapter-agnostic testing approach

Technical Details

  • Finch adapter handles both URI structs and strings
  • Header format conversion to match Req's format (map with list values)
  • Automatic JSON body parsing when response is valid JSON
  • Conditional supervision - Finch only started if loaded via Code.ensure_loaded?/1
  • Default pool configuration: 10 connections per pool, one pool per scheduler
  • Configurable pools via config :httpower, :finch_pools
  • Test coverage excludes Finch adapter (tested via integration tests)
  • All production features work consistently across Finch, Req, and Tesla adapters
  • Middleware pattern provides clearer mental model for request/response pipeline
  • All 357 tests passing with zero compilation warnings

0.11.0 - 2025-10-13

Changed

  • Refactored client to extensible pipeline architecture

    • Introduced HTTPower.Feature behaviour for composable request pipeline features
    • New HTTPower.Request struct for context passing between pipeline stages
    • Generic recursive step executor works with ANY feature implementation
    • Compile-time feature registry with zero overhead for disabled features
    • Runtime config merging (runtime takes precedence over compile-time)
    • Features can inspect, modify, short-circuit, or fail requests
    • Clean separation: features communicate via request.private map
  • URL validation and URI struct improvements

    • Early fail-fast URL validation with clear error messages
    • Parse URL once at request start, use URI struct throughout pipeline
    • Direct field access: request.url.host instead of helper functions
    • SSL check now uses pattern matching: %URI{scheme: "https"}
    • Eliminates repeated URL parsing for better performance
  • Feature implementations refactored to use pipeline architecture

    • All features (RateLimiter, CircuitBreaker, Dedup) now implement HTTPower.Feature behaviour
    • Simplified key extraction: request.url.host instead of URL parsing
    • Features store state in request.private for post-request processing
    • Circuit breaker and dedup can short-circuit pipeline with {:halt, response}
    • More consistent error handling across all features
  • Request execution flow improvements

    • Request struct now built early in request lifecycle and passed throughout pipeline
    • Cleaner parameter passing: 2 parameters instead of 6 in execution functions
  • Code cleanup and documentation

    • Removed ~57 redundant comments that described what code does rather than why
    • Kept important comments explaining architectural decisions
    • Client.ex, RateLimiter.ex, CircuitBreaker.ex, Dedup.ex all cleaned up
    • Improved code readability while maintaining comprehensive inline documentation

Technical Details

  • Pipeline execution: Features run in order (RateLimiter → CircuitBreaker → Dedup)
  • Zero overhead: Disabled features not included in compiled pipeline
  • Post-request cleanup: Circuit breaker recording and dedup completion handled automatically
  • Extensibility: Adding new features requires only implementing HTTPower.Feature behaviour
  • Type safety: URI structs ensure valid URLs throughout the pipeline
  • Testability: Generic pipeline executor simplifies testing of new features
  • Request flow: Request struct created early (line 103), passed through entire pipeline
  • All 348 tests passing
  • Zero compile warnings
  • Net code addition: 434 lines (+721 -287) with significant architectural improvements

0.10.0 - 2025-10-07

Added

  • Structured logging with metadata for log aggregation
    • All log entries now include structured metadata via Logger.metadata()
    • Request metadata: httpower_correlation_id, httpower_event, httpower_method, httpower_url, headers, body
    • Response metadata: httpower_correlation_id, httpower_event, httpower_status, httpower_duration_ms, headers, body
    • Exception metadata: httpower_correlation_id, httpower_event, httpower_duration_ms, httpower_exception_kind, httpower_exception_reason
    • Enables powerful querying in log aggregation systems (Datadog, Splunk, ELK, Loki)
    • Query examples: httpower_duration_ms:>1000, httpower_status:>=500, httpower_correlation_id:"req_abc123"
    • All metadata respects log_headers and log_body configuration
    • Large bodies automatically truncated to 500 characters in metadata
    • All sensitive data sanitized before adding to metadata
    • Added 9 comprehensive tests for metadata functionality

Changed

  • Performance: ETS write concurrency optimization

    • Added {:write_concurrency, true} to all ETS tables (CircuitBreaker, RateLimiter, Dedup)
    • Expected 2-3x throughput improvement under high concurrency (50+ concurrent requests)
    • Enables parallel writes across multiple processes without serialization
    • Production-grade performance for high-traffic scenarios
  • Performance: CircuitBreaker async recording

    • Switched from synchronous GenServer.call to async GenServer.cast for result recording
    • Expected 5-10x improvement in high-throughput scenarios
    • Non-blocking operation: requests don't wait for state updates
    • Eventually consistent state (5-10ms delay acceptable for circuit breaker logic)
    • Updated tests to handle async state changes with polling helper
  • Performance: Configuration caching optimization

    • Implemented compile-time config caching using Application.compile_env
    • Eliminates repeated Application.get_env calls on every request
    • Module attributes cache default config values at compile time
    • Config resolution order: request-level → compile-time cached → runtime → hardcoded default
    • Runtime fallback ensures tests can dynamically override config
    • Particularly beneficial for high-throughput scenarios

Technical Details

  • ETS concurrency: {:write_concurrency, true} uses distributed locks for better parallelism
  • CircuitBreaker: Test updates include await_state/3 helper for polling async state changes
  • Config caching: @default_adapter, @default_config, @default_failure_threshold, etc. cached at compile time
  • Structured logging: Machine-readable metadata fields for production observability
  • All 348 tests passing (9 new tests for structured logging metadata)
  • Comprehensive documentation updates across README, guides, and CLAUDE.md

0.9.0 - 2025-10-06

Added

  • Comprehensive telemetry integration using Erlang's :telemetry library
    • HTTP request lifecycle events: [:httpower, :request, :start], [:httpower, :request, :stop], [:httpower, :request, :exception]
    • Retry attempt events: [:httpower, :retry, :attempt] with attempt_number, delay_ms, and reason
    • Rate limiter events: [:httpower, :rate_limit, :ok], [:httpower, :rate_limit, :wait], [:httpower, :rate_limit, :exceeded]
    • Circuit breaker events: [:httpower, :circuit_breaker, :state_change], [:httpower, :circuit_breaker, :open]
    • Deduplication events: [:httpower, :dedup, :execute], [:httpower, :dedup, :wait], [:httpower, :dedup, :cache_hit]
    • All events include rich measurements (duration, timestamps) and metadata (method, url, status, etc.)
    • URLs automatically sanitized (query params/fragments stripped) for low cardinality in metrics
    • Default ports (80/443) excluded from URL telemetry for cleaner metrics
    • Zero dependencies (:telemetry ships with Elixir)
    • Full observability guide with Prometheus, OpenTelemetry, and LiveDashboard examples

Integration Examples:

# Prometheus metrics
distribution("httpower.request.duration",
  event_name: [:httpower, :request, :stop],
  measurement: :duration,
  unit: {:native, :millisecond},
  tags: [:method, :status]
)

# OpenTelemetry
OpentelemetryTelemetry.register_application_tracer(:httpower)

# Custom logging
:telemetry.attach("httpower-logger", [:httpower, :request, :stop], &log_request/4, nil)

Documentation:

  • Added comprehensive observability guide at guides/observability.md
  • Updated README with Observability & Telemetry section
  • Added 11 new telemetry integration tests (339 total tests, all passing)

0.8.1 - 2025-10-01

Fixed

  • Documentation updates for v0.8.0 breaking changes
    • Updated all error atom references in README examples
    • Updated configuration reference with new error atoms
    • Updated migration guides (Tesla and Req)
    • Updated production deployment guide examples
    • Restructured Configuration Availability Matrix for better HTML rendering
    • All documentation now correctly references :too_many_requests and :service_unavailable

0.8.0 - 2025-10-01

Changed

  • BREAKING: Plug-compatible error atoms for Phoenix integration
    • Changed :rate_limit_exceeded:too_many_requests (HTTP 429)
    • Changed :circuit_breaker_open:service_unavailable (HTTP 503)
    • Enables seamless Phoenix/Plug integration without manual error mapping
    • HTTPower-specific atoms preserved: :rate_limit_wait_timeout, :dedup_timeout, transport errors
    • All error handling code must be updated to use new atoms

Migration Guide:

# Update error pattern matching:
{:error, %{reason: :rate_limit_exceeded}}    # OLD
{:error, %{reason: :too_many_requests}}      # NEW

{:error, %{reason: :circuit_breaker_open}}   # OLD
{:error, %{reason: :service_unavailable}}    # NEW

0.7.1 - 2025-10-01

Fixed

  • Critical: CircuitBreaker race condition in half-open state
    • Fixed race condition where multiple concurrent processes could exceed half_open_max_requests limit
    • Increment counter BEFORE allowing request through (prevents concurrent bypass)
    • Added comprehensive concurrent request test (10 concurrent requests, verifies only 3 allowed)
  • Critical: ETS table orphaning on GenServer crash
    • Fixed GenServer crashes orphaning ETS tables and causing supervisor restart loops
    • Added {:heir, :none} to all ETS table creations (RateLimiter, CircuitBreaker, Dedup)
    • Tables now automatically deleted when owning process terminates
    • Added crash recovery tests for all three GenServers
  • Critical: Dedup waiter timeout memory leak
    • Fixed memory leak where dead/timeout waiter processes remained in memory indefinitely
    • Added process monitoring to detect when waiters die or timeout
    • Automatic cleanup removes dead waiters from in-flight request lists
    • Added tests for waiter death and timeout scenarios

Changed

  • Performance: RateLimiter config caching
    • Cache default configuration at GenServer startup (eliminates repeated Application.get_env calls)
    • ~15-20% reduction in rate limiter overhead per request
    • Config changes now require GenServer restart to take effect (production-realistic behavior)
    • Helper functions split into public (backward compatible) and optimized (GenServer) versions

Technical Details

  • All fixes based on comprehensive architectural review (see doc/architecture-improvements.md)
  • CircuitBreaker: Inverted condition and atomic increment prevents race
  • ETS tables: {:heir, :none} ensures clean supervisor restarts
  • Dedup: Process monitors with :DOWN message handling for cleanup
  • RateLimiter: Cached config in GenServer state, passed to callbacks
  • All 328 tests passing with comprehensive coverage of new scenarios

0.7.0 - 2025-10-01

Added

  • Request deduplication - Prevent duplicate operations from double-clicks and race conditions
    • HTTPower.Dedup GenServer for tracking in-flight requests
    • Hash-based fingerprinting using method + URL + body
    • Response sharing: duplicate requests wait for first request to complete
    • Automatic cleanup of completed requests after 500ms TTL
    • Global configuration: config :httpower, :deduplication, enabled: true
    • Per-request control: deduplicate: true or deduplicate: [key: custom_key]
    • Custom deduplication keys for fine-grained control
    • Client-side protection that complements server-side idempotency keys
    • Integrated into request pipeline (after rate limit check, before circuit breaker)
    • 18 comprehensive tests covering hash generation, in-flight tracking, response sharing, cleanup, and high concurrency
  • Rate limit headers parsing - Automatic detection and parsing of server rate limits from HTTP response headers
    • HTTPower.RateLimitHeaders.parse/2 - Parses rate limit headers from responses
    • Supports multiple common formats:
      • GitHub/Twitter style: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
      • RFC 6585/IETF style: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset
      • Stripe style: X-Stripe-RateLimit-* headers
      • Retry-After header (integer seconds format)
    • Auto-detection with :auto format (default), or explicit format specification
    • Case-insensitive header matching
    • Handles header values as strings, lists, or integers (adapter-agnostic)
  • Rate limiter integration with server headers - Synchronize local rate limiter with server state
    • HTTPower.RateLimiter.update_from_headers/2 - Updates bucket state from parsed headers
    • HTTPower.RateLimiter.get_info/1 - Returns current bucket information
    • Server-provided limits synchronize with token bucket algorithm
    • Buckets continue to refill after synchronization

Technical Details

  • Header parser uses format auto-detection, trying GitHub → RFC → Stripe formats in order
  • Parser handles all adapter header formats (Req's list of tuples, Tesla's various formats)
  • Integration updates token bucket state to match server's remaining count
  • Comprehensive test coverage: 38 tests for parser, 7 tests for integration, 5 tests for Retry-After
  • HTTP date format in Retry-After supported since v0.15.2 (previously only integer seconds)
  • Automatic backoff - Retry logic respects Retry-After header on 429/503 responses
    • When server provides Retry-After header (integer seconds), HTTPower uses that exact wait time
    • Falls back to exponential backoff when header is missing
    • Only applies to 429 (Too Many Requests) and 503 (Service Unavailable) status codes
    • Other retryable status codes (408, 500, 502, 504) continue using exponential backoff

Changed

  • Code organization and readability improvements in HTTPower.Client
    • Reorganized file into logical sections: Public API, Main Pipeline, Retry Logic, Adapters, Test Mode, Rate Limit Config, Circuit Breaker Config, Logging, Error Handling
    • Refactored request flow to use clean with pipelines instead of nested case statements
    • All helper functions now return explicit {:ok, value} or {:error, reason} tuples for consistency
    • Moved test mode check to beginning of request pipeline for fail-fast behavior
    • Removed redundant code comments (kept only user-facing messages and section headers)
    • Simplified parameter passing by extracting options at point of use
    • Eliminated unnecessary wrapper functions for cleaner call stack
    • Net reduction of 27 lines while improving code clarity

Technical Details

  • No performance changes - refactoring only improves maintainability
  • Rate limiter and circuit breaker already have early-exit optimizations when disabled
  • All 304 tests passing, 0 compile warnings
  • Renamed do_http_request/3execute_http_request/3 for clarity
  • Removed do_request/3 wrapper that just delegated to execute_http_request/3

0.6.0 - 2025-09-30

Added

  • Global adapter configuration - Configure HTTP adapter application-wide
    • config :httpower, adapter: HTTPower.Adapter.Req for global adapter selection
    • config :httpower, adapter: {HTTPower.Adapter.Tesla, tesla_client} for pre-configured clients
    • Configuration priority: per-request > per-client > global
    • Allows adapter switching without code changes
  • Comprehensive documentation structure in guides/ directory
    • guides/migrating-from-tesla.md - Complete 7-step Tesla migration guide
      • Emphasizes adapter-agnostic final code
      • Shows HTTPower.Test for testing (not Tesla.Mock)
      • Global and per-client configuration examples
    • guides/migrating-from-req.md - Req migration guide with error handling differences
    • guides/configuration-reference.md - Complete option reference with availability matrix
    • guides/production-deployment.md - Production deployment guide with supervision tree, monitoring, security
    • Moved examples to guides/examples/ directory
  • Configuration availability matrix showing which options work at global/per-client/per-request levels
  • Updated ExDoc integration with organized guide groups (Migration Guides, Guides, Examples)

Changed

  • README improvements
    • Simplified "Adapter Support" section (removed confusing adapter-specific examples)
    • Added "Perfect For" section at top showing target use cases
    • Updated "Basic Usage" to show both direct and client-based patterns
    • Split "Correlation IDs" into standalone section (not just PCI logging)
    • Removed redundant "Production Considerations" and "Why HTTPower?" sections
    • Updated all references from Req.Test to HTTPower.Test
  • Documentation corrections across all guides
    • Fixed sanitization config structure: sanitize_headers and sanitize_body_fields (not nested under sanitize:)
    • Clarified that custom sanitization fields are additive (supplement defaults, not replace)
    • Updated all examples to use correct configuration structure

Technical Details

  • Global adapter configuration integrates with existing per-client/per-request options
  • Adapter detection order: per-request option → global config → auto-detection (Req preferred)
  • Documentation now correctly reflects implementation details
  • All configuration examples consistent across README, guides, and reference docs

0.5.0 - 2025-09-30

Added

  • Circuit breaker pattern implementation for protecting against cascading failures
  • HTTPower.CircuitBreaker GenServer with three-state machine (closed, open, half-open)
  • Sliding window failure tracking with unified request history
    • Tracks last N requests (both successes and failures) in a single sliding window
    • More accurate than separate windows for each result type
  • Dual threshold strategies:
    • Absolute threshold: Open circuit after N failures
    • Percentage threshold: Open circuit when failure rate exceeds X%
  • Automatic state transitions:
    • Closed → Open: When failure threshold is exceeded
    • Open → Half-Open: After timeout period expires
    • Half-Open → Closed: When all test requests succeed
    • Half-Open → Open: When any test request fails
  • Half-open state with configurable test request limit
  • Manual circuit control:
    • HTTPower.CircuitBreaker.open_circuit/1 - Manually open a circuit
    • HTTPower.CircuitBreaker.close_circuit/1 - Manually close a circuit
    • HTTPower.CircuitBreaker.reset_circuit/1 - Reset circuit to initial state
    • HTTPower.CircuitBreaker.get_state/1 - Check current circuit state
  • Flexible circuit breaker configuration:
    • Global configuration via config :httpower, :circuit_breaker
    • Per-client configuration via HTTPower.new/1
    • Per-request configuration via request options
    • Custom circuit keys for grouping requests
  • Integration with existing retry logic: Circuit breaker complements exponential backoff
    • Retry logic handles transient failures (timeouts, temporary errors)
    • Circuit breaker handles persistent failures (service outages, deployment issues)
  • Comprehensive test suite (26 new tests covering all states, thresholds, transitions)
  • Added circuit breaker section to README with examples and best practices

Changed

  • Refactored circuit breaker sliding window implementation
    • Changed from separate success/failure windows to unified request tracking
    • Uses tuples {:success | :failure, timestamp} for better accuracy

    • Window size now correctly limits total requests tracked (not per-type)
  • Updated HTTPower.Client to integrate circuit breaker into request flow
  • Circuit breaker wraps retry logic to provide fail-fast behavior when circuit is open
  • Updated documentation to clarify relationship between circuit breaker and retry logic

Technical Details

  • Circuit breaker uses ETS for thread-safe state storage
  • State transitions are logged for observability
  • Circuit keys default to URL host but can be customized
  • Sliding window implementation ensures accurate failure rate tracking
  • Half-open state prevents thundering herd by limiting concurrent test requests

0.4.0 - 2025-09-30

Added

  • Built-in rate limiting with token bucket algorithm
  • HTTPower.RateLimiter GenServer for managing rate limit state
  • HTTPower.Application supervision tree for fault tolerance
  • Two rate limiting strategies:
    • :wait - Blocks until tokens are available (up to max_wait_time)
    • :error - Returns {:error, :too_many_requests} immediately
  • Flexible rate limit configuration:
    • Global configuration via config :httpower, :rate_limit
    • Per-client configuration via HTTPower.new/1
    • Per-request configuration via request options
    • Custom bucket keys for grouping requests
  • ETS-based storage for high performance and low latency
  • Automatic bucket cleanup removes inactive buckets after 5 minutes
  • Time window support: :second, :minute, :hour
  • Thread-safe rate limiting with atomic ETS operations
  • Comprehensive test suite (23 new tests covering token bucket algorithm, strategies, concurrent access)

Changed

  • Updated HTTPower.Client to check rate limits before each request
  • Rate limiting integrated into request flow (happens before logging)
  • Default bucket key uses URL host (can be overridden with :rate_limit_key)

Fixed

  • Fixed Plug.Conn undefined warnings by adding @compile {:no_warn_undefined} directive to HTTPower.Test

Technical Details

  • Token bucket algorithm: tokens refill continuously at configured rate
  • Refill rate calculated as: max_tokens / time_window_ms
  • Elapsed time since last refill determines available tokens
  • GenServer handles concurrent access with ETS atomic operations
  • Cleanup runs every 60 seconds, removes buckets inactive for 5+ minutes
  • Works consistently across all adapters (Req, Tesla)

0.3.1 - 2025-09-30

Added

  • PCI-compliant HTTP request/response logging with automatic data sanitization
  • HTTPower.Logger module for production-ready logging with security built-in
  • Correlation IDs for distributed tracing - every request gets a unique ID (format: req_abc123...)
  • Request duration tracking - logs include timing information in milliseconds
  • Automatic sanitization of sensitive data in logs:
    • Credit card numbers (13-19 digits with optional spaces/dashes)
    • CVV codes (3-4 digits)
    • Authorization headers (Bearer tokens, Basic auth)
    • API keys and secret tokens
    • Password fields in JSON bodies
    • Configurable custom fields via application config
  • Configurable logging via application config:
    • enabled - Enable/disable logging globally (default: true)
    • level - Log level: :debug, :info, :warning, :error (default: :info)
    • sanitize_headers - Additional headers to sanitize
    • sanitize_body_fields - Additional body fields to sanitize
  • Comprehensive test suite for logging (42 new tests, 98.67% module coverage)
  • Logging works consistently across all adapters (Req, Tesla)

Changed

  • Updated HTTPower.Client to integrate logging at request/response boundaries
  • Request flow now includes: correlation ID generation → request logging → execution → response/error logging
  • All HTTP operations now automatically log with sanitization (can be disabled via config)

Technical Details

  • Correlation IDs generated using cryptographically secure random bytes
  • Sanitization uses regex patterns for credit cards, CVV codes
  • JSON field sanitization supports nested maps and arrays
  • Headers normalized to lowercase for consistent sanitization
  • Large response bodies (>500 chars) are truncated in logs
  • Logging sits above adapter layer - works identically with Req or Tesla

0.3.0 - 2025-09-30

Added

  • Adapter pattern supporting multiple HTTP clients (Req and Tesla)
  • HTTPower.Adapter behavior for implementing custom adapters
  • HTTPower.Adapter.Req - adapter using Req HTTP client
  • HTTPower.Adapter.Tesla - adapter for existing Tesla users
  • HTTPower.Test - adapter-agnostic testing module with zero external dependencies
  • HTTPower.TestInterceptor - clean separation of test logic from production code using compile-time checks
  • Comprehensive test suite proving adapter independence (50 tests total)
  • adapter option for specifying which adapter to use
  • Smart adapter detection - automatically uses available adapter (Req preferred if both present)
  • Both Req and Tesla are now optional dependencies - choose the one you need
  • Example files moved to docs/ directory

Fixed

  • Critical: Fixed double retry bug where Req's built-in retry ran alongside HTTPower's retry
  • Critical: Fixed double error wrapping where HTTPower.Error structs were wrapped multiple times
  • Req adapter now explicitly sets retry: false to disable Req's retry mechanism
  • Retry logic now runs consistently once per request attempt
  • Error handling now checks if errors are already wrapped before wrapping again

Changed

  • Internal architecture refactored to use adapter pattern (public API unchanged)
  • HTTPower.Client now routes requests through adapters instead of calling Req directly
  • Design principle updated from "Req-Based" to "Adapter-Based"
  • Documentation updated to emphasize adapter flexibility and production features
  • Refactored control flow to eliminate nested conditionals (removed if inside with, case inside if)
  • Main test suite now uses HTTPower.Test instead of Req.Test for true adapter independence

Technical Details

  • Adapter abstraction allows production features (retry, circuit breaker, rate limiting) to work consistently across HTTP clients
  • Symmetric dependencies: Both Req and Tesla are optional - install only what you need
  • Smart adapter detection automatically selects available adapter (prefers Req if both present)
  • Backward compatible: existing code continues to work (Req auto-detected if installed)
  • Tesla users can now adopt HTTPower without pulling in Req dependency
  • Clear error message if neither adapter is installed
  • Test-mode blocking works seamlessly across both adapters
  • All 50 tests passing (29 main + 9 Req adapter + 9 Tesla adapter + 3 transport error tests)

0.2.0 - 2025-01-09

Added

  • Client configuration with HTTPower.new/1 for reusable HTTP clients
  • Base URL support for configured clients with automatic path resolution
  • Option merging between client defaults and per-request settings
  • Support for all HTTP methods (GET, POST, PUT, DELETE) with client instances
  • Comprehensive test coverage for client configuration functionality
  • HTTP status code retry logic following industry standards (408, 429, 500, 502, 503, 504)
  • Exponential backoff with jitter for intelligent retry timing
  • Configurable retry parameters: base_delay, max_delay, jitter_factor
  • Fast unit tests for retry decision logic separated from execution

Improved

  • Retry test suite performance improved by 70% (48s → 15s) through separation of concerns
  • Refactored retry decision functions for better testability and maintainability

Fixed

  • Corrected changelog to accurately reflect implemented vs planned features
  • Updated documentation to clarify current capabilities

0.1.0 - 2025-09-09

Added

  • Basic HTTP methods (GET, POST, PUT, DELETE) with clean API
  • Test mode request blocking to prevent real HTTP requests during testing
  • Req.Test integration for controlled HTTP testing and mocking
  • Smart retry logic with configurable policies and error categorization
  • Clean error handling that never raises exceptions
  • SSL certificate verification with configurable options
  • Proxy configuration support (system proxy and custom proxy settings)
  • Request timeout management with sensible defaults
  • Comprehensive error messages for common network issues
  • Support for custom headers and request bodies
  • Automatic Content-Type headers for POST requests
  • Connection close headers for reliable request handling
  • Mint.TransportError handling for detailed network error reporting
  • Complete test suite with 100% coverage
  • Full documentation with examples and API reference
  • Hex package configuration for easy installation

Technical Details

  • Built on top of Req HTTP client for reliability
  • Uses Mint for low-level HTTP transport (via Req)
  • Supports HTTP/1.1 and HTTP/2 protocols
  • Elixir 1.14+ compatibility
  • Production-ready error handling and logging
  • PCI DSS compliance considerations in design