Here’s how a junior engineer ships DID (Decentralized Identifier) verification: spin up a Node.js service, pull in a universal resolver library, wrap it in a Docker container, deploy one pod per tenant behind an Ingress, and call it multi-tenant. It works in the demo. It works at 500 requests/sec. Then a customer with 40,000 tenants signs the contract, and the on-call rotation discovers what “works” actually meant.
The framework hid three things from that engineer: the cost of process isolation at scale, the cost of a JS event loop doing cryptographic work it wasn’t built for, and the cost of talking to a resolver over HTTP for every single verification. None of that shows up in a load test against 50 tenants. All of it shows up at 40,000.
This lesson isn’t about DIDs as a spec —
did:keyanddid:webresolution and Ed25519 signature verification are the easy part, maybe 200 lines of real logic. This lesson is about where that logic runs, because at hyperscale, placement is the whole engineering problem.
Container-per-tenant identity verification fails along three measurable axes:
TLB shootdowns. Every process (or process-mimicking container) owns its own page tables. When the CFS scheduler migrates a verification worker from core 3 to core 11 — which it will, constantly, under bursty identity-check traffic — every other core sharing that address space has to invalidate and refetch TLB entries. At 40,000 tenants each with their own process boundary, you are not verifying signatures anymore; you are running a TLB invalidation service that occasionally checks a signature.
CFS scheduler thrashing. DID verification requests are bursty and short — a login storm, a batch of API-key rotations, a webhook fan-out. Thousands of short-lived runnable processes competing for CPU means the scheduler spends a non-trivial fraction of wall-clock time context-switching rather than executing. Each context switch costs roughly 1–2μs of pure overhead (register save/restore, cache and TLB pollution) before a single instruction of your Ed25519 verification runs. When your actual crypto work is itself low-single-digit microseconds, a 1–2μs tax per request is not overhead — it’s the majority of your latency budget.
Connection pool exhaustion. The naive path resolves
did:webover HTTPS to an external or internal resolver on every call. At scale this means ephemeral port exhaustion, resolver-side connection pool saturation, and — because DNS and TLS handshakes are themselves latency-bound — a tail latency profile dominated by network round trips instead of computation.None of these are “the language is slow” problems. They’re placement and lifecycle problems. Fixing them means moving the decision as close to the packet as the logic allows, and keeping the actual verification work in a runtime that doesn’t pay process-creation tax per call.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.