Tentacle QUIC Support Implementation Plan
Goal: Add native QUIC support to Tentacle using quinn, with non-DNS /ip4|ip6/.../udp/.../quic addresses, peer identity derived from the existing secio public key, and support for building QUIC on top of an already hole-punched UDP socket.
Architecture: Keep the existing TCP/WebSocket/TLS stack unchanged. Add a separate QUIC stack that bypasses secio + yamux and uses quinn::Connection plus QUIC bidirectional streams as the transport/session substrate, while continuing to expose SessionContext.remote_pubkey and PeerId with the current semantics. Replace CA-based TLS validation with strict custom rustls certificate verification that binds a self-signed TLS certificate to the node's long-term secio identity.
Tech Stack: quinn, rustls, rcgen, x509-parser, existing tentacle-secio, existing protocol negotiation and codec pipeline.
Fixed Product Decisions
- QUIC is a new transport path, not an extension of
HandshakeType::Secio and not a new muxer under the current stream path.
- QUIC addresses are only:
/ip4/<addr>/udp/<port>/quic
/ip6/<addr>/udp/<port>/quic
- optional
/p2p/<peer_id> suffix
- DNS-based QUIC addresses are rejected in v1.
PeerId remains PeerId::from_public_key(secio::PublicKey).
- QUIC certificate identity is only a proof carrier for that existing identity.
- QUIC uses only bidirectional streams in v1.
- No QUIC datagrams, uni-streams, 0-RTT, UDP proxy, UDP onion, or UDP UPnP in v1.
- NAT integration is done by injecting a usable UDP socket into Tentacle, then letting Tentacle build and manage the
quinn::Endpoint.
TLS Identity Model
- Keep the current long-term node identity keypair
K_secio from the existing KeyProvider.
- Generate one QUIC TLS keypair
K_tls per service instance, using Ed25519 by default.
- Generate one self-signed leaf certificate and reuse it for the service lifetime.
- Add one private X.509 extension with a fixed Tentacle OID. Encode the extension payload as:
TentacleQuicIdentityV1 {
version: u8 = 1,
secio_pubkey: Bytes,
peer_id: Bytes,
binding_sig: Bytes,
}
- Compute:
peer_id = PeerId::from_public_key(secio_pubkey)
binding_sig = secp256k1_sign(K_secio, sha256("tentacle-quic-binding/v1" || tls_spki_der))
tls_spki_der is the DER-encoded SubjectPublicKeyInfo of the TLS certificate public key.
- Verification rule:
- the verifier extracts
secio_pubkey
- recomputes
peer_id
- verifies the secp256k1 signature over the leaf certificate SPKI
- if the dial target has
/p2p/<peer_id>, it must match exactly
- CA validation is not used at all.
- Hostname validation is not used at all.
- TLS remains strict because the certificate must still pass custom identity verification.
TLS Verification Rules
Client verifier
- Build rustls client config without any root store.
- Use
dangerous().with_custom_certificate_verifier(...).
- Require the local side to present its own certificate too.
- Verification steps:
- Reject empty certificate chains.
- Reject chains containing intermediates.
- Parse the single leaf certificate.
- Check certificate validity period.
- Extract the Tentacle QUIC identity extension.
- Decode the Molecule payload.
- Validate
peer_id == PeerId::from_public_key(secio_pubkey).
- Validate the secp256k1 binding signature against
sha256("tentacle-quic-binding/v1" || tls_spki_der).
- If the target multiaddr contains
/p2p/<expected>, validate expected == peer_id.
- Ignore SAN and hostname contents.
- Use a fixed placeholder server name, such as
tentacle.invalid when dialing.
Server verifier
- Build rustls’ server config without CA-based client authentication.
- Use
with_client_cert_verifier(...).
- Make client authentication mandatory.
- Verification steps are the same as on the client side, except there is no dial-target peer check.
Handshake result propagation
- After QUIC handshake succeeds, call
quinn::Connection::peer_identity().
- Re-parse the peer leaf certificate.
- Re-extract
secio_pubkey.
- Convert it into
secio::PublicKey.
- Store it in
SessionContext.remote_pubkey.
- Keep existing
PeerIdNotMatch, repeated-connection detection, identify, and discovery logic based on remote_pubkey.
Development tasks
multiaddr supports the QUIC protocol
- Verifying the connectivity of QUIC, which is expected to use the ed25519 algorithm in TLS
- Implement self-signed certificate generation and identity binding
- Implement rustls‘ custom verifiers for QUIC
- Use the custom extension to exchange peerid and pubkey
- Add QUIC configuration and builder surface
Tentacle QUIC Support Implementation Plan
Goal: Add native QUIC support to Tentacle using
quinn, with non-DNS/ip4|ip6/.../udp/.../quicaddresses, peer identity derived from the existing secio public key, and support for building QUIC on top of an already hole-punched UDP socket.Architecture: Keep the existing TCP/WebSocket/TLS stack unchanged. Add a separate QUIC stack that bypasses
secio + yamuxand usesquinn::Connectionplus QUIC bidirectional streams as the transport/session substrate, while continuing to exposeSessionContext.remote_pubkeyandPeerIdwith the current semantics. Replace CA-based TLS validation with strict custom rustls certificate verification that binds a self-signed TLS certificate to the node's long-term secio identity.Tech Stack:
quinn,rustls,rcgen,x509-parser, existingtentacle-secio, existing protocol negotiation and codec pipeline.Fixed Product Decisions
HandshakeType::Secioand not a new muxer under the current stream path./ip4/<addr>/udp/<port>/quic/ip6/<addr>/udp/<port>/quic/p2p/<peer_id>suffixPeerIdremainsPeerId::from_public_key(secio::PublicKey).quinn::Endpoint.TLS Identity Model
K_seciofrom the existingKeyProvider.K_tlsper service instance, using Ed25519 by default.peer_id = PeerId::from_public_key(secio_pubkey)binding_sig = secp256k1_sign(K_secio, sha256("tentacle-quic-binding/v1" || tls_spki_der))tls_spki_deris the DER-encoded SubjectPublicKeyInfo of the TLS certificate public key.secio_pubkeypeer_id/p2p/<peer_id>, it must match exactlyTLS Verification Rules
Client verifier
dangerous().with_custom_certificate_verifier(...).peer_id == PeerId::from_public_key(secio_pubkey).sha256("tentacle-quic-binding/v1" || tls_spki_der)./p2p/<expected>, validateexpected == peer_id.tentacle.invalidwhen dialing.Server verifier
with_client_cert_verifier(...).Handshake result propagation
quinn::Connection::peer_identity().secio_pubkey.secio::PublicKey.SessionContext.remote_pubkey.PeerIdNotMatch, repeated-connection detection, identify, and discovery logic based onremote_pubkey.Development tasks
multiaddrsupports the QUIC protocol