Skip to main content

Crate baukit_push

Crate baukit_push 

Source
Expand description

Provider-neutral push delivery with an Expo adapter.

The crate separates a small PushSender port from its ExpoPushSender adapter. Domain code builds PushMessage values and reads PushOutcome values; nothing above the port names a provider. Scheduling, quiet hours, and deciding who gets notified stay in the product.

§Two-phase delivery

Expo answers a send with a ticket per notification, then confirms delivery later through a receipt. ExpoPushSender runs both phases per batch, so one call returns PushDeliveryStatus::Delivered or PushDeliveryStatus::Rejected wherever Expo has settled, and PushDeliveryStatus::Accepted where it has not. Never resend on Accepted; the notification is in flight.

§Deferred receipts

An Accepted outcome carries the provider’s PushTicketId. Record those with PendingReceiptStore::record_accepted and let the product’s job runner call poll_pending_receipts, which reads the receipts through the PushReceiptSource port and invalidates a DeviceNotRegistered token that Expo only reported after the send. The crate ships no scheduler.

§Device registry

A device token stops working once the app is uninstalled. Expo reports that as DeviceNotRegistered, which becomes PushRejection::DeviceNotRegistered. The DeviceRegistry port stores each owner’s tokens and removes the dead ones in one call after a send:

use baukit_push::{DeviceRegistry, PushMessage, PushSender};
use chrono::Utc;

async fn deliver(
    sender: &impl PushSender,
    registry: &impl DeviceRegistry,
    messages: Vec<PushMessage>,
) -> Result<u64, Box<dyn std::error::Error>> {
    let sent_at = Utc::now();
    let outcomes = sender.send(messages).await?;
    Ok(registry.invalidate_dead_tokens(&outcomes, sent_at).await?)
}

The registry also registers, rotates, unregisters, lists, and erases tokens, and caps devices per owner by evicting the least recently registered. The optional sqlx-postgres feature adds PostgresDeviceRegistry over the schema in POSTGRES_PUSH_DEVICES_MIGRATION_SQL. DeviceToken redacts itself in Debug, so PushMessage and PushOutcome are safe to log, and no error carries a token.

§Daily delivery claims

A scheduled sender claims a DeliveryClaim keyed by owner, local date, and kind before it sends, and releases it if the send fails, so two workers never deliver the same daily notification twice. The DeliveryClaimStore port is separate from the registry; event-driven senders skip it. Eligibility, copy, quiet hours, and channel choice stay in the product.

§Retries

Whole-request failures arrive as PushError::Transport carrying a baukit_http::RetryClass, so an Expo rate limit with a Retry-After header reaches the caller as a concrete delay through PushError::retry_after. Per-notification refusals are not errors: inspect PushRejection::is_retryable on each outcome instead.

Structs§

DeliveryClaim
The right to send one kind of notification to one owner on one local date.
DeliveryKind
A product-defined name for one kind of scheduled notification.
DeviceRegistration
One device’s request to receive pushes for an owner.
DeviceTimeZone
The time zone a device reported when it registered, as an IANA name.
DeviceToken
A provider-issued device token.
ExpoPushSender
Expo push adapter.
PendingReceipt
An accepted notification whose receipt has not been read yet.
PushConfig
Deserializable push configuration for a product’s config section.
PushMessage
One notification addressed to a single device token.
PushOptions
Validated options an ExpoPushSender is built from.
PushOutcome
What happened to one notification, keyed by the token it was addressed to.
PushTicketId
The ID a provider issued for one accepted notification.
ReceiptPoll
What one poll_pending_receipts run did.
RegisteredDevice
A device as the registry stores it.
RegistrationOutcome
What a registration or rotation changed besides the registered device.

Enums§

DevicePlatform
The mobile platform a device token belongs to.
PushDeliveryStatus
The delivery state a provider reported for one notification.
PushError
A failure that prevented the whole batch from being processed.
PushOptionsError
Invalid push adapter configuration.
PushReceipt
The settled receipt for one accepted notification.
PushRejection
Why a provider rejected one notification.
PushStoreError
A store failure.
PushValidationError
A value rejected before it reached a store.
ReceiptPollError
A failure that stopped a receipt poll.

Constants§

DEFAULT_DEVICES_PER_OWNER
Devices an owner keeps when a store is built without an explicit cap.
DEFAULT_EXPO_ENDPOINT
Expo’s public push send endpoint.
MAX_BATCH_SIZE
Largest batch Expo accepts in one /push/send request.
MAX_DELIVERY_KIND_LENGTH
Longest delivery kind accepted, in bytes.
MAX_DEVICE_TIME_ZONE_LENGTH
Longest device time zone name the registry accepts, in bytes.
MAX_DEVICE_TOKEN_LENGTH
Longest device token the registry accepts, in bytes.
MAX_PUSH_TICKET_ID_LENGTH
Longest provider ticket ID accepted, in bytes.
MAX_RECEIPT_BATCH_SIZE
Most ticket IDs Expo accepts in one /push/getReceipts request.
POSTGRES_PUSH_DELIVERY_CLAIMS_MIGRATION_SQL
Reference PostgreSQL schema for the sqlx-postgres feature’s PostgresDeliveryClaimStore.
POSTGRES_PUSH_DEVICES_MIGRATION_SQL
Reference PostgreSQL schema for the sqlx-postgres feature’s PostgresDeviceRegistry.
POSTGRES_PUSH_PENDING_RECEIPTS_MIGRATION_SQL
Reference PostgreSQL schema for the sqlx-postgres feature’s PostgresPendingReceiptStore.
POSTGRES_PUSH_PENDING_RECEIPT_OWNERS_MIGRATION_SQL
Reference PostgreSQL migration that adds the owner to each pending ticket.
RECEIPT_POLL_DELAY
How long a recorded ticket waits before its first poll, and between polls.
RECEIPT_RETENTION
How long the provider keeps a receipt after the send.

Traits§

DeliveryClaimStore
Storage port for daily delivery claims.
DeviceRegistry
Storage port for the push tokens each owner’s devices registered.
PendingReceiptStore
Storage port for tickets whose receipts are still outstanding.
PushReceiptSource
Outbound port for reading the receipts of accepted notifications.
PushSender
Outbound port for delivering notifications to devices.

Functions§

accepted_receipts
Returns a pending receipt for each accepted outcome of one send to owner_id.
dead_tokens
Returns the distinct tokens a send reported as dead.
poll_pending_receipts
Polls one batch of due receipts and applies what settled.

Type Aliases§

PushFuture
The future returned by PushSender::send.
PushReceiptFuture
The future returned by PushReceiptSource::receipts.
PushStoreFuture
The future returned by DeviceRegistry and DeliveryClaimStore methods.