Change:
for the Connections that they are about to make, or to query the API about potential Connections they could make. <del> A Preconnection Object represents a potential Connection.  It has state that describes the properties of a Connection that might exist in the future.  This state comprises Local Endpoint and Remote Endpoint Objects that denote the endpoints of the potential Connection (see endpointspec), the Selection Properties (see selection-props), any preconfigured Connection Properties (connection-props), and the security parameters (see security- parameters): </del> <ins> A Preconnection Object represents a potential Connection.  It is a passive Object (a data structure) that merely maintains the state that describes the properties of a Connection that might exist in the future.  This state comprises Local Endpoint and Remote Endpoint Objects that denote the endpoints of the potential Connection (see endpointspec), the Selection Properties (see selection-props), any preconfigured Connection Properties (connection-props), and the security parameters (see security-parameters): </ins> At least one Local Endpoint MUST be specified if the Preconnection is used to Listen() for incoming Connections, but the list of Local

Discussion:
This is quite minimal... meant as a start. Or is this already enough?
Discussion at the 11 Jun 2021 Interim: The content of any objects passed as part of NewPreconnection(), Initiate(), Listen(), or Rendezvous() is frozen at the moment of Initiate/Listen/Rendezvous. Thus, objects sent to NewPreconnection (Endpoints and Properties) are mutable, and it might be possible (depending on the implementation) to change them in the preconnection by modifying the object directly. Applications should either (1) Instantiate the connection before modifying Endpoint or Property objects for another preconnection. (2) If there are open preconnections that will initiate further connections, create new instances of Properties and Endpoints to modify their attributes in a different preconnection. There are other ways to do this. For example, NewPreconnection() could do a deep copy of the properties and endpoints, and therefore reusing or freeing the argument objects would always be safe. But this was not the sense of the group at the interim. While this issue is uncomfortably close to the "implementation dependent", not having fixed assumptions about this increasing the friction of switching platforms using TAPS, and would probably be the source of nasty bugs.
Reference: (URL)
Comment by NAME in the interim chat: Also in section 2 in the impl draft it says "Preconnection objects should be implemented as bundles of properties that an application can both read and write. Once a Preconnection has been used to create an outbound Connection or a Listener, the implementation should ensure that the copy of the properties held by the Connection or Listener is immutable." which I assume means that the properties the bundle is made of can also be overwritten
to be clear, IMO the draft is quite clear about freezing at Initiate(), but it is not clear about NOT freezing at NewPreconnection().
At Interim, we heard that this might be fixed by emphasising in the preconception text that everything is frozen (aka deep-copied) at Initiate.
I will also point out that this sits somewhat uneasily with Sec 3.1 of taps-impl, where we should fail the preconnection immediately if nothing matches the desired properties. What if I change the desired properties after getting the preconnection? It's not insurmountable, but it's conceptually not very clean.

NAME I think this is only because you assume that "we should fail the preconnection immediately". Nowhere does it say that "the preconnection" should fail - it just says that trying to make meaningless changes should result in an error. This could be the function call that intends to make a change failing, not the object itself becoming unusable. Or am I misreading your concern? To be clear, my view on this is: create a preconnection: If you do it wrong, you get a failure and you don't get one. You can try again. change something on a preconnection: if you do it wrong, the change fails, but the preconnection object doesn't "fail" or disappear, it's just unaffected. That's probably not how you read the text in this section - what should change? Best to make a suggestion directly inside PR , I think.
I would like to have the discussion about what the right thing to do here in the issue, and the wordsmithing in the PR. It's not meaningless changes. It's adding constraints that reduce the pool of protocols to zero. None of this is impossible to implement in code, but what you suggest is the following is quite inelegant: App instantiates satisfiable transport properties App instantiates a preconnection with that TP, success App changes the transport properties -- so you are proposing that each preconn with a reference to the properties would check that they are still satisfiable; if yes, then accept the change; if not, ignore it? Alternatively, the initiate/listen command could fail due to the updated properties Or, the preconnection could fail when the properties become unsatisfiable Or, the transport properties object itself could fail, if it is aware of the protocol choices. Or, objects passed to the preconnection could be immutable. ** Zooming out for a bit, I don't see the preconnection object as adding a ton of value as currently specified. Perhaps it's too late to rethink that. Anyway that discussion is too big for this issue.
Hi, first, just to be clear, by suggesting to move this to the PR I didn't mean to be dismissive about your comment! I just thought it's an easy fix - it seems not, my apologies! So, let's keep discussing here. I still see things very differently.

