genalpha-bss
A composable, multi-tenant Business Support System on TM Forum ODA
Product overview · 31 ODA components · 5 channels · 2 demo operators on one deployment ·
July 2026 · github.com/kundanvarma/genalpha-bss
Executive summary
genalpha-bss is a complete telecom BSS built the way TM Forum's Open Digital
Architecture says systems should be built: as independent components, each exposing a standard
TMF Open API, composed behind one gateway. It is vendor-neutral — any OIDC identity
provider, any PostgreSQL, any Kafka-protocol broker — and multi-tenant to the bone:
two demo operators, GenAlpha and Nova Telecom, run side by side on a single deployment with
cryptographic identity isolation and database row-level security between them.
Three properties distinguish it from reference implementations and demo-ware:
- Everything is verified end-to-end in a real browser. Twenty-eight Playwright suites walk the
storefront, guest checkout, both consoles, the mobile app, tenant isolation, role administration
and campaign journeys on every change. The five core components also pass the official TM Forum conformance kits (CTK) with
zero failures.
- Composability is real, not aspirational. Every optional module can be left out of a
deployment; dependencies are enforced by a composer tool; the channels quietly hide any
feature whose component is absent.
- The business loops actually close. Orders complete themselves through a thin
production layer, usage becomes overage on real bills, promo codes become bill discounts,
saved cards pay next month's invoice, and a network alarm becomes an outage banner an agent
can see — all demonstrable in minutes.
The five-minute story
- An anonymous visitor browses GenAlpha's storefront, configures a triple-play bundle
(phone model, color, storage — the price follows each choice), applies promo code
WELCOME10, and sees the discount before having any identity.
- Checkout validates the address (TMF673 standardizes it), checks fiber serviceability
(TMF679), books an installer slot (TMF646), reserves the phone from stock (TMF687), and
authorizes payment (TMF676) — the visitor registers mid-flow and the interrupted checkout
resumes itself.
- A purely digital order needs nobody: the thin SOM (TMF641/640) activates it in seconds,
draws a phone number from the tenant's pool (TMF685), and completes the order machine-side —
provisioning inventory (TMF637), capturing payment, minting a 12-month commitment agreement
(TMF651) and redeeming the promo (TMF671) on the way.
- The network reports 12.3 GB of roaming (TMF635); the customer's meter shows
12.3 / 10 GB — over allowance. The monthly billing run (TMF678) puts the overage
and the promo discount on one bill, which the customer pays with the card vaulted at
checkout (TMF670) — one click, no retyping.
- A critical alarm (TMF642) becomes a service problem (TMF656) and every agent's CSR console
shows the outage banner; resolution clears it. Each event along the whole journey became a
customer notification (TMF681).
- Meanwhile Nova Telecom's customers do all of the same on
shop.nova.localhost
against Nova's own identity realm, Nova's own catalog and Nova's own number range — and neither
operator can see one byte of the other's world.
The module catalog
Core commerce — always deployed
| Component | TMF API | Port | Purpose |
| product-catalog | TMF620 | 8081 | Offerings, prices, bundles, commitment terms |
| product-ordering | TMF622 | 8082 | Order capture, validation, completion orchestration |
| product-inventory | TMF637 | 8083 | What each customer has, provisioned per order item |
| party-account | TMF632 / 666 / 669 | 8084 | Individuals, organizations, accounts, party roles |
| gateway | ODA exposure | 8080 | Single entry point; white-label host → tenant routing |
Optional components — compose per deployment
| Component | TMF API | Port | Purpose |
| product-stock | TMF687 | 8086 | Device shelf: reserve at order, consume at completion |
| payment | TMF676 | 8087 | Authorize/capture behind a PSP adapter (mock PSP in dev) |
| billing | TMF678 | 8088 | Billing runs: recurring + usage + discounts on one bill |
| qualification | TMF679 | 8089 | Serviceability: where fiber-class offerings can be delivered |
| appointment | TMF646 | 8091 | Installer slots, booked at checkout |
| trouble-ticket | TMF621 | 8092 | Support cases, org-scoped for partner agents |
| party-interaction | TMF683 | 8093 | Every touchpoint on the customer timeline |
| communication | TMF681 | 8095 | Event-driven notifications (the martech door) |
| shopping-cart | TMF663 | 8096 | Server-side carts: guest secret-id, claim on login, abandonment events |
| usage | TMF635 / 677 | 8097 | Mediation intake, rating, allowance meters, overage charges |
| agreement | TMF651 | 8098 | Commitment periods minted automatically at order completion |
| promotion | TMF671 | 8099 | Promo codes: anonymous validation → redemption → bill discount |
| user-roles | TMF672 | 8100 | Tenant admins manage staff via TMF API over their own IdP |
| geographic-address | TMF673 | 8101 | Address validation and standardization at checkout |
| recommendation | TMF680 | 8102 | Cross-sell with a learning seam: rule-selected candidates, popularity-ranked; a trained model plugs into the same Ranker interface |
| payment-method | TMF670 | 8103 | Tokenized card vault: save at checkout, pay bills one-click |
| document | TMF667 | 8106 | Content store: tenant logos and offering artwork the channels wear |
| campaign | martech | 8108 | Event-triggered journeys: once-per-customer messages carrying promo codes — marketers run them from the console GUI |
| quote | TMF648 | 8110 | B2B quotes born from TMF921 intents: OSS proposal priced, token allowances on line items, acceptance places the order |
| intelligence | AI | 8109 | Any-LLM seam with per-tenant overrides: copy assistant + CSR copilot + a churn engine that begins as transparent rules (commitment ending, allowance pressure, support friction, outage exposure) and learns in production — daily feature snapshots + labeled outcomes train a per-tenant model in-service, or import the operator's history and be production-grade on day one |
| policy | rules | 8113 | Business rules AND dynamic pricing as data, not code: eligibility / quantity / incompatibility / verified-identity rules enforced at order time, plus pricing rules (percent or amount adjustments by segment / cart / verified identity) applied at cart and bill time — author or disable any in the console as JSON-logic, no redeploy. Pluggable engine seam (JSON-logic today, Drools/CEL swappable); RLS-isolated; fails open if unreachable |
Production (OSS) — the layer below, thin but real
| Component | TMF API | Port | Purpose |
| service-orchestration | TMF641 / 638 / 640 / 685 | 8104 | Digital orders decompose, activate (drawing MSISDNs from per-tenant resource pools) and complete themselves; physical orders wait for fulfilment |
| assurance | TMF642 / 656 | 8105 | Critical alarms become service problems; the CSR console shows live outages |
The production block is deliberately thin: the boundaries
(APIs, events, machine identity, tenancy) are built at full fidelity, while internals — instant
mock activation, one-rule alarm correlation, counter-based number pools — are placeholders
behind seams. An operator's real OSS, or deeper adapters, slot in without touching anything
around them; the same pattern as the mock PSP behind the payment adapter.
Channels — one build each, white-labeled per tenant by hostname
| Channel | Path | Audience |
| storefront | /shop | Self-service: guest browse → configure → cart → checkout → bills → support (React + Vite PWA). Devices sell with product-shot galleries, colour-variant imagery that follows the picker, and an About-this-device facts table — all plain TMF620 attachments, from the internal content store or the operator's own PIM (per-tenant seam) |
| business-console | /biz | B2B self-care: a company admin adds people, orders their subscriptions (own-org boundary enforced server-side), sees live lines, reads the consolidated company invoice, and sets the company policy — a configurable device allowance. Split billing: company-placed orders are payer-stamped to the org; a member's own purchases and any device cost above the allowance bill to the member personally |
| csr-console | /csr | Assisted service: customer 360, ticket queue, outage banner, number-porting panel + service cease, org-scoped agents |
| admin-console | /console | Back office: catalog, stock, campaigns, AI audit, porting orders, and business Rules with a live dry-run (authored live, no redeploy) |
Architecture at a glance
Engagement — channels
storefront /shop · csr-console /csr · admin-console /console — one build each; the hostname decides the tenant, a gateway-served config script decides the IdP
▼
API Gateway (8080)
Single entry point · routes TMF paths to components · maps hostname → tenant for anonymous traffic (X-Tenant-Id, inbound copies stripped)
▼
Party management party-account (632/666/669) · user-roles (672, over each tenant's own IdP)
Core commerce catalog · ordering · inventory · cart · qualification · address · appointment · stock · agreement · promotion · recommendation
Revenue payment · payment-method vault · billing · usage
Care & Production trouble-ticket · interaction · communication · service-orchestration · assurance
▼
Backbone
PostgreSQL (one DB per component, tenant_id + row-level security) ·
Kafka (transactional outbox, tenant-tagged TMF688-style envelopes) ·
OIDC IdPs (one issuer per tenant; Keycloak realms in dev, Cognito/Entra-ready)
Machine-to-machine calls (ordering → catalog/stock/payment/agreement/promotion,
billing → inventory/usage/promotion, SOM → ordering, payment → vault) always use the
acting tenant's client credentials, so downstream tenancy holds even for background work.
Interactive diagrams: docs/architecture.md.
Multitenancy — the pool model with two locks
- Identity lock: a tenant is a verified OIDC issuer. Users cannot claim a
tenant — their token's issuer, validated against per-tenant keys, decides it. Each tenant can
bring its own IdP (a Keycloak realm in dev; a Cognito pool or Entra tenant in production works
identically).
- Data lock: every domain row carries
tenant_id, enforced in code on
every query and by PostgreSQL Row-Level Security — services connect as restricted
database roles that see zero rows without a session tenant, even for SQL with no
predicate at all. Cross-tenant access reads as 404 everywhere: foreign ids never leak
existence.
- White-labeling: anonymous traffic gets its tenant from the hostname at the gateway,
so each operator's shop window shows only its own catalog, prices, promos and numbers.
The same scoping stacks a level deeper: partner/business-unit agents are confined by an
org claim within their tenant.
Composability
The ODA promise made operational: every optional module has an enabled flag in
the Helm chart, the composer page enforces the dependency matrix
(billing needs payment; appointment needs qualification; the vault needs payment…) and emits a
ready-to-use values override, and the channels degrade gracefully — a deployment without the
stock component simply has nothing stock-managed, without qualification nothing is
serviceability-gated, without the vault the card form still works. Cross-component calls go
through conditional clients with no-op fallbacks, so absent modules cost nothing at runtime.
How it is verified
- ~260 unit and integration tests, including real-PostgreSQL migration tests per
component and an RLS proof that connects as the restricted role and demonstrates
zero-visibility without a tenant.
- Twenty-one browser E2E suites (Playwright) driving the real stack: the storefront
journey, guest checkout, both consoles, the business console (B2B: org boundary, consolidated
invoicing, invitations, member my-page), plan change (same number, commitment guard), SIM
self-care, the Family Max configurable bundle, value-added services (partner entitlement /
security feature / billing-only), business rules and dynamic pricing, soft-bundle
cardinality, two-operator tenant isolation including a full purchase in Norwegian with
NOK, TMF672 role administration, number porting both directions, the mobile app, and the
martech loop. The suites run after every change.
- Official TM Forum CTKs: eleven kits pass with zero failures (TMF620, 622, 632,
637, 666, 663, 669, 687, 635, 677, 678) — plus two components that fail their kits on
purpose, with the reasons documented.
- The journey film — a 2:54 recording of a human using every surface, re-shot after
every feature against the live stack; it has caught real bugs twice.
- CI builds and tests every component on every push.
Security & post-quantum readiness
The BSS embeds almost no cryptography of its own — identity, transport and payment crypto
live behind the same seams as every other vendor dependency, which is precisely the
crypto-agility that post-quantum migration guidance calls for. The full inventory is published
in docs/pqc-readiness.md; the shipped state:
- Quantum-safe edge, today. The Helm chart ships an Ingress (TLS 1.3 only) plus
controller values enabling X25519MLKEM768 hybrid key exchange (ML-KEM-768, FIPS 203,
classical fallback preserved) — countering harvest-now-decrypt-later, the one
quantum risk with a present-tense clock.
- Card secrets encrypted at rest. SIM PUKs are AES-256-GCM field-encrypted with the
ICCID bound in as authenticated data; the key arrives from a KMS/secret store. AES-256 is
post-quantum comfortable.
- A runtime that already speaks post-quantum. The fleet runs on a JVM carrying
ML-KEM and ML-DSA, so the day any OIDC provider signs tokens post-quantum, verification
follows via JWKS — a configuration change at the identity seam, not a rewrite.
- Honest remainder: today's token signatures are the IdP's RS256. That is the one
quantum-vulnerable primitive left, and it is swappable without touching business code —
which is the difference between a PQC rewrite and a PQC checklist.
Deployment
Local: mvn package → docker compose up (~27 containers, seeded demo
data, both operators live). Kubernetes: a Helm chart carrying both realms, per-service RLS
roles and per-tenant issuers, composed via the module picker; Terraform stacks target EKS and
AKS. Dedicated single-tenant deployments (the “silo” tier for regulated customers) use the same
chart with one tenant configured — the pool and silo models are one codebase.
Roadmap
- BankID step-up at checkout for offerings requiring verified identity (brokered IdP,
no service changes — the vendor-neutral JWT model already carries it).
- Real PSP adapter (Stripe/Adyen) behind the existing payment seam.
- More intelligence: the AI platform now closes its own learning loop — the churn
engine snapshots features daily (BSS + CSR + assurance signals), records outcomes, and trains
a per-tenant logistic model in-service; operators with history import it and start
production-grade. Deeper models (gradient boosting, network QoE features via TMF628) slot
behind the same interfaces when real network data arrives.
- Deeper production internals where a concrete integration demands them: pluggable
activation adapters, alarm correlation, TMF648 Quote for B2B.