Change:
packets.  Alternatively, that document could specify that compatible version negotiation causes 0-RTT data to be rejected by the server. <del> 10. In order to facilitate the deployment of future versions of QUIC, designers of future versions SHOULD attempt to design their new version such that commonly deployed versions are compatible with it. For example, a successor to QUIC version 1 may wish to design its transport parameters in a way that does not preclude compatibility. 11. </del> <ins> 9. </ins> The security of this version negotiation mechanism relies on the authenticity of the Version Information exchanged during the

Discussion:
While discussing with NAME I realized that the draft tries to be pretty clear about what are invariant properties of QUIC and VN, and what is specific to QUICv1. The Sections on Retry and 0-RTT somewhat stick out there, because those are concepts that are specific to QUICv1 and don't exist in the QUIC invariants. To make this clearer, we should have one section that contains all of our Considerations for compatibility with QUICv1 - guidance for writing specifications that define compatibility with QUICv1, such as draft-ietf-quic-v2.
0-RTT is a weird case. 0-RTT packets are sent by the client using the "old" version, but they will be received by the server after the server has completed the negotiation to the new version. There are many plausible solutions, such as: 1) Do not do both 0-RTT and version negotiation. If a session resumed ticket was received after negotiating from version=A to version=B, then the resumed connection shall use version=B. 2) Accept that 0-RTT packets are decrypted with version=A. 3) Maybe let clients recompute the 0-RTT key after the negotiation completes. All of these have pros and cons, but they should be specified.
Interaction with 0-RTT is discussed in . What's missing?
It is probably possible to express the issue in a version independent way. Something like: Client sends a first flight composed on Version A packets Midway through that flight, server receives enough data to decide to upgrade to version B Server keeps receiving Version A packets for some times after the upgrade Upgrade specification shall say what to do with these "after upgrade" packets. Also, you probably want to say how negotiation interacts with session resume. Shall the client remember that this particular session resume ticket was received after negotiating version B? Is it OK to just attempt to resume using version B? What does that do to server farms and partial upgrades? If the server farm is not completely updated, should servers list the version B in their supported version list?
My impression is that Section 9 pretty much says this already, but perhaps I'm misunderstanding what you're looking for. Could you send a PR with what you have in mind?

As far as I can tell, TLS 1.3 session tickets aren't tied to a transport or application protocol, so they can be used across QUIC versions, right? This is discussed in .
I think that the draft should say that a client that receives a session ticket MUST remember the version that was negotiated in that session and attempt a connection with that version only when using the ticket. The server MAY reject resumption if the ticket version doesn't match. Yes, you could just resume with TLS and do a concurrent version upgrade. TLS won't care, but your application state might if it cares about version-specific capabilities. Especially with 0-RTT. I don't see much point in engaging with that. Just require that the version stay the same.
I believe that we may want different behavior based on whether or not the negotiated version is supported. Suppose two connections in which the client initiated a connection with version A. In the first, the server parameters contain: negotiated: version B supported: [ version A, version B] In that case, I think we agree that the client should resume using version B, and may use 0RTT. In the second case, the server parameters contain: negotiated: version B supported: [ version A] For me the second variant indicates that the server would like further connections to be initiated with version A. Could be for many reasons, but a common one would be partial deployment of version B in a server farm. In that specific case, the client may well want to resume with version A and use 0RTT of version A.
This is good input. I think that we might recommend something like that, though I don't think we need to go any further.
Unless I'm misunderstanding something, I think NAME and NAME proposals are mutually exclusive. In the second case from , the first connection will be using version B (because the server decided to perform compatible VN from A to B) and therefore the client will receive a version B resumption ticket (since tickets are sent in 1-RTT packets). So if the client then tries to resume with version A, it runs afoul of MT's . Am I missing something?
I was originally convinced of the merits of Christian's approach here (I even opened an issue on our implementation to consider it), but I've since revised that.

Though - in theory - this is right, the chances that resumption is needed during the time that a cluster is partially upgraded is small enough that I don't think that it's worth the extra complication. If you have a server cluster that is rolling out v2, then it might be that you choose v2. When the client later resumes, presumably the cluster is further along with their rollout, making the odds of hitting a v1-only node low. Even with an extended rollout and a rapid resumption, which should be rare, the cost of that is a Version Negotiation packet and a full handshake: one round trip extra, maybe two. Given the transient nature of rollouts, this seems workable. Rollback is the other case where you might encounter this sort of skew. That is something that generally only happens when things have gone pear-shaped, so I'm not going to worry too much about some extra latency there.
NAME you are assuming that a significant time elapses between initial connection and resumption. That assumption may not be true for all applications. In DoQ for example, resumption is used as an alternative to keep-alive. The interval between connection may well be on the same order of magnitude as the idle timeout -- 10 or 20 seconds.
That seems enough that circumstances could have changed, or at least that failed resumptions won't adversely affect performance.
I am trying to think how these issues play out at the TLS level. Are we going to require that implementations encode the version number in the resumption token? If we don't, then there is hardly any enforcement possible, and the arguments behind use-it-or-lose-it come to mind. But then, can implementations actually tie a TLS resumption token to a QUIC version? I think I could implement that in Picoquic, but given the "black-box" API between TLS and QUIC, I would not assume that all implementations can. Then there is of course the whole issue of tying -- or not -- versions of QUIC to versions of TLS. Or, for that matter, using something else than TLS in a future version of QUIC. And there is a specific case of V1->V2. There is no actual functional benefit to V2.