First, your bullet list begins with "App instantiates satisfiable transport properties". The way I regard these properties is: they are atomary elements of an object. Attributes of a Preconnection object, in OO language terms. Members of a Preconnection record (or struct), in more old fashioned language terms. Not objects in their own right - and a Preconnection doesn't get a reference to them, it "contains" them (i.e., memory allocated for each). So - the Preconnection's the thing that's instantiated, and the properties "belong" to it. Then, the statement from bullet 3: "each preconn with a reference to the properties would check..." - is wrong in my view of the world (I agree that this would otherwise be quite inelegant). So here are my answers to the possible failures, in "my" context: Yes. >Or, the preconnection could fail when the properties become unsatisfiable No: the preconnection per se doesn't fail, the call to create it may, and a call to make a change to one of its properties may. >Or, the transport properties object itself could fail, if it is aware of the protocol choices. A "transport properties object" doesn't exist from my point of view. >Or, objects passed to the preconnection could be immutable. But where do we say that "objects" are passed to a preconnection? So... either I'm totally wrong, or you're thinking of a very obscure language (you said C, which isn't SO obscure?), or the current draft is extremely ambiguous ... but then, about what? If others agree with my view of the world, then there must be something very bad in the text that misled you. We should fix it! Regarding "Zooming out": why, what's the issue? I don't personally think it matters very much if we define that changing things on a Preconnection can already fail or not, if that's what disturbs you - but we need to be able to hand over some input to the racing algorithm before trying to initiate or listen on a Connection. How else would you do it? I can see no way around collecting the application's requirements / requests somewhere, in some sort of data structure, and handing that over.
No worries!

I think that's a fine approach, but it is not what we discussed at the interim. Your proposal would make the properties "immutable" once the preconnection formed. tp := URL() pc1 := URL(tp) tp.SetProperty(foo, true) pc2 := URL(tp) Do pc1 and pc2 have the same properties? At the interim, we said yes! You're arguing no. FWIW, I agree with you, but this is something we should work out. Gorry's PR, IIUC, is trying to capture the interim's consensus. Your perception of what the preconnection is doing is not what the group told me at the interim. See also my "preconnection confusion" email. The preconn takes localEndpoints, remoteEndpoints, transportProperties, and securityProperties. It simply stores all of this stuff, except for checking that the properties are satisfiable. It does not build a candidate tree, does not resolve FQDNs, and definitely does not do any racing. Two things: 1) I object to TAPS taking a bunch of data in the preconnection and just storing it well before it's needed. Just send it on Initiate()! 2) The preconnection could be much more active, building the candidate tree so that it's ready for racing. At the maximum, it could begin name resolution. This is getting long so maybe I should write an email. It may be that everyone is on the same page but me and we just need to clarify, or perhaps there is no true consensus on these points. They are kind of subtle when you're reviewing the drafts, but when I tried to implement them I found these concepts to be quite vaguely written and inconvenient to code in a low-level language.
Maybe we can narrow things down again. This is a core piece of what you wrote: >tp := URL() >pc1 := URL(tp) >tp.SetProperty(foo, true) >pc2 := URL(tp) Aha! I get it now. I think I misunderstood things at the interim, then, and the deep copy should already apply when creating a Preconnection. Would that be better? (I think we should hear more voices!) Towards the end of your answer above, you say: ... and I agree. However, you suggest other possibilities towards the end (your two things).

