CMP42 // BENCHMARK // 2026-10-02ALPHA RUNNING · BERLIN 52.52°N
// Benchmark · Alpha 2026.10

SIXTEEN MILLION CONTACTS,
SIXTEEN MEGABYTES OF BROWSER.

Excel stops at 1,048,576 rows. HubSpot's standard limit is fifteen million records per object. This week we ran sixteen million on a two-core machine — and two things broke along the way, which is the more useful half of this post.

CMP42 key visual — a network of connected points forming one context graph across several separate stacks.
// Records, relations and legal state in one graph. The workbench is one way to look at it.

Somewhere between forty thousand and two hundred thousand records, a familiar thing happens. Excel still opens the file, but it stops being a place you can think in. The CRM holds the same data quite happily and gives you a screen that shows forty rows of it. Neither is broken. Both have quietly stopped being useful for the thing you actually wanted to do, which was to look at all of it at once and change some of it.

That gap is what the Podma Workbench is for. This week it went past the point where the gap usually becomes an argument about which tool to buy. Every figure below links to the benchmark page, where it is quoted with the machine it ran on.

Where a spreadsheet gives up.

In the browser, the engine holds a CRM-shaped sheet at Excel's exact ceiling: 1,048,575 rows, 12.6 million cells. It loads in 9.0 seconds, sorts by a text column in 4.1 seconds, filters on two conditions in 0.14 seconds, and a single edit stays at 0.1 ms at the 95th percentile no matter how large the sheet is. On a two-core container with 7 GB of memory. The developer laptop is not meaningfully faster, which is the more interesting half of that sentence.

That is the end of what a browser can hold, and it is where most tools stop and hand you a pagination control. Past that point a CMP42 sheet keeps behaving like a sheet — which is the whole of the next section.

16,000,000contacts measured in one sheet · machine A
7–42 msto reach any one of them
16 MBbrowser memory at that size
75 sto sort all of them — the slow end

Sixteen million, and the browser barely notices.

At 16,000,000 contacts — past HubSpot's documented limit of fifteen million records per object — the first rows are there in 10 ms and a jump to the very last record takes 7 ms. Jumping into the middle of a sorted view costs 15 ms. Scrolling one screen costs about what jumping across sixteen million rows costs.

In an actual browser, not the server talking to itself: the sheet opened in 1.3 seconds, pressing End reached record 16,000,000 in 312 ms, and two hundred page-ups followed by a jump to record eight million took 1.6 seconds. The JavaScript memory settled at about 16 MB.

Compare that with the 793 MB a single million rows costs when the browser carries the whole sheet. That is the number we would put on a wall, and not because it is impressive on its own: it means the tool does not get heavier as the business grows. The laptop of the colleague who has been with you longest works as well as the one that arrived yesterday.

What computes over every record is the other half of the story, and it takes real time. A full sort of sixteen million by surname is 75 seconds on the small machine; counting by country across all of them is 27 seconds. That is not a fault, it is sixteen million comparisons on two cores — and we publish it in the same tables as the millisecond figures, because a page that only shows the fast half is a brochure.

And nobody gets mailed by accident.

The second half of this week is less spectacular and matters more. A recipient list in Podma does not come into being by hand. It exists only through the sending step, and that step checks every single record against your account's legend — the reason it may be contacted, the legal basis, the consent date, the last contact.

That check runs on every record, at any size, by the same standard. 2.1 seconds for a million, 38 seconds for sixteen million, on the small machine. A list holds up to 100,000 recipients; ask for more and it is refused, and the refusal is written to the change log as an event. A refusal you can show someone next year is worth more than one that merely happened.

At this size that stops being hypothetical. A single country view holds more than a million eligible recipients — well over the cap — so the step refuses it, as designed, and records the refusal. One honest boundary belongs in the same breath: the check reads your legend, it is not legal advice, and it does not replace suppression lists, opt-outs or a channel-by-country policy. Today it produces a file with a log entry; channelSENDER sends over the approved lists1 as the first module on Podma.

One was the size. One was a bug. Both are gone.

The start page could not open an account this large. It read too much at once and took the server down with it. Found on the Thursday, fixed on the Thursday: it opens an account of sixteen million now, in 8.4 seconds — which is what counting sixteen million records off disk costs on two virtual cores. Not counting them on arrival is the next step. We publish the number because it is the slowest thing we measured, not because it is a bad one.

One compliance check was an actual bug. Checking a filtered view for who may be contacted took 84 seconds. Unlike the sort or the start page, that was not what the size costs: nineteen times slower for four times the data, and three times faster on different hardware. That shape says defect. We wrote here that the paragraph would stay until the bug did not — it is gone, and the same check over 1,600,000 contacts now takes 10.5 seconds. The paragraph stays in this shape so you can see what it said.

Everything here is single-user, on development machines. Not production, not concurrent load. The data is generated — no customer record and no personal data was used or measured. And three things on the way are written on the benchmark page as what CMP42 does, with a footnote until they ship: erasure that reaches the audit trail, retention and legal hold as an enforced refusal rather than a documented field, and tenant separation enforced by the database itself.1

¹ Planned for the next release.

Headless is available today.

An agent read our object model over MCP this week: six object types, ten relation kinds, every field typed and carrying its legal basis. Not a demo — a tool call against a running server, returning the registry as data.

That is not a second system standing next to the first. The Podma Workbench is the engine of CMP42, and it has two front doors: a person opens the workbench, an agent opens the MCP tools, and both work the same records through the same guarded write path. What an agent may do is arrange — views, layout, what is on screen. Writing values, files or the sending step stays with the app. That boundary is in the code, not in a policy document.

Which is worth being plain about. CMP42 is a headless CRM. The records, the relations and the legal state live in the graph; the workbench is one surface onto it and an agent is another. Nothing in that design requires a CRM underneath. If you are starting fresh, or your current system has already stopped fitting, you can run on the graph from day one.

If you are not starting fresh, nothing has to move. Keep your CRM, put the workbench on top of it, and let the graph fill from the side. The two paths are the same product; they differ only in how much you want to carry across, and what a move looks like is a question you can answer later.

We are not going to tell you which one you are. The first Design Partners are finding out now, and we will publish what they run into — including the parts that do not work.

→ Read the full benchmark, with every machine and method.

About the author. Christian Schappeit runs protagx, a Berlin practice building production-quality digital systems for regulated industries. Reach out at hello@protagx.com.

Cite this piece. Schappeit, C. (2026). Sixteen million contacts, sixteen megabytes of browser. protagx. https://cmp42.com/blog/sixteen-million-contacts/

Licence This article is published under CC BY-NC-ND 4.0 — share it, in full and with credit. Quoting needs no permission. What that means exactly.