RFC 5628
Registration Event Package Extension for Session Initiation Protocol (SIP) Globally Routable User Agent URIs (GRUUs)
RFC 5628: Registration Event Package Extension for SIP Globally Routable User Agent URIs (GRUUs) π
Author: P. Kyzivat of Cisco Systems, Inc. π Category: Standards Track π¦ Date: October 2009 β¨ Stream: Network Working Group πΈ
Quick title-block tea: this one doesn't list anything it obsoletes or updates, so there's no older RFC to chase. π
Abstract β¨
Okay so [RFC 3680] lowkey gave us the SIP event package for registration state πΈ It lets a watcher peek at what a SIP registrar has stored, including its registered contact.
BUT the registered contact is frequently unreachable π so it's not always useful for watchers. The Globally Routable User Agent URI (GRUU), defined in RFC 5627, is a URI that can actually reach a particular contact β¨ However, that URI isn't in the document format from RFC 3680. This spec adds an extension to the registration event package so the GRUUs assigned by the registrar can be included. Main character energy π
Status of This Memo π
This document specifies an Internet standards track protocol for the Internet community, and asks for discussion and suggestions for improvements. For the standardization state and status of this protocol, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited. π¦
Copyright Notice π
Copyright (c) 2009 IETF Trust and the persons identified as the document authors. All rights reserved. π
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they stand on the date of publication. Please read these carefully, because they spell out your rights and restrictions for this document. Any Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as the BSD License describes. π§
This document may contain material from IETF Documents or IETF Contributions that were published or made publicly available before November 10, 2008. The people controlling the copyright in some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. Unless you get an adequate license from those copyright holders, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process. The exceptions are formatting it for publication as an RFC and translating it into languages other than English. π
Table of Contents π
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4
3. Description . . . . . . . . . . . . . . . . . . . . . . . . . 4
4. Notifier Processing of SUBSCRIBE Requests . . . . . . . . . . 4
5. Notifier Generation of NOTIFY Requests . . . . . . . . . . . . 4
6. Subscriber Processing of NOTIFY Requests . . . . . . . . . . . 5
6.1. Managing Temporary GRUU Lifetime . . . . . . . . . . . . . 5
7. Sample reginfo Document . . . . . . . . . . . . . . . . . . . 7
8. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
8.1. Example: Welcome Notice . . . . . . . . . . . . . . . . . 8
8.2. Example: Implicit Registration . . . . . . . . . . . . . . 8
9. XML Schema Definition . . . . . . . . . . . . . . . . . . . . 11
10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 12
10.1. URN Sub-Namespace Registration . . . . . . . . . . . . . . 12
10.2. XML Schema Registration . . . . . . . . . . . . . . . . . 13
11. Security Considerations . . . . . . . . . . . . . . . . . . . 13
12. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 14
13. References . . . . . . . . . . . . . . . . . . . . . . . . . . 14
13.1. Normative References . . . . . . . . . . . . . . . . . . . 14
13.2. Informative References . . . . . . . . . . . . . . . . . . 14
1. Introduction π¦
[RFC 3680] (cited as [2]) gave us a Session Initiation Protocol (SIP) [5] event package for registration state. This package lets a watcher learn about the information a SIP registrar stores, including the registered contacts. β¨
Here's the catch: a registered contact is frequently unreachable from hosts outside the User Agent (UA)'s domain. It's usually a private address. And if it's a public address, firewalls might block access to it. π
The Globally Routable User Agent URI (GRUU), defined in RFC 5627 [3], is a URI that reaches a particular UA instance, and any host on the Internet can reach it. GRUUs assigned by the registrar are extra registration state, but they're not included in the notifications from RFC 3680. For a lot of registration event package use cases, you need the GRUU, not the registered contact. π
Let's look at the Welcome Notices example in [2]. It only works if the contact address in the registration event notification is reachable by whoever sends the welcome notice. When the registering device uses the GRUU extension, the registered contact address probably won't be globally addressable, so a GRUU should be used as the target address for the MESSAGE. πΈ
This feature can also help inside the Third Generation Partnership Project (3GPP) IP Multimedia Subsystem (IMS) π§ In IMS, a REGISTER of a contact to one Address of Record (AOR) causes an implicit registration of that same contact to other associated AORs. If GRUUs get requested and obtained as part of the registration request, the implicit registrations need GRUUs too. Assigning those extra GRUUs is the easy part. Telling the registering UA about them is the hard part π IMS UAs usually subscribe to the registration event, and each subscription notification for an AOR carries the registration state of all the associated AORs. This extension gives an easy way to deliver the GRUUs for those associated AORs. π
Now, about temporary GRUUs. As RFC 5627 [3] specifies, a temporary GRUU gets invalidated when the contact address bindings for the corresponding AOR and instance ID aren't refreshed, or when a registration to that AOR and instance ID happens with a new Call-ID. A UA can't always be certain which temporary GRUUs are still valid, just from the responses to the REGISTER requests it sent, or from notifications under RFC 3680 [2]. This document's extension gives a UA enough information to figure out which temporary GRUUs are valid. π₯
The registration event package has room for extension elements inside the <contact> element. This document defines new elements that can go there to deliver the public and temporary GRUUs for the contact. π«Ά
2. Terminology π
Here's the BCP 14 vibe check π The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document get read as described in RFC 2119. [1]
3. Description π
Two new elements, <pub-gruu> and <temp-gruu>, are defined. Each one contains a GRUU. The <temp-gruu> element also identifies the oldest temporary GRUU that is currently valid. β¨
These optional elements can go inside the body of a NOTIFY for the registration event package when GRUUs are associated with the contact. Then the contact URI and the GRUUs are all available to the watcher. π¦
4. Notifier Processing of SUBSCRIBE Requests β
Unchanged from RFC 3680 [2]. πΈ
5. Notifier Generation of NOTIFY Requests π
A notifier for the registration event package [2] SHOULD include the <pub-gruu> element when a contact has an instance ID and a public GRUU is associated with the combination of the AOR and the instance ID. When it's present, the <pub-gruu> element MUST be positioned as a child of the <contact> element. π
A notifier for the registration event package [2] MAY include the <temp-gruu> element when a contact has an instance ID and a temporary GRUU is associated with the combination of the AOR and the instance ID. This element SHOULD be included if the subscriber is also authorized to register to the AOR. This element SHOULD NOT be included if the subscriber is not authorized to register to the AOR, unless there is an explicitly configured policy directing that it be included. When it's present, the <temp-gruu> element MUST be positioned as a child of the <contact> element. π§
Heads up: a<contact>element can never have more than one<pub-gruu>and one<temp-gruu>child. That's because a particular contact can't be tied to more than one instance ID, so it's all good. π
It's possible for multiple registered contacts to share the same instance ID. When that happens, each <contact> element gets child <pub-gruu> and <temp-gruu> elements, and those match the corresponding child elements in the other <contact> elements that share that instance ID. π«Ά
If the notifier includes the <pub-gruu> element, it MUST populate the element with the public GRUU that's associated with the instance ID and AOR of the registered contact. π₯
If the notifier includes the <temp-gruu> element, it MUST populate the element with the most recently assigned temporary GRUU associated with the instance ID and AOR of the registered contact. It MUST also populate the element with a cseq attribute, which corresponds to the first (oldest) currently active temporary GRUU associated with the instance ID and AOR of the registered contact. The value of the cseq attribute is set to the value of the CSeq header field of the REGISTER request that caused that first temporary GRUU to be assigned. π
6. Subscriber Processing of NOTIFY Requests π¦
When a subscriber gets a registration event notification with a <contact> containing a <pub-gruu>, it MAY associate the public GRUU with the corresponding AOR and instance ID. Any previously received public GRUU for the same AOR and instance ID MUST be discarded. (It will no longer function.) π
When a subscriber gets a registration event notification with a <contact> containing a <temp-gruu>, it MAY associate the temporary GRUU, together with the callid and cseq attributes, with the corresponding AOR and instance ID. β¨
Subscribers that don't know about this extension will ignore the <pub-gruu> and <temp-gruu> elements, as [2] requires. π
Part 1 of 3 wraps here. Section 6.1 and everything after it pick up in part 2. π¦
6.1. Managing Temporary GRUU Lifetime π
okay so bestie, section 4.2 of [3] (RFC 5627) is where the guidance for UA developers lives on making sure only valid temporary GRUUs get kept and used π«Ά. ngl a UA can't always be 100% sure which temporary GRUUs are still valid just from the REGISTER responses it got, or from notifications that only follow [2] (RFC 3680) π. the extension in this doc gives the UA enough extra info to figure it out, and the RFC 3680 extension in here is the part that adds that info β¨. the steps the UA MAY take to make sure it only keeps valid GRUUs are:
- The UA should subscribe to the registration event package for the AOR it is registering.
- When a UA receives a 2xx response to a REGISTER request, it may extract and retain temporary GRUUs from the response for future use, as long as they remain valid. Appropriate GRUUs to retain are those corresponding to the contact address and instance ID it has registered. (Typically, the UA will register only one contact address, and so receive at most one temporary GRUU.)
- The UA may add the temporary GRUU to the set of valid temporary GRUUs associated with the AOR. (Note, in this case AOR is the To-address of the REGISTER request.) To aid in tracking validity, the UA should also associate a
callidattribute andcseqattribute with the temporary GRUU, with values obtained respectively from the Call-ID and CSeq values of the REGISTER response containing the temporary GRUU. - If the UA receives a registration event notification with an AOR (that it supports) and a
<contact>, for a contact address and instance ID (that it has registered and that contains a<temp-gruu>), it may update its set of valid temporary GRUUs associated with the AOR, as follows:- It may add the temporary GRUU to the set. To aid in tracking validity, the UA should associate the
callidandcseqattributes of the<contact>with the GRUU in the set. - It should remove any temporary GRUUs with a
callidattribute value different from that in the value of thecallidattribute of the<contact>, or with acseqattribute with value less than the value of thefirst-cseqattribute of the<temp-gruu>.
- It may add the temporary GRUU to the set. To aid in tracking validity, the UA should associate the
- If the UA receives a registration event notification with an AOR that it supports, and there are no
<contact>entries for its instance ID, then it should discard all the temporary GRUUs it has saved for that AOR.
7. Sample reginfo Document β¨
Note: This example and others in the following section are indented for readability by the addition of a fixed amount of whitespace to the beginning of each line. This whitespace is not part of the example. The conventions of [7] are used to describe representation of long message lines.
here's an example registration information document that includes the new element π§:
<?xml version="1.0"?>
<reginfo xmlns="urn:ietf:params:xml:ns:reginfo"
xmlns:gr="urn:ietf:params:xml:ns:gruuinfo"
version="0" state="full">
<registration aor="sip:user@example.com" id="as9"
state="active">
<contact id="76" state="active" event="registered"
duration-registered="36001" expires="3599"
callid="1j9FpLxk3uxtm8tn@192.0.2.1" cseq="54321"
q="0.8">
<uri>sip:user@192.0.2.1</uri>
<allOneLine>
<unknown-param name="+sip.instance">
"<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
</unknown-param>
</allOneLine>
<allOneLine>
<gr:pub-gruu uri="sip:user@example.com
;gr=hha9s8d-999a"/>
</allOneLine>
<allOneLine>
<gr:temp-gruu uri="sip:8ffkas08af7fasklzi9@example.com
;gr" first-cseq="54301"/>
</allOneLine>
</contact>
</registration>
</reginfo>
8. Examples π
Note: In the following examples, the SIP messages have been simplified, removing headers that are not pertinent to the example.
When the value of the Content-Length header field is "...", this means that the value should be the computed length of the body.
8.1. Example: Welcome Notice π
Consider the Welcome Notices example in [2]. When the application server receives a notification of a new registration containing the reginfo shown in Section 7, it should address messages using the contained public GRUU as follows:
MESSAGE sip:user@example.com;gr=hha9s8d-999a SIP/2.0
To: <sip:user@example.com>
From: "SIPland Notifier" <sip:notifier@example.com>;tag=7xy8
Content-Type: text/plain
Content-Length: ...
Welcome to SIPland!
Blah, blah, blah.
8.2. Example: Implicit Registration π¦
In a 3GPP IMS setting, a UA may send a single register message, requesting assignment of GRUUs, as follows:
REGISTER sip:example.net SIP/2.0
From: <sip:user_aor_1@example.net>;tag=5ab4
To: <sip:user_aor_1@example.net>
Call-ID: faif9a@ua.example.com
CSeq: 23001 REGISTER
Contact: <sip:ua.example.com>
;expires=3600
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
Supported: path, gruu
Content-Length: 0
The response reports success of the registration and returns the GRUUs assigned for the combination of AOR, instance ID, and Contact. It also indicates (via the P-Associated-URI header [6]) that there are two other associated AORs that may have been implicitly registered using the same contact. Each of those implicitly registered AORs will have unique GRUUs assigned. The REGISTER response will not include those GRUUs; it will only include the GRUUs for the AOR and instance ID explicitly included in the registration. πΈ
SIP/2.0 200 OK
From: <sip:user_aor_1@example.net>;tag=5ab4
To: <sip:user_aor_1@example.net>;tag=373392
Call-ID: faif9a@ua.example.com
CSeq: 23001 REGISTER
Path: <sip:proxy.example.net;lr>
Service-Route: <sip:proxy.example.net;lr>
Contact: <sip:ua.example.com>
;expires=3600
;+sip.instance="<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
;pub-gruu="sip:user_aor_1@example.net;gr=hha9s8d-999a"
;temp-gruu="sip:8ffkas08af7fasklzi9@example.net;gr"
P-Associated-URI: <sip:user_aor_2@example.net>,
<sip:+358504821437@example.net;user=phone>
Content-Length: 0
The UA then subscribes to the registration event package as follows:
SUBSCRIBE sip:user_aor_1@example.net SIP/2.0
From: <sip:user_aor_1@example.net>;tag=27182
To: <sip:user_aor_1@example.net>
Call-ID: gbjg0b@ua.example.com
CSeq: 45001 SUBSCRIBE
Route: <sip:proxy.example.net;lr>
Event: reg
Expires: 3600
Accept: application/reginfo+xml
Contact: <sip:user_aor_1@example.net;gr=hha9s8d-999a>
Content-Length: 0
(The successful response to the subscription is not shown.) Once the subscription is established, an initial notification is sent giving registration status. In IMS deployments, the response includes, in addition to the status for the requested URI, the status for the other associated URIs.
NOTIFY sip:user_aor_1@example.net;gr=hha9s8d-999a SIP/2.0
From: <sip:user_aor_1@example.net>;tag=27182
To: <sip:user_aor_1@example.net>;tag=262281
Call-ID: gbjg0b@ua.example.com
CSeq: 633 NOTIFY
Subscription-State: active;expires=3600
Event: reg
Content-Type: application/reginfo+xml
Contact: <sip:registrar.example.net>
Content-Length: ...
<?xml version="1.0"?>
<reginfo xmlns="urn:ietf:params:xml:ns:reginfo"
xmlns:gr="urn:ietf:params:xml:ns:gruuinfo"
version="1" state="full">
<registration aor="sip:user_aor_1@example.net" id="a7"
state="active">
<contact id="92" state="active" event="registered"
duration-registered="1" expires="3599"
callid="faif9a@ua.example.com" cseq="23001">
<uri>
sip:ua.example.com
</uri>
<allOneLine>
<unknown-param name="+sip.instance">
"<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
</unknown-param>
</allOneLine>
<allOneLine>
<gr:pub-gruu uri="sip:user_aor_1@example.net
;gr=hha9s8d-999a"/>
</allOneLine>
<allOneLine>
<gr:temp-gruu uri="sip:8ffkas08af7fasklzi9@example.net
;gr" first-cseq="54301"/>
</allOneLine>
</contact>
</registration>
<registration aor="sip:user_aor_2@example.net" id="a8"
state="active">
<contact id="93" state="active" event="created"
duration-registered="1" expires="3599"
callid="faif9a@ua.example.com" cseq="23001">
<uri>
sip:ua.example.com
</uri>
<allOneLine>
<unknown-param name="+sip.instance">
"<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
</unknown-param>
</allOneLine>
<allOneLine>
<gr:pub-gruu uri="sip:user_aor_2@example.net
;gr=hha9s8d-999b"/>
</allOneLine>
<allOneLine>
<gr:temp-gruu uri="sip:07hcovy36vp6vngvbia@example.net
;gr" first-cseq="54301"/>
</allOneLine>
</contact>
</registration>
<registration
aor="sip:+358504821437@example.net;user=phone"
id="a9"
state="active">
<contact id="94" state="active" event="created"
duration-registered="1" expires="3599"
callid="faif9a@ua.example.com" cseq="23001">
<uri>
sip:ua.example.com
</uri>
<allOneLine>
<unknown-param name="+sip.instance">
"<urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6>"
</unknown-param>
</allOneLine>
<allOneLine>
<gr:pub-gruu uri="sip:+358504821437@example.net
;user=phone;gr=hha9s8d-999c"/>
</allOneLine>
<allOneLine>
<gr:temp-gruu uri="sip:h99egjbv17fe8ibvlka@example.net
;gr" first-cseq="54301"/>
</allOneLine>
</contact>
</registration>
</reginfo>
The status indicates that the associated URIs all have the same contact registered. It also includes the unique GRUUs that have been assigned to each. The UA may then retain those GRUUs for use when establishing dialogs using the corresponding AORs. π
9. XML Schema Definition β¨
okay so the <pub-gruu> and <temp-gruu> elements live in their very own XML namespace, urn:ietf:params:xml:ns:gruuinfo. π Here's the schema, bestie:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="urn:ietf:params:xml:ns:gruuinfo"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="urn:ietf:params:xml:ns:gruuinfo">
<xs:complexType name="pubGruu">
<xs:attribute name="uri" type="xs:anyURI"
use="required"/>
</xs:complexType>
<xs:complexType name="tempGruu">
<xs:complexContent>
<xs:extension base="tns:pubGruu">
<xs:attribute name="first-cseq"
type="xs:unsignedLong"
use="required"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="pub-gruu" type="tns:pubGruu"/>
<xs:element name="temp-gruu" type="tns:tempGruu"/>
</xs:schema>
10. IANA Considerations πΈ
ngl this part is short and sweet: there are two IANA considerations for this spec. π
10.1. URN Sub-Namespace Registration
so this section registers a brand new XML namespace, following the guidelines in [4]. π¦
URI: urn:ietf:params:xml:ns:gruuinfo
Registrant Contact: IETF, SIPPING working group, <sipping@ietf.org>, Paul Kyzivat <pkyzivat@cisco.com>
Here's the XML that goes with it:
BEGIN
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
"http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type"
content="text/html;charset=iso-8859-1"/>
<title>Reg Information GRUU Extension Namespace</title>
</head>
<body>
<h1>Namespace for Reg Information GRUU Extension</h1>
<h2>urn:ietf:params:xml:ns:gruuinfo</h2>
<p>See <a href="http://www.rfc-editor.org/rfc/rfc5628.txt">
RFC5628</a>.</p>
</body>
</html>
END
10.2. XML Schema Registration
this section registers an XML schema using the procedures in [4]. π
URI: urn:ietf:params:xml:schema:gruuinfo.
Registrant Contact: IETF, SIPPING working group, <sipping@ietf.org>, Paul Kyzivat <pkyzivat@cisco.com>
The XML for this schema is in Section 9. β¨
11. Security Considerations π
okay this section is serious business, so let's get into it. π₯
Security considerations for the registration event package are covered in RFC 3680 [2], and everything in those considerations applies here too. π«Ά
So, if a contact address you got from subscribing to the registration event package isn't reachable by the subscriber, then disclosing it might arguably be a minimal security risk. π In that situation, including a GRUU could be seen as raising the risk, because it hands over a reachable address. BUT, requests sent to a GRUU always hit the servicing proxy first, before they reach the intended User Agent. The proxy can control access however it likes, same as it can for the AOR. For example, the proxy servicing a GRUU could accept requests from senders on a white list and reject everyone else. So honestly? Disclosing a GRUU is no more risky than disclosing the AOR. π
Temporary GRUUs come with an extra security consideration, and it's a big one. The whole point of a temporary GRUU is to give you a contact address that can't be linked back to the identity of whoever is calling. π But the recipient of a call using a temporary GRUU could guess who the caller is, then try to get the temporary GRUUs assigned to that caller just to confirm the guess. There are two possible ways to get those temporary GRUUs:
- Send a REGISTER request to a conjectured caller.
- Send a SUBSCRIBE request for the registration event package to the conjectured caller.
Typically, REGISTER is limited to devices or users who are authorized to originate and receive calls with the AOR. Anonymity among users of the same AOR is hard to get and usually not needed anyway. π It is recommended (see Section 5) that the authorization policy for the registration event package only let subscribers who are authorized to register to the AOR receive temporary GRUUs. With that policy in place, the confidentiality of the temporary GRUU is the same whether or not the registration event package is involved. β
Heads up, bestie: confidentiality does NOT extend to parties that are allowed to register to the AOR, or to anyone who can get the temporary GRUU by subscribing to the registration event package. User Agents using a temporary GRUU should keep that in mind. π¨
12. Acknowledgements π§
the author would like to thank Jonathan Rosenberg for help with this document, and Jari Urpalainen for assistance with the XML. π«Άπ
13. References π
13.1. Normative References
- [1] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
- [2] Rosenberg, J., "A Session Initiation Protocol (SIP) Event Package for Registrations", RFC 3680, March 2004.
- [3] Rosenberg, J., "Obtaining and Using Globally Routable User Agent (UA) URIs (GRUU) in the Session Initiation Protocol (SIP)", RFC 5627, October 2009.
- [4] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, January 2004.
13.2. Informative References
- [5] Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston, A., Peterson, J., Sparks, R., Handley, M., and E. Schooler, "SIP: Session Initiation Protocol", RFC 3261, June 2002.
- [6] Garcia-Martin, M., Henrikson, E., and D. Mills, "Private Header (P-Header) Extensions to the Session Initiation Protocol (SIP) for the 3rd-Generation Partnership Project (3GPP)", RFC 3455, January 2003.
- [7] Sparks, R., Hawrylyshen, A., Johnston, A., Rosenberg, J., and H. Schulzrinne, "Session Initiation Protocol (SIP) Torture Test Messages", RFC 4475, May 2006.
Author's Address π¦
Paul H. Kyzivat Cisco Systems, Inc. 1414 Massachusetts Avenue Boxborough, MA 01719 USA
EMail: pkyzivat@cisco.com π
π¦ where this page comes from
- ietf: rfc5628 2009-10
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms