Change:
When offered by the client, the extension appears only in the ClientHelloOuter.  The payload MUST have the following structure: <ins> The ECHConfigContents.key_config.config_id for the chosen ECHConfig. </ins> The cipher suite used to encrypt ClientHelloInner.  This MUST match a value provided in the corresponding "ECHConfigContents.cipher_suites" list. <del> The configuration identifier, equal to "Expand(Extract("", config), "tls ech config id", 8)", unless it is optional for an application; see optional-configs. "config" is the "ECHConfig" structure.  "Extract" and "Expand" are as specified by the cipher suite KDF.  (Passing the literal "" as the salt is interpreted by "Extract" as no salt being provided.) </del> The HPKE encapsulated key, used by servers to decrypt the corresponding "payload" field.

Discussion:
We may want to change how we deal with GREASE (does a randomly selected byte stick out?), but this should address the issue for now. I think we can (should) treat these issues separately, and focus just on the simplification here. cc NAME NAME NAME NAME
My intuition is that one byte is too short; I think 32 bits would be wiser. However, this is a slightly less serious problem than usual because we can rev the ECHConfig version number and the ECH extension if we find something longer is needed.
Yep, that sounds reasonable to me.
This is somewhat of a bike shed, but 8 bytes seems quite large for something that will only have a handful of possible values at any given point in time. Is there a reason to make this longer beyond the performance cost of decryption failing as a result of collisions?
I guess the only reason I can see is that we've settled on q^2 / 2^64 as our threshold for operational risks. In particular, the "acceptance" signal in the URL is 8 bytes (URL). I suppose we could get away with a smaller identifier, since the q is much smaller in this case.
This seems different from acceptance signal collision, which actually causes breakage. In contrast, a key_id collision here just means that the server wastes cycles attempting decryption, right?
A config collision is bad because the server has to figure out which config the client used. It can only do so by trial decryption.
Eh? If there's a collision, the server knows which key to try (the one it collided with!), and it will do so, fail to decrypt, and move on as per usual. (There's no trial decryption here, but maybe I'm missing something.)
Just to put a fine point on this: the spec SHOULD NOT require that implementations support trial decryption. In particular, we ought to ensure that, with high probability, configuration identifiers don't collide. A one-byte, randomly-generated identifier would make collisions inevitable, meaning that implementations will have to support trial decryption.
Yeah, that's reasonable. If we're going to abide by this, then the ID needs to be at least 8 bytes. I'd be fine with that that for now assuming others are too. (NAME suggested it be longer at the top anyway, so many we should just do it.)

NAME we're now at 8 bytes :-)
I don't think one-byte necessary collides. First, note that the number of configs a server needs to have at a time is relatively small. Past the config's DNS TTL, you can retire it. That means you only need O(dnsttl / timebetweenrotation) configs at once. (From , I think the exact value is 1 + ceil(dnsttl / timebetweenrotation). Maybe add a little bit of leeway for people using stale DNS caches, though note we have a recovery flow so you only need to cover most, not all, of stale caches.) This means we don't need to worry about that many values. Additionally, everything here is server-allocated. Whether you're worried about a birthday bound depends on how you allocate IDs. If it's random, you have a birthday bound, yes. If you cycle through a counter, you get the full range. (Compare how QUIC and DTLS 1.3 only send the low-order bits of the epoch.) If it's random, but you check for duplicates at key issuance, you also get the full range. The counters or duplicate checks are trickier to issue, of course, though you can implement the trial decryption as a safety net. As of , the trial decryption and config_id modes are already unified. Specifically, decryption failure falls back to the outer ClientHello.
I agree with everything you said, but want to drill in on this: This seems to be key. I agree that the fallback case is clear and well understood, though one consideration is the case wherein a client attempts GREASE to a server that actually supports ECH. The probability of collision in that scenario is high, and if that scenario happens often, then decryption failure and fallback to the outer CH happens quite regularly. Bumping the length back to eight bytes removes the collision probability for that scenario, I think. I'd like to propose we treat the two problems here separately. That is, let's first see if we can land the key ID change (from a client-computed hash to a server-selected ID). And then let's see if we can safely make that ID smaller in size. I don't think we need to address both here. Would that work?

