should be accessible through a unified set of API calls.  An application should be able to implement logic for its basic use of transport networking (establishing the transport, and sending and <del> receiving content) once, and expect that implementation to continue to function as the transports change. </del>  Any Transport Services API must expose the distilled minimal set of features offered by transport protocols I-D.ietf-taps-minset. using a specific protocol, and not when using others.  For example, if an application is using UDP, it may require control over the checksum or fragmentation behavior for UDP; if it used a protocol to <del> frame its content over a byte stream like TCP, it would not need these options.  In such cases, the API should expose the features in such a way that they take effect when a particular protocol is selected, but do not imply that only that protocol may be used if there are equivalent options. </del>  Other specialized features, however, may be strictly required by an application and thus constrain the set of protocols that can be used. For example, if an application requires encryption of its transport <del> content, only protocol stacks that include some transport security </del>  protocol are eligible to be used.  A Transport Services API must allow applications to define such requirements and constrain the system's options.  Since such options are not part of the core, if it is used to Listen() for incoming connections. Connection: A Connection object represents an active transport <del> protocol instance that can send and/or receive Content between a </del>  Local Endpoint and a Remote Endpoint.  It holds state pertaining to the underlying transport protocol instance and any ongoing data transfer.  This represents, for example, an active connection in a Initiate() is the primary action that an application can take to create a Connection to a remote endpoint, and prepare any required <del> local or remote state to be able to send and/or receive content. </del>  For some protocols, this may initiate a server-to-client style handshake; for other protocols, this may just establish local state; and for peer-to-peer protocols, this may begin the process 4.1.4. <del> Content: A Content object is a unit of data that can be </del>  represented as bytes that can be transferred between two endpoints <del> over a transport connection.  The bytes within a unit of Content are assumed to be ordered within the unit itself.  That is, if an </del>  application does not care about the order in which a peer receives two distinct spans of bytes, those spans of bytes are considered <del> independent units of Content.  Content may or may not be usable if incomplete or corrupted.  Boundaries of Content may or may not be understood or transmitted by transport protocols.  That is, what one application considers to be two units of Content sent on a stream-based transport may be treated as a single unit of Content by the application on the other side. Send() is the action to transmit Content over a Connection to a remote endpoint.  The interface to Send may include options specific to how this Content is to be sent.  Status of the Send operation may be delivered back to the application in an event events. </del>  Receive() is an action that indicates that the application is <del> ready to asynchronously accept Content over a Connection from a Remote Endpoint, while the Content itself will be delivered in an event events.  The interface to Receive may include options specific to the Content that is to be delivered to the application. </del>  4.1.5. Connection Ready: A Connection Ready event signals to an application that a given Connection is ready to send and/or <del> receive Content.  If the Connection relies on handshakes to </del>  establish state between peers, then it is assumed that these steps have been taken. Connection Finished: A Connection Finished event signals to an application that a given Connection is no longer usable for <del> sending or receiving Content.  This should deliver an error to the application that describes the nature of the termination. </del>  Connection Received: A Connection Received event signals to an application that a given Listener has passively received a Connection. <del> Content Received: A Content Received event delivers received content to the application, based on a Receive action.  This may include an error if the Receive action cannot be satisfied due to the Connection being closed. </del>  <del> Content Sent: A Content Sent event notifies the application of the status of its Send action.  This may be an error if the Content cannot be sent, or an indication that Content has been processed </del>  by the protocol stack. Path Properties Changed: A Path Properties Changed event notifies Protocol Instance: A Protocol Instance is a single instance of one protocol, including any state it has necessary to establish <del> connectivity or send and receive content. </del>  Protocol Stack: A Protocol Stack is a set of Protocol Instances (including relevant application, security, transport, or Internet protocols) that are used together to establish connectivity or <del> send and receive content.  A single stack may be simple (a single </del>  transport protocol instance over IP), or complex (multiple application protocol streams going through a single security and transport protocol, over IP; or, a multi-path transport protocol