If there is a performance cost, then the upgrade is going to only be used rarely...
It's trivial in my implementation, black box and all. That is, if you do 0-RTT, you have to remember a bunch of stuff with tickets, so this is just one more piece of data (4 bytes at most). So implementation-wise, I can't see any issue. Keep in mind that this performance cost occurs rarely, only during times when a cluster has uneven support AND when that uneven support covers a connect-resume cycle. Seems like it isn't worth solving generically.
It is trivial to do that in a client implementation. Not so much in a server implementation, especially those that put all the state in the encrypted token. That gets a bizarre tradeoff in which clients can decide to abide by the matching token version requirement although it might in some case impede performance, but servers have a hard time enforcing that requirement.
Why is this not trivial on the server? Since servers already need to encode TPs and h3 settings as a binary blob in the ticket, adding the version to that blob should be trivial right?
Servers do not need to use the initial settings -- this is only for clients sending 0-RTT. The servers only sends data after the negotiation has been done. Servers may need to verify that the client is not sending something excessive in 0-RTT, but they have a very good idea of what initial parameters they use by default, and thus do not need to replicate them in the blob. Might be interesting to ask server developers what they actually do.
The h3 draft states: So the server needs to store something about settings. It could be the full settings, a hash, or just a number, but it needs something if it wants to be able to ever change its settings without rotating session ticket keys at the same time. Speaking as a server developer, our stack serializes the entire settings frame into the ticket. I could imagine a server just rotating their keys when they change settings or transport parameters and not having to store data in the ticket, but it's pretty much required if you want to support multiple keys at once which is required for good performance with proper key rotation practices.
So THAT's why they are so huge!

(Seriously though, that's exactly what we do as well, even though our server is not deployed in quite the same way, it's the easiest thing to do.)
OK, Google and Mozilla. I am almost convinced. Care to ask the wider group?
The nice thing there is that it trains middleboxes to support very large resumption tickets. I can think of quite a few hacks based on that property...
The normative requirement on servers is unenforceable, so I'd suggest adding it anyway - since most servers will enforce this, clients will do the work of not reusing across versions, so even servers that do not enforce will ultimately be safe. I'll write up a PR to the VN draft that mentions this in vague terms, and filed URL to add normative text to the QUICv2 draft.
Allowing Retry to retain the same version makes it a lot harder to define a transform into a compatible version because it means that the entire Retry exchange needs to be accounted for. As noted, allowing the compatible version selection to be deferred is probably the right answer, because it means that the server generating the Retry doesn't need to unwrap the Initial. However, I don't think that a naive design like that naturally retains the properties we designed for Retry. The current text seems to be disavowing any responsibility for dealing with Retry. I don't think that is acceptable. A v2 without Retry that is compatible with v1 could just ignore the Retry, but that would lose the protections we specifically built in v1. What I think needs to happen is that v2 would not be compatible with v1 unless it was able to carry the information the v2 server needs to authenticate the v1 Retry. That is, there is a function that transforms a v1 Initial that includes a token into a v2 equivalent and there is a mechanism in v2 that provides the same retryconnectionid and so URL if v2 doesn't include a similar Retry mechanism natively.
We explicitly didn't define anything concrete for protocol features that aren't invariant, such as retry. But we could add some guidelines for this. Can you suggest text?
How about we agree on a design first. Do you think that this is a reasonable approach?

Absolutely, if two versions have a similar concept of retry and similar safety mechanisms to protect it, then retry can be made to work with compatible VN if the compatibility doc describes how to convert the retry safety mechanisms.
So: Endpoints send Retry (or any stateless exchange that is equivalent) in the version that matches the thing they respond to. Endpoints that choose a compatible version can only do so if the compatible version describes how to translate what happened into something the new version can use. That seems right. What I find interesting about this is that v2 might define the same retry authentication as v1, in which case it says "if retry happened, then do the same, whether it was v1 or v2 originally". That's easy. We might also have a protocol that doesn't bother to authenticate Retry, in which case translation is as simple as "forget that you sent a Retry". Anything is possible.
I like this framing. I'll write it up as a PR unless you'd rather do it.
You're the editor :)