Should a fleet force, allow, or exclude Go's X25519MLKEM768 key exchange, and what does each choice commit you to?
answer
- the interesting choice is force, not enable
- sort the fleet by who owns each peer
- secrecy horizon is the deciding axis
- forcing pins every peer's toolchain
- publish a percentage, not a claim
basics
~20 sAllow it everywhere by leaving tls.Config.CurvePreferences nil; force it only where you own both ends; exclude it only as a scoped, expiring exception. Forcing commits every peer to a post-quantum-capable toolchain — a cross-team promise, not a config change.
solid answer
~50 sSplit the fleet by who owns the other end. For internal service-to-service traffic where both binaries are yours, forcing `X25519MLKEM768` turns a silent downgrade into a loud failure — but it pins every one of those services to a post-quantum-capable toolchain, so a lagging team's Go upgrade can now break a handshake. For anything crossing to partners you do not control, allow rather than force: leave `CurvePreferences` nil, take post-quantum where the peer supports it, and accept classical where it does not. Exclude only per destination, with an owner and an expiry. Drive the decision from secrecy horizon: traffic that must stay confidential for a decade justifies effort, a cache-warming call does not. And measure adoption as the share of handshakes negotiating the hybrid group rather than as a binary claim — the honest limit is that authentication is still classical, so this buys confidentiality of recorded traffic and nothing else.
go deeper
Know that the protection arrives by default and that the real question is whether to make it mandatory, which is a decision above your pay grade rather than a config you should quietly change.
Be able to describe the three configurations — nil, hybrid-first, hybrid-only — and what each does to a peer that lacks the group, so you can implement whichever posture is chosen.
Argue the rollout: canary before fleet, per-destination exceptions with expiries, and adoption measured as the share of handshakes negotiating the group rather than as a version number.
Own the framing and the limits. Sort traffic by secrecy horizon and by who owns the far end, state what forcing pins in toolchain and partner commitments, and name in advance who can overrule the call and on what evidence.
## The decision, stated properly The question is not "is post-quantum key exchange good". Go already decided that for you: leave `tls.Config.CurvePreferences` nil and you get `X25519MLKEM768` on TLS 1.3 handshakes as of Go 1.24. The decision you actually own is whether to *force* it, and what forcing costs the organisation. ## Three postures and what each one is really for **Allow (leave it nil).** Opportunistic: post-quantum where the peer supports it, classical where it does not, no compatibility risk, no code. This should be the default posture for nearly everything, and it has a property that is easy to undervalue — your protocol defaults track the toolchain, so the *next* change arrives with a Go upgrade instead of an edit in every repository. The cost is that you cannot claim coverage; you can only measure it. **Force (list only the hybrid group).** Converts a silent classical downgrade into a handshake failure. That is genuinely valuable where the downgrade would be invisible and unacceptable, and it is the only way to make a compliance claim that is actually true rather than aspirational. The commitment is the point: every peer on that path must be post-quantum-capable, forever, including peers added later by teams who never read your decision. Inside a fleet where you own both binaries, that is enforceable. Across an organisational boundary, it is a promise you cannot keep. **Exclude (list classical groups only).** Legitimate only as a scoped exception for a peer that demonstrably breaks — typically one that mishandles the larger `ClientHello`. It belongs on one destination's config, not on the fleet, and it needs an owner, a linked upstream ticket, and a date. Exceptions without expiries are how a fleet drifts back to classical with nobody having decided it should. ## What forcing actually pins Be explicit about this in the write-up, because it is the part that gets discovered late. - **A toolchain floor.** Every service on the path needs Go 1.24 or newer. Upgrade cadence stops being hygiene and becomes a security dependency with a deadline. - **Integrations you do not control.** A partner's stack, a vendor appliance, an inspection proxy in someone else's data centre. You cannot schedule their upgrade. - **A little capacity.** Roughly 1.5 KB more per full handshake and a small amount of CPU. Usually noise next to round-trip time, and resumed sessions pay nothing — but "usually" is not a number. Get one from a benchmark harness on your own hardware and traffic mix before anyone argues about it. ## How to decide, concretely Classify traffic by **secrecy horizon** — how long the plaintext must stay secret. That single axis does most of the work, because store-now-decrypt-later only bites content whose value outlives the arrival of a cryptographically relevant quantum computer. Health records, legal material, key material and long-lived credentials sit at one end; a cache refresh or a health check sits at the other. Effort belongs at the first end. Then cut by ownership. Both ends yours → forcing is cheap and you should probably do it on the high-horizon paths. Crossing an organisational boundary → allow, measure, and negotiate rather than mandate. Then roll it out as a canary rather than a flag day: a slice of the fleet on the new posture, compared against the control on handshake failures per destination, handshake latency percentiles, and counts of handshakes by negotiated group. That third counter is the one to publish, because it converts adoption from an assertion into a percentage — and it is what catches a stale `CurvePreferences` in some service quietly suppressing the group you believe you enabled. ## The honest limits, which you should say out loud Hybrid key exchange protects the confidentiality of traffic recorded today. It does not make your TLS post-quantum: certificates are still signed classically, and Go's TLS stack verifies them that way. That is a defensible order of work — signatures cannot be forged retroactively for sessions that already happened — but it means a claim of "we are post-quantum" is wrong, and someone will eventually quote it back to you. It also does nothing for TLS 1.2 connections, nothing for data at rest, and nothing for a plaintext hop inside your own network. If an adversary can record traffic somewhere you have not encrypted it, the key exchange is not the control that helps. ## Who can overrule you, and on what grounds Security or compliance can override an "allow" posture on a specific path where a regulator or a customer contract demands demonstrable post-quantum protection — and that argument beats the compatibility objection, because store-now-decrypt-later is a real threat with a specific deadline nobody can date. Equally, an SRE can veto forcing when the canary shows handshake failures against peers you do not control; availability of a live integration outranks a protection whose payoff is years out. A good decision document names both of those in advance, states which traffic classes are in scope, and sets the review date — because the correct posture in two years, once partner support is universal and the next Go default has landed, is not the one you are choosing today.
- Where is forcing the hybrid group actually defensible?On paths where you own both binaries and a silent classical downgrade would be unacceptable — typically internal service-to-service traffic carrying long-horizon secrets. There the failure is loud, it lands in your own canary, and you can schedule every peer's upgrade. Across an organisational boundary you cannot, so allow and measure instead.
- How do you report adoption honestly to a security stakeholder?As the share of handshakes that negotiated the hybrid group, per destination, over a window — not as a toolchain version or a config assertion. A version proves the capability exists; only the counter proves it reached the wire, and it is what exposes a stale CurvePreferences suppressing the group somewhere.
- What argument would make you defer adoption entirely?Almost none for allowing it, since the default costs nothing and needs no decision. Deferring *forcing* is easy to justify: unowned peers, no traffic with a long secrecy horizon, or a canary showing real handshake failures. Deferring the default itself would mean paying an audit cost to opt out of protection you already have.
- What does this posture not protect, and why say so?Certificates are still signed classically, TLS 1.2 connections get nothing, and data at rest and unencrypted internal hops are untouched. Say it because the overclaim — 'we are post-quantum' — will be quoted back to you, and because the gaps it hides are where the remaining work actually is.
saying these in an interview costs you the question
- Forces the hybrid group on paths with external partners
- Claims the fleet is post-quantum once handshakes use it
- Treats a toolchain version as proof of adoption
- Ignores that forcing pins every peer's Go version
- Applies one posture to all traffic regardless of secrecy horizon
- Grants compatibility exceptions with no owner or expiry