I think that the only thing that pushes toward a larger identifier space for me is the risk of grease colliding with an active key. I agree with David, that your likely number of keys is ~2 (I might go as high as 4 if the server is in an extended period of offering two types of KEM). With a one-byte identifier, that's only 1/64 greased sessions hitting the expensive decryption code. Flexibility here seems appealing, but I'm not sure that this is a place that needs it. Operationally, this seems very much dependent on centralization, so I'm not buying any story based on loose coordination. I also note that very small spaces reduce the options servers have for targeted tracking through the use of keys (though it is impossible to eliminate tracking, small spaces make the cost higher to operate tracking at any reasonable scale). That is why I was motivated to go as low as 1 byte. If you prefer a much lower probability of having to deal with failed decryption in response to greasing, then 2 bytes gets that down to lower than your likely failed connection rate at 1/16k.
Indeed, this is why I propose we separate these issues. Specifically, let's use this PR to land the server-chosen key ID, and then make it smaller (as needed) in a followup change. (Personally, I'm fine with one, two, or four bytes here, as I think the decryption failure case is unavoidable.) I'll file an issue for the size of the ID in a bit. NAME NAME NAME NAME what say you? :-)
Ditto.
After very useful discussion with many folks, it seems now is the time to remove config_id. There are a couple reasons to do this: Servers need to know the correct hash function to use when computing the ID, and possibly need to compute multiple IDs for a given public key (since a config can have multiple hash-based KDFs), which is somewhat annoying and redundant. The entire ECHConfig is already authenticated, so using a hash to prove that the client made a determination based on the correct config contents is unnecessary. Servers will likely only ever have a small handful of keys in motion at any point in time, so anything more than a couple of bits to identify one of said keys is excessive. ... there are probably more. The original design .

(Oops!) It seems we're now in a better position to bring this back, but with some simplifications.
Hiya, Can you send a pointer to those discussions? (Sorry, I seem to have missed 'em;-) Ta, S.
Sorry -- these were informal conversations off-list.
Ah. That's a recurring problem. And even this exchange is actually off-list. Can we please fix that? I think I've whined about that a few times now, and would hope that continual whining is not a requirement;-) S.
few times now, and would hope that continual whining is not a requirement;-) Fix what? A few of us had a private conversation about some ideas and came to the conclusion that it would be better to replace the hash with a key_id. Then Chris and Martin brought it to a Github issue. This is precisely in line with the WG consensus to use Github issue discussion mode, as documented in 8874 S 5.3.
I'm guessing that was a private discussion amongst what could well be described as the usual suspects or an old-boys club (or are a few of you "many folks?"). Correct me if I'm wrong but that had zero visibility and zero traceability and zero accountability for anyone who isn't one of those old boys. And yet we're zapping a field in a PDU from what was declared as an "implementation draft" a day or so after one major deployment declared they had an interop target. To me, that smells of disrespect for outsiders tbh. You can probably guess what I think about that state of affairs:-) I don't buy that tbh. Chris' mail was presented as a conclusion, from a wg-chair following a private discussion that for sure excluded some implementers. (Me being one, albeit a not very good one:-) And you yourself are an ex WG-chair so I'd guess you ought also care about exclusivity here. What gain is there from that kind of behaviour? I don't see it myself. IMO, there is no need nor benefit from being so exclusive wrt this spec, yet that is what is happening and has been for a few years. I'd encourage those of you who may be "old-boys" to consider whether or not that is really the best way to proceed.

IMO - it is not, you lose way more than you might think you gain, almost all the time. Cheers, S. PS: I don't disagree with the outcome, though have opinions on what -10 might contain. I do very much dislike the awful process, which is my point here.
NAME I would completely agree with you had the proposed PR been merged and the associated issue been closed. That has not happened. Further, it appears that you might be reading a bit more into Chris' issue summary than is really there. He is not acting in his WG chair capacity here because he is an author; I know he knows that and I really hope you can give him the benefit of the doubt here so that we do not have to suffer the "no WG chair hat on" in every message/post. The process, as I see it, is pretty darn near normal. Somebody files an issue and optionally (but preferably) files a PR to address said issue. Discussion ensues amd if substantive it's taken to the list. The PR may or may not be modified based on that discussion. If consensus is reached, the PR is merged and the issue is closed. We are at the discussion point. Can we do that now?
Perhaps it'd be better to not start a mail with "After very useful discussion with many folks, it seems now is the time to remove config_id." in cases where that discussion was purely private? I can understand it being hard to keep track with the various tools, but for me, that still reads as a decision being announced even if that was not the intent. Sorry for being over-sensitive to this but IMO there is a history and ongoing tendency with this spec for a subset of people to act as if they and not the IETF/WG have change control. I fully accept that they are not deliberately aiming to avoid having to garner WG consensus but consider that ongoing instances of carelessness such as the above demonstrate that there is an insider/outsider thing going on here. S.
Happy to have the identifier discussion separately. It is unfortunately not important or clear enough to lead to easy agreement. I can live with any outcome.One little nit then -> LGTM.