The post Softswitch migration: match invoices before cutover appeared first on Kolmisoft Blog.
]]>A wholesale carrier does this on M4. A hosted-PBX or white-label operator does it on MOR. There is no one-click migration, and no promised number of days. You recreate subscriptions. On M4, DID import from CSV is marked in development, so leave it out of the plan.
The product notes, if you are still choosing, are the VOS3000 alternative, VoipSwitch alternative, and ASTPP alternative pages.
On 13 October 2026, jackson posted Planning a softswitch migration? Don’t forget the billing side. Export customers, balances, and price lists. Map the CDR file to your rating rules. Run both systems for a billing cycle if you can. Keep the old CDRs for disputes. Test the invoice before you go live.
The invoice shows up after the customers have moved. A 61-second call on the wrong increment doubles a destination.
Sample price, 0.012 per minute:
| Increment 1 | Increment 60 | |
|---|---|---|
| Answered seconds | 61 | 61 |
| Seconds you bill | 61 | 120 |
| Amount | 0.0122 | 0.024 |
MOR prices that CDR file.
An admin login, or a reseller login on MOR. Both can run the data import. An accountant with permission to manage DIDs can run the MOR X10 DID CSV.
Export users, devices, CLIs, DIDs, price lists, opening balances, one month of CDRs, and the invoices for that month. Leave the old switch able to export.
Match the CSV column separator and the decimal separator to the files. On MOR they sit under Settings → Setup → Settings → Functionality. A comma in the file and a semicolon in the settings stops the import.
The CSV matches a tariff or an LCR by number, not by name. Open the tariff, click edit, and take the last number in the address bar. LCR “Retail EU” with ID 2 goes in the file as 2.
Create the tariffs and LCRs first. Then open Settings → Users → Import. MOR keeps device import closed until the users are in, and DID import closed until the devices are in.
0 is prepaid and 1 is postpaid. Leave it blank and MOR imports the user as prepaid. A postpaid customer imported as prepaid stops at balance zero.Wait to press Clear temporary information until the CLI file and the DID file are in.
From MOR X10, Add new DID(s) has a second CSV at the bottom of the form. It creates the DID and the DID rates. A Device ID column assigns the number. Required columns are the DID and the provider ID. A simple reseller omits the provider ID. Use this file for the rate columns. Use Settings → Users → Import when the rows still depend on temporary IDs.
Then the rate rows. Prefix and price are required. On MOR the prefix is E.164, with no leading 00 or 011. Connection fee, increment, and minimum time use the defaults when those columns are empty, which is how a 61-second call changes price. Copy them from the old price list. Set Effective From on or before the first day of the shadow month. A rate dated today does not price last month.
Email price files that keep arriving are a separate setup: Process of Automatic Tariff Import.
Subscriptions have no CSV. Create the service, then assign it in the GUI or with the subscription API, one service or several IDs for one user. A flat-rate subscription created through the API covers a full month.
Compare each opening balance with the export. Change it with a manual payment, before anyone dials. A prepaid user at zero cannot place a call.
M4 has no user, device, and CLI CSV of the MOR kind. Create the user under Settings → Users. Add a connection point. Set Act as Origination Point to yes, and leave Active off. An inactive origination point accepts no calls, so you can check the tariff first.
Import rates under Billing → Tariffs → Import from CSV. Prefix and rate are required. M4 accepts alphanumeric prefixes. Effective From still has to cover the call date, or a later re-rate skips the row. Empty increment and minimum time take the defaults.
Later email price files use the automatic import post above.
M4 DID CSV import is marked in development. Add pilot numbers in DID Inventory.
CDR import is Settings → Billing → CDR → Import. MOR bills CDRs there that came from another switch.
Save the layout under Billing → CDR → Import → Templates: name, column separator, decimal separator, header lines to skip, date format, and the column map. The next upload with that template skips column assignment.
Map Billed Seconds to talk time. 13/10/2026 14:05:02 and 2026-10-13 14:05:02 need different date formats. The wrong format puts the calls on the wrong day, or fails the row.
MOR assigns each row by:
An admin can assign a call to a CLI on a simple reseller’s user. An admin cannot import calls for Reseller PRO users. That reseller runs the import.
MOR prices the matched rows with the reseller, user, and provider tariffs, the same logic as CDR rerating.
MOR drops a second row with the same date, source, and destination. If the old switch billed both, change one of those three fields. Otherwise that call is missing on the new invoice.
Leave disposition and hangup cause empty, and MOR marks billed seconds above zero as ANSWERED with hangup cause 16. It marks billed seconds at zero as FAILED with hangup cause 0. Map the columns when the old invoice includes failed calls.
MOR prices a call to the caller’s own DID at 0, and the new invoice comes out lower.
Check localization first. MOR applies it on import. A rule that strips 00, plus a rule that turns a leading 9 into 349, takes 00972… to 972… on the export and to 34972… on the import. MOR then rates the row as Spain. Fix the rules, then import.
On M4 you save a template at Billing → CDR → Import → Templates: separator, decimal mark, skipped lines, date format, columns. Billed Seconds is answered time, without setup. Duration is setup plus talk. Treat that template as the column map. MOR is the product that bills the imported file.
For calls already on M4, rerating is Billing → CDR → Rerating. Set From, To, the user, and Rerate as Client or Supplier. Update the tariff first. M4 runs the job in the background. Before any traffic moves, price the heavy destinations from the old invoice against the new tariff: prefix, increment, minimum time, connection fee.
Call-row comparisons, once both sides have CDRs, are Your partner’s invoice and your CDRs disagree.
Generate the shadow-month invoice for the users you imported. Set it beside the old invoice for the same customers. Start with destination totals.
A full CDR file and a zero invoice is an Effective From problem. MOR uses the rate whose Effective From is on or before the call date. With no such rate, the price stays empty. Cover the whole shadow month and run it again.
On MOR the screen is Settings → Billing → CDR → Rerating. Pick the users and the period. MOR recalculates minimum time, increment, and connection fee. You see the original duration. The user sees the rounded duration. Settle that before the trunk moves.
MOR skips a call when a localization rule changed its DID, LCR, or owner device. Check that rule when one prefix comes back empty.
Fix the rate or the increment, rerate, generate the invoice again. Move traffic when you would send that invoice to the customer.
On MOR, send one reseller’s SIP across. Leave the other resellers on the old switch. On M4, set Active on for one origination point. Leave the others off, or on the old switch.
Keep the old login and stop new calls to it, so a dispute about the previous month still has the source rows. Store the imported CSV with the old invoices. That file is the archive.
MOR has no subscription CSV. Create the service, then assign it in the GUI or with the subscription API. A flat-rate subscription created that way covers a full month.
Those IDs link the files to each other. MOR assigns new ones. Tariff and LCR cells are the IDs MOR already has. Those have to match.
M4 DID CSV import is marked in development. The MOR DID CSV, from X10, creates DIDs and DID rates.
MOR drops a second row with the same date, source, and destination. MOR rejects the file when you map a provider ID that is not in the system. With disposition empty, MOR uses billed seconds to choose ANSWERED or FAILED.
No fixed number of days. Move one reseller or one origination point after that slice matches.
Pick the reseller, or the origination point, whose invoice you trust least.
Hosted PBX and white-label operators open the import on the MOR online demo. Wholesale carriers open tariffs and rerating on the M4 SBC online demo. Each demo is the admin screen. It does not place live calls. Bring a small CSV. If the columns parse and the 61-second destination matches the table, that is the pilot. Move the trunk after the full month matches.
Run one month before you move a trunk
Open the MOR or M4 demo with a small CDR file. Move the trunk after that month's invoice matches.
The post Softswitch migration: match invoices before cutover appeared first on Kolmisoft Blog.
]]>The post Answer an ITG traceback from your own CDRs appeared first on Kolmisoft Blog.
]]>A traceback on that traffic names a date, a time, a calling number, and a dialed number. You have 24 hours to find the call on M4 SBC or on MOR, name the customer and the trunk it came in on, and stop further calls from that trunk.
On 27 August 2026 the FCC Enforcement Bureau told nine providers to cure Robocall Mitigation Database (RMD) certifications after those nine left 51 traceback requests unanswered. TCPA.com reported the order (DA 26-873) on 25 October 2026. The certification includes a commitment to answer traceback requests from the FCC, law enforcement, and the ITG in 24 hours. If the Bureau removes a certification, other providers have to stop accepting that company’s calls, except calls to 911.
October added three more orders:
In April 2026 the FCC proposed a $4.5 million penalty against gateway provider Voxbeam. Voxbeam had accepted 60,873 calls from Axfone, a foreign provider that was not listed in the RMD. Axfone had been a customer since 2012 and had placed no calls from 17 November 2018 until 31 March 2025. The Commission started from $5,625,000, on 2,250 verified calls, and the proposed penalty is 20% lower because Voxbeam blocked that traffic in the 24 hours after the first of four ITG requests.
A proposed RMD rule, published 9 September 2026, set comments for 9 October 2026 and reply comments for 9 November 2026. The dates are on the Federal Register PDF.
This is not legal advice. Talk to your counsel before you answer the portal or change an RMD filing.
Use the four fields on the request. This example is not a real traceback: 20 October 2026, 14:03, calling number 18005550100, dialed number 12025550199.
Open REPORTS → Calls List. Set the time around 14:03 in the zone your screen uses. Type the calling number in Source. Type the dialed number in Destination.
| Request field | Where it goes |
|---|---|
| Time | The Calls List time range, in the zone the screen shows |
| Calling number | Source |
| Dialed number | Destination |
| After you have the row | Call Info: PAI Number, Diversion Number, LegA Call-ID |
Destination accepts a pattern:
12025550199 returns that number.1202555% returns numbers that begin with 1202555.%0199 returns numbers that contain 0199.1202555019_ returns that prefix plus one more character.% stands for any run of characters. _ stands for one character. The same patterns work on Source.
Source stores the calling number as it arrived. If the switch replaces that number from a number pool before the call goes to the terminator, it stores the sent value in Source (CallerID) sent to TP. A Source search misses the row. Go to MAINTENANCE → Settings → Calls List and enable Show CallerID sent to TP. The search field appears under Source. Search it with the same patterns.
You can also search by Originator (the user), Origination Point, Termination Point, or UniqueID. Export to CSV writes out the calls on the screen. Export to PDF writes those same calls.
Open the call-info icon on the row.
Call Info shows PAI Number, then Diversion Number, then LegA Call-ID. Diversion Number is the SIP Diversion header stored with the call. It stays empty when the call had no Diversion header.
Call Tracing is a different tool. From REPORTS → Calls List, or from the Origination Point, it places a new test call and does not ring terminators. A second run can come out different. For the call in the request, open Call Log on that Call Info page.
On MOR, open Last Calls for that user and that hour, then open Call Info. The same three fields are there. MOR deletes the Asterisk Call Log after 14 days. The CDR row is the record you can still keep.
The Calls List row names the Originator and the Origination Point. The Originator is the user, the customer account. The Origination Point is the trunk the call came in on. Those two names are the upstream you put in the reply.
Export to CSV while the filter still shows that hour and that calling number. The file is the set of rows you hand to the person who writes the ITG reply. The switch does not send the reply.
The 8 October Aspireistic order says the company’s ITG replies named the end customer and did not name an upstream provider on ten requests. The Originator and the Origination Point are those two names.
.* when you want every other CLI to pass.If the export shows several Origination Points for the same user, set Active to no on each of them. An alert can disable the client, and that covers every trunk on the account.
The Voxbeam notice is a quiet account that woke up. Axfone placed no calls from 17 November 2018 until 31 March 2025, then 60,873 calls from 31 March to 2 April 2025. M4 has no dormant-account flag. Use a TOTAL CALLS alert on that client.
Open SECURITY → Alerts.
SIMULTANEOUS CALLS and PRICE SUM apply to users and providers. Put those on the client. PRICE SUM is the spend alert. Prepaid cut-off and a cap on a customer whose login is valid are in Your Firewall Passed. Your Balance Didn’t.
On an Origination Point alert you can also set a routing group, or deactivate a dial peer. Disable Primary Object is the action that stops the object.
CPS is separate from the alert. On the Origination Point, Advanced, set Limit up to X calls, during Y seconds. You pick X and Y from the pace you accept from that trunk. When the trunk crosses the limit, the CDR shows hangup cause 304 (Originator’s CPS Limit reached) and disconnect code 826 (OP CPS limit reached). On MOR the same limit sits on the device (hangup cause 244) and on the provider (hangup cause 243).
On each Origination Point that sends US-bound traffic, turn on Add STIR/SHAKEN Identity. Set the attestation level to By TP, A, B, C, or Default. Default uses the level in the server configuration. If the Origination Point and the Termination Point both sign, the Origination Point level wins, unless you chose By TP. M4 builds the SIP Identity header on Kamailio 5.7 or later. The private key and the public certificate come from a STIR/SHAKEN authority. Kolmisoft does not supply them. This step signs calls you send. It does not check signatures on calls you receive, and it does not score the traffic. The MOR path, including what A, B, and C stand for, is STIR/SHAKEN with MOR. MOR signing needs X19, and it runs on PJSIP devices and providers.
On MOR, open SETTINGS → Setup → Settings → Calls. Archive Calls older than defaults to 365 days. Set the number to 0 and archiving stays off. Delete Calls instead of Archiving removes the old rows. Archive only ANSWERED calls deletes failed, busy, and no-answer calls, so leave that box off if a traceback might name a call that did not connect. Storage is the MySQL table mor.calls_old, a TGZ of CSV files (50,000 lines per file), or that TGZ sent to FTP. Read the rows at STATISTICS → Calls → Archived Calls. The files list is on that page, and admins can open it.
On M4, open MAINTENANCE → Settings → Archived Calls. The choices match MOR: another table, a TGZ, or FTP. The M4 settings page does not print a default number of days. Read the value on your system before you assume a call from last year is still on Calls List. Archived rows are at Reports → Call list → Archived Calls. TGZ files are at REPORTS → Call list → Archived Calls → Files.
M4 also removes stored CDR export files after a number of days. The default is 30, and the minimum is 7. That timer covers the export folder, not the calls table. Copy a traceback CSV off the server if you still need it after that window.
Signing builds the SIP Identity header on the way out. You bring the certificate. The switch does not verify another carrier’s signature in this feature, and it does not produce a robocall score.
Enable Show CallerID sent to TP, then search Source (CallerID) sent to TP. Match the request’s clock to the time on Calls List. A % pattern on Destination catches a dialed number that arrived with an extra prefix.
No. Export the CSV. A person sends the reply. There is no ITG submission on the switch.
Copy the time, the calling number, and the dialed number. Search Source and Destination on Calls List, open Call Info, and export the user and the Origination Point. Set Active to no on that trunk. Add a TOTAL CALLS alert on that customer with Disable Primary Object, and read the archive day-count so the row is still there next month.
To click the same screens on a Kolmisoft system, open the M4 online demo. It is the web interface. It does not place calls.
Find the call on your switch
Take the time, calling number, and dialed number from the request. Search Calls List, export the Origination Point, and set that trunk to inactive. The M4 online demo shows the same screens.
The post Answer an ITG traceback from your own CDRs appeared first on Kolmisoft Blog.
]]>The post What your softswitch can stop in a SIP DDoS attack appeared first on Kolmisoft Blog.
]]>A softswitch can stop a SIP flood that reaches the server. It cannot clear a pipe that is already full. There is no guaranteed way to stop a targeted DDoS. Once the uplink is saturated, a firewall rule and a call limit have nothing left to act on. Customers will report dead calls, a dead GUI, or both. That report does not name the layer. The first job is to name it: host, server, or call control.
A volumetric flood fills the path. The server can be up and still be unreachable, because the loss happens on the way in. A SIP-layer flood is REGISTER and INVITE traffic, plus scanners, aimed at the softswitch. The path still has room. The process does not.
| What you measure | Layer | Who can act |
|---|---|---|
| The port graph sits at the line rate, or the path dies while the server stays up | Network | The host, or a filtering network in front of the host |
| The port has room, and the SIP log shows scanner names or a subnet you can name | Server | iptables on the box: scanner rules, countries, blocked addresses |
| Calls sit pending, one device or origination point sends the setup rate, or one number is the target | Call | CPS limits, Asterisk maxload on MOR, a per-number burst limit on M4 |
VirtualGlobalPhone published the volumetric case on 24 July 2026. The attack followed an extortion demand they called consistent with a campaign against several U.S. service providers in the weeks before. Traffic came from thousands of places, across the whole prefix, on every upstream. The upstream link was already full, so the filter at their edge had nothing clean to pass. Blackhole routing of one address left the rest of the prefix under attack. Each source sent a small share, so automated detection missed the pieces. VoIP and the web panel both failed.
FibreMax reported the same split on 15 May 2026. Their servers stayed up. Hosted voice failed because the attack hit the hosting provider’s upstream. FibreMax says the host put the volume at about 400 Gbps. The outage started about 1:00 PM AEST. The host brought in GSL Networks, a filtering network, about 6:30 PM AEST. Phone service was back by 10:00 PM AEST. The 400 Gbps figure is the host’s number, as FibreMax reported it.
Ringotel hit the second kind on 31 August 2026. A coordinated attempt against their SIP infrastructure started about 11:30 AM UTC in US East and US West, then London, Dublin, Singapore, and a few on-premise regions. Calls failed in bursts. The regions stayed up in a degraded state, so automatic failover did not move traffic. Staff moved them by hand. Customers who had allowed one IP on their firewall could not reach the secondary address. Most regions were back by 2:30 PM UTC. A few stayed affected until about 3:00 AM UTC on 1 September. Ringotel says no customer data was read and no unauthorised calls were placed.
Read the port graph and ask the host if they see an attack on your prefix. A full graph, or a host ticket that names an attack upstream of you, is the network layer. A quiet graph plus a storm of REGISTER or INVITE is the server or the call layer. Do that check before you edit the switch.
Hetzner includes DDoS filtering at no extra charge, and detection runs all the time. The filter list Hetzner publishes names DNS reflection, NTP reflection, UDP floods on port 80, SYN floods, DNS floods, and invalid packets. SIP on 5060 is absent from that list. A move from a 1 Gbit port to a 10 Gbit port gives legitimate packets more room beside the flood. The attack is still on the wire.
OVHcloud includes Anti-DDoS on every product, at no extra charge, with detection left on. They publish more than 17 Tbit/s of filtering capacity and say the system has mitigated attacks up to 1.3 Tbit/s. The price includes mitigation for the whole attack, with no cap on how many times you are hit. They also say many of these floods fill the link in front of the server, which means the server cannot filter them. That page does not describe a SIP parser. Ask whether they scrub UDP 5060 and your RTP range.
A Cloudflare proxy in front of the GUI covers HTTP and HTTPS when staff open the GUI by domain name. Any other protocol ignores that proxy. The same domain cannot carry the GUI and SIP. Someone who knows the server IP sends the attack at the IP and skips the proxy. Cloudflare is a separate company. Kolmisoft does not support Cloudflare products.
Cloudflare Spectrum is a different product: a TCP and UDP proxy with layer 3 and layer 4 filtering. Custom TCP and UDP applications sit on the Enterprise plan as a paid add-on. Spectrum drops packets for a protocol or a port you did not list. It carries SIP and RTP when you list those ports, and RTP means the whole media range. Plan on several thousand USD a month. The range we give clients is about 3,000 to 4,000 USD and up, on a quote, with no public price list. For many of those providers the quote is higher than what they pay for the softswitch. Spectrum sits next to the filter the host already includes. It is one option with a price.
If the path is full, the host is the party who can act. VirtualGlobalPhone’s edge never got a clean path. FibreMax’s platform stayed up while the host’s upstream did not.
Use this layer when the port graph still has room and the SIP log shows a tool name, a country you do not serve, or a subnet you can write down.
MOR from X16, and M4, install iptables rules that drop SIP scanners by User-Agent. The default list is friendly-scanner, sundayddr, sipsak, sipvicious, iWar, sipcli, VaxSIPUserAgent, and pplsip. Add or remove names in system.conf. The switch rewrites the rules every 15 minutes. Run the block-scanners script when the new list has to hit the wire now.
Blocked Countries, under Security, loads country address lists from ipdeny.com into ipset. MOR and M4 both have the screen. The lists refresh every month. A save with many countries can take 4 to 5 minutes. Each country list keeps an emergency allow: the server’s own address, Kolmisoft support access, the server’s DNS resolvers, the list source, and SSH on port 22. The lists are not exact, so a new carrier address can sit inside a country you blocked. The kernel has to support ipset (2.6.32 or newer, with ip_set and ip_set_hash_netport). Otherwise the GUI warns you, and that server does not enforce the list.
Blocked IPs, also under Security, takes a single address, a subnet in CIDR form, or a range. The GUI stores a CIDR entry in canonical form: 2.2.2.1/24 becomes 2.2.2.0/24. A range such as 2.2.2.1-125 becomes one row per address, so prefer a subnet. The block takes effect within one minute. On M4 you can aim it at one server or at every server. You cannot block the server’s own address, a local address, or a private address.
The rules drop packets that arrive. Packets the uplink already discarded never reach them.
Use this layer when the SIP comes from a customer you allow, or when one destination is the number under attack. The firewall is the wrong tool for a customer who is supposed to send you calls.
On MOR, set “Limit up to (number) calls, during (number) seconds” on the device, and the same pair on the provider. Hangup cause 244 is the device cap. 243 means the provider was skipped for the cap. MOR documents a system ceiling of 100 CPS. One Asterisk server is documented up to 50 CPS, and in practice nearer 20. A steady 150 CPS or more into MOR, with no proxy in front, can take the server down. The caps do not save that box. The setups overwhelm it before a reject can run.
On M4, set “Limit up to X calls, during Y seconds” on the origination point or the termination point, and again on a dial-peer and termination-point pair. M4 documents a system ceiling of 1,000 CPS. Hangup cause 304 is the origination point. 329 is the termination point. 342 is the system incoming cap. 345 is the cap inside a dial peer.
On MOR, a screen of pending calls that never connect means Asterisk is past its load. Set maxload in /etc/asterisk/asterisk.conf to the number of cores, or lower, and restart Asterisk. Read high Asterisk CPU before that restart. A flood is one cause. A stuck query, a full disk, or qualify traffic is another, and the restart hides which one you had.
On M4, destination DDoS protection is a per-number call-burst limit in /etc/m2/system.conf. It ships off (dst_ddos_check_enabled = 0). Turn it on and M4 blocks a destination after Y attempts inside Z seconds, then holds the block for X seconds. The defaults are 10 attempts, a 60-second window, and a 60-second block. The block can cover every user, or a profile can name user ids (up to 99 profiles). A profile can also leave the check off for a dialer whose retries are real traffic. The CDR shows hangup cause 385, “Destination number temporarily blocked (DDoS protection).” That cap stops setup toward one number. It does not filter the uplink.
Ringotel’s regions never went dark, so the automatic move never ran. The move staff made by hand then failed for customers whose firewall allowed one address.
Publish the secondary SIP addresses before you need them. A move to an address their firewall drops looks like a second outage. Test the move while SIP is slow, not after you power a server off. If failover waits for a dead peer, the pair stays put while SIP is slow. That is the state Ringotel was in.
High availability, redundancy, and failover separates an automatic failover from a switchover you start by hand. Read it before you tell customers the second server takes over on its own.
A competitor can sign up as a customer, learn the SIP address you hand them, and send a DDoS at that address. If your trusted clients use that same address, the flood hits them too.
Run two servers and two addresses. One address is for new customers. Every new signup registers there and sends traffic there. All of them share that one address. The other address is for trusted clients. A new signup does not receive it.
After some time, once you have decided a signup is a real customer, move that customer to the trusted-client server. Until the move, an attack that follows a signup takes the new-customer server down. The trusted clients stay up.
A second address in the same prefix still goes down with that prefix. VirtualGlobalPhone’s flood covered every address they announced. Put the new-customer server on a different prefix, and on a different upstream if you have one.
maxload case. Set it to the core count. Use the CPU article before you restart.Ask the host this, before an extortion mail shows up:
Set the durable pieces on a quiet day. Leave the scanner rules on. Block countries you do not serve, and know what the emergency allow keeps open. Put CPS caps on dialer customers before a campaign. Decide the M4 per-number burst in advance, with a profile for a user whose retries are legitimate. Put secondary SIP addresses on customer firewalls now. Point new customers at the new-customer server, and move one to the trusted-client server only after you have decided they are real. If the GUI sits behind a web proxy, give that proxy its own domain and leave SIP on the address. The longer checklist for passwords, open ports, and who may send you SIP is PBX security best practices.
They can limit a SIP flood that reaches the server: scanner User-Agents, country lists, named addresses, CPS, Asterisk maxload on MOR, and a per-number burst on M4. They cannot clear a saturated uplink. There is no guaranteed way to stop a targeted DDoS.
It protects HTTP and HTTPS when people open the GUI by the domain. It does not protect SIP. That domain cannot carry SIP as well. An attacker who knows the server IP bypasses the proxy.
On M4 it blocks one destination number after too many attempts in a window. The default is 10 attempts in 60 seconds, then a 60-second block, and the CDR shows hangup cause 385. The uplink is untouched.
Ringotel moved regions by hand, and customers who had allowed one IP could not connect. Put both addresses on the customer firewall while the primary path is healthy.
The one address on the new-customer server. Every new signup uses that same address. The trusted-client address stays off that account until you move them. A second address in the same prefix can still go down with the rest of the prefix.
A full pipe goes to the host. A SIP flood that reaches the server goes to scanner rules, Blocked IPs, a CPS cap, and on M4 the per-number burst. If you want those call controls checked on the MOR or M4 you already run, open a ticket at support.kolmisoft.com.
Match the control to the layer
A full uplink goes to the host. A SIP flood that reaches the server goes to scanner rules, blocked addresses, a CPS cap, and on M4 the per-number burst.
The post What your softswitch can stop in a SIP DDoS attack appeared first on Kolmisoft Blog.
]]>The post 2026 October: Two critical PHP bugs appeared first on Kolmisoft Blog.
]]>Apache with mod_php is the highest risk. A request can hit the first bug before your application code runs.
On the hosted servers Kolmisoft manages, we installed the PHP security updates. If you run the server yourself, install the updates today.
Memory handling and error handling run on a normal PHP request. A quiet page stays exposed when the worker behind it lacks the fix.
CVE-2026-109618 (GHSA-w8hw-w9fx-j79w) is a fault in how PHP handles an out-of-memory condition. A request that reaches memory_limit at the wrong moment interrupts PHP while internal request data is half set up. Cleanup then treats that invalid data as valid and corrupts memory.
On Apache with mod_php, a request from someone who is not logged in can reach this path before your application code runs. PHP demonstrated code execution in that setup with default settings. PHP also confirmed code execution on PHP-FPM, through paths in application code.
CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp) is state left behind after a fatal error. The error hits at the wrong point, and internal data points at memory that belonged to a previous request. The next request on the same worker can use that freed memory before normal startup clears it.
PHP confirmed a use-after-free and a worker crash. The reporter demonstrated code execution against a real application on Apache with mod_php.
On 20 October 2026 a CVSS score of 10.0 circulated with the two CVE ids. PHP scored each bug 9.2.
PHP lists every install that serves web requests: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed. The highest risk is mod_php.
A voice server is exposed when any of those processes answer HTTP. The softswitch can be a different program and the box can be exposed. Look for a database admin panel, a customer website, an old billing screen, or a container that runs PHP. The Swiss National Cyber Security Centre makes the same point for appliances and internal admin tools, not for the public website alone.
Check PHP that the internet can reach first. Then check PHP that staff alone can reach, when that process can see customer records or the billing database.
php -v
pgrep -a php
apachectl -M 2>/dev/null | grep php
php_module in the Apache module list means mod_php. That is the setup PHP ranks as the highest risk. An empty pgrep is a good result for that machine. It says nothing about a second server, a container, or a panel on another port.
Say you SSH to a server you administer. php -v prints 8.3.35, and apachectl -M shows php_module. That host needs PHP 8.3.36, or an OS package whose changelog names CVE-2026-109618 and CVE-2026-109619. Install it and restart Apache. If you installed upstream 8.3.36 and php -v prints 8.3.35, the worker is the September build. If the package came from your OS vendor, read the changelog. The printed number can stay 8.3.35.
| PHP | Fixed release | Note |
|---|---|---|
| 8.5 | 8.5.12 | |
| 8.4 | 8.4.27 | |
| 8.3 | 8.3.36 | 8.3.35 is the 24 September 2026 release and does not contain this fix |
| 8.2 | 8.2.35 | Security fixes for 8.2 stop on 31 December 2026 |
| 8.1 and older | No release from the PHP project | Ask your OS vendor for a backport, build the minimal source backports PHP published for 7.4, 8.0, and 8.1, or move to a fixed 8.2 or newer |
The PHP downloads page has the source packages and the Windows builds. Linux distributions received the patches before the public release. Install your OS vendor’s package when they have published one.
A backport can sit inside a version string you recognize. If the package prints 8.3.35, read the vendor advisory before you treat the number as proof. The Swiss NCSC states the rule in one line: an unchanged PHP major version does not mean the patch is missing, and it does not mean the patch is present. The package changelog is the proof.
On the hosted servers Kolmisoft manages, we installed the PHP security updates.
If the server is yours, the update is yours. Install the vendor package, or one of the four fixed releases, today. Then restart PHP so the workers load the new binary. The old workers keep running until you restart them.
Restart php-fpm, Apache, or both, the same way the site runs. For a container, rebuild the image and start the new one. Check that php -v, and the version the web worker reports, name a fixed release or a package your vendor marks as fixed.
PHP says an update is the complete fix. The settings below cut some paths and leave others open. Use them for the hours it takes to get the package onto the machine.
For CVE-2026-109618, set memory_limit=-1 where the application cannot put a finite limit back. php_admin_value in the FPM pool or in the Apache config is the place PHP names. This removes the PHP memory-limit failure a caller can aim at. An allocation failure under that limit can reach the bug. If you set RLIMIT_AS or RLIMIT_DATA, the process can hit that same allocation failure.
For CVE-2026-109619, set session.upload_progress.enabled=0 in php.ini or in the FPM pool, then restart each worker. ini_set() and an FPM .user.ini apply too late. This blocks the session paths PHP confirmed. Other paths stay open. On PHP 8.5, set fatal_error_backtraces=0 and restart.
A web application firewall does not close these bugs. The Swiss NCSC says the same about stopping at the temporary steps.
If a PHP app is reachable from the internet and you cannot update it today, remove it from the internet until the package is on.
Read the logs for the window before the restart. The update does not undo a compromise that happened earlier. The Swiss NCSC tells operators to look for unusual requests, crashes, modified files, and unexpected processes.
The older PBX security notes tell you to apply a supplier advisory when it ships. This PHP release is one of those advisories. A patched PHP worker does not stop a stolen SIP password from placing calls. We wrote that case up in Your firewall passed. Your balance didn’t.
We installed the PHP security updates on the hosted servers we manage. If you run a separate machine, a VM, or a container that we do not manage, you update PHP there.
Skip the update when the server has no PHP process serving web requests. A panel, a website, or an admin tool on the same host is enough for these bugs to apply. Run the process check above before you decide.
No. The fixed release on PHP 8.3 is 8.3.36. A distribution package can ship the fix under a different package version. Read that vendor’s security note, restart the workers, and check the binary that is running.
Read the PHP security advisory and install the fixed build today on each server you administer. The advisory lists the temporary limits, including request-size caps, for the hours before that package is on the machine.
Servers you run
Install the PHP security update today. The advisory lists the fixed versions. The temporary limits do not replace the update.
The post 2026 October: Two critical PHP bugs appeared first on Kolmisoft Blog.
]]>The post High Asterisk CPU: find the cause before a restart appeared first on Kolmisoft Blog.
]]>The time inside the Asterisk process tells you which cost you are looking at. User time is codec math, dialplan, and log lines. System time is the kernel moving packets. A restart, a SIP debug session, or a module unload throws away the minute you needed. Collect that minute while the percentage is high, then change the thing the minute names.
pidstat prints a task as a share of one CPU unless you pass -I. The pidstat manual divides that share by the processor count when -I is set. Leave -I off and a process on more than one core prints a number above 100. 170% system time is about 1.7 cores inside the kernel. The rest of the server can be idle. One thread pinned near a single core is a separate problem, and top -H is how you see it.
top uses the same kind of scale: 100% is one logical CPU. A host with 8 logical CPUs can show Asterisk at 100% while seven cores are free. The process ceiling on that box is about 800%. 100% is one core. People open a ticket because Asterisk “hit 100%” and calls keep flowing. Compare the percentage with the core count before you call the host full.
A host-wide figure from mpstat is a third scale. 14% system time on the whole machine and 150% system time on the Asterisk process can both be true in the same minute. Compare last week when both readings came from the same tool. Mixing -I and no -I compares two different scales.
top also prints steal time and I/O wait. Steal time means the hypervisor gave the CPU to another guest. Two VMs with the same core count can show different Asterisk percentages because one of them shares a noisy neighbor. I/O wait means a disk is the stall. Neither number is fixed by a codec change.
Asterisk’s own high-CPU note makes the same cut. Poor audio or a long setup is the fault you chase. The percentage is the measurement. Define the number you will accept before you edit a config file. Troubleshooting High CPU Utilization starts there.
Sort top by CPU. Asterisk, mysqld, a backup compressor, a SIP proxy, and fail2ban-server are different jobs. If the database leads, the work is the query that is running. If a compressor leads, the work is the archive job. If a proxy leads, restarting Asterisk does not touch the process that is hot. If Asterisk leads, stay with Asterisk.
A spike at midnight, on the hour, or on a clock you can predict is a scheduled job until you prove otherwise. Database dumps, pbzip2 or gzip on those dumps, CDR deletion, and log rotation all sit on a core for the length of the job. A dump that runs 00:00 to 01:30 will page you every night and leave call quality alone. Confirm the clock against cron and the process name in top before you retune SIP.
CPU with almost no calls is a different shape from CPU that tracks the call count. No calls, plus a hot Asterisk, points at OPTIONS probes, a debug flag, a stuck dialog, a timer, or a thread spinning. Calls present, plus system time, points at packets. Calls present, plus user time, points at codecs, mixing, or logs.
Then sample the Asterisk pid for ten seconds:
pidstat -u -p "$(pidof asterisk)" 1 10
Leave -I off so a multi-core process can print above 100.
| Column | Where the time went |
|---|---|
| %usr | Application code: codec translation, dialplan, log lines |
| %system | Kernel: sockets, RTP, softirq |
| %CPU | Those shares added together, on the one-CPU scale |
A sample where %system is several times %usr is a packet path. A sample where %usr leads, and translators are in use, is codec work.
Asterisk documents a bridge as the object that shares media between channels. Dial() creates one when the outbound channel answers. One ordinary call is two channels in one bridge. The shapes are in Asterisk’s bridge note.
A native RTP bridge (native_rtp in bridge show all) is the native technology for two RTP channels. It has two shapes.
* Local. Each device sends RTP to Asterisk, and Asterisk forwards it to the other device. There is no transcoding. Asterisk remains in the media path. This is the common case when either side is behind NAT.
* Remote. Asterisk redirects the media so the two devices send RTP to each other. Asterisk calls this direct media. It is the lowest CPU of the three shapes. Both channels have to support it, and neither device can be hidden behind NAT.
A core bridge (simple_bridge) is the fallback when Asterisk must see the audio: different codecs, recording, a forced jitter buffer, or in-band DTMF. A mixing bridge (softmix) is a conference. Mixing shows up as application time. A local native relay shows up as system time.
canreinvite and directmedia are the same sip.conf option. Asterisk renamed canreinvite to directmedia and kept the old name. In the Asterisk 20 sample config:
* yes tries to send the RTP from caller to callee. That is the default in the sample.
* no refuses that redirect, so a native_rtp bridge stays local and the host keeps the packets.
* nonat allows the redirect when the peer is not behind NAT, and refuses it when the peer is.
* update uses a SIP UPDATE for the redirect. Combined with nonat it is update,nonat. It implies yes.
* outgoing avoids an immediate re-INVITE glare with another server that does the same. It implies yes.
Hold and T.38 can send a re-INVITE with no set. no blocks the re-INVITE whose job is to take Asterisk out of the audio path. A peer section wins over [general]. [general] can say directmedia=yes while dozens of peers say no. Those peers stay on the host.
On chan_pjsip the matching idea is direct_media on the endpoint. The same NAT limit applies: two private addresses cannot send RTP to each other just because the option is on.
A 20 ms G.711 frame is 50 packets per second in one direction on one leg. A local bridge has two legs, so the host receives on the order of 100 UDP packets per second per call. About 200 per call is 10 ms framing. Same-codec forwarding is cheap next to transcoding, and a local bridge pulls every one of those packets through the server. If you drop ptime from 20 ms to 10 ms on the phones or the trunk, you about double the packet rate and the kernel time, with the codec pair unchanged.
Count packets without turning SIP debug on. A short tcpdump of UDP length and rate, or the interface counters in /proc/net/snmp and /proc/net/dev ten seconds apart, is enough to see 20 ms versus 10 ms. Debug writes a copy of every SIP message and makes the CPU worse.
A local native relay cannot record, and it cannot watch in-band tones. The moment you record, force a jitter buffer, generate in-band DTMF, or mix a conference, Asterisk has to see the samples. bridge show all names the type. simple_bridge on recorded calls is expected. softmix on a conference is expected.
transmit_silence in asterisk.conf is an easy one to miss. The sample file says the silence is generated as signed linear and then translated into the channel’s own format, so a channel that would have stayed native grows a translation path. Turn it on for a reason you can name. Leave it off when you are chasing CPU.
Music on hold, prompts, and voicemail are the same conversion. If the prompt files exist as G.729 and the caller is G.711, Asterisk translates for the length of the prompt. Keep files in the formats your callers use. Signed linear at 8 kHz and 16 kHz is the cheaper stand-in when you cannot store every codec.
In-band DTMF keeps Asterisk on the media path so it can watch the audio. The codec pair can stay the same. RFC 2833 or SIP INFO can leave a native bridge in place.
Forwarding two legs that share a codec is the cheap case. Transcoding is the expensive one. Asterisk’s high-CPU page prints core show translation as microseconds per second of audio on that machine. Compare the cells with each other. Your call count comes from a sample on your host. This table compares codecs on the machine that produced it.
On the machine in that doc, one second of audio costs about 9,150 microseconds from µ-law to A-law, about 15,000 from µ-law to G.729, and about 23,000 from µ-law to Opus. An Opus path to µ-law has four steps: Opus at 48 kHz, signed linear at 48 kHz, signed linear at 8 kHz, then µ-law. A WebRTC leg or an AI-agent leg that offers Opus, against a PSTN leg that is G.711, pays that path on every call. If the far end accepts 8 kHz signed linear, offer that and skip Opus.
µ-law and A-law are both G.711, and they are not the same encoding. The translation cell between them is the small one on the chart. It is a conversion. Calls that look free on a bandwidth chart and sound thin can be this pair. Check the read format and the write format on the channel. The allow list in sip.conf is what you permit. The channel line is what this call negotiated.
%usr leads. core show channels shows a translator, or a different read format and write format. bridge show all shows simple_bridge where you expected native_rtp. Recent CDRs disagree on the two legs. Read the translation table you have. Skip core show translation recalc during the hot minute. Recalc runs a new timing test on the cores you are trying to measure.
Match the codecs when you can. Let the carrier translate at the edge when the contract allows it. Hardware offload is a third option when you must translate at volume.
top -H -p shows threads. One Asterisk thread near a full core, with the host idle, will not go faster because you add vCPUs. Extra cores sit behind a thread that never leaves the one it has. Cutting vCPUs because “Asterisk is at 100%” throws away the idle cores. The 100% was one core. The other cores were the headroom.
chan_sip keeps a monitor thread (do_monitor) whose job is SIP I/O. If top -H shows that thread at a full core and the call setup rate is the thing falling over, you are looking at the SIP read loop, not at a shortage of idle cores. chan_pjsip spreads SIP work across a threadpool. The pool size is set in the system section of pjsip.conf. A pool that is too small starts dropping new SIP work so the threads can catch up. Raising the pool has a limit too: past it, you profile the next bottleneck instead of guessing a bigger number.
core show threads names the Asterisk-side threads (bridge, mixmonitor, taskprocessor, the SIP monitor). Match the hot thread in top -H to a name there before you decide the host is small.
qualify=yes sends SIP OPTIONS to each peer on a timer (qualifyfreq in sip.conf, qualify_frequency on a PJSIP AoR). A few hundred peers are a light tick. Thousands of peers on a short timer are a burst: CPU, disk, and a wave of registrations that drop together when the burst overruns the SIP thread. The graph shows a spike on a schedule, including minutes with almost no phone calls. Lengthen the timer or stop qualifying peers you do not need to mark unreachable.
A flood is a different shape. Many SIP packets from one address, or a SIP rate that is high for the number of real calls. Count packets with tcpdump or interface counters. Leave sip set debug on off while you count. One remote address at the top of that count, with no matching customer, is the source to reject in your firewall.
Stuck dialogs are a third shape. An originator sends INVITE and then goes quiet. On chan_sip:
asterisk -rx "sip show channels"
A long list with the same address and the same Last Message is that pile. On PJSIP, pjsip show channels is the matching view. Find the sender. A restart hides the list.
Read today’s log before you add debug. chan_sip logs Serious Network Trouble when __sip_xmit fails, and it logs retransmission timeouts when the other side never answers. Thousands of those lines in a day mean DNS or the path to the peer is failing. Asterisk then looks frozen: the SIP thread is busy timing out work that will never complete. A restart clears the queue. The timeouts return with the next outage. Fix the resolver and the route. A core upgrade does not answer a DNS query.
High latency with an idle Asterisk is the same family. A database on the other side of a slow link, or a trunk whose qualify RTT is hundreds of milliseconds, stalls setup without filling the CPU graph. Measure the RTT and the query time. Do not buy cores for a path that is waiting on the network.
Logging spends CPU to build the line and I/O to write it. Asterisk measured Channel Event Logging at almost 40% of the instructions in the process on versions before 23.3.0, 22.9.0, and 20.19.0. That was more than the instructions spent on the calls. Those three releases brought the share under 10%. If you do not need CEL, turn it off. Verbose logging writes a line for every dialplan step. On a busy system that is hundreds of lines a second. Keep console and file logging at NOTICE, WARNING, and ERROR unless you are tracing one call. Debug on a production system is for that one issue, for a short window, and then off.
fail2ban is a separate process. Its line in top is its own CPU. Asterisk system time is a different line. A large ban list plus a long chain of firewall rules shows up as softirq and as Asterisk system time together: every RTP packet walks the chain. Rotate the logs fail2ban reads. Leave fail2ban running. Stopping it removes fail2ban’s CPU for as long as it is down, drops the bans, and leaves the RTP cost in place.
A spinning disk under a database makes calls fail while the CPU graph looks confusing. wa in top or iostat is the tell. Slow queries pile up, the GUI times out, trunks drop, and someone restarts Asterisk because that is the service they can name. The restart coincides with the queries finishing. Ask the disk. iostat -xz 1 during the minute, and SHOW FULL PROCESSLIST on the database, name the query. A per-call blacklist, a rating lookup, or a stats page that scans a large table will do this at a modest call rate. Move that work off the call path, or give the database an SSD, before you add Asterisk cores.
CDR deletion and call archiving are the same class of job as the midnight backup. If they run through the business day they compete with the SIP thread and the disk. Schedule them for the trough, and check the next day’s graph before you declare the SIP stack healed.
Free memory has its own door. minmemfree in asterisk.conf stops new calls when free RAM falls below a watermark. A host that “runs out of channels” with idle CPU may be on that floor. Read the setting and free -m in the same minute.
A critical sensor line that says the processor throttled is a hardware event. It can arrive on the same morning as a high Asterisk percentage. The two can be different problems. Check the governor and the current clock (cpupower frequency-info, or the values under /sys/devices/system/cpu/cpu*/cpufreq/). A core whose governor says performance and whose clock sits far below the maximum is being held down by heat. A sensor at a high temperature in that same minute confirms it. A reading an hour later describes a different minute.
A lower clock makes the same packet rate look like a higher percentage, because each core does less work per second. Switching tuned profiles (latency-performance, network-latency, powersave) is a decision for the person who owns the chassis and the BIOS power cap. Do that after you know the clock was down during the Asterisk spike. If the clocks were at maximum during the spike, the governor is the wrong afternoon.
maxload in asterisk.conf is a load-average ceiling. Above it, Asterisk refuses new calls. The CLI names the configured limit and the load it just measured. Load average is not the Asterisk %CPU you read in top. A ceiling of 0.9 or 1.0, which is what the sample comment and the core docs page use as illustrations, will refuse calls on a many-core host that is almost idle. The core config page shows maxload = 1.0 and warns that the page can lag the sample file. The current sample leaves the line commented and illustrates 0.9. Read the number in your file.
Raising maxload lets more calls in. The CPU cost of each call stays the same. Set the ceiling from the core count and from the load you have measured, then find out why the load is high. If Asterisk keeps refusing calls after a higher limit, the remaining options are more hardware, another Asterisk with traffic moved onto it, or less traffic.
Do this while the percentage is high. A quiet hour produces a quiet sample and a wrong story. Hours later, the process list is a different process list.
core show channels count) and whether audio or setup is worse than the hour you treat as normal.top for ten seconds. Record the top three processes, %wa, and %st (steal).pidstat -u -p "$(pidof asterisk)" 1 10 without -I.top -H -p long enough to see whether one thread sits near 100%.bridge show all and a handful of channel read/write formats.SHOW FULL PROCESSLIST, or iostat -xz 1, or the proxy’s own stats.Serious Network Trouble lines in today’s log, without enabling debug.Leave SIP debug off while you collect this. Leave chan_sip.so loaded. Unloading it drops live calls. Leave directmedia / canreinvite as they are until you have checked NAT on that peer. A global yes fails when the two ends cannot reach each other, and the result is one-way audio. Leave fail2ban running. Leave the tuned profile in place until the clocks say they dropped. Leave maxload in place until you know which process created the load.
A restart is the last step, and it is a way to restore service after you have the notes. It is a poor way to learn. If a restart buys a few hours and the heat returns, you are looking at a leak, a stuck dialog, or a job that starts again. The next restart can wait until the list above is on disk.
On a multi-core host, 200% without pidstat -I is about two cores. Read idle and steal in top, and host system time in mpstat, before you order hardware.
The call can be a local native bridge. Same codec skips the translator. The packets remain. Check bridge show all and %system against %usr. Also check ptime. 10 ms framing doubles the packet rate of 20 ms framing.
It can, when both ends support a remote bridge and neither side is behind NAT. When that path fails you get one-way audio. Peers set to no ignore a yes in [general].
Record the minute first. Debug makes the CPU worse. A restart drops live calls and deletes the shape you needed.
Look at cron and at the process name. Backup compression and CDR cleanup run on that clock. If the process is pbzip2, gzip, or mysqld and Asterisk is quiet, the calls are not the cost.
This is a production Asterisk we measured. Names, addresses, and the platform around it are left out. The shape is the one the sections above are for.
The morning started with a critical hardware event: the CPU temperature sensor reported that the processor had throttled. The host was on the latency-performance tuned profile. The question was whether to load network-latency while calls were up. That choice belongs to the person who owns the hardware. It is not an Asterisk setting.
Later the same day Asterisk’s own percentage was the complaint. On a comparable business day the week before, the process had peaked around 120%. This day it was around 250%, and the traffic count was not higher. A restart the next morning, outside the busy hours, did not bring it back. At 160 concurrent calls, with the day expected to reach 300 to 350, the reading was about 100%.
A one-minute sample that morning caught 223 calls:
| Reading | Value |
|---|---|
| Asterisk %system | 151.8% (about 1.5 cores in the kernel) |
| Asterisk %usr | 21.26% (about 0.2 cores in the application) |
| G.729 translators | none in use on the channels we sampled (0 of 20) |
| Bridges | native_rtp 113, simple_bridge 0, softmix 0 |
| UDP in | 83.3 packets per second per call, in the 20 ms band rather than the 10 ms band |
[general] |
directmedia unset, canreinvite=yes |
| Peers | canreinvite yes or update: 0. no: 42 |
| Codec mismatch on recent CDRs | a few hundred mixed legs against a few hundred thousand CDRs on the busy peers. Largest pair A-law to G.729 |
| fail2ban-server | 31.60% of one core |
| Log growth in the window | about 1.0 MB |
| Debug / verbose | off / not raised |
| Host %system this sample | 13.56% |
| Same hour, 7 days earlier | host %system 14.32%, host %user 5.24% |
System time was about seven times user time. The bridges were local native RTP. Translators were idle. The general section allowed a remote bridge. Forty-two peers refused it. Those peers were set that way before this morning. Incoming UDP sat with 20 ms framing (on the order of 100 packets per second per call). 10 ms framing would sit on the order of 200.
The host-wide system time matched the same hour a week earlier (13.56% against 14.32%). The box had the same kernel share. Asterisk’s process time was kernel time on a local relay, and fail2ban was a second process at about a third of one core. RTP volume against the prior week did not show a rise, and spot checks showed no transcoding. That agrees with the sample. The jump from about 120% to about 250% stays unexplained if the packet volume was flat and both percentages used the same scale. Heat can do that when a clock drops. This minute did not show a throttle in progress. A restart had happened earlier that morning, and the relay was the shape at 223 calls.
At 223 calls the kernel share was about 1.5 cores. A later peak of 300 to 350 calls on the same local path will spend more kernel time if each call keeps that packet rate. One minute cannot size the host. It can stop you treating 14% host system time as a full server. The next facts, during a high minute, are the throttle bit, the core clocks, one thread versus many in top -H, and the fail2ban ban count. An Asterisk reinstall skips those facts.
The checks above are one file, asterisk-cpu-load.sh. It reads Asterisk, the CPU clock, bridges, codec modules, direct media, and fail2ban. It does not restart Asterisk, turn on SIP debug, or change a setting.
Sample the hot minute before you restart
Record the process split, the bridge type, and the codec path while the percentage is high. A restart throws that minute away. Send the sample with the ticket if you want a second look.
The post High Asterisk CPU: find the cause before a restart appeared first on Kolmisoft Blog.
]]>The post Introducing Italian, Ukrainian, and Spanish on Kolmisoft.com appeared first on Kolmisoft Blog.
]]>If Italian, Ukrainian, or Spanish is the language you use at work, you should not have to fight through English just to understand what we sell and how to reach us. Kolmisoft.com now speaks those languages alongside English.
You pick a language once on the homepage. From there, product pages, solutions, pricing, and contact follow that choice.
Buying or comparing a softswitch is already a technical decision. Language should not add friction on top.
Operators in Italy, Ukraine, Spain, and Spanish-speaking teams often ask the same first questions in their own language: what MOR and M4 are for, how rent or purchase options work, and who to contact. We added Italian, Ukrainian, and Spanish so those first answers are easier to read before you open a ticket or send a pricing request.
English stays the default. We started with the languages where we already talk to many operators, rather than listing every language on day one.
After you switch on the homepage, you can browse the same kinds of pages you use in English:
Direct links:
This change is about the public website. Your MOR or M4 installation and billing logic do not change when you read the site in another language. The Kolmisoft Blog is still in English for now.
If your browser language matches one of these, you may see a short question on the first visit. You decide; nothing redirects you without that choice.

