@galinum/server/communications exports the PostgreSQL communication transaction,
shared push and in-app projections, and canonical event ingestion. The self-host
runtime uses these same SQL queries and projection rules.
The adapter borrows an existing transaction. It does not create a pool, initialize
a project, migrate a schema, commit, or close the caller’s connection. Supply an
authorized project ID and the canonical product tables. CommunicationDB selects
users, events, campaigns, variants, goals, audience versions, segments, deliveries,
installations, installation requests, push records and in-app feedback. Segments
are needed to validate pinned audience references.
Concrete transaction access
Keep the host’s concrete transaction alongside the projected product view:AppDatabase must contain the real product columns, plus any host-owned tables.
Do not assert an incompatible schema into this type. The public tests include a
concrete extended schema with ledger, first-delivery marker and outbox tables.
Pass pushStore to createPushEngine. Its maySend and recordAcceptance
callbacks retain the concrete adapter type: tx.data.executor remains the host
transaction. Use inAppTransaction(data, media, projectId, effects) with
createInAppService; its serving and exposure callbacks retain the same data
object. Hold the project fence around every borrowed in-app or event operation.
All eligibility writers must participate in that fence.
Facts and effects
CommunicationEffects<Data> provides two optional transactional effects:
recordActivity(data, fact)runs for a fresh canonical event. Explicit self-host identify also emits an identify fact. Internal event activity updates do not emit an identify fact.recordFirstDelivery(data, fact)runs at the first generic push acceptance or first generic in-app shown transition. It is separate from per-attempt acceptance and per-feedback-ID exposure effects.
recordServerEvent returns an inserted or replay result with the original
canonical event-row ID and ingestion time. Exact project-scoped eventId replay
preserves properties and causes no activity refresh, new effect, or conversion
rerun. Conflicting user, event name or properties rejects the transaction.
A first-seen shown feedback ID invokes recordExposure with its first receipt
time. Exact replay returns that receipt without invoking the effect again.
A distinct shown ID may invoke exposure in another period while the generic
shown timestamp remains unchanged. Each distinct accepted campaign attempt
invokes recordAcceptance; selected-device tests do not invoke it.
Effects must await their database writes through the supplied transaction.
Throwing rolls back source facts and effect rows together. Do not make external
meter calls within these effects. Write a durable first-delivery marker and
outbox in the same transaction. A process-local callback is not a recovery queue.
Drain committed outbox records separately, using stable external idempotency IDs.
A failed drain must not change the committed SDK acknowledgement or resend push.
The application owns scheduling and external dispatch for its outbox.
Provider submission cannot commit atomically with PostgreSQL. If a provider
accepts and a local effect fails, local acceptance rolls back and the durable
reservation remains. Recovery records an uncertain outcome. Do not automatically
retry the transaction or resubmit the notification to recover an effect.
Compose this storage boundary with the public runtime invocation and worker
API. It binds host authentication to communication handlers
and provides push admission limits for units, discovery pages, and deadlines.
The embedding application supplies credential verification and scheduling.
Admission deadlines do not impose a maximum process duration.
Activation and raw readiness
@galinum/server/activation exports createPostgresActivationSessionData and
ActivationSessionDB for activation persistence and campaign preparation. This
view requires six real tables: project launch settings, deployment mappings and
their source bindings, campaign activation state, campaign shipping warnings, and
campaign shipping preparations. It does not require stock shipping sources or
project controls. Pass a concrete transaction’s
$pickTables<keyof ActivationSessionDB>() view and retain that original transaction
for host effects. createPostgresActivationData remains the complete stock helper.
Both activation and communication exports expose lockProject. All writable
views of the same project must acquire it before row locks. It rejects a plain
database executor and uses the same advisory fence as provider phase two.
campaignDefinitionReadiness accepts raw variant contentJson and, for push,
raw campaign pushJson from the stored push_json column. Missing, malformed or
invalid push settings fail readiness. Supply project-scoped goal, audience, media
and channel facts. The helper owns content, settings, allocation and resource
validation. Web CTAs use typed destinations; email retains its HTTP CTA contract.
The stock push readiness check requires persistent encryption and a locally
usable matching credential for each positive-weight variant’s supported platform.
It does not require an installation to exist or claim successful external
authentication. Device consent, registered actions/categories/channels and
credential revisions remain dispatch-time checks. Changed or unavailable
readiness blocks later delivery and reports campaign_not_ready.
pushTransaction receives { projectId, vault, media } as its third argument.
Omitting it leaves push readiness unverified and blocks delivery. inAppTransaction
also checks current definition and media readiness before selecting candidates.