Proposed (STA-806). It names the ports through which
0356
(a provider per tenant and Market) is carried out, and settles that ADR’s open question 1. It
reads with
0308
(connections),
0355
(one Integration per provider) and
0370 (market
packs). It answers the wave 10 notifications builder’s questions 1, 2 and 9 and the payments
builder’s question 7.
Amended 2026-10-26 (STA-1282, capability map M2, owner decision R3-1): decision 1’s list gains
verification (KYC and KYB, pack key
providers.verification). Figentra’s own checks of a
tenant are pinned to platform Connections (ProviderResolver#candidates(..., { pin: "managed" })),
so a tenant’s own account never answers them; a tenant’s checks of its own customers resolve the
tenant’s own Connection first, then the managed one. @stackra/identity keeps the Verification
port and runs the list in VerificationService#verifyThrough.Context
Wave 10 built, in@stackra/integrations, a ProviderResolver service over three dependencies (the ConnectionStore,
a ProviderDefaults port and a MarketContext port), a CredentialRef type <provider>:conn_<id>, and a
VoiceGateway interface for telephony. It left open which Market keys a non-payment capability, who binds the two
ports, and how a tenant gets a provider Figentra runs for it. Since then the owner set the principle “managed or bring
your own” for every provider category (2026-10-26), review round 2 named the records the ports read (X2-1, X2-18),
round 3 named the managed half (R3-SO-03), the overlay subject (R3-SO-01) and the voice split (R3-SO-11), and X2-30
found that a licensed Saudi carrier offers a SIP trunk, not conversational steps.
Decision
ProviderResolveris the one choice rule. Every capability with several providers (SMS, WhatsApp, voice, speech, mail, geocoding, accounting, tax, terminals, shipping) resolves the Connection for a call throughProviderResolver: the tenant’s own Connection of a provider the Market offers wins; otherwise the Market’s platform Connection serves; a stored record reloads the exact Connection that served it. Payments keep their ownPaymentGatewaysresolver on the same rule.@Replacesis never used for this (0356 decision 1).ProviderDefaultsreads two records. Its driver readsmarket-pack:<code>.providersfor what depends on the market (payments, sign-in, messaging, tax, terminals, telephony) andregion-map:<region>.providersfor what depends on where the tenant’s data lives (mail, AI, speech, storage), and returns the providers in order with the platform Connection of each (X2-1). A tenant changes its provider through its own Connection, never by editing a pack.MarketContextresolves by subject. Each pack key declares the subject it applies to:location,buyer,entityortenant(R3-SO-01).MarketContextanswers the Market of a call (market(), as built) and gainspackFor(record, subject)for record rules. The seeded subjects for providers are: SMS, WhatsApp and voice by the recipient (buyer), because sender-ID, consent and quiet-hours rules are the destination’s law; mail, accounting and fiscal by the legal entity (entity); payments by the checkout’s Market (0244); a shared process (a data subject request clock, retention, a breach notice) takes the strictest active pack (RV-4, X2-18). The subject of a key is pack data; the driver lives in the OS tenancy service.- Who binds the ports.
@stackra/market-packsverifies and resolves packs; the OS tenancy and markets service fetches pack versions (through@stackra/assetscontent hashes), and bindsProviderDefaults,MarketContextand the other pack readers (MarketPaymentDefaults,FiscalPackSource, settings floor sources) over it. No Stackra package fetches a pack. - Managed or bring your own. A Connection is one of two modes: managed (Figentra’s account and key, plus a
per-tenant resource on it: a messaging subaccount and its registrations, a connected payments account, a sending
domain, a WhatsApp number, a tax company) or own (the tenant’s own account and key). An Integration that
offers the managed mode implements
ManagedProvisioning(create, verify, suspend and remove the per-tenant resource, report its status and its cost events), run byworkflow-template:os:managed-provisioning:<category>. Which modes a category offers per pack, and the fee of each, areprovider-category:<category>and rate-card records (R3-SO-03). The Connection carriesmode: live | test; the tenant kind sets it (X2-24). - Credential references. A record that must remember an account stores a
CredentialRef,<provider>:conn_<id>, never a secret.ProviderResolver#byRefloads that Connection through the store, which applies the caller’s scope and decrypts. A reference whose Connection is gone or out of scope refuses; it never falls back to another Connection. A reference is classifiedinternal; the Connection’s secrets stay in the integrations vault. - Telephony is two ports.
VoiceGateway(in@stackra/integrations/contracts) places, steers, records and ends calls over a provider (Twilio) or a SIP driver for a carrier that only gives a trunk.Speech(streaming speech-to-text and text-to-speech) is a separate port; language and dialect come from pack data, the provider fromregion-map:<region>.providers.speech(in-region and without retention for the health and privileged classes), and a device-audio driver serves drive-through and till microphones. The voice provider per pack is pack data (X2-30). Calls and speech are metered asvoice.minutesandspeech.seconds(R3-SO-11). - One Meta Integration. WhatsApp, ads, catalogue and tracking are roles of one Meta Integration in
@stackra/integrations(0355 decision 5);notificationsuses its WhatsApp role. - Values. Pack data:
providers.*per market, each key’s subject, telephony and speech providers, recording notice floors. Seed data:region-mapproviders,provider-categoryrecords, rate cards, managed-provisioning workflow templates. Settings: none. Rule sets: message channel order and fallback (rule-set:os:message-fallback). Deployment data: platform Connection ids.
Consequences
- A provider change for a market is a pack edit; a tenant’s own provider is an app-market install.
- Every call can say which Connection served it, which reconciliation and audit need.
- The SIP driver and the
Speechport are new build items; the speech-through-voice-provider shortcut of wave 10 goes away.
Enforcement
planned —@stackra/integrations tests: resolution order, byRef refusing a missing Connection, and no fallback for a
stored record; the market-pack section schema refuses a providers key without a subject.
Supersedes and sources
Supersedes: none. Settles 0356 open question 1. Sources:notes/handover/build-wave10-2026-10-25.md (notifications
builder, questions 1, 2 and 9; payments builder, question 7); notes/handover/cross-product-answers.md (managed or
bring your own); notes/handover/review-round-2/cross.md (X2-1, X2-18, X2-24, X2-30);
notes/handover/review-round-3/stackra-os.md (R3-SO-01, R3-SO-03, R3-SO-11, R3-SO-19).
Amendment 2026-10-26: decisions 5 and 8, vendor code in OS integrations (STA-1280, 0443)
- Decision 5. The
ManagedProvisioningport, the Connection modes and the provisioning workflow contracts stay in@stackra/integrations. Each vendor’s implementation ofManagedProvisioninglives with that vendor’s Integration in the OS integrations service (owner R3-8). AmanagedConnection may also point at a self-hosted open-source default that Figentra runs on the regional tools stack (Documenso, PaddleOCR or Surya, Nominatim and Photon, LibreTranslate, Chatwoot; owner R3-5); it resolves throughProviderResolverlike any other Connection. - Decision 8. The one Meta Integration is an OS integrations class, not a class in
@stackra/integrations; the rule of one Integration with several roles stands.