Off the path: introducing ticketconnect
Most of what we write here is about getting into a TLS connection to upgrade its cryptography — inline, redirect, or proxy. Every one of those stands somewhere in the path. There is a fourth move that stands nowhere in the path at all, and today we are releasing it as open source: ticketconnect.
The endpoints you can't touch
Post-quantum crypto is arriving on a schedule, and it keeps running into the same wall: the long tail of endpoints you can't touch. Fleets of legacy services on OpenSSL that can't negotiate X25519MLKEM768 and can't be rebuilt, re-imaged, and redeployed on the deadline's timetable. You cannot post-quantum-upgrade what you cannot recompile.
Resume your way in
ticketconnect leans on one property of TLS 1.3: PSK resumption derives its session keys from the ticket, not from a fresh key exchange. So if a legacy client resumes on a ticket that came from a post-quantum handshake, that connection is keyed by quantum-safe material — even though the client itself never negotiated PQC.
It makes that happen without changing the client. A node agent performs the X25519MLKEM768 handshake the client can't, and installs the resulting resumption session directly into the client's own process. The client's next connection simply resumes — on a PQC-rooted PSK it never asked for. Off the data path: no proxy, no socket, no config, no image rebuild. Deploy one DaemonSet and the fleet's connections start resuming post-quantum.
Public API only
Here is the part we are proud of. An eBPF uprobe catches SSL_connect and briefly freezes the thread; the installation then happens over ptrace, using only public OpenSSL APIs (d2i_SSL_SESSION + SSL_set_session) — never a private struct offset.
That single constraint collapses three problems at once:
- No per-build offset database. The functions we call are exported, so they live in the library's
.dynsym— the dynamic symbol table, which survives stripping because dynamic linking needs it. The library ships its own address map; we read it at runtime. - No memory-corruption bug class. We never write a field at a guessed offset. libssl's own validated code does the writes; a bad payload returns
NULL, it does not corrupt anything. The worst case is “no upgrade,” never a broken process. - A tiny version-compat surface — a handful of serialization formats that fail closed, instead of a signature per binary build.
It is fail-closed throughout, and there is no silent downgrade: a miss falls back to ordinary TLS and emits an event. On the wire, that was always the only ground truth worth trusting.
Proof, not a demo
We care about not overclaiming, so the claims are verified rather than asserted. On a real Kubernetes cluster, an unmodified looping client did full handshakes — then, the moment the agent was deployed and with zero changes to the client, its connections became 111 consecutive X25519MLKEM768 resumptions. A packet capture proves the resumed ticket is byte-for-byte the one the agent fetched, and a negative control shows the client cannot resume on its own. So it is genuinely the injected ticket, not a coincidence.
What it is, and isn't
ticketconnect is an honest, experimental v1 — a compliance and crypto-agility bridge to buy time for legacy fleets while proper endpoint migration happens over years, not a replacement for it. Today it is OpenSSL and x86-64 only, single-destination, and resumption-based (the first connection to a cold destination is classical). And it is a privileged, node-wide agent, so it warrants the same scrutiny as any eBPF/ptrace tool. We would rather say that plainly than oversell it.
It's open source
ticketconnect is MIT-licensed and on GitHub, with the design docs, a security model, and container images. You can watch the whole thing happen on one host in a single command:
git clone https://github.com/connectedinformation/ticketconnect
cd ticketconnect && ./demo/run_host_demo.sh
Where our inline work stands in the path, ticketconnect stands beside the process. Both are ways to give a connection post-quantum protection it could not negotiate for itself — you pick the one that fits what you're allowed to touch. If you work on PQC migration, we'd value your eyes on it, including where you think it's wrong: github.com/connectedinformation/ticketconnect.
← All posts