Bulbul · Telephony · Proposal
Numbers, trunks, and connections after the self-serve launch: why the per-number trunk field should retire, the model I propose we land on, and a migration that cannot burn the house down.
60-second primer
Five hops and one word of jargon carry this whole deck.
Where we are
A hand-typed field, copied onto each number when it is registered. It was the right bridge during migration, and it is now the weakest link in the chain.
Why it existed: the field was deliberate backward compatibility, a bridge to carry pre-existing numbers into the new model until trunks could be consolidated. The consolidation was always the plan; connections shipping to prod is what finally makes it possible.
Why now
We just shipped SIP connections: a first-class object describing a carrier relationship. That should make the hand-typed trunk field on numbers redundant. Working on exactly that this week convinced me the removal has to be sequenced carefully.
The destination is right: nobody should type trunk IDs once a connection owns routing. The UI just must not get there before the backend does. So the real question is: what is the destination, precisely?
Proposal
Every number resolves its route through one of three explicit shapes, and “no route” becomes a visible state instead of an empty string.
Decision 1 · platform-routed connections
For connections that route through our Kamailio stack, LiveKit needs a trunk pointing at it. The hidden cost of doing that per connection: Kamailio only accepts callers it can authenticate, so every auto-created trunk also needs an auto-created credential pair (“subscriber”) on the Kamailio side, generated, stored, rotated, and cleaned up in sync.
N trunks, N credential pairs
1 trunk, 1 credential pair, set up once in Terraform
Not mutually exclusive forever: B now, and A remains available later for any tenant that needs isolation, without changing the domain model.
Decision 2 · direct carriers
A few carriers (the Maqsam-style setups) peer with LiveKit Cloud directly today. Full consolidation would eventually put everything behind our own stack.
One routing plane
Two supported shapes, both first-class
B keeps A available as opportunistic drift: any carrier can be moved later, one at a time, with no model change.
Design point · there is no secret backup path
A tempting design is “if the connection is down, quietly fall back to the number’s old trunk”. But the legacy trunks point at Kamailio too, and the carrier routing they ride on is exactly the config that connections now own. The old trunk is the same road with an older signpost, not an independent route.
This also makes onboarding safe by construction: linking a number to a new connection changes nothing about routing. Only the deliberate cutover step does, and only after the connection is verified.
The plan
Safety property: only steps 4 and 5 change live routing, and both are per-connection or per-number, verified with real test calls, monitored, and instantly reversible. There is no flag day and no deadline pressure.
This week
Appendix on request: TEL-172/190/191/193/194/196, prod trunk-distribution queries.