Configuration management for PushX.
Configuration Options
APNS (Apple Push Notification Service)
:apns_key_id- The Key ID from Apple Developer Portal:apns_team_id- Your Apple Developer Team ID:apns_private_key- The private key, either:- A raw PEM string
{:file, "/path/to/AuthKey.p8"}{:system, "ENV_VAR_NAME"}
:apns_mode-:prodor:sandbox(default::prod)
FCM (Firebase Cloud Messaging)
:fcm_project_id- Your Firebase project ID:fcm_credentials- Service account credentials, either:{:file, "/path/to/service-account.json"}{:json, "...json string..."}{:system, "ENV_VAR_NAME"}(expects JSON string)
:fcm_token_fetcher- (optional, advanced) bring your own OAuth: an{module, function, args}tuple that replaces the Goth process PushX would otherwise start. Seefcm_token_fetcher/0.
Testing
:delivery-:live(default) or:test— record sends locally instead of contacting APNS/FCM; seePushX.Test
Finch Pool
:finch_name- Name of the Finch pool (default:PushX.Finch):finch_pool_size- Connections per pool (default: 25):finch_pool_count- Number of pools (default: 2)
Request Timeouts
:receive_timeout- Timeout for receiving response in ms (default:15_000):pool_timeout- Timeout for acquiring connection from pool in ms (default:5_000):connect_timeout- TCP connection timeout in ms (default:10_000)
Retry Settings
:retry_enabled- Enable automatic retry (default:true):retry_max_attempts- Maximum retry attempts (default:3):retry_base_delay_ms- Base delay in milliseconds (default:10_000):retry_max_delay_ms- Maximum delay in milliseconds (default:60_000):reconnect_cooldown_ms- Minimum time between automatic Finch pool restarts triggered by connection errors, per pool (default:5_000); seePushX.ReconnectGuard. ManualPushX.reconnect/0is not gated.
Batch Sending
:batch_concurrency- Default:concurrencyforPushX.push_batch/4,push_batch_stream/4and the providersend_batch/3functions (default:50)
Circuit Breaker (opt-in)
:circuit_breaker_enabled- (default:false):circuit_breaker_threshold- consecutive failures that open the breaker (default:5):circuit_breaker_cooldown_ms- open → half-open delay (default:30_000)
See PushX.CircuitBreaker.
Rate Limiting (opt-in)
:rate_limit_enabled- (default:false):rate_limit_apns,:rate_limit_fcm- max sends per window per provider (default:5_000):rate_limit_window_ms- fixed window length (default:1_000)
See PushX.RateLimiter.
Token Cleanup
:on_invalid_token-{module, function, args}invoked asynchronously asapply(module, function, [provider, token | args])whenever a response says the token should be removed (PushX.Response.should_remove_token?/1); see the README's "Token Cleanup Callback".
Internal / test-only
:apns_url_override,:fcm_url_override- point the real send paths at a local HTTP server. Used by PushX's own test suite; not for production. For testing your application use:delivery(PushX.Test) instead.
Example Configuration
config :pushx,
apns_key_id: "ABC123DEFG",
apns_team_id: "TEAM123456",
apns_private_key: {:file, "priv/keys/AuthKey.p8"},
apns_mode: :prod,
fcm_project_id: "my-project-id",
fcm_credentials: {:file, "priv/keys/firebase.json"}
Summary
Functions
Checks if APNS is configured.
Gets the APNS Key ID.
Gets the APNS mode (:prod or :sandbox).
Gets the APNS private key content. Supports file paths, environment variables, and raw strings.
Gets the APNS Team ID.
Default per-task timeout for batch sends, in milliseconds.
Like batch_timeout_ms/0, but for a specific per-call retry policy:
retry: :none means a single attempt, so the budget is the 30 s floor.
Gets the cooldown time in milliseconds before the circuit transitions
from :open to :half_open.
Default: 30 seconds.
Checks if the circuit breaker is enabled.
Default: false (opt-in feature).
Gets the number of consecutive failures before the circuit opens. Default: 5.
Gets the TCP connection timeout in milliseconds. Default: 10 seconds.
Delivery mode: :live (default) sends to the providers; :test records
sends locally instead — see PushX.Test.
Checks if FCM is configured.
Gets the FCM credentials for Goth. Returns a map suitable for Goth configuration.
Gets the FCM project ID.
Returns the custom FCM OAuth token fetcher, if one is configured.
Gets the Finch pool name.
Gets the Finch pool count (number of connection pools).
Gets the Finch pool size (connections per pool).
Returns the Finch request options with configured timeouts.
Gets a configuration value.
Gets a required configuration value. Raises if the value is not configured.
Gets the callback for invalid token cleanup.
Gets the pool timeout (time to wait for a connection from pool) in milliseconds. Default: 5 seconds.
Gets the receive timeout (time to wait for response data) in milliseconds. Default: 15 seconds.
Gets the overall request timeout in milliseconds. Default: 30 seconds.
Gets the base delay for exponential backoff in milliseconds. Default: 10 seconds (Google's recommended minimum).
Checks if retry is enabled.
Gets the maximum number of retry attempts.
Gets the maximum delay for exponential backoff in milliseconds. Default: 60 seconds.
Functions
@spec apns_configured?() :: boolean()
Checks if APNS is configured.
@spec apns_key_id() :: String.t()
Gets the APNS Key ID.
@spec apns_mode() :: :prod | :sandbox
Gets the APNS mode (:prod or :sandbox).
@spec apns_private_key() :: String.t()
Gets the APNS private key content. Supports file paths, environment variables, and raw strings.
@spec apns_team_id() :: String.t()
Gets the APNS Team ID.
@spec batch_timeout_ms() :: pos_integer()
Default per-task timeout for batch sends, in milliseconds.
Retries block the sending task (see PushX.Retry), so a batch task can
legitimately outlive any flat timeout while it backs off between attempts.
When retries are enabled this covers the worst-case retry budget:
attempts × (receive_timeout + pool_timeout)
+ (attempts − 1) × max(retry_max_delay_ms, 60s rate-limit delay)With retries disabled — globally (retry_enabled: false) or for the call
(retry: :none, see batch_timeout_ms/1) — it is 30 seconds. An explicit
:timeout option on PushX.push_batch/4 / send_batch/3 always takes
precedence.
@spec batch_timeout_ms([{:retry, :blocking | :none}]) :: pos_integer()
Like batch_timeout_ms/0, but for a specific per-call retry policy:
retry: :none means a single attempt, so the budget is the 30 s floor.
@spec circuit_breaker_cooldown_ms() :: pos_integer()
Gets the cooldown time in milliseconds before the circuit transitions
from :open to :half_open.
Default: 30 seconds.
@spec circuit_breaker_enabled?() :: boolean()
Checks if the circuit breaker is enabled.
Default: false (opt-in feature).
@spec circuit_breaker_threshold() :: pos_integer()
Gets the number of consecutive failures before the circuit opens. Default: 5.
@spec connect_timeout() :: pos_integer()
Gets the TCP connection timeout in milliseconds. Default: 10 seconds.
@spec delivery() :: :live | :test
Delivery mode: :live (default) sends to the providers; :test records
sends locally instead — see PushX.Test.
@spec fcm_configured?() :: boolean()
Checks if FCM is configured.
Gets the FCM credentials for Goth. Returns a map suitable for Goth configuration.
@spec fcm_project_id() :: String.t()
Gets the FCM project ID.
Returns the custom FCM OAuth token fetcher, if one is configured.
By default PushX starts a Goth process
(PushX.Goth) from :fcm_credentials and calls Goth.fetch/1 before every
FCM send. Set :fcm_token_fetcher to an {module, function, args} tuple to
supply the OAuth access token yourself instead — for example to reuse a
Goth process your application already runs, or to fetch tokens from a
secrets service:
# config/runtime.exs
config :pushx,
fcm_project_id: "my-project",
fcm_token_fetcher: {MyApp.PushOAuth, :fetch, []}
defmodule MyApp.PushOAuth do
# PushX passes the Goth name it would have used as the first argument;
# a fetcher that reuses your own Goth simply ignores it.
def fetch(_goth_name), do: Goth.fetch(MyApp.Goth)
endThe function is invoked as apply(module, function, [goth_name | args])
and must return {:ok, %{token: access_token}} or {:error, reason}. It
runs on the send path, so keep it cheap (Goth caches; do the same). PushX
guards the call: a fetcher that raises, exits, or returns {:error, _} is
reported as a retryable :connection_error; one that returns any other
shape as :auth_error. Neither escapes as an exception.
When a fetcher is set, PushX starts no PushX.Goth process and
:fcm_credentials becomes optional. This option applies to the static
configuration only: named FCM instances (PushX.Instance) authenticate
with their own :credentials, or their own per-instance :token_fetcher
config key — a global fetcher never silently takes over a tenant's OAuth.
The test suite uses this seam to exercise the real FCM send path without
Google.
@spec finch_name() :: atom()
Gets the Finch pool name.
@spec finch_pool_count() :: pos_integer()
Gets the Finch pool count (number of connection pools).
Default: 2 (increased from 1 in v0.6.0 to handle traffic bursts better)
@spec finch_pool_size() :: pos_integer()
Gets the Finch pool size (connections per pool).
Default: 25 (increased from 10 in v0.6.0 to handle traffic bursts better)
@spec finch_request_opts() :: keyword()
Returns the Finch request options with configured timeouts.
Gets a configuration value.
Gets a required configuration value. Raises if the value is not configured.
Gets the callback for invalid token cleanup.
When set, this MFA tuple is called asynchronously whenever a push
returns :invalid_token, :expired_token, or :unregistered.
The callback receives (provider, token, ...extra_args).
Example
config :pushx,
on_invalid_token: {MyApp.Push, :handle_invalid_token, []}
@spec pool_timeout() :: pos_integer()
Gets the pool timeout (time to wait for a connection from pool) in milliseconds. Default: 5 seconds.
@spec receive_timeout() :: pos_integer()
Gets the receive timeout (time to wait for response data) in milliseconds. Default: 15 seconds.
@spec request_timeout() :: pos_integer()
Gets the overall request timeout in milliseconds. Default: 30 seconds.
Note: This value is not currently passed to Finch requests. Use
:receive_timeoutand:pool_timeoutinstead.
@spec retry_base_delay_ms() :: pos_integer()
Gets the base delay for exponential backoff in milliseconds. Default: 10 seconds (Google's recommended minimum).
@spec retry_enabled?() :: boolean()
Checks if retry is enabled.
@spec retry_max_attempts() :: pos_integer()
Gets the maximum number of retry attempts.
@spec retry_max_delay_ms() :: pos_integer()
Gets the maximum delay for exponential backoff in milliseconds. Default: 60 seconds.