PushX.Instance (PushX v0.11.0)

Copy Markdown View Source

Runtime management of named push notification instances.

Allows starting, stopping, and reconfiguring APNS and FCM instances at runtime, enabling multi-provider setups from a database-backed admin panel.

Usage

# Start an APNS instance
PushX.Instance.start(:apns_prod, :apns,
  key_id: "ABC123",
  team_id: "TEAM456",
  private_key: "-----BEGIN EC PRIVATE KEY-----\n...",
  mode: :prod
)

# Start an FCM instance
PushX.Instance.start(:my_fcm, :fcm,
  project_id: "my-project",
  credentials: %{"type" => "service_account", ...}
)

# Send via instance
PushX.push(:apns_prod, token, msg, topic: "com.example.app")

# Lifecycle management
PushX.Instance.disable(:apns_prod)
PushX.Instance.enable(:apns_prod)
PushX.Instance.reconfigure(:apns_prod, mode: :sandbox)
PushX.Instance.stop(:apns_prod)

Credential Rotation Without Restart

Use reconfigure/2 to hot-swap credentials (e.g., after revoking an APNS .p8 key or rotating an FCM service account). It stops the old pool and starts a fresh one with new credentials. In-flight requests on the old pool get connection errors, which the retry logic handles automatically.

# Load new key from database/file/env
new_key = MyApp.Repo.get_latest_apns_key()

PushX.Instance.reconfigure(:apns_prod,
  key_id: "NEW_KEY_ID",
  private_key: new_key
)

Summary

Functions

Disables an instance. New pushes are rejected, but the pool stays warm.

Re-enables a disabled instance.

Lists all running instances.

Stops and restarts an instance with updated config.

Restarts the Finch HTTP pool for a named instance.

Resolves an instance name to its info for sending.

Starts a named instance.

Returns the status of a named instance.

Stops a named instance and cleans up all resources.

Functions

disable(name)

@spec disable(atom()) :: :ok | {:error, :not_found}

Disables an instance. New pushes are rejected, but the pool stays warm.

enable(name)

@spec enable(atom()) :: :ok | {:error, :not_found}

Re-enables a disabled instance.

list()

@spec list() :: [map()]

Lists all running instances.

reconfigure(name, new_config)

@spec reconfigure(
  atom(),
  keyword()
) :: {:ok, atom()} | {:error, term()}

Stops and restarts an instance with updated config.

Merges new_config into the existing config. Use this to hot-swap credentials (e.g., after revoking an APNS .p8 key) without restarting the application. The old Finch pool is terminated and a new one starts with fresh connections. In-flight requests on the old pool receive connection errors, which the retry logic handles automatically.

Examples

# Rotate APNS key
PushX.Instance.reconfigure(:apns_prod,
  key_id: "NEW_KEY_ID",
  private_key: new_pem_string
)

# Switch APNS environment
PushX.Instance.reconfigure(:apns_prod, mode: :sandbox)

reconnect(name)

@spec reconnect(atom()) :: :ok | {:error, term()}

Restarts the Finch HTTP pool for a named instance.

resolve(name)

@spec resolve(atom()) :: {:ok, map()} | {:error, :not_found | :disabled}

Resolves an instance name to its info for sending.

Returns {:error, :disabled} if the instance exists but is disabled, {:error, :not_found} if it doesn't exist.

start(name, provider, config)

@spec start(atom(), :apns | :fcm, keyword()) :: {:ok, atom()} | {:error, term()}

Starts a named instance.

Arguments

  • name - Unique atom name for this instance (e.g., :apns_prod)
  • provider - :apns or :fcm
  • config - Provider-specific configuration (keyword list)

APNS Config Keys

  • :key_id - (required) Apple Key ID
  • :team_id - (required) Apple Team ID
  • :private_key - (required) PEM string, {:file, path}, or {:system, "ENV_VAR"}
  • :mode - :prod or :sandbox (default: :prod)
  • :pool_size - Finch pool size (default: 2)
  • :pool_count - Finch pool count (default: 1)

FCM Config Keys

  • :project_id - (required) Firebase project ID
  • :credentials - (required) Service account credentials map or JSON string
  • :pool_size - Finch pool size (default: 2)
  • :pool_count - Finch pool count (default: 1)

Returns

  • {:ok, name} on success
  • {:error, :reserved_name} if name is :apns or :fcm
  • {:error, :already_started} if instance already exists
  • {:error, {:missing_config, keys}} if required config is missing

status(name)

@spec status(atom()) :: {:ok, map()} | {:error, :not_found}

Returns the status of a named instance.

stop(name)

@spec stop(atom()) :: :ok | {:error, :not_found}

Stops a named instance and cleans up all resources.