First, don't they contradict each other? FWIW, I don't think it would be against the current spec if a TAPS implementation started doing things early when it a Preconnection is created... I agree, name resolution could be one of the things to do right away, why wait. But, doing this requires handing over information early. Anyway I don't think we should guarantee that a TAPS system does anything before Initiate() (in the active open case) is called... but making it possible requires handing over data early. Hm. I have a feeling that I'm sowing seeds of confusion. Do I represent the group consensus, or have I been misunderstanding things? I'm not sure anymore. It would be better for others to chime in.
I agree that we should from more voices. One quick point: >the deep copy should already apply when creating a Preconnection. Would that be better? Conceptually, it's much cleaner. Unfortunately, it also means I'm doing a deepcopy twice: once from the object into the preconnection, and then again from the preconnection to the connection. Sounds expensive! Which is why I'm gravitating to one of the two options. Either (1) build the candidate tree in the preconnection, so the connection doesn't need any of that stuff, or (2) don't store it needlessly in the preconn, to avoid a deep copy. All this said, I am disinclined to blow up the whole conceptual model at this stage in the life of the doc, where Apple already has some deployed code.
A quick glance at Apple's NetworkFramework makes me believe they don't have the concept of the Preconnection at all, which is informative to me.

Well, things might be different to the way I thought, so.... I didn't think it was ever wise to do: tp := URL() pc1 := URL(tp) tp.SetProperty(foo, true) pc2 := URL(tp) c1 := pc1.Initiate() c2 := pc2.Initiate() But it did seem OK to do: tp := URL() pc1 := URL(tp) c1 := pc1.Initiate() tp.SetProperty(foo, true) pc2 := URL(tp) c2 := pc2.Initiate() I thought it was OK to add remote information to a preconnection, as a part of some out of band signalling. To do clever traversal stuff. Gorry
If we want to forbid the first and allow the second, then the answer would be to make pc1 do a deep copy of tp when created. Though doing two deep copies seems unnecessarily expensive.
I'm probably confused, but I don't understand why we can't rely on the usual programming language semantics for whether parameters are passed by reference, copied, etc. Preconnections and transport properties are just structs, and whatever value they have when is called is used.
I very much agree with NAME that this is down to programming language semantics. The preconnection is just a "struct", a bunch of data grouped together. Let's not overcomplicate it.
Please can we try amend our text to explain a little more that this is the case? I think this (possibly with my example above) clarifies what the purpose is, and we might be able to close this after we have that. My suggestion is a small explanation on re-use of a previous pre-connection after section 3.1.2.
I'm good with this!Looks good to me... In my slow-moving implementation project, I have finally gotten to setting up the preconnection. After reading through the api-drafts, I still lack a clear picture as to what data items TAPS is tracking after the Preconnection call. My perception of this is too foggy to file a useful issue yet. 1) The preconnection inputs are a list of endpoints, the transport priorities, and the security priorities. AFAICT the one thing the drafts explicitly ask of TAPS with a new preconnection is fail immediately if the required and prohibited transport properties result in no viable protocol choices.

So what is TAPS doing with the other arguments at this stage? It seems like it's only during connection establishment that we resolve the endpoints into addresses, so why isn't this just sent as an argument to the Connection establishment calls? It would appear the application is simply passing a bunch of stuff to TAPS to hold until it's actually needed for a later call. My initial, lazy reading made me think that the preconnection was building the candidate tree by resolving hostnames, etc. That appears to not be the case, but the text is not super clear to me. Of course, since establishing a preconnection does not return an event, it is synchronous and could not do anything with unbounded latency, like a hostname resolution. 2) Relatedly, but at the risk of going too deep into the "implementation-dependent," it would be nice for applications if there could be a standard as to whether these objects are immutable after being passed. In other words, either (a) The application passes an endpoint to create the preconnection; if there are later changes/additions to the endpoint, that updates the preconnection as well. (b) Later changes to the endpoint object do not affect the preconnection, meaning TAPS needs to do a deep copy. (c) The endpoint object is no longer accessible to the application after passing through the preconnection, meaning that it would be very useful to have a cloning method in the object. Is there a consensus on which of the choices is most desirable? I am probably missing something obvious here. I hope these questions make sense to people. Regards, Martin