Looking at retail VoIP or Class 5 (MOR). Switch language, open softswitches, then request pricing when you care about capacity and commercial options.
Looking at wholesale or Class 4 (M4 SBC). Same idea: switch language, read the M4 story, then pricing when you are ready.
Already a client. Use contact when you need sales or support. Send a colleague the Italian, Ukrainian, or Spanish homepage if that is easier for them than English.
If you want to try the products after reading, start from try Kolmisoft softswitches.
Many deals start with a short browse, then a form or a chat message. When the page is already in the language your team uses, that first message is usually more precise: capacity, retail versus wholesale, rent versus purchase, and what you need from support.
We still answer in English when that works better for you. The point of this launch is choice on the website, not forcing one language into every conversation.
We will keep improving wording where telecom terms need a clearer human pass, and we will add more languages only when operators actually use them.
Open kolmisoft.com in Italian, Ukrainian, or Spanish, then go to pricing or softswitches when you want the next step.
Open Kolmisoft.com in your language
Choose Italian, Ukrainian, or Spanish on the homepage, then explore softswitches or pricing.
The post Introducing Italian, Ukrainian, and Spanish on Kolmisoft.com appeared first on Kolmisoft Blog.
]]>The post Call-center traffic on a softswitch: what operators need appeared first on Kolmisoft Blog.
]]>A predictive dialer can fire more attempts in one second than a small office places in a morning. Many of those attempts never answer. The ones that do often last less than a minute. Concurrent calls can still look calm while attempt rate, database writes, and balance checks climb.
If you sell minutes to dialer originators, or you run outbound campaigns through your own switch, you need a softswitch sized for that shape. Not a phone system with a higher concurrent-call licence.
Contact-center software (queues, agent desktops, predictive dialers) is one product. Carrying and billing the voice path those dialers generate is another.
Termination sellers call the second product “CC routes”: wholesale capacity built for high calls-per-second (CPS), short average call duration (ACD), and answer rates that decide whether a campaign pays. Softswitch vendors sit one layer underneath. Your customers may run Vicidial, GoAutoDial, or a custom Asterisk dialer. You still have to authorize, route, rate, limit, and report every attempt.
Kolmisoft builds the softswitch and billing layer. We do not ship the dialer or the agent screen. Operators already pick their campaign stack; they need a switch that survives the traffic that stack produces.
For short definitions of dialer traffic, CPS, ASR, and ACD, see the VoIP business glossary.
Outbound dialer campaigns often sit under about 20% answer seizure ratio (ASR), with many short answered calls. Predictive dialers may place on the order of 20–30 attempts to keep one agent busy with a live answer. Conversational wholesale and residential traffic usually sit higher on ASR and longer on call duration. Treat those figures as rough industry bands, not as your network’s measured reality.
In production the pattern is usually:
| Signal | Typical dialer / CC shape | Why the switch cares |
|---|---|---|
| CPS | Bursts at campaign start and during predictive pacing | Signaling and authorization load |
| Concurrent calls | Can look “reasonable” | Licence and media path |
| ASR | Often low | Many failed attempts still cost CPU and CDR writes |
| ACD | Often short on answered calls | Billing increment and route economics |
| Originator mix | One hot dialer can dominate | Per-account CPS limits matter |
CPS and concurrent calls are separate ceilings. A trunk can be fast at attempts and thin on live channels, or the reverse. Calls per second vs concurrent calls covers the math. Why dialers get 503s when channels still look free covers the operator failure mode when those numbers disagree.
Generic wholesale routes sometimes throttle or surcharge short-duration, high-CPS traffic. Dedicated CC routes exist because that pattern is normal for dialers, not proof that the route is broken. Your softswitch still has to bill and contain the same pattern on your side of the interconnect.
A call-center PBX allowed at 10 CPS can still try 100 attempts in the same second. Softswitch platforms reject the excess (often with SIP 503) so one account cannot take down the node. Attempts also arrive in bursts (many calls in a few seconds), so limits have to cope with spikes, not only a neat one-call-per-second average.
On Kolmisoft platforms you set CPS limits per device or origination/termination point. Stated global guidance is on the order of ~100 CPS for MOR and ~1,000 CPS for M4, with architecture-dependent reality. Those are planning ceilings from our product documentation, not a promise that every deployment hits them.
When several dialers share one credential, they share one CPS bucket. Separate originators that need separate limits.
Sales talks often start with concurrent calls. Dialer traffic fails that framing. Two numbers belong in every go-live discussion:
If the second number is blank, both sides are guessing. We have watched operators ask for more concurrent-call headroom when the real wall was CPS. A licence bump does not raise attempt-rate capacity.
Outbound call centers pay most attention to connects. Attempts still hit your prepaid balance checks, postpaid credit limits, and CDR pipeline. Low ASR means lots of signaling for little billable duration. Rating increments (per-second vs full-minute rounding) matter more when answered calls are short. Termination sellers publish worked examples where increment policy alone moves monthly cost by thousands of dollars at the same headline rate.
Your softswitch has to keep real-time authorization honest under that load, or you discover the damage in disputes after the campaign.
Least-cost routing that only looks at price will push dialer traffic onto routes that cannot hold under high CPS and low ACD. Operators who carry CC originators need ASR/ACD (and related quality signals) in routing decisions, plus the ability to isolate a bad originator before it burns a vendor relationship. Which dialer customer burned your CPS route is the containment playbook.
When attempt rate spikes, the GUI and reports are under the same pressure as the signaling path. Live tracing and call lists that stay usable at peak are part of the product requirement, not a luxury. M4 call tracing exists for that job. If you cannot prove which originator, dial peer, or terminator failed during the burst, you cannot calm a customer who already has another vendor on speed dial.
Smaller contact-centre stacks often need recording for quality or compliance. MOR supports recordings as an add-on path. High-volume wholesale CC feeding many third-party dialers usually leans on M4 for capacity first, with recording decisions made per account and storage plan.
Some markets limit outbound dialer windows. Time-period routing can reject or divert dialer traffic outside allowed hours. See Automating time-based routing for dialer calls with MOR.
| Situation | Usual lean |
|---|---|
| Smaller dialer / CCaaS stack, recordings, richer Class 5 features | MOR |
| High-volume wholesale feeding dialer originators | M4 |
| Approaching or past ~1,000 CPS | Read when your voice business needs more than 1,000 CPS and talk capacity before you promise the deal |
MOR vs M4 SBC is the product comparison. The decision for call-center traffic is usually volume and CPS, not a feature checklist.
We do not sell Vicidial, a predictive dialer, or a branded softphone. If you need a turnkey dialer-plus-switch stack from one vendor, other platforms advertise that bundle. Our fit is the operator who already has (or will choose) a dialer and needs billing, CPS control, routing, and support that hold under attempt-heavy load.
Ask these of every dialer originator (and of yourself if you are the dialer):
If you cannot answer (1) and (2), do not size the switch from concurrent calls alone.
No. Business VoIP is extensions, voicemail, and staff calling. Call-center / dialer traffic is high attempt rate, often low ASR, often short ACD, with revenue tied to connects. Provision and bill them as different shapes.
Often yes, at different layers. A CC route is how you buy termination sized for dialer patterns. A softswitch is how you sell, limit, and bill that traffic to your own customers. Kolmisoft addresses the softswitch layer.
Because CPS or another attempt-rate / authorization limit was hit, not because every concurrent channel was busy. See the dialer 503 post.
No. MOR’s Auto-Dialer add-on is a voice-broadcast style tool, not a full predictive contact-center dialer. Bring your own campaign platform; use MOR or M4 for the carrier path.
Call-center traffic is not more of the same wholesale. It is a traffic class with its own attempt rate, answer economics, and failure modes. Operators who serve it need per-originator CPS limits, honest concurrent-call vs CPS scoping, billing that survives attempt storms, quality-aware routing, and tracing that works at peak.
If you are sizing MOR or M4 for dialer or call-center originators, tell us peak CPS, concurrent calls, and destination mix. We will be clear about what the platform supports today.
Contact us to discuss capacity for call-center traffic.
Sizing a softswitch for call-center traffic?
Tell us peak CPS, concurrent calls, and destination mix. We will be clear about what MOR or M4 supports today.
The post Call-center traffic on a softswitch: what operators need appeared first on Kolmisoft Blog.
]]>The post When your voice business needs more than 1,000 CPS appeared first on Kolmisoft Blog.
]]>These businesses serve different customers, but they can meet the same capacity problem: incoming call attempts approach or pass 1,000 calls per second, even while concurrent calls still look manageable.
At that point the operator needs more than extra channels. They need a platform that can process the attempt rate, keep records accurate, and leave routing controls and reports usable while traffic peaks.
At Kolmisoft, a growing customer hit the limits of our current M4 platform. We should have locked their peak CPS requirements earlier. Their experience also pushed us to study how M4 could support businesses that operate past our current capacity of about 1,000 CPS.
Not every wholesale carrier or call centre needs more than 1,000 CPS. The need comes from the traffic pattern, how concentrated that pattern is, and the combined demand across customers.
A wholesale operator may carry traffic from many customers. Each originator can look fine on its own, while their peaks together put heavy load on the platform.
Growth is often uneven. One customer can raise its attempt rate much faster than the rest, and that one account changes the capacity needs of the whole deployment.
Operators need both numbers in view: total incoming CPS, and how much each originator contributes. If you plan only around the largest customer, you can still miss simultaneous peaks across several accounts.
Outbound dialling can create many attempts that never become answered calls. Busy destinations, unanswered calls, and rejected attempts still consume processing.
That creates a capacity need that concurrent-call figures alone cannot describe. A provider may still have free channels while the platform is already struggling to accept new attempts.
For these businesses, visibility matters as much as throughput. Operators need to see which customers create the load, how attempts fail, and whether a routing change improves the picture.
Several campaigns that start or accelerate together can create concentrated peaks.
Average daily traffic can look comfortable while a short interval places far higher demand on the platform. Capacity planning has to account for how campaigns overlap and how long their peaks last.
A brief burst and a sustained high attempt rate are different operating conditions. You need both understood before you choose a deployment.
Some providers run wholesale, outbound, and other voice traffic on shared infrastructure.
As the mix changes, older capacity assumptions stop helping. An increase in attempts can stress shared resources even when answered-call counts move only a little.
The useful question is: what must the whole platform support when these traffic sources peak together?
Concurrent calls measure how many calls are active at once. Calls per second measure how fast new calls are attempted. In this article, CPS means incoming call attempts per second.
The two measurements are related, but they are not interchangeable. For the operator-side basics, read calls per second vs concurrent calls and why dialer 503s happen when channels still look free.
Many short or unsuccessful attempts can create a high CPS workload without a large concurrent-call count. Longer connected calls produce a different balance.
Daily call volume is another incomplete measure. The same volume spread flat across a day places different demands on a platform than the same volume packed into busy periods.
A useful capacity discussion therefore includes:
A concurrent-call licence upgrade does not, by itself, raise CPS processing capacity.
A softswitch must do more than accept incoming attempts.
It must make routing decisions, handle signalling, produce accurate records, and give operators access to the information they need to run the business.
As traffic rises, a bottleneck can appear in any part of that chain.
Attempts consume resources even when calls never connect. How much work each attempt needs depends on the deployment and its routing behaviour.
If attempts arrive faster than the system can process them, delays and failures follow. The operator needs to know the supported operating range before production traffic reaches it.
High attempt rates also increase the call data the platform must handle.
Reporting infrastructure can come under pressure alongside call processing. Elasticsearch documents how excess indexing work can saturate a cluster and affect search and other operations; see their indexing pressure documentation.
For an operator the consequence is practical: investigating traffic gets harder in the moments when clear information is most valuable.
Operators need to inspect failures, adjust routes, and answer customer questions during an incident.
If administrative access becomes slow or unavailable, the impact extends past call processing. The team loses the tools it needs to diagnose and respond.
Higher capacity therefore has to include operational usability, not only throughput.
One of our largest customers grew into a traffic pattern our current M4 platform could not sustain. One originator alone generated bursts near 2,000 CPS, above our current capacity of about 1,000 CPS.
During those periods, reporting and administrative access became difficult. We lost the customer.
We should have established their peak attempt rate earlier and made the capacity boundary clearer during sales and onboarding.
The experience also showed us a product requirement we needed to examine: how to support growing operators whose traffic approaches or passes that boundary.
Better qualification helps us recommend suitable deployments today. Development past the current capacity addresses operators who need more.
For how operators contain a single hot originator on their side today, see which dialer customer burned your CPS route.
Our current stated M4 capacity is about 1,000 CPS on a multi-server architecture (see also MOR vs M4 SBC). Operators who plan traffic near or above that level should discuss their requirements with us before they commit to a deployment.
That conversation needs to cover the full traffic profile and the supporting infrastructure. A headline CPS figure only makes sense in the context of configuration, workload, and operating conditions.
We are working toward higher capacity. Until we test and establish improvements, do not treat them as available production capability.
Raising capacity means finding where the current constraints sit and judging how each change affects the complete platform.
Several engineering approaches may help.
Profiling can find expensive processing steps, repeated lookups, or inefficient data handling.
Improving those areas may raise throughput. Any change must still preserve correct routing and accurate accounting.
Sharing traffic across processing nodes is an established approach in SIP infrastructure. Tools such as Kamailio provide traffic-distribution and overload-control mechanisms; see the Kamailio dispatcher documentation.
Distributing incoming traffic is only part of the challenge. Shared services, billing, reporting, and failure handling also need to carry the higher load.
Separating some reporting work from immediate call processing may help absorb peaks and reduce competition for resources.
This introduces trade-offs. Queues must stay manageable, records must be preserved, and operators need to know how current their reports are.
Every deployment has limits. Monitoring and traffic controls should help operators spot rising pressure and see which originators contribute to it.
As capacity grows, predictable behaviour near those limits remains essential.
These are possible engineering directions. They are not a promise that any one approach will deliver a specific CPS figure.
A useful benchmark needs to explain the conditions behind its result.
That includes the hardware, configuration, traffic profile, concurrent calls, routing workload, and duration of the test.
It should also examine whether records stay accurate, reports stay usable, and the platform recovers in a reliable way after overload.
A short peak in a simplified test does not establish sustained production capacity. Operators need a supported operating range with clear conditions.
As development progresses, those are the results that matter.
The customer experience that prompted this work showed us how fast a growing operator’s requirements can change.
We are investigating how M4 can support higher attempt rates while preserving the visibility and control operators need.
If your business is approaching or passing 1,000 CPS, we want to understand your requirements. Tell us your total peak incoming CPS, burst duration, concurrent calls, and what becomes difficult during busy periods.
We will be clear about what M4 supports today and discuss how your traffic requirements could shape the work ahead.
Contact us to discuss your high-CPS requirements.
Need more than about 1,000 CPS?
Tell us your peak incoming CPS, burst duration, and concurrent calls. We will be clear about what M4 supports today.
The post When your voice business needs more than 1,000 CPS appeared first on Kolmisoft Blog.
]]>The post How we lost our largest client appeared first on Kolmisoft Blog.
]]>They were a fast-growing wholesale operator on M4. At peak they ran about 30 million calls per day. One of their originators alone burst near 2,000 CPS. M4 is built for up to about 1,000 CPS. Elasticsearch lagged. The GUI stopped responding. Reports and day-to-day admin became hard or impossible at the worst moments. That originator was only one of their clients.
They did not fail. They grew into a traffic shape our product could not sustain. We hit a hard ceiling we had not put clearly enough into the sales conversation.
Wholesale and call-center traffic often looks fine on concurrent calls while attempt rate climbs. Many attempts never answer, so concurrent calls stay “reasonable” while CPS and database write load climb. The platform spends energy on failed attempts that barely move the concurrent-call counter. From the outside it looks like “we still have channel headroom.” Inside the switch, the CPS wall is already there.
The operator’s own customers then see 503 and related failures. Telling those customers “our switch is full” is a bad commercial day. Live ops needs the Calls List and similar reports while the fire is burning. If the GUI is slow or stuck, you cannot prove what failed, fix a route, or calm a client who already has another vendor on speed dial.
Our support team answered tickets. That was never the main failure. The product limit was. So was the way we scoped the deal.
If you need the basics of calls per second vs concurrent calls, or why dialers get 503s while channels still look free, those posts cover it. This one is the vendor side of the same problem.
Peak CPS never came up in sales. We talked capacity mainly in concurrent calls. Nobody locked an expected peak from their largest originator. The ~2,000 CPS bursts showed up in production after traffic ramped. That is on us. We did not accept a 2,000 CPS design and hope. We under-asked, then met the number in live traffic.
After the pain started, they asked for more concurrent-call licence headroom. We spent too long explaining why more CC does not raise the CPS ceiling. A licence bump does not fix a CPS wall. We already published the operator playbook for that mistake; we still burned calendar time repeating it while their GUI was struggling.
Two numbers belong in every go-live conversation:
If number two is blank, both sides are guessing. Ask it before the first production week, not after the GUI dies at peak.
Some people say voice traffic is falling. We still see part of our client base hit new highs on their own accounts and keep expanding, especially in wholesale and call-center direction. Losing our largest client at the 1,000 CPS wall was a scale problem, not proof that the market is finished.
That growth is why the ceiling matters. It is also why “reduce your CPS so the switch stays stable” is a common vendor answer, and a frustrating one if your business is built on attempt-heavy traffic. We have given that kind of advice when the platform was already at the limit. We do not want that to be the end of the product story for operators who need to grow.
When your largest client grows that fast, they write the next product chapter whether you planned it or not. We are grateful for the pressure. It showed the limit clearly.
We can see a path to raise CPS capacity several times above today’s ~1,000 CPS ceiling, and we are working on it. We are not publishing a ship date here. We are naming the direction: M4 and our stack need to serve wholesale operators who already live near or past that wall.
For how operators contain a single hot originator on their side today, see which dialer customer burned your CPS route. That series is the playbook. This post is us applying the same honesty to ourselves.
State the CPS ceiling early. Ask for peak CPS from the largest originators before go-live. Do not treat a concurrent-call upgrade as a CPS cure. Prefer an honest “this traffic shape is above what we sustain today” over a licence change that leaves the GUI dark at peak.
We will also make sure both sides share the same meaning of CPS before anyone signs or goes live. Concurrent calls and calls per second sound related; they are not interchangeable. If the buying conversation stays only in concurrent calls, the CPS question never gets a real answer. Our job is to walk that difference until the client can state their peak attempt rate in CPS and match it to what the platform sustains. That is part of sales and onboarding, not a support ticket after the GUI fails.
We would rather lose a deal than pretend there is no ceiling. We would rather build past that ceiling than tell growing wholesale operators that the only path to stability is to shrink.
If you need more than about 1,000 CPS, or you are close and stability is already the daily fight, and other vendors mainly tell you to cut CPS, contact us. Tell us your concurrent-call licence and the peak CPS from your largest originator. We will be straight about what we can carry today and what we are building toward.
Thanks to that client for the pressure test. We got sharper. We are still here for operators who need to scale wholesale voice beyond 1,000 CPS.
Need more than about 1,000 CPS?
If vendors only tell you to cut CPS for stability, contact us with your concurrent-call licence and peak CPS from your largest originator.
The post How we lost our largest client appeared first on Kolmisoft Blog.
]]>The post Why carriers regret writing their own billing appeared first on Kolmisoft Blog.
]]>That is how many small carriers start. The regret arrives later: not on the first invoice, but when the person who wrote the scripts leaves, when a rate deck changes twice a week, when least-cost routing fails over in a way nobody wrote down, or when the tenth edge case lands in production on a Friday night.
This piece is for founders running a growing operator on homegrown Asterisk and scripts. Building is sometimes right. The question is whether you are building a temporary tool or a second company you never meant to start.
Homegrown billing wins the whiteboard math.
Open-source charging engines sit in the same story. CGRateS is powerful and engine-agnostic, but it is a charging engine you wire yourself, not a finished admin panel with invoices and day-to-day ops. That gap shows once finance, not only engineering, lives inside the system.
Buying looks like cost. Building looks like control. For the first year of a thin operation, that framing is often true.
These are not three war stories. They stack. Most DIY shops hit several before they admit the whiteboard math was wrong.
Release is not the end of the cost. Libraries go out of date. Security holes appear in code that worked last year. PHP, MySQL, and the OS under Asterisk move on whether you touch the rating script or not. Teams that skip that work watch a system that “works” get harder to change until a small fix becomes a rewrite.
Industry guidance often budgets on the order of 15–25% of the original build cost every year just to keep custom software alive. That line rarely sits on the whiteboard next to “we already built it.”
The founder who can read dialplan and SQL becomes the only person who can change a balance rule, fix a hung import, or explain why last month’s invoice is wrong.
That is fine at five customers. It is a business risk at fifty. Miss that engineer for two weeks and nobody else can change a balance rule. Research on turnover technical debt names the same failure: the component that only one person understood becomes expensive the day they leave. A commercial softswitch does not remove the need for a technical founder. It stops the billing engine from being one person’s private codebase.
This is the regret operators underestimate most. The first version rates answered calls by prefix. Then production teaches you the rest:
Routing adds its own pile. One example from how MOR orders providers and fails over: dial 921…, Provider A offers prefix 92 at 0.1, Provider B offers 92 at 0.05 and 921 at 0.2. Longest matching prefix decides which rate applies; LCR order can put A ahead of B on the shorter match. Failover: if the first leg is answered, busy, or no-answer, the call ends. The next provider is used only on congestion.
None of that is exotic once you have lived it. Each one was invisible on day one. On a DIY stack every new edge case is a custom patch. On a product platform it is someone else’s already-shipped rule. When cheapest is not good enough, quality-based routing is another layer; see Quality based routing.
Every week spent debugging a rating bug or a broken import is a week not spent on routes, partners, or the service customers pay for. Build-from-scratch advocates outside telecom say the same thing: the initial build is the small cost; the long tail of maintenance is where engineers stop shipping the product. For a carrier, billing is plumbing. Selling minutes and quality is the product.
Homegrown PHP and AGI stacks sit on the public internet next to SIP. A dependency you pinned two years ago can grow a known hole. Vendor software ships security updates as part of the product. DIY means you are the security team for the system that holds balances and card or payment integrations. Skipping patches is not thrift. It is deferred incident cost.
Provider rate decks arrive in a new spreadsheet layout. A switch upgrade changes CDR columns. A partner adds a timezone offset. Neon’s build-vs-buy write-up lists the same pain: rate cards weekly, tax rules by jurisdiction, switch CDR formats that move. Each change is a support ticket against software only your team understands. Automatic tariff import on a softswitch exists because that work never ends (process overview).
An incorrect discount, increment, or tax line early in the rating chain multiplies across every call. Duplicate or dropped CDRs under-bill or double-bill. You often learn from a partner PDF, not from your own dashboard. If you already fight invoice totals against CDRs, you know reconciliation is half of billing; see Your partner’s invoice and your CDRs disagree. DIY shops rarely build dual-run and exception queues before go-live. They build them after the first expensive month.
The “ship it this weekend” paths that got you live become the paths nobody dares touch. Documentation describes the ideal system; production runs the hacks. Adding one prepaid rule then means negotiating with every workaround already on the bus. Delayed maintenance gets more expensive the longer you wait; the eventual “rewrite” is often years of skipped upkeep paid at once.
Batch rating after midnight is not live balance control. Wholesale volume, concurrent prepaid cut-off, and fraud spikes need real-time debit and limits. Operators who treat charging as a nightly job then fail under load or leak margin. Scale is not only more servers. It is a different design.
You add a reseller layer, a new country, a Teams trunk, or a dialer farm. The scripts that fit last year’s product become the reason you say no to next year’s deal. Software that cannot change with the business stops being an asset. It becomes the constraint. Buying a maintained core does not remove every custom need; it keeps the core changeable while you spend custom effort only on the thin slice that is truly yours.
Buy is not always the answer.
Build (or keep building) when:
Then treat billing as two jobs. Buy or adopt a maintained core for invoices, balances, and everyday ops where you can. Build only the thin slice no vendor will model. Rebuilding prepaid, porting, taxes, LCR, invoicing, and the admin UI from scratch is a different project from extending one honest gap.
If you are still on A2Billing and the question is open-source succession rather than greenfield DIY, start from A2Billing still runs in 2026 instead of this article.
Use this list in demos. Ask the vendor to show each item with your traffic shape, not a slide.
| Check | Why it bites DIY shops |
|---|---|
| Prepaid cut-off under load | Scripts that rate overnight are not live balance control. |
| Number portability / LRN where you sell | Ported numbers wrong-rate if you only match the dialled prefix. |
| Taxes and regulatory fees for your markets | US stacked telecom tax is a specialist product. Do not assume a European softswitch is a tax engine. |
| LCR failover rules | Congestion-only failover vs retry-on-anything changes margin and dispute rates. |
| Rate / tariff import | Weekly decks should not mean a weekend of hand edits. |
| Invoices finance can defend | Line items, currencies, and periods that match how you settle. |
| Class 4 vs Class 5 job split | Wholesale transit and retail PBX billing are different jobs; see Class 4 vs Class 5. |
Kolmisoft’s softswitches put rating, routing, and VoIP billing in one platform. Automatic tariff import is the path for provider rate files that arrive by email in messy spreadsheet shapes. Invoices are a first-class output, not a weekend PHP job.
Say in the demo what is addon territory (for example local number portability) and what is core. A gap you discover after go-live costs more than a gap you plan for.
The fear behind DIY is not only price. It is “we will need a weird rule next year and the vendor will say no.”
That fear is rational. Rigid platforms strand operators who need SIP auth quirks, unusual reseller flows, or a report that matches a local interconnect. The buy that works for a technical founder is a maintained core for LCR, rating, invoices, and routing, plus a vendor who still develops to client request instead of closing the ticket with “not on the roadmap.”
Kolmisoft’s pitch to small and mid-sized operators is that combination: softswitch with billing built in, and custom work when the standard screen is not enough. You do not need to become a software company to keep the one feature that wins a partner. You do need a partner who will build that feature.
If you are choosing between another year of scripts and a supported platform, bring the LCR failover question and one real rate file to the walkthrough. Those two tests separate a billing UI from a system you can run money through.
No. Build the thin rating gap no vendor models. Buy the core that must stay correct every week: balances, invoices, imports, and LCR rules you can explain to a partner.
If you keep the PBX and move money off scripts or aging AGI billers, a Class 5 softswitch with billing behind the PBX is the usual path. If your pain is wholesale transit at volume, look at Class 4 first. Match the product to the job, not to the brand slide.
Vendor write-ups often cite a year or more to production-grade CDR rating and partner reconciliation. Operator stories put multi-year effort on routing and LCR alone. Your timeline depends on markets, taxes, and how many providers send decks. Plan for maintenance forever, not only the first release, and for the edge cases that only appear after real traffic.
Download The Hard Truth About Running a VoIP Business if you want the operator-side framing before a product call. If you already know the scripts are the bottleneck, ask for a walkthrough and bring one rate file plus the LCR failover behaviour you expect.
Still running billing on Asterisk scripts?
Bring one real rate file and the LCR failover behaviour you expect. We will walk through whether buy beats another year of DIY.
The post Why carriers regret writing their own billing appeared first on Kolmisoft Blog.
]]>