A service mesh in one HTTP header - and the measured case against mTLS
How Rezee secures service-to-service calls with a shared-secret header instead of mutual TLS - the threat model each covers, a measured 1.2ms mTLS handshake that keep-alive erases, and why the real decision is operational, not performance.
Jul 15, 2026 · 7 min read · Kash Gohil
Rezee is one workspace for the whole product development lifecycle - code, planning, CI/CD, chat, and docs, served by a handful of services. denji (git) and kobeni (CI) both call makima (the API) for the things only makima knows: is this push authorized, claim this pipeline run, whose SSH key is this. Those internal endpoints must be closed to the outside world and open to sibling services. The industry's default answer is mutual TLS. Rezee's answer is one HTTP header. This post is the measured case for that choice - including where it stops being the right one.
The entire mechanism
Every internal endpoint sits behind one guard, and it's the same three lines in every module (services/makima/src/modules/internal/index.ts, .../pipelines/index.ts):
.onBeforeHandle(({ request }) => {
if (request.headers.get("x-internal-secret") !== config.internalSecret) {
return status(403, "forbidden");
}
})denji and kobeni attach X-Internal-Secret to every internal request; makima rejects anything without the matching value with a 403 before the handler runs. The /internal/* routes are never mapped through the public reverse proxy either, so this header is defense in depth on top of network isolation, not the only thing standing between the internet and /internal/authorize. That's the whole "service mesh": a shared secret, checked at the door of every internal route.
The obvious question is why this instead of mutual TLS, where each service carries a client certificate and proves its identity cryptographically on every connection. The honest way to answer is to measure what mTLS actually costs and to be precise about what the header actually fails to provide.
What does mTLS cost?
We benchmarked both (scripts/bench/service-mesh): a plaintext server with the header check versus an HTTPS server requiring a CA-signed client certificate, under two regimes - a fresh connection per request, and a reused keep-alive connection:
On a fresh connection, mTLS costs 1.2ms against the header's 0.1ms - roughly 11x - and that gap is almost entirely the TLS handshake: the asymmetric crypto and the certificate-chain verification that happen once per connection. If every internal call opened a new socket, mTLS would add a real, per-call tax.
But connection reuse erases it. With keep-alive, both land at ~0.05ms, because the handshake is paid once and then amortized across every request on that socket - and services calling each other in a hot loop (kobeni claiming runs, denji authorizing pushes) keep connections warm exactly this way. The measured conclusion is the important one: at steady state, mTLS is not meaningfully slower than a header. Anyone arguing for the shared secret on performance grounds is, by our own numbers, mostly wrong. So the decision has to be made on other grounds, and that's the honest heart of it.
The real trade is operational, and the threat model is the price
Since performance is a wash with connection reuse, the choice is between what each model costs to run and what each model defends. Here's the honest ledger:
| shared-secret header | mutual TLS | |
|---|---|---|
| Stops external callers | yes (must know the secret) | yes (must present a CA-signed cert) |
| Per-service identity | no - denji and kobeni are indistinguishable | yes - each cert names one service |
| Encrypts the channel | no - relies on network isolation | yes |
| Survives a network sniffer | no - the secret is plaintext on the wire | yes |
| Operational cost | one env var, shared | CA, per-service certs, issuance, rotation, expiry monitoring |
| Rotation | change one value everywhere | reissue and redeploy certs |
The shared secret buys operational simplicity - one REZE_INTERNAL_SECRET, no certificate authority, no rotation ceremony, no expiry pager alert at 3am - and it pays for that with a weaker threat model: it assumes the internal network is trusted, because a sniffer on that network sees the plaintext secret, and it can't tell denji from kobeni because they share it. mTLS inverts the trade: real cryptographic service identity and channel encryption, at the cost of running a CA and its whole lifecycle.
For Rezee today - services on one private network inside one deployment, where the network boundary is already the primary control - the shared secret is the right point on that curve. The header is defense in depth behind network isolation, not a substitute for it, and adding a certificate authority to distinguish services that fully trust each other on an isolated network would be operational weight bought for a threat we don't currently have.
When the header stops being enough
The threat model is a choice, and it expires. The shared secret is the wrong answer the moment any of these becomes true, and naming them is part of choosing it honestly:
- The internal network stops being trusted. Multi-tenant runner infrastructure, a shared VPC with other systems, or any path where an attacker could sniff internal traffic turns "plaintext secret on the wire" from a non-issue into the issue. That's when channel encryption (mTLS, or at least TLS) stops being optional.
- You need per-service identity or audit. The day "which service made this internal call" has to be answerable - for audit, for least-privilege between services, for revoking one service's access without rotating everyone's - the shared secret can't answer it, because it's shared. mTLS's per-service certs are exactly that identity.
- Compliance requires encryption in transit everywhere. Some regimes mandate it for internal traffic too, full stop, and then the measurement above stops mattering.
Our own scaling roadmap has the first trigger in it: horizontally scaled, per-workspace-isolated runners are exactly the "internal network stops being fully trusted" case, and that's the milestone where this decision gets revisited. We wrote the shared secret knowing its expiry date, which is the difference between a simplification and a mistake.
One honest note on the shipped code: the comparison above is a plain !==, which is not constant-time - a timing side channel on the secret. On an internal-only network the attack surface is negligible (an attacker who can time this precisely can already sniff the plaintext), but a crypto.timingSafeEqual is the correct hardening and what our benchmark used; it's a one-line change worth making regardless.
FAQ
Isn't a shared secret just a worse password?
It's a bearer token for services, and its weaknesses are a bearer token's: anyone who holds it is trusted, and it's only as safe as the channel it travels on. That's precisely why it's paired with network isolation rather than standing alone - the secret assumes the wire is private, and the deployment makes the wire private. The failure mode isn't "someone guesses the secret" (it's a long random value); it's "someone is already on the internal network," which is a compromise the secret was never meant to survive and which mTLS would.
Why not just use TLS without the mutual part?
Plain TLS (server cert only) would encrypt the channel and stop sniffing, but it wouldn't authenticate the caller - anyone who can reach makima over TLS and knows the secret still gets in, and anyone without the secret still doesn't. So plain TLS plus the header is a reasonable next step that adds encryption without the full CA-and-client-cert lifecycle, and it's the likely intermediate point when the network-trust assumption first weakens, before full mTLS with per-service identity.
How is this different from the reverse proxy already not exposing /internal?
They're two layers doing two jobs. Caddy not routing /internal/* means external traffic can't reach those endpoints through the front door at all - a network control. The header means that even something that does reach makima's internal port (another service, a misconfiguration, a future internal caller) still has to present the secret - an application control. Removing either one weakens the system; keeping both is the defense-in-depth the shared-secret model relies on to make "plaintext on the wire" an acceptable assumption.