LastVet Hybrid Post-Quantum TLS Posture¶
Status: Implemented (August 2026)
Related: POSTURE.md, ../architecture/COX_STATIC_IP_INGRESS.md
Introduction¶
This document describes LastVet's hybrid post-quantum (PQ) Transport Layer Security configuration at the public API edge. It is written for security engineers and compliance auditors evaluating how LastVet protects veteran and provider traffic against long-horizon adversaries.
Public HTTPS for api.last.vet and api-staging.last.vet terminates on Caddy 2.11 on lastvet-edge (192.168.1.89). Cloudflare provides authoritative DNS only (grey-cloud, not proxied). No third-party CDN terminates TLS on API hostnames.
Threat model addressed¶
Harvest now, decrypt later¶
State-level and well-resourced adversaries can passively record encrypted TLS traffic today and attempt decryption when cryptographically relevant quantum computers exist. Classical ECDH key exchange (e.g., X25519 alone) provides no protection against this future threat: recorded handshake material could be broken retroactively.
Hybrid key exchange mitigates this by combining a classical Diffie-Hellman group with a post-quantum KEM so that breaking the session key requires defeating both primitives.
What this change does not address¶
| Gap | Notes |
|---|---|
| TLS certificate signatures | Let's Encrypt certificates remain ECDSA P-256. PQ signature migration (ML-DSA) awaits ACME ecosystem maturity |
| Application-layer auth | JWT validation, consent checks, and RLS are unchanged |
| East-west TLS | Postgres and MinIO TLS posture are separate documents |
| Compromised endpoint | Host or process compromise bypasses transport encryption |
What was implemented¶
| Item | Detail |
|---|---|
| Hybrid key exchange | X25519 + ML-KEM-768 (X25519MLKEM768 in Go TLS / IETF hybrid construction) |
| Hostnames | api.last.vet (production), api-staging.last.vet (staging) |
| TLS version | TLS 1.3 |
| Fallback | Clients without PQ support negotiate classical X25519 |
| Edge software | Caddy v2.11.4 (Go 1.26.3) on lastvet-edge |
| Explicit curve list | x25519mlkem768 x25519 secp256r1 secp384r1 in each site tls block |
Canonical config: ../infra/lastvet-edge/Caddyfile (live copy at /etc/caddy/Caddyfile on lastvet-edge).
Architecture¶
Client (Chrome/Safari/iOS/curl)
│ TLS 1.3 handshake
│ Preferred: X25519MLKEM768 (hybrid)
│ Fallback: X25519 (classical)
▼
Caddy on lastvet-edge :443
│ Let's Encrypt cert (ECDSA P-256)
│ Reverse proxy (plaintext HTTP to origin)
▼
lastvet-api :3001 (prod) / :3000 (staging)
Cryptographic choices¶
Hybrid design rationale¶
Hybrid TLS combines:
- X25519 — mature classical elliptic-curve Diffie-Hellman (128-bit classical security).
- ML-KEM-768 — NIST-standardized post-quantum KEM (FIPS 203, August 2024).
If either primitive is broken independently, the other still contributes key material to the TLS 1.3 key schedule. This follows the hybrid design principles in IETF draft-ietf-tls-hybrid-design.
Go's crypto/tls implements the X25519MLKEM768 group as a single negotiated curve identifier combining both mechanisms.
Why not PQ-only?¶
PQ-only key exchange would break compatibility with clients that do not yet advertise ML-KEM groups. Hybrid preserves classical security for legacy clients while upgrading capable clients immediately.
Client compatibility¶
| Client | PQ hybrid support | Behavior |
|---|---|---|
| Chrome 124+ (April 2024) | Yes | Prefers X25519MLKEM768 when server offers it |
| Safari 17.4+ / iOS 17.4+ (March 2024) | Yes | Same |
| Older browsers / tools | No | Fall back to X25519 (classically secure, not PQ-safe) |
| OpenSSL 3.0.x | No PQ group in -groups |
Cannot test PQ via s_client; use Go 1.23+ or OpenSSL 3.5+ |
| OpenSSL 3.5+ | Yes | openssl s_client -groups X25519MLKEM768 |
LastVet iOS ships to US App Store territories on iOS versions that support hybrid PQ when linked against current Apple TLS stacks.
Operational notes¶
| Operation | Command / practice |
|---|---|
| Validate config | sudo caddy validate --config /etc/caddy/Caddyfile |
| Apply changes | sudo systemctl reload caddy (preserves in-flight connections) |
| Do not use | systemctl restart caddy unless recovery requires it |
| Cert storage | /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/ |
PQ curve configuration does not alter ACME issuance or renewal. Certificates remain Let's Encrypt domain-validated ECDSA P-256.
Testing methodology¶
Verified August 2026 after explicit curve list deployment:
Hybrid PQ negotiation (Go 1.26 TLS client)¶
host=api-staging.last.vet test=pq negotiated=X25519MLKEM768 cipher=TLS_AES_128_GCM_SHA256
host=api.last.vet test=pq negotiated=X25519MLKEM768 cipher=TLS_AES_128_GCM_SHA256
Classical fallback¶
Service health after reload¶
https://api-staging.last.vet/health→ HTTP 200https://api.last.vet/health→ HTTP 200
Certificate validity (unchanged by PQ config)¶
| Hostname | Issuer | Valid |
|---|---|---|
api-staging.last.vet |
Let's Encrypt (YE1) | Aug 12 – Nov 10, 2026 |
api.last.vet |
Let's Encrypt (YE2) | Aug 12 – Nov 10, 2026 |
OpenSSL 3.0.13 on ops hosts cannot request PQ groups natively. Use Go 1.23+, OpenSSL 3.5+, or SSL Labs for external verification.
Compliance and references¶
| Reference | Relevance |
|---|---|
| NIST FIPS 203 | ML-KEM standard |
| NIST IR 8105 | PQ migration guidance |
| IETF draft-ietf-tls-hybrid-design | Hybrid TLS rationale |
| HIPAA §164.312(e)(1) | Transmission security (complements application-layer controls) |
Version history¶
| Version | Date | Notes |
|---|---|---|
| v1 | August 2026 | Explicit x25519mlkem768 curve preference on Caddy 2.11.4 / Go 1.26.3 |
Future work¶
| Item | Status |
|---|---|
| PQ certificate signatures (ML-DSA / Dilithium) via Let's Encrypt | Planned when ACME support matures |
| SSL Labs / external monitor cadence | Recommended quarterly check |