Option for addressing : replace Content in the architecture with Message (where appropriate); use more general concepts like data in the background/design explanation, use Message in the concepts section, and refer to a Message's content when we need to talk about the bytes specifically in this Message.
I am not hugely sold on the definition of /Content: A Content object/. I struggled in NEAT with the best word, and we eventually decided on /data block/. That's not perfect, but it at least keeps away from the notion of content delivery networks, and various other uses of the word "Content". Can we please reconsider this?
Sure, we can reconsider. Data Block is one option; we could also just do Data, but then the singular chunk is more awkward. Some test phrases: "a given Connection is ready to send and/or receive Content" "a given Connection is ready to send and/or receive Data Blocks" "a given Connection is ready to send and/or receive Data" Yeah, I don't like making this seem like a Content Delivery Network, but when this does get applied to protocols like QUIC and HTTP/2 as a transport, then there are instances in which this is the Content portion of the Header/Content divide. Any other votes from the group?
<whispers> message
I mean, message works nicely—other than the fact that it's a bit awkward when describing incomplete message content.
Hard to come up with a good term for this one. In the test phrases above I think Data works quite well, but I am not sure it will in all situations.
I suggested "data block" or "data", but I really would not have questioned "message".
With our Socket Intents work, we started at "object" and finally came to "message". A message can easily be spread across several PUDs for transmission. If re-assembly fails, we end up with partial messages.
One comment on the relationship between Message and Content: if we do Message as the main architecture concept (which I'm fine with to be clear), I can imagine that implementations or APIs can still refer to the "content of the message" as opposed to metadata about the message—for example, when I receive a UDP packet as a Message, the content is the interior payload of the datagram, but my metadata includes the ECN flags received on the packet. Does that sound good?
Foe me trhat would be fine ((... we could people thinking about the content of messages, or content messages, etc - but at least we don't have "content of content" etc ;-) ))
does this for Architecture; we still need to make this change in API (and make sure it carries forward into Implementation as necessary)
Yes, needed for API as well. I already modified Implementation for this.
NAME as you're updating the API document with Message, make sure to make it clear that a single Send() or Receive() does not need to correspond in all cases to a complete Message, and that the Message's content is passed or delivered, along with an indication of whether this Message is now complete. I'm referencing this in the implementation for TCP and protocols with very long frames, so it needs to be reflected cleanly in the API too.
NAME ACK. Not having to make the distinction was one of the nice things about "Content", but we should be explicit about partials in the API anyway.
my current proposal for -00 is to acknowledge it both on send and receive, and to punt on the details as implementation-specific.
The details of what "complete" means for a Message is certainly implementation-determined; as long as there's a flag in the Send() call, and in the Message Received callback, that's fine.
So did changing the name from Content to Message now change the semantic of things? I thought we were using the AFra-Bytestream concept from minset with the Framer as a convenience, but I do not understand how it maps to the partial messages?
I hope not. I think it's fine (and good!) for the send call to have a flag, and for the receive call to have a flag - so an app CAN be informed about a complete message arriving, when the receiving transport system knows that it is indeed complete. However, the transport system doesn't automatically guarantee to the sender that the receiver will be informed about the end of the message that the sender signifies with the flag. In other words: I had an issue with text in the postsockets document that talked about messages, which indicated a guarantee that these messages will be transferred as such. I don't have an issue with flags in send and receive that MAY work (but are not guaranteed to always work).
The flag for a Message being complete is purely local to an implementation: If you use TCP, or some long frames, you can send Message data and not be complete; and you'll only send a FIN, or whatever end-of-frame marker you have when you send data with the complete flag. If the frame needs length-value, it will wait until you mark complete. If you use UDP, if you don't mark your Message as complete, the implementation would pend up data for the Message and wait til its complete before sending the datagram out.If you use TCP, or some long frames, when you receive, the complete flag indicates whether or not this was a FIN or the end of your frame. Since the Message could be gigabytes long, you can't just wait to deliver all at once sometimes. For UDP, Messages will be complete when received by default, but I can request to only receive two bytes at a time or something, and get delivered little chunks until the datagram is completely read.
But how do I send multiple messages over a TCP connection, this is the part I do not understand also from the implementation draft. I do not know what is underneath, so I send a bunch of messages on a connection and if it is TCP is gets sent as a stream of bytes and if it is SCTP it gets sent as a sequence of messages? Or I am only allowed to send one message over a TCP connection?
You can't send multiple Messages on a TCP stream without using a framing protocol on top of TCP; you can, of course, send multiple pieces of Message content, and mark the final one as the Message being complete. There is only one sensible Message per direction in TCP.
As things are now, the application can't know that there is only TCP, and the transport system doesn't know whether a framing protocol is used. So, when a message is complete, the sender must be allowed to say that it's complete, whenever it wants. As you said this is local, and that shouldn't be a problem. When there is a framing protocol, there is no need for the transport system to convey "end of message" via TCP, so there is no problem if this is ignored. I find the logic of "one message = one TCP connection" disturbing. If I send 50 short messages, do I risk getting connections permanently opened and torn down because my "end of message" becomes a FIN? That doesn't sound like a good idea and it doesn't make much sense to me (e.g., it wrecks congestion control).
No, you should not be sending 50 short messages on a single connection. That would cause a lot of havoc on the connection. This really isn't a problem if we're looking at falling back between equivalent protocols. The protocols that will make sense to an application need to have equivalent contracts if they aren't rewriting any code. So, I can use the same code for TCP or TLS over TCP or a QUIC Stream etc, since those all look the same. But if I'm writing discrete messages that are very short on a connection, I need a protocol that supports that—my use case here is IKE sending short messages either over UDP or over Length-Value frames on TCP (RFC8229). HTTP messages over TLS or QUIC or whatever also are equivalent.
I don't see why 50 short messages on a single connection would cause a problem. It really shouldn't! What if I'm writing a game that only needs one connection and sends many small updates over it? So if you implement IKE over the transport system, IKE is understood (can be parsed) from the receiver side data stream, even if you hand it over as individual data blocks - you don't need to tell the receiver-side IKE instance where its message ends. Where's the problem? I don't understand what this has to do with "end of message = FIN" and why anyone would need that. It doesn't make sense to me and sounds like unnecessary trouble.
50 short messages is fine, if the protocol knows what a message is. And you could have a degenerate "framer" over TCP which doesn't add any bytes to indicate length or framing and just puts the Message content down in bytes—essentially, ignore any completion markers. But it should also be possible to communicate over a stream or long message: "I was writing a long gigabyte message, and now I'm done".
Hm. Sure. And if an app wants to make absolutely sure this "URL gigabyte message, and now I'm done" information reaches the other end, it can always use Close.
Yeah, that works for TCP, but say I have a framing protocol on top of TCP—HTTP for example, and I'm doing pipelining of messages. I can still have mega-byte or giga-byte long messages that I send in multiple chunks on this Connection, and the completion bit tells the protocol when the HTTP request is done, and we need to start the headers for the next one.
Sure! No problem in this case either
... but there is text along these lines that doesn't sound right to me in the implementation draft, section 5.1.1. This: The effect of the application sending a Message is determined by the top-level protocol in the established Protocol Stack. That is, if the top-level protocol provides an abstraction of framed messages over a connection, the application will be able to send multiple Messages on that connection, even if the framing protocol is built on a byte-stream protocol like TCP. URL as if the top-level protocol is inside the transport system and that top-level protocol must provide framed messages, or else multiple messages can not be sent over a byte-stream protocol like TCP. However: it's nice if there is a framing protocol like HTTP or whatever on top of your internal stack, but it should also be possible to send multiple messages just over TCP. This shouldn't be a problem as long as my receiver-side application can detect where messages begin and end.
I think that what you're describing is the "degenerate framing protocol" that I referred to earlier—yes, we can let something just write on the stream, but either: You have actual boundaries that can be detected, in which case we call this a framing protocol (may be very very simple) You don't have any on-the-wire detectable boundaries, in which case you can have a degenerate framer that just lays down the content of the Message without boundaries. You can do this, but it's a bit silly. Or you can just treat the stream as one big message if you want.
When you say "can be detected, in which case we call this a framing protocol", can that be outside the transport system or not? My complaint is about the text sounding as if the framing protocol must be inside the transport system. With what I call an "Application-Framed Bytestream", a sender-side application simply hands over messages, and as long as the receiver-side application (all outside the transport system!) is able to detect them, these messages can go over e.g. SCTP, where they can arrive out-of-order, or TCP, where they will arrive in-order, without requiring any special intervention - it's always the same receive call. You may receive your messages in parts but they are in-order "inside" (of course) and you (the receiver-side app) can detect where they end.
NAME By "can be detected", I meant "is represented in the on-the-wire format such that both the local and remote endpoints are explicitly communicating this message boundary", like HTTP or SCTP. If you just lay the bytes out in TCP without any other encoding, you're doing Message boundaries that only make sense to one end (which can be okay, but is a bit degenerate).
NAME perhaps we're finally on the same page: yes I mean a case where only one end (the sender side) of the transport system knowing about the message boundaries in case of TCP, but the application on top knows the boundaries, on both the sender and receiver side. The point of my presentations and text about the "Application-Framed Bytestream" was that this is enough: you can do everything you want with this, you don't actually need anything else. And it makes ideas like informing the receiver app about partial message reception unnecessary. The idea is that the receiver only accepts data blocks until it sees a message boundary that it knows about - then it has a complete message.
lgtm

should be accessible through a unified set of API calls.  An application should be able to implement logic for its basic use of transport networking (establishing the transport, and sending and  <ins> receiving data) once, and expect that implementation to continue to function as the transports change. </ins> Any Transport Services API must expose the distilled minimal set of features offered by transport protocols I-D.ietf-taps-minset. using a specific protocol, and not when using others.  For example, if an application is using UDP, it may require control over the checksum or fragmentation behavior for UDP; if it used a protocol to  <ins> frame its data over a byte stream like TCP, it would not need these options.  In such cases, the API should expose the features in such a way that they take effect when a particular protocol is selected, but do not imply that only that protocol may be used if there are equivalent options. </ins> Other specialized features, however, may be strictly required by an application and thus constrain the set of protocols that can be used. For example, if an application requires encryption of its transport  <ins> data, only protocol stacks that include some transport security </ins> protocol are eligible to be used.  A Transport Services API must allow applications to define such requirements and constrain the system's options.  Since such options are not part of the core, if it is used to Listen() for incoming connections. Connection: A Connection object represents an active transport  <ins> protocol instance that can send and/or receive Messages between a </ins> Local Endpoint and a Remote Endpoint.  It holds state pertaining to the underlying transport protocol instance and any ongoing data transfer.  This represents, for example, an active connection in a Initiate() is the primary action that an application can take to create a Connection to a remote endpoint, and prepare any required  <ins> local or remote state to be able to send and/or receive Messages. </ins> For some protocols, this may initiate a server-to-client style handshake; for other protocols, this may just establish local state; and for peer-to-peer protocols, this may begin the process 4.1.4.  <ins> Message: A Message object is a unit of data that can be </ins> represented as bytes that can be transferred between two endpoints  <ins> over a transport connection.  The bytes within a Message are assumed to be ordered within the unit itself.  That is, if an </ins> application does not care about the order in which a peer receives two distinct spans of bytes, those spans of bytes are considered  <ins> independent Messages.  Messages may or may not be usable if incomplete or corrupted.  Boundaries of a Message may or may not be understood or transmitted by transport protocols.  That is, what one application considers to be two Messages sent on a stream-based transport may be treated as a single Message by the application on the other side. Send() is the action to transmit a Message or partial Message over a Connection to a Remote Endpoint.  The interface to Send may include options specific to how the Message's content is to be sent.  Status of the Send operation may be delivered back to the application in an event events. </ins> Receive() is an action that indicates that the application is  <ins> ready to asynchronously accept a Message over a Connection from a Remote Endpoint, while the Message content itself will be delivered in an event events.  The interface to Receive may include options specific to the Message that is to be delivered to the application. </ins> 4.1.5. Connection Ready: A Connection Ready event signals to an application that a given Connection is ready to send and/or  <ins> receive Messages.  If the Connection relies on handshakes to </ins> establish state between peers, then it is assumed that these steps have been taken. Connection Finished: A Connection Finished event signals to an application that a given Connection is no longer usable for  <ins> sending or receiving Messages.  This should deliver an error to the application that describes the nature of the termination. </ins> Connection Received: A Connection Received event signals to an application that a given Listener has passively received a Connection.  <ins> Message Received: A Message Received event delivers received Message content to the application, based on a Receive action. This may include an error if the Receive action cannot be satisfied due to the Connection being closed. </ins>  <ins> Message Sent: A Message Sent event notifies the application of the status of its Send action.  This may be an error if the Message cannot be sent, or an indication that Message has been processed </ins> by the protocol stack. Path Properties Changed: A Path Properties Changed event notifies Protocol Instance: A Protocol Instance is a single instance of one protocol, including any state it has necessary to establish  <ins> connectivity or send and receive Messages. </ins> Protocol Stack: A Protocol Stack is a set of Protocol Instances (including relevant application, security, transport, or Internet protocols) that are used together to establish connectivity or  <ins> send and receive Messages.  A single stack may be simple (a single </ins> transport protocol instance over IP), or complex (multiple application protocol streams going through a single security and transport protocol, over IP; or, a multi-path transport protocol


optimize its behavior based on the application requirements and network conditions, without requiring changes to the applications. This flexibility enables faster deployment of new features and <del> protocols.  It can support applications by offering racing and </del>  fallback mechanisms, which otherwise need to be separately implemented in each application. <del> It derives specific path and protocol selection properties and </del>  supported transport features from the analysis provided in RFC8095, RFC8923, and RFC8922.  The design encourages implementations underneath the interface to dynamically choose a transport protocol The following notations, which can be combined, are used in this document: <del> An Action creates an Object: </del>  <del> An Action creates an array of Objects: </del>  <del> An Action is performed on an Object: </del>  An Object sends an Event: ways: by initiating the Preconnection (i.e., actively opening, as in a client; initiate), through listening on the Preconnection (i.e., passively opening, as in a server listen), or rendezvousing on the <del> Preconnection (i.e.  peer to peer establishment; rendezvous). </del>  Once a Connection is established, data can be sent and received on it in the form of Messages.  The interface supports the preservation of 3.1. The following usage examples illustrate how an application might use <del> a Transport Services Interface to: </del>  Act as a server, by listening for incoming connections, receiving requests, and sending responses, see server-example. but the list of Remote Endpoints MAY be empty if the Preconnection is used to Listen() for incoming Connections.  At least one Local Endpoint and one Remote Endpoint MUST be specified if a peer-to-peer <del> Rendezvous is to occur based on the Preconnection. </del>  If more than one Local Endpoint is specified on a Preconnection, then all the Local Endpoints on the Preconnection MUST represent the same An Endpoint Object can be configured with the following identifiers: <del> Hostname (string) </del>  <del> Port (a 16-bit integer) or a Service (string) that maps to a port </del>  <del> IP address (IPv4 or IPv6 address) </del>  <del> Interface (string name) </del>  An Endpoint cannot have multiple identifiers of a same type set. That is, an endpoint cannot have two IP addresses specified.  Two 4.3.1. <del> Common parameters such as TLS ciphersuites are known to </del>  implementations.  Clients should use common safe defaults for these values whenever possible.  However, as discussed in RFC8922, many transport security protocols require specific security parameters and

Fix a bunch of nits
Looks good.
I would just go ahead and merge it, but, sigh, it has a conflict... I hope someone more git-savvy than me can solve this.
Looks good

optimize its behavior based on the application requirements and network conditions, without requiring changes to the applications. This flexibility enables faster deployment of new features and  <ins> protocols, and can support applications by offering racing and </ins> fallback mechanisms, which otherwise need to be separately implemented in each application.  <ins> This API derives specific path and protocol selection properties and </ins> supported transport features from the analysis provided in RFC8095, RFC8923, and RFC8922.  The design encourages implementations underneath the interface to dynamically choose a transport protocol The following notations, which can be combined, are used in this document:  <ins> An Action that creates an Object: </ins>  <ins> An Action that creates an array of Objects: </ins>  <ins> An Action that is performed on an Object: </ins> An Object sends an Event: ways: by initiating the Preconnection (i.e., actively opening, as in a client; initiate), through listening on the Preconnection (i.e., passively opening, as in a server listen), or rendezvousing on the  <ins> Preconnection (i.e., peer to peer establishment; rendezvous). </ins> Once a Connection is established, data can be sent and received on it in the form of Messages.  The interface supports the preservation of 3.1. The following usage examples illustrate how an application might use  <ins> the Transport Services Interface to: </ins> Act as a server, by listening for incoming connections, receiving requests, and sending responses, see server-example. but the list of Remote Endpoints MAY be empty if the Preconnection is used to Listen() for incoming Connections.  At least one Local Endpoint and one Remote Endpoint MUST be specified if a peer-to-peer  <ins> Rendezvous() is to occur based on the Preconnection. </ins> If more than one Local Endpoint is specified on a Preconnection, then all the Local Endpoints on the Preconnection MUST represent the same An Endpoint Object can be configured with the following identifiers:  <ins> Hostname (string): </ins>  <ins> Port (a 16-bit integer) or a Service (string) that maps to a port: </ins>  <ins> IP address (IPv4 or IPv6 address): </ins>  <ins> Interface name (string): </ins> An Endpoint cannot have multiple identifiers of a same type set. That is, an endpoint cannot have two IP addresses specified.  Two 4.3.1.  <ins> Common security parameters such as TLS ciphersuites are known to </ins> implementations.  Clients should use common safe defaults for these values whenever possible.  However, as discussed in RFC8922, many transport security protocols require specific security parameters and


even if the Advertisement of Alternative Addresses property (see altaddr below) is set to false. <del> Enumeration values other than "Disabled" are interpreted as a preference for choosing protocols that can make use of multiple paths.  The "Disabled" value implies a requirement not to use multiple paths in parallel but does not prevent choosing a protocol that is capable of using multiple paths, e.g., it does not prevent choosing TCP, but prevents sending the "MP_CAPABLE" option in the TCP handshake. </del>  4.2.15.

I know we have discussed Multipath Transport many times, but when reading is seems strange when you get to Multipath Transport and here there is no option to "Require" this property, only to "Prefer" it. We should at least say why. It is perhaps intuitive that you do not need to distinguish between Avoid and Prohibit, but not why Require would not be useful.
Add a sentence after this to explain why "require" wouldn't make sense:
This reflects what we agreed on, during the last interim.

even if the Advertisement of Alternative Addresses property (see altaddr below) is set to false.  <ins> Note that Multipath Transport has no corresponding Selection Property of type Preference.  Enumeration values other than "Disabled" are interpreted as a preference for choosing protocols that can make use of multiple paths.  The "Disabled" value implies a requirement not to use multiple paths in parallel but does not prevent choosing a protocol that is capable of using multiple paths, e.g., it does not prevent choosing TCP, but prevents sending the "MP_CAPABLE" option in the TCP handshake. </ins> 4.2.15.


satisy the requested Stream Send or Stream Receive Bitrate, based on the observed maximum throughput; <del> Capacity-Seeking: Prefer adpating to paths to determine the </del>  highest available capacity, based on the observed maximum throughput.

This is a first cut, I'll update next.
The Capacity Profile in the API draft (now) lists these possible choices: Default, Scavenger, Low Latency/Interactive, Low Latency/Non-Interactive, Constant-Rate Streaming and Capacity-Seeking. The text in section 4.1.3 only discusses Scavenger, Low Latency/Interactive, and Constant-Rate Streaming.
Aha - I could offer to align the Capacity Profile classes?
See a PR to align Capacity profile with API
Resolved
LGTM, thanks!

satisy the requested Stream Send or Stream Receive Bitrate, based on the observed maximum throughput;  <ins> Capacity-Seeking: Prefer adapting to paths to determine the </ins> highest available capacity, based on the observed maximum throughput.


defines the concepts that are used by both the API and implementation documents.  The Preconnection allows applications to configure Connection Properties, and the Connection represents <del> an object that can be used to send and receive Messages.  A connection is a local operation that serves to simplify the local send/receive functions and to filter the traffic for the endpoint. This might (as in TCP) or might not (as in UDP) represent an end- to-end protocol connection across the network path. </del>  1.3.

Added a couple of sentences to Arch that are hoped to early introduce that connections are things in the API, not aretfacts of the transport protocol instances. Different to, but related to an issue for the description in API.
"A is an abstraction that represents the communication. If the transport services interface selects a protocol such as TCP for the communication, a will correspond to an underlying protocol connection. In the other hand, if a protocol such as UDP is selected, the remains an abstraction in the end points and doesn't correspond to explicit protocol state" ?
NAME : Could you live with the update in this PR?
yes, I think so (assuming I'm reading the diff right)

defines the concepts that are used by both the API and implementation documents.  The Preconnection allows applications to configure Connection Properties, and the Connection represents  <ins> an object that can be used to send and receive Messages. A Connection is an abstraction that represents the communication. If the transport services interface selects a protocol such as TCP for the communication, a Connection will correspond to an underlying protocol connection.  If however a protocol such as UDP is selected, the Connection remains an abstraction at the end points. </ins> 1.3.


For Connections that are Established or Closing: Transport Properties that the application specified on the Preconnection. Selection Properties indicate whether or not the Connection has or <del> offers a certain Selection Property, see selection-props </del>  The actually instantiated protocol stack might not match all Protocol Selection Properties that the application specified on the Preconnection.  For example, a certain Protocol Selection

In response to ... This moves the discussion of protocol instances into bullet 4, and the properties (mainly selection) into bullet 5. It does not intend to change any text, and is intended to avoid what appeared to be overlap.
Resolved issue following MW review
I do not understand the relationship between bullets four and five. They seem to overlap?
NAME : Anna, is this resolved by this PR: Re-group managing properties & protocol instances ?
LGTM, but note that I marked a nit (missing full stop).

For Connections that are Established or Closing: Transport Properties that the application specified on the Preconnection. Selection Properties indicate whether or not the Connection has or  <ins> offers a certain Selection Property, see selection-props. </ins> The actually instantiated protocol stack might not match all Protocol Selection Properties that the application specified on the Preconnection.  For example, a certain Protocol Selection


to a remote application using the Transport Services Interface, and send a request as well as receive a response on each of them.  3.1.3. This is an example of how an application might establish a connection 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>  At least one Local Endpoint MUST be specified if the Preconnection is used to Listen() for incoming Connections, but the list of Local

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

to a remote application using the Transport Services Interface, and send a request as well as receive a response on each of them. <ins> Preconnections are reusable after being used to initiate a Connection.  Hence, for example, after the Connections were closed, the following would be correct: ~~~ //.. carry out adjustments to the Preconnection, if desire Connection := Preconnection.Initiate() ~~~ </ins> 3.1.3. This is an example of how an application might establish a connection for the Connections that they are about to make, or to query the API about potential Connections they could make.  <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


1.1. <del> This API is described in terms of Objects with which an application can interact; Actions the application can perform on these Objects; Events, which an Object can send to an application asynchronously; and Parameters associated with these Actions and Events. </del>  The following notations, which can be combined, are used in this document: interface itself; they are equivalent to Actions on a per-application global context.  We also make use of the following basic types: Boolean: Instances take the value "true" or "false". can have multiple aliases set. In order to scope an alias to a specific transport protocol, an <del> Endpoint can specify a protocol identifier.  These identifiers MUST only be set for aliases. </del>  The following example shows a case where "example.com" has a server running on port 443, with an alternate port of 8443 for QUIC. to those that would be sent by an invocation of Initiate() followed immediately by an invocation of Send(), with the caveat that a send failure that occurs because the Connection could not be established <del> will not result in a SendError separate from the InitiateError </del>  signaling the failure of Connection establishment. 7.2.6. Closed<> occurs when a Connection transitions to Closed state without error. <del> InitiateError<> occurs when a Connection created with Initiate() transitions from Establishing state to Closed state due to an error. </del>  ConnectionError<> occurs when a Connection transitions to Closed state due to an error in all other circumstances. Connection. No events will occur on a Connection after it is Closed; i.e., <del> after a Closed<> event, an InitiateError<> or ConnectionError<> will not occur on that connection.  To ensure this ordering, Closed<> will not occur on a Connection while other events on the Connection are still locally outstanding (i.e., known to the interface and waiting to be dealt with by the application). </del>  10.

Interface, section 4.1.2: Why the limitation to aliases? Specifying a protocol might limit the utility of connection racing, so should perhaps be discouraged, but does it break anything to specify a protocol for a non-alias Endpoint?
Interim: suggest to remove the second sentence.
They are currently just called "Table"
In the interface draft, Section 1.1: Where is the event sent? Perhaps ?
Interim: add "to the application" (but double check, related to framers)
Could alternatively say the same another way around: "Applications can use these events..." (i.e. avoid saying only applications do this).
Section 6 in impl-draft has the parts where the framer objects receive events. There is no terminology section in implementation, so not sure if the terminology section in API should cover it as well.
We already say this in the intro text to this section, but I can tweak the structure and add some additional text to make this more clear.
The appendix text about convenience functions talks about "implementations should" provide various things. This reads strangely to me. I think these are not requirements and the text should be more in the form of it can be useful for implementations to provide various things.
How about: " implementations can provide special actions for adding each preference level i.e, TransportProperties.Set(someproperty, avoid) would be equivalent to TransportProperties.Avoid(someproperty):..."
EstablishmentError. Fixed in an upcoming PR.

1.1.  <ins> This API is described in terms of Objects with which an application can interact; Actions the application can perform on these Objects; Events, which an Object can send to an application to be processed aynchronously; and Parameters associated with these Actions and Events. </ins> The following notations, which can be combined, are used in this document: interface itself; they are equivalent to Actions on a per-application global context. <ins> Events are sent to the application or application-supplied code (e.g. framers, see framing) for processing; the details of event processing are platform- and implementation-specific. </ins> We also make use of the following basic types: Boolean: Instances take the value "true" or "false". can have multiple aliases set. In order to scope an alias to a specific transport protocol, an  <ins> Endpoint can specify a protocol identifier. </ins> The following example shows a case where "example.com" has a server running on port 443, with an alternate port of 8443 for QUIC. to those that would be sent by an invocation of Initiate() followed immediately by an invocation of Send(), with the caveat that a send failure that occurs because the Connection could not be established  <ins> will not result in a SendError separate from the EstablishmentError </ins> signaling the failure of Connection establishment. 7.2.6. Closed<> occurs when a Connection transitions to Closed state without error.  <ins> EstablishmentError<> occurs when a Connection created with Initiate() transitions from Establishing state to Closed state due to an error. </ins> ConnectionError<> occurs when a Connection transitions to Closed state due to an error in all other circumstances. Connection. No events will occur on a Connection after it is Closed; i.e.,  <ins> after a Closed<> event, an EstablishmentError<> or ConnectionError<> will not occur on that connection.  To ensure this ordering, Closed<> will not occur on a Connection while other events on the Connection are still locally outstanding (i.e., known to the interface and waiting to be dealt with by the application). </ins> 10.


Similarly, specifying a multicast group address on the Remote Endpoint will indicate that the resulting connection will be used to <del> send multicast messages. </del>  6.1.2.

The implementation draft defines a template for what mappings should define. This needs to include newer group operations like CloseGroup/AbortGroup
The interface draft, section 4.1.1, talks about multicast endpoints, but doesn't mention whether such endpoints can be used with Rendezvous().
What is your proposal?
I wouldn't have thought this is unambiguous, so I'm not quite sure what text is needed that would clarify this. To me at first blush this seems covered by the Rendezvous description in 5.3, between what Rendezvous does in the 4th paragraph: "If there are multiple Local Endpoints or Remote Endpoints configured, then initiating a Rendezvous() action will systematically probe the reachability of those endpoint candidates following an approach such as that used in Interactive Connectivity Establishment (ICE) [RFC5245]." And what happens if it doesn't work in the final paragraph: "An EstablishmentError occurs either when the Properties and Security Parameters of the Preconnection cannot be fulfilled for rendezvous or cannot be reconciled with the Local and/or Remote Endpoints, when the Local Endpoint or Remote Endpoint cannot be resolved, when no transport-layer connection can be established to the Remote Endpoint, or when the application is prohibited from rendezvous by policy:" Absent an ICE-like mechanism that works with multicast (is there such a thing?) I don't see how someone would expect anything other than this error condition if they did this. Was there a context to this question? It wouldn't have occurred to me there was another possible answer that needed an explanation to avoid confusion on this point, but maybe there's a case I haven't considered?
Thinking about this some more, I think there's an ASM vs. SSM question here. For SSM, the model of receivers call and sender calls makes a lot of sense, and matches the asymmetry of the underlying data flows. For ASM, things are more symmetric, and might be the right abstraction.
I'm finding it hard to think we need to combine "rendezvous" and "multicast".
rendezvous is for peer-to-peer connections, and ASM multicast is peer-to-peer
Unsure about this... ASM can also be used unidirectionally also, it's just not source-specific. I had other things in mind with Rendezvous. Anyway, others may think differently.
ASM is deprecated interdomain (). Within a domain I don't think it needs a rendezvous? Or at least I don't know of anyone trying to use it that way. I take NAME point that it's possible someone could want to build something for a peer to peer multicast use case. If they do so, I'm guessing this would include some kind of ICE-like underlying rendezvous protocol that can support multicast, and at that point a rendezvous operation might reasonably start working. To me this looks like a function of whether there's an ICE-like protocol that can perform a rendezvous for the endpoints, so it's correct to avoid calling out non-support of rendezvous inside the multicast endpoint description so that it doesn't need to be specifically updated if the situation changes, though in practice today it will fail due to being unsupported if someone tries it with today's rendezvous protocol implementations. (At least, I assume so? Haven't tried it...)
I read as setting up a peer-to-peer connection, not as necessarily running a specific rendezvous protocol. For unicast, it needs something like ICE, I agree. For ASM, I was assuming a would just turn into a multicast group that I could send to/receive from with no additional signalling, whereas SSM multicast needs explicit send-to-group and receive-from-group calls. Do we have different models for how this works?
I don't understand the claim that SSM would require explicit send-to-group or receive-from-group calls. To me if you've made a local endpoint with a group address, the connection is non-sending, and if you made a remote endpoint with a group address, the connection is non-receiving. With that said: a search for "multicast stun" did find what seems like a non-IETF STUN usage proposal that does something with multicast, so maybe somebody's using it? I have no idea: URL Anyway, my starting assumption was that if you make a multicast group endpoint and give it a "withStunServer" and use it in a preconnection that you Rendezvous(), you'll get an error unless the stack and the server thinks it knows what to do with that, in which case sending or receiving will just work if there's a remote app that did the opposite. In the (unlikely?) case the stack does support it, like with unicast, I guess I'd assume if you did a sending preconnection (remote group address), you'd want to do the withStunServer on the remote endpoint, and if you did a receiving preconnection (local group address), you'd do the withStunServer on the local endpoint. I guess also I'd assume that if you'd like bidirectional ASM, you'll want to make 2 separate connections, one for sending (with the group address as the remote endpoint) and the other for receiving (with the group address as the local endpoint), and on the receive one you won't provide a remote address. AFAICT all the above is sort of implicit in what's already written in the spec, and I'm not sure it needs spelling out if that sort of usage would work in a stack that cared to implement it. But I do worry I'm missing something, and it could be annoying to find out in 5 years that the API structurally doesn't work with any kind of Rendezvous because it was specified wrong. And on that score I'm worried that I might not be fully understanding what happens in a Rendezvous. And when I tried to follow the pseudocode example in section 3.1.3, I noticed there's a complicated bit that's just comments as an exercise for the reader, which perhaps contains some assumptions I haven't really understood: Anyway, does anyone know what the STUN/TURN servers actually do here? Taking a step back from what I'd expect out of an API that did support it, I'd think defining a new usage for STUN is probably out of scope for this doc, if it's not written up and deployed somewhere already.
For SSM, the model of to create a send-only connection and to create a receive-only connection makes sense and matches the semantic of the multicast group. The sender creates the group, and everyone else listens in. For ASM, the underlying group is peer-to-peer and members can both send and receive. It's symmetric. To me, that doesn't fit the asymmetric model of and , and seems a better conceptual fit for a semantic. A will typically devolve to STUN/ICE for unicast, but I don't see that it has to use STUN, and I don't see that it would use STUN for multicast. The key difference is that is peer-to-peer while / are client-server, and I view ASM as peer-to-peer and SSM as closer to client-server.
Ah, ok, thanks for explaining. I guess the idea here is to have bidirectional ASM represented as a single connection object instead of 2 separate ones? I guess that's a way to consider it, but I think this conflicts with the local/remote endpoint concept, more than it conflicts with Initiate/Listen, with the problem being that you both receive with the group address and send to the group address. In general, for a UDP connection it's possible to just Initiate, followed by both Send and Receive, correct? So the trick here if we need a bidirectional Connection for multicast (whether ASM or SSM) would maybe be to do something like "preconnection.BidirMulticastEndpoint(endpoint).WithASM()" (with default as SSM-only, pulled from the addresses in the remote endpoint and using ASM only if WithASM was called), and then call Initiate, with both Receive and Send available. That would be doable, either now or as a later (or optional) extension if it seems useful enough. But either way, Initiate() seems to me like a better fit a sending or bidirectional UDP than Rendezvous().
Yes. I guess so, although UDP also doesn't fit well with the model - the actual semantics depend on the upper layer protocol implemented over UDP, and plain UDP isn't useful except as a substrate. For QUIC you want Initiate/Listen, but I'd argue that Rendezvous is the right model for unicast RTP since it's naturally peer-to-peer and wants NAT traversal. for bidirectional UDP would work, but I'm not sure I agree that it's right.
Ok, after one more pass at reading Rendezvous text in the draft I'm starting to get it. That's an interesting point about generalized peer to peer connections, I don't think I had grasped that difference between this API and the way sockets are used. Backing up a bit further, I think I'm also finally understanding something I missed in an earlier comment from more than a year ago when somebody tried to make the case that Listen() should be the call the sender side of a multicast connection makes, and Initiate() should be the receiver side. That position makes more sense to me now that I'm finally realizing this API is trying to normalize the concept of clients vs servers vs peers at the app layer, rather than describing socket-like things to do. I think maybe there is a wider issue here that this naming scheme is fundamentally confusing when you're looking at a non-TCP-like app. Maybe "Listen()" should be renamed to "ServerInitiate()", "Initiate()" should be renamed to "ClientInitiate()", and "Rendezvous()" should be renamed to "PeerInitiate()"? Under that naming scheme it would make complete sense for a multicast sender doing a ServerInitiate to get an immediate unidirectional sending Connection object, whereas it makes no sense at all to get that behavior out of a call named "Listen". I guess this probably needs a list discussion. Now that you've got me understanding what the viewpoint was that drove these comments, I think the right thing to do is probably to fix all 3 drafts to make it explicit that the these APIs are designed to support 2 different kinds of connections (Client-Server or Peer-Peer) with 3 different roles, and to start the connection with a call that explicitly declares the role this connection object will be playing, and to name the initiation calls to match that. If that ends up matching with the wg consensus, then I agree the multicast section needs to be refactored to make ServerInitiate() be similar to the action from today's Initiate(), and ClientInitiate() to be the action from today's Listen(), and that in this case a PeerInitiate() would be good to specify also, as well as adding a call to add a GroupEndpoint (with an ASM/SSM selector) that can be used to support bidirectional multicast traffic. To me this sounds like a design improvement to the API, and thus the right thing to do. If the group vetoes that direction and chooses to stick with the current naming scheme that describes things that map onto socket behaviors, then I think it's better to stick with the current approach. Also, the claim that UDP is only useful as a substrate seems at odds with the text in the current draft. The explanation of what a connection is explicitly mentions plain UDP as one possibility: https://ietf-URL "Connection: A Connection object represents one or more active transport protocol instances that can send and/or receive Messages between local and remote systems. It holds state pertaining to the underlying transport protocol instances and any ongoing data transfers. This represents, for example, an active Connection in a connection-oriented protocol such as TCP, or a fully-specified 5-tuple for a connectionless protocol such as UDP." This reinforces my suspicion that this issue is not specifically about multicast, but rather about the confusing names of the connection initiation operations, where multicast is one example that highlights the problem well. I'm glad you raised the issue, I think I'm finally starting to understand what you mean and I think there is a very good point in here that rates to make this API better :) But we probably need to take this to the list, I don't think this should be taken as a minor "fix up the multicast section" point, if I'm understanding it correctly.
There are several things in this discussion that I don't understand (just because I don't know enough about multicast), so take my answer with a grain of salt. This said, I believe I understand that your proposal of ServerInitiate() etc. can make sense and be "cleaner" (more general). I do understand that, as you say, it makes no sense at all to obtain an immediate unidirectional sending Connection object from a call named "Listen". However, for unicast client-server communication, "ServerInitiate()" is an odd term, IMO. I think it's common for people to think of an "active" open and a "passive" open. The key distinction here, at the interface level, is that "active" sends something towards a destination - you give it a destination address, but for "passive", you don't. So then, "ServerInitiate" sounds like it might just be "listen", yet it does something active, i.e. it MIGHT signal something to a destination (though, when used as a unicast server "listen", it won't be given a destination address, so that couldn't happen... but why is it not called "Listen", then?). "Rendezvous" is of course more symmetric, but the term makes it explicit that this is neither a "normal" active open nor a passive open. Terminology like "ServerInitiate" is just strangely ambiguous in a unicast case, IMO. Technically, if I want to rename API primitives for such usage, I can easily solve this by introducing new Actions like ServerInitiate etc., which do nothing but hand over the calls (i.e., ServerInitiate calls Listen, etc.). Wouldn't it be a way out to suggest this as a solution for implementers? If this is broader than multicast, this suggestion can be placed outside the multicast section. E.g.: make an appendix called "Alternative Action and Event Names for Connection Establishment", and refer to it from mulitple places - in the "normal" establishment text, there could be statements like this: "The terminology here (Initiate(), Listen() etc.) is based on a unicast model. There are communication models where these terms are misleading: for example, (...something...). It is therefore recommended to additionally offer the same Actions with a different set of terms for non-unicast communication. Appendix XX introduces these Action names and their mapping." Would this be a viable approach?
I guess in this model I would think ServerInitiate() calls Initiate() if there's a multicast remote endpoint, but calls Listen() if there's a unicast local endpoint and no multicast remote endpoint. I guess I doubt it's worth it if it's done as an appendix instead of making the concepts of Server and Client and Peer more central in the API. Maybe I should look for a smaller scope that's more multicast-specific. I guess another alternative would be to add a few things to the Preconnection just for Rendezvous, like "AddBidirectionalGroup" that takes a remote endpoint with a group address, then Rendezvous would make Send use the group address. I guess another reasonable alternative would be to just say "Rendezvous is not supported for multicast endpoints. Bidirectional communication should be implemented with separate connections for sending and receiving."
Hm, would "Rendezvous is not supported for multicast endpoint" take all this complexity away? Then this would sound like a good way to go, for me - based on your earlier argument that interdomain ASM is deprecated. But ... I'm stepping on very thin ice here. Better for others to chime in.
Well, it wouldn't change the fact that the API does not highlight the different kinds of roles of an app in a connection (like server or client), but rather refers to socket behaviors such as listening, and so I think the API is not as effective as it could be. It also would add a little bit of complexity for NORM implementors if we ever get any, because the NACKs can be sent between peers for data recovery according to the spec IIRC, and for a use case like this they would be obliged to use multiple unidirectional Connection objects instead of a single bidirectional object. There could be other similar use cases where between-peer communication within the domain (most likely on the same LAN) could be helpful. However, that's kind of a corner case that doesn't hurt all that badly, and this change would be much smaller for the current API. I guess I'll add that as a PR and call it a fix for this issue, as there wasn't much traction on the idea of normalizing the app mode behavior. Thanks for the comments.
You say "there wasn't much traction on the idea" but it was just me. Maybe people haven't been following this conversation - it may still be worth bringing the general API change idea to the list, as you first suggested.
This doesn't seem to be converging on something that most people really need - so do you think we need to make a move to do something small in a PR that avoids this becoming a rat-hole.
Yes, I submitted a PR (originally misnamed for solving 872 instead of 852): URL I do still think it's a fine idea to make connections explicitly play a Server, Client, or Peer role instead of inferring them from the socket activities that are named, and that the raising of this issue helped me to understand that approach to thinking about the possibility of an API with such a structure. However, to make such a change just to support this use would be the tail wagging the dog, I think. A separate change could be proposed under a separate issue (along with clarification that a Peer connection may or may not have to use an underlying rendezvous protocol, as opposed to calling it Rendezvous and leaving that explanation implicit). I would support such a suggestion and offer to add support for the Peer usage in multicast if it got traction. But I'm not going to push it myself if the others I polled offline don't see the utility, and I think we can live without the use case as a practical matter. And without such a change, I think offering to "Rendezvous" on a multicast connection (just meaning establishing 2-way peer to peer connectivity, without reference to an underlying rendezvous protocol that could support it across nats the way the ICE-based stuff does for unicast) would add unnecessary confusion if done without a broader and more explicit change to make the role-based (as opposed to activity-based) way of thinking about connections more central in the normal API usage.
LGMT

Similarly, specifying a multicast group address on the Remote Endpoint will indicate that the resulting connection will be used to  <ins> send multicast messages, and that the Preconnection will support Initiate() but not Listen().  The Connection created this way is send-only. A Rendezvous() call on Preconnections containing group addresses results in an EstablishmentError as described in rendezvous. </ins> 6.1.2.


be used to receive multicast messages.  The Remote Endpoint can be used to filter incoming multicast from specific senders.  Such a Preconnection will only support calling Listen(), not Initiate(). <del> The accepted Connections are receive-only. </del>  Similarly, specifying a multicast group address on the Remote Endpoint will indicate that the resulting connection will be used to send multicast messages, and that the Preconnection will support <del> Initiate() but not Listen().  The Connection created this way is </del>  send-only. A Rendezvous() call on Preconnections containing group addresses results in an EstablishmentError as described in rendezvous.  6.1.2. An Endpoint can have an alternative definition when using different Specify a Local Endpoint using a STUN server:  Specify a Local Endpoint using an Any-Source Multicast group to join on a named local interface: Source-Specific Multicast requires setting both a Local and Remote Endpoint:  6.2. A Preconnection Object holds properties reflecting the application's

I realise I'm late reviewing this, but this approach seems pretty cumbersome. Why do we need two connections, once send only and one receive only, rather than a single bidirectional connection? It also seems cumbersome to implement: both objects will be bound to the same port, and so will receive the multicast packets once the group has been joined. The send-only must then filter out those received packets?
In the multicast endpoint section, we should have explicit examples for patterns of ASM and SSM. Listen(multicast address, port, interface) Get a connection from a specific remote endpoint Initiate a connection back to talk to that endpoint, etc
We should mention the IGMP joining in Implementation, etc too
If we haven't done so, should note than one process may receive from the same address with SO_REUSEADDR so that every incoming multicast or broadcast UDP datagram destined to the shared port is delivered to all.
NAME I think the other bits about reuse address belong in implementation, filed
See my comment on – this approach seems cumbersome
For SSM, -> send only, -> receive only, makes sense. But for ASM, I'm increasingly thinking that we also want -> multicast peer that can both send and receive on a single local port: you want sender and receiver to be bound to the same local address/port pair.
I feel uncomfortable here. I have been persuaded we should add ASM Multicast. I suggest try a PR and see if this is simple and helpful?
Interim: waiting on NAME
CSP to propose a PR to see if this needs to be taken in a different direction.

be used to receive multicast messages.  The Remote Endpoint can be used to filter incoming multicast from specific senders.  Such a Preconnection will only support calling Listen(), not Initiate().  <ins> Calling Listen() will cause the Transport Services system to register for receiving multicast, such as issuing an IGMP join RFC3376 or using MLDv2 for IPV6 RFC4604.  Any Connections that are accepted from this Listener are receive-only. </ins> Similarly, specifying a multicast group address on the Remote Endpoint will indicate that the resulting connection will be used to send multicast messages, and that the Preconnection will support  <ins> Initiate() but not Listen().  Any Connections created this way are </ins> send-only. A Rendezvous() call on Preconnections containing group addresses results in an EstablishmentError as described in rendezvous. <ins> See multicast-examples for more examples. </ins> 6.1.2. An Endpoint can have an alternative definition when using different Specify a Local Endpoint using a STUN server: <ins> 6.1.4. </ins> Specify a Local Endpoint using an Any-Source Multicast group to join on a named local interface: Source-Specific Multicast requires setting both a Local and Remote Endpoint: <ins> One common pattern for multicast is to both send and receive multicast.  For such cases, an application can set up both a Listener and a Connection.  The Listener is only used to accept Connections that receive inbound multicast.  The initiated Connection is only used to send multicast. </ins> 6.2. A Preconnection Object holds properties reflecting the application's


DTLS session, a SCTP stream, a QUIC stream, or an HTTP/2 stream). The properties held by a Connection or Listener is independent of other connections that are not part of the same Connection Group. <del> Once Initate has been called, the Selection Properties and Endpoint </del>  information are immutable (i.e, an application is not able to later modify Selection Properties on the original Preconnection object). Listener objects are created with a Preconnection, at which point

Added something directly from RFC8085 that UDP can be connected in a sense.
If this is any help, RFC8085 said: "Many operating systems also allow a UDP socket to be connected, i.e., to bind a UDP socket to a specific pair of addresses and ports. This is similar to the corresponding TCP sockets API functionality. However, for UDP, this is only a local operation that serves to simplify the local send/receive functions and to filter the traffic for the specified addresses and ports."
Added text in

DTLS session, a SCTP stream, a QUIC stream, or an HTTP/2 stream). The properties held by a Connection or Listener is independent of other connections that are not part of the same Connection Group.  <ins> Connection establishment is only a local operation for a Datagram transport (e.g., UDP(-Lite)), which serves to simplify the local send/receive functions and to filter the traffic for the specified addresses and ports RFC8085. Once Initiate has been called, the Selection Properties and Endpoint </ins> information are immutable (i.e, an application is not able to later modify Selection Properties on the original Preconnection object). Listener objects are created with a Preconnection, at which point


4.1.2. Preconnection: A Preconnection object is a representation of a <del> potential Connection.  It has state that describes parameters of a Connection that might exist in the future: the Local Endpoint from </del>  which that Connection will be established, the Remote Endpoint (preestablishment) to which it will connect, and Transport Properties that influence the paths and protocols a Connection <del> will use.  A Preconnection can be fully specified such that it represents a single possible Connection, or it can be partially specified such that it represents a family of possible Connections.  The Local Endpoint (preestablishment) is required if the Preconnection is used to Listen for incoming Connections.  The Local Endpoint is optional if it is used to Initiate Connections. The Remote Endpoint is required in the Preconnection that is used to Initiate Connections.  The Remote Endpoint is optional if it is used to Listen for incoming Connections.  The Local Endpoint and the Remote Endpoint are both required if a peer-to-peer Rendezvous is to occur based on the Preconnection. </del>  Transport Properties: Transport Properties allow the application to express their requirements, prohibitions, and preferences and

Gorry's attempt to improve preconnection ()
The big paragraph that is the first bullet of section 4.1.2 doesn't read well. Pleas consider restructuring it, and simplifying the sentences as much as possible.
I think this reads better

4.1.2. Preconnection: A Preconnection object is a representation of a  <ins> Connection that has not yet been established.  It has state that describes parameters of the Connection: the Local Endpoint from </ins> which that Connection will be established, the Remote Endpoint (preestablishment) to which it will connect, and Transport Properties that influence the paths and protocols a Connection  <ins> will use.  A Preconnection can be either fully specified (representing a single possible Connection), or it can be partially specified (representing a family of possible Connections).  The Local Endpoint (preestablishment) is required for a Preconnection used to Listen for incoming Connections, but optional if it is used to Initiate a Connection.  The Remote Endpoint is required in a Preconnection that used to Initiate a Connection, but is optional if it is used to Listen for incoming Connections.  The Local Endpoint and the Remote Endpoint are both required if a peer-to-peer Rendezvous is to occur based on the Preconnection. </ins> Transport Properties: Transport Properties allow the application to express their requirements, prohibitions, and preferences and


Message Framer, all bytes received on the Connection will be represented as one large Message of indeterminate length.  9.3.2.3. A ReceiveError occurs when data is received by the underlying

Reading 7.3.2 I found the concepts there quite abstract. I think it would really help to be a bit more specific and e.g. take UDP and TCP as examples basically saying some like: as UDP is consider a message based protocol, each received datagram will trigger receive() of a complete message. As TCP is stream-based all TCP segments will be delivered by ReceivedPartial().
My impression here is that we could use some examples for different protocols for sending and receiving (for messages, datagrams, and raw streams). This could be a 7.4 Examples section.
Do we need to do this - can we just close this?
I think it's still valuable to have an example of ReceivedPartial somewhere.
The ReceivedPartial example is good. I think we may need clarification that Received can support object-based rather than just byte-array-based responses.
LGMT

Message Framer, all bytes received on the Connection will be represented as one large Message of indeterminate length. <ins> In the following example, an application only wants to receive up to 1000 bytes at a time from a Connection.  If a 1500-byte message arrives, it would receive the message in two separate ReceivedPartial events. </ins> 9.3.2.3. A ReceiveError occurs when data is received by the underlying


application requirements and network conditions, without requiring changes to the applications.  This flexibility enables faster deployment of new features and protocols.  It can also support <del> applications by offering racing and fallback mechanisms, which otherwise need to be implemented in each application separately. </del>  This document was developed in parallel with the specification of the Transport Services API I-D.ietf-taps-interface and Implementation

Describe what you mean by racing earlier in the document - the use in the Introduction is bare.
As NAME said.

application requirements and network conditions, without requiring changes to the applications.  This flexibility enables faster deployment of new features and protocols.  It can also support  <ins> applications by offering racing mechanisms (attempting multiple IP addresses, protocols, or network paths in parallel), which otherwise need to be implemented in each application separately (see racing). </ins> This document was developed in parallel with the specification of the Transport Services API I-D.ietf-taps-interface and Implementation


may have implementation-specific default handlers.  The application should not assume that ignoring Events (e.g., Errors) is always safe. <del> pre-establishment, establishment, sending, receiving, and termination describe the details of application interaction with Objects through Actions and Events in each phase of a Connection, following the phases (Pre-Establishment, Establishment, Data Transfer, and Termination) described in Section 4.1 of I-D.ietf-taps-arch. </del> 3.1. The following usage examples illustrate how an application might use

At the end of the API summary, there's a paragraph that looks like a "Structure of this document" discussion - should it be in a different place? The list in the paragraph is odd - why is Section 8, for instance, not in the list?

may have implementation-specific default handlers.  The application should not assume that ignoring Events (e.g., Errors) is always safe.  3.1. The following usage examples illustrate how an application might use


Explicit support for multistreaming and multipath transport protocols, and the grouping of related Connections into Connection <del> Groups through cloning of Connections.  This allows applications to take full advantage of new transport protocols supporting these features. </del>  3.

The concept of cloning is used in the overview without introduction. I've suggested an earlier discussion of it in the architecture document, which would help, but even in this document, there should be an introduction or forward reference.

Explicit support for multistreaming and multipath transport protocols, and the grouping of related Connections into Connection  <ins> Groups through "cloning" of Connections (see groups).  This function allows applications to take full advantage of new transport protocols supporting these features. </ins> 3.


Transport Properties are referred to by property names.  For the purposes of this document, these names are alphanumeric strings in <del> which words may be separated by hyphens.  These names serve two purposes: </del>  Allowing different components of a TAPS implementation to pass Transport Properties, e.g., between a language frontend and a

Note that this PR does not say only the following characters are allowed... it just lists some that are allowed.
At the discussion of Transport Property Names at 4.1, I think you need to be more specific than "alphanumeric". Explicitly list what characters are allowed.
Since we've overspecified already: [A-Za-z0-9_]
Or go back to underspecifying: say they're human-readable textual labels that have namespaces, and don't specify the syntax of exactly how that namespacing works.

Transport Properties are referred to by property names.  For the purposes of this document, these names are alphanumeric strings in  <ins> which words may be separated by hyphens.  Specifically, the following characters are allowed: lowercase letters "a-z", uppercase letters "A-Z", digits "0-9", the hyphen "-", and the underscore "_".  These names serve two purposes: </ins> Allowing different components of a TAPS implementation to pass Transport Properties, e.g., between a language frontend and a


The "Connection Priority" Connection Property operates on Connections in a Connection Group using the same approach as in msg-priority: when allocating available network capacity among Connections in a <del> Connection Group, sends on Connections with lower Priority values will be prioritized over sends on Connections with higher Priority values.  Capacity will be shared among these Connections according to the Connection Group Transmission Scheduler property (conn- scheduler).  See priority-in-taps for more. </del>  8. 100 <del> This Property is a non-negative integer representing the relative inverse priority (i.e., a lower value reflects a higher priority) of </del>  this Connection relative to other Connections in the same Connection <del> Group.  It has no effect on Connections not part of a Connection Group.  As noted in groups, this property is not entangled when Connections are cloned, i.e., changing the Priority on one Connection in a Connection Group does not change it on the other Connections in the same Connection Group.  No guarantees of a specific behavior regarding Connection Priority are given; a Transport Services system may ignore this property.  See priority-in-taps for more details. </del>  8.1.3.

At 8.1.2 you say lower values have higher priorities for connPrio. But later, at msgPrio, higher values have higher priority, and in the examples where you speak of the interaction of connPrio and msgPrio, you imply that higher means higher for both properties. Please check for consistency and clarify.
Lower values having higher priorities for connPrio is a remnant that we must have missed when we flipped this, e.g. PR which I don't know... too many iterations have mangled the text.
Aha, now I see what happened: following some more PRs, also , in PR , NAME re-introduced some text saying that sends on Connections with lower-priority values will be prioritized over sends on Connections with a higher-priority value. This reverses the logic for some part of the text - I suspect that NAME (very understandably!) forgot what happened and this created this mix. We have been editing these documents for way too long!
LGTM.

The "Connection Priority" Connection Property operates on Connections in a Connection Group using the same approach as in msg-priority: when allocating available network capacity among Connections in a  <ins> Connection Group, sends on Connections with higher Priority values will be prioritized over sends on Connections that have lower Priority values.  Capacity will be shared among these Connections according to the Connection Group Transmission Scheduler property (conn-scheduler).  See priority-in-taps for more. </ins> 8. 100  <ins> This Property is a non-negative integer representing the priority of </ins> this Connection relative to other Connections in the same Connection  <ins> Group.  A higher value reflects a higher priority.  It has no effect on Connections not part of a Connection Group.  As noted in groups, this property is not entangled when Connections are cloned, i.e., changing the Priority on one Connection in a Connection Group does not change it on the other Connections in the same Connection Group. No guarantees of a specific behavior regarding Connection Priority are given; a Transport Services system may ignore this property.  See priority-in-taps for more details. </ins> 8.1.3.