RFC 8433

A Simpler Method for Resolving Alert-Info URNs

πŸ“… published August 2018, from ietf

πŸ“œ RFC 8433: A Simpler Method for Resolving Alert-Info URNs πŸ¦‹

Independent Submission ✨ This one came in from outside the usual RFC stream, so the RFC Editor published it at their discretion.

Written by: D. Worley, from Ariadne πŸ’œ

Category: Informational 🌸 (so it's for info only, not an Internet Standard)

Date: August 2018 πŸ“…

ISSN: 2070-1721

The title block doesn't list anything it obsoletes or updates, so this RFC doesn't replace or patch any earlier document. It's a standalone bestie ✨


Abstract 🫢

okay so the alert namespace of Uniform Resource Names (URNs) can be used in the Alert-Info header field of Session Initiation Protocol (SIP) requests and responses. Its job is to tell a voice over IP (VoIP) telephone, which is the user agent, what kind of call it's dealing with, whether it originated or terminated the call. The user agent must resolve the URNs into a signal. In other words, it has to pick the best available signal to show its user so the call's characteristics come through πŸ””

RFC 7462 describes a non-normative algorithm for signal selection, which is giving "here's one way, use it if you want" energy πŸ’… This document is the glow-up πŸ”₯ It describes a more efficient alternative. The user agent's designer looks at the user agent's signals and what they mean, then builds a finite state machine (FSM) that processes the URNs to pick a signal. And the whole thing obeys the restrictions in the definition of the alert URN namespace. Iconic, honestly ✨


Status of This Memo πŸ“

This document is not an Internet Standards Track specification. It's published for informational purposes, so no pressure 🌈

It's a contribution to the RFC Series, independently of any other RFC stream. The RFC Editor chose to publish it at their discretion, and they make no statement about its value for implementation or deployment. Documents the RFC Editor approves for publication are not candidates for any level of Internet Standard. See Section 2 of RFC 7841 for that 🧚

You can get info on the current status of this document, any errata, and how to send feedback at https://www.rfc-editor.org/info/rfc8433 πŸ’Œ


Copyright (c) 2018 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 (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, because they describe your rights and restrictions with respect to this document πŸ™ˆ


Table of Contents πŸ—ΊοΈ

1. Introduction ....................................................3
   1.1. Requirements Governing Resolution Algorithms ...............4
   1.2. Summary of the New Resolution Algorithm ....................5
   1.3. Conventions Used in This Document ..........................7
   2. Selecting the Signals and Their Corresponding "alert" URNs ......7
   3. General Considerations for Processing Alert-Info ................9
   4. Constructing the Finite State Machine for a Very Simple
      Example ........................................................10
      4.1. Listing the Expressed URNs ................................11
      4.2. Constructing the Alphabet of Symbols ......................11
      4.3. Constructing the States and Transitions ...................13
      4.4. Summary ...................................................17
      4.5. Examples of Processing Alert-Info URNs ....................19
   5. Further Examples ...............................................20
      5.1. Example with "source" and "priority" URNs .................20
      5.2. Example 1 of RFC 7462 .....................................24
      5.3. Examples 2, 3, and 4 of RFC 7462 ..........................30
      5.4. An Example That Subsets Internal Sources ..................33
      5.5. An Example of "alert:service" URNs ........................34
      5.6. An Example Using Country Codes ............................34
   6. Prioritizing Signals ...........................................40
   7. Dynamic Sets of Signals ........................................41
   8. Security Considerations ........................................43
   9. IANA Considerations ............................................43
   10. References ....................................................44
      10.1. Normative References .....................................44
      10.2. Informative References ...................................44
   Acknowledgments ...................................................45
   Author's Address ..................................................45

1. Introduction πŸŽ€

okay so when a SIP user agent (UA) server gets an incoming INVITE request, it picks an alerting signal (the ring tone) to play for its user, who is the called user. It does this by processing the Alert-Info header field(s) in that incoming INVITE request [RFC3261]. Same vibe on the other side: a SIP UA client picks an alerting signal (the ringback tone) for its user, who is the calling user. It does this by processing the Alert-Info header field(s) in the incoming provisional response(s) to its outgoing INVITE request πŸ“ž

[RFC3261] pictured the Alert-Info header field value as a URL the UA could use to fetch the encoded media for the signal. That usage has security problems, and it's annoying to implement in practice πŸ™ƒ

[RFC7462] came in with a better plan ✨ The Alert-Info values can be URNs from the alert URN namespace, and they describe features of the call or of the signal that should be shown to the user. [RFC7462] defined a large set of alert URNs and procedures for extending that set πŸ’œ

A UA probably won't have more than a small set of alerting signals, but there are an infinite number of possible combinations of alert URNs. So a UA often has to pick an alerting signal that only shows part of the info in the Alert-Info header field(s). That's the resolution process for alert URNs 🧩 The requirements for resolving them are in Section 11.1 of [RFC7462].

Section 12 of [RFC7462] gives a (non-normative) resolution algorithm for picking a signal that meets the requirements of Section 11.1 of that document. The algorithm works no matter which alerting signals the UA has or what they mean. The existence of the [RFC7462] algorithm shows the resolution requirements can always be met. The catch is that it's complex and slow 🐒

So this document is the upgrade πŸš€ It describes an improved implementation, a more efficient resolution algorithm that picks signals and still conforms to the requirements of Section 11.1 of [RFC7462]. (Of course, like any such algorithm, it's non-normative. The implementation is free to use any algorithm that conforms to the requirements of Section 11.1 of [RFC7462].)

Here's the new move. Once the UA designer has picked the set of signals the UA produces, and the alert URNs those signals express, a finite state machine (FSM) gets built. The FSM picks alerting signals based on the URNs in the Alert-Info header field(s) of a SIP message. The incoming alert URNs go through a straightforward preprocessing step that turns them into a sequence of "symbols" from a fixed finite set. Those symbols are fed into the FSM as input. After the input is processed, the FSM's state tells you which alerting signal to show the user πŸ””

The preprocessor and the FSM are both determined only by the selected set of signals and the set of alert URNs those signals express. So the whole processing machinery can be fixed when the UA is designed 🧠✨

1.1. Requirements Governing Resolution Algorithms πŸ“‹

The requirements for resolving alert URNs are in Section 11.1 of [RFC7462]. In short, they go like this:

  • The alert URNs are processed from left to right. Each alert URN has precedence over all the URNs after it, and its interpretation is subordinate to all the URNs before it.
  • As each URN is processed, one of the UA's signals gets chosen. That signal expresses the URN as far as possible without reducing how well any of the preceding URNs were expressed by the signal chosen for the preceding URN. So as processing goes on, the chosen signals get more and more specific and carry more info. But everything the signal chosen for a URN expresses about that URN is also expressed by the signals chosen for all the following URNs.
  • If the entire current URN can't be expressed by any allowed signal, then each trailing alert-ind-part (the sections separated by colons) gets removed, one at a time, until the reduced URN can be expressed by some signal. That signal also has to express at least the same reduced versions of the preceding URNs that the signal chosen for the preceding URN expressed. This can be described as "a signal that expresses as much of the current URN as possible while still expressing as much of the previous URNs as the preceding signal did." πŸ’…

Let's look at an example, because it's giving clarity πŸ‘€ Say you're processing this:

Alert-Info: urn:alert:category-a:part-a1:part-a2,
            urn:alert:category-b:part-b1:part-b2

If the UA has no signal for urn:alert:category-a:part-a1:part-a2, it removes part-a2 from the URN and checks whether it has a signal for the less-specific URN urn:alert:category-a:part-a1. If it has no signal for that one either, it gives up on the URN entirely. (urn:alert:category-a doesn't exist and can be treated as expressing nothing about the call.) The chosen signal ends up being the UA's default signal, which is the signal used when there's no Alert-Info. Not me giving up on the first URN 😭

But let's say the UA does have a signal for urn:alert:category-a:part-a1, and it chooses that signal when processing the first URN. From that point on, all processing is restricted to signals that express urn:alert:category-a:part-a1 or a more specific URN of the category "category-a". Main character energy πŸ‘‘

Next up is urn:alert:category-b:part-b1:part-b2. If there's a signal that expresses both urn:alert:category-a:part-a1 and urn:alert:category-b:part-b1:part-b2, the UA chooses that signal. If there's no such signal, the second URN gets reduced to urn:alert:category-b:part-b1, and the UA checks for a signal that expresses that URN along with urn:alert:category-a:part-a1. If no signal meets that relaxed requirement, the second URN gets reduced to urn:alert:category-b, which is discarded. Then the signal chosen for the first URN is also chosen for the second URN. Either way, all processing after this point is restricted to signals that express urn:alert:category-a:part-a1 or a more specific URN of the category "category-a", and that also express the chosen part of urn:alert:category-b:part-b1:part-b2 🌸

This keeps going until the last alert URN has been processed. The signal chosen for the last URN is the signal the UA uses πŸŽ‰

1.2. Summary of the New Resolution Algorithm ✨

okay so bestie, the whole point of this doc is to describe a resolution algorithm that follows Section 11.1 of [RFC7462] πŸ’… but is way simpler than the one in Section 12 of [RFC7462]. The big idea: once the UA designer has picked a set of signals and the URNs they express, an FSM gets built that picks alerting signals based on the URNs in the Alert-Info header field(s) in a SIP message πŸ¦‹

  • The designer selects the set of signals that the UA produces, matching each signal to a set of "alert" URNs that together specify the meaning carried by the signal. (If the signal is a "default" signal that has no specific meaning, the set is empty. If the signal carries the meaning of one "alert" URN, the set contains that URN. If the signal carries a meaning that is the logical AND of two or more "alert" URNs, the set contains those URNs.)
  • Based on the UA's signals and their meanings, the designer constructs an "alphabet" containing a finite number of symbols. Each possible "alert" URN is mapped into one particular symbol.
  • The designer constructs an FSM whose input is the alphabet of symbols and whose states describe the information extracted from the Alert-Info URNs.
  • Each state of the FSM has an associated signal. Processing the Alert-Info URNs leaves the FSM in some particular state, and the UA renders the signal attached to that final state.

To select a ring tone or ringback tone based on a SIP message, the UA processes the "alert" URNs in the Alert-Info header field from left to right. Initially, the FSM is in a designated initial state. The UA maps each successive URN into the corresponding symbol and then executes the state transition of the FSM specified by the symbol. The state of the FSM after processing the URNs determines which signal the UA will render to the user.

Note that the UA generally has two FSMs, because a UA usually wants to signal different information in ring tones than it signals in ringback tones πŸŽ€ One FSM is used to select the ring tone to render for an incoming INVITE request. The other FSM is used to select the ringback tone to render based on an incoming provisional response to an outgoing INVITE request. Both FSMs are constructed in the same way, but the constructions are based on different lists of signals and corresponding URNs.

All of the steps of the method after the designer has selected the signals and their URNs are algorithmic, and the algorithm of those steps ensures that the operation of the FSM will satisfy the constraints of Section 11.1 of [RFC7462]. A Python implementation of the algorithmic steps is provided in [code] 🐍

In simple situations, a suitable FSM or equivalent ad hoc code can be constructed by hand using ad hoc analysis. Generally, this is only practical when a small number of alert-categories and alert-indications are signaled and the categories interact in a simple, uniform way. For example, the examples in Sections 5.1 and 5.2 could be constructed by ad hoc analysis. But automatic processing is valuable if the situation is too complicated to construct a correct FSM by ad hoc analysis, or if the set of signals will change too frequently for human production to be economical 🧚

1.3. Conventions Used in This Document

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here πŸ“–

2. Selecting the Signals and Their Corresponding "alert" URNs

The designer must select signals that the UA will generate and define the meanings that the signals will have to the user. Based on this, the designer determines for each signal the "alert" URN or combination of "alert" URNs that (1) indicate that signal's meaning in SIP messages and (2) consequently should elicit that signal from the UA πŸ’œ

For example, suppose the UA has a particular ring tone for calls from an external source. A call from an external source is marked with the URN urn:alert:source:external (specified in Section 9 of [RFC7462]). Thus, the table of signals includes:

    Signal                          URN(s)
    ----------------------------    -------------------------------
    external source                 urn:alert:source:external

Similarly, if the UA has a particular ring tone for calls from an internal source, the table includes:

    Signal                          URN(s)
    ----------------------------    -------------------------------
    internal source                 urn:alert:source:internal

If the UA has ring tones for calls that are marked as having higher or lower priority, then the table includes:

    Signal                          URN(s)
    ----------------------------    -------------------------------
    high priority                   urn:alert:priority:high
    low priority                    urn:alert:priority:low

Note that the UA must be able to signal for a message that has no "alert" URNs in the Alert-Info header field, which means that there must always be a default signal that has zero corresponding URNs:

    Signal                          URN(s)
    ----------------------------    -------------------------------
    default                         (none)

A signal can be defined to indicate a combination of conditions. For instance, a signal that is used only for high-priority, internal-source calls expresses two URNs and will only be used when both URNs are present in Alert-Info:

    Signal                          URN(s)
    ------------------------------  -------------------------------
    high priority, internal source  urn:alert:priority:high,
                                        urn:alert:source:internal

A signal can be defined to cover a number of related conditions by specifying a URN that is the common prefix of the URNs for the various conditions. For instance, the URNs for "recall due to callback", "recall due to call hold", and "recall due to transfer" all start with urn:alert:service:recall, so one signal can be provided for all of them by:

    Signal                          URN(s)
    ----------------------------    -------------------------------
    recall                          urn:alert:service:recall

But if a specific signal is also provided for "recall due to callback" by this entry:

    Signal                          URN(s)
    ----------------------------    ---------------------------------
    recall generally                urn:alert:service:recall
    recall due to callback          urn:alert:service:recall:callback

then if the message contains urn:alert:service:recall:callback, the "recall due to callback" signal will be chosen instead of "recall generally", because the UA chooses the signal that most completely expresses the information in the Alert-Info header field 🎯

The designer may wish to define extension URNs that provide more specific information about a call than the standard "alert" URNs do. One method is to add additional components to standard URNs. For instance, an extra-high priority could be indicated by the URN urn:alert:priority:high:extra@example. The final "extra@example" is an "alert-ind-part" that is a private extension. (See Sections 7 and 10.2 of [RFC7462] for a discussion of private extensions.) In any case, adding an alert-ind-part to a URN makes its meaning more specific, in that any call to which the longer URN can be applied can also have the shorter URN applied. In this case, "extra-high-priority calls" are considered a subset of "high-priority calls" 🌈

    Signal                URN(s)
    --------------------- -----------------------------------------
    high priority         urn:alert:priority:high
    extra-high priority   urn:alert:priority:high:extra@example.com

Of course, for this extension to be useful, the senders of SIP messages (e.g., other UAs) must generate the extension URN in suitable circumstances 🫢

In some circumstances, the designer may want to create an entirely new category of "alert" URNs to indicate a type of information that is not indicated by any standard category of URNs. In that case, the designer uses a private extension as the alert-category (the third component of the URN), combined with whatever alert-ind-part (fourth component) values are desired. For example, a simplified version of the U.S. military security designations could be:

    Signal                    URN(s)
    -----------------------   ---------------------------------------
    unclassified              urn:alert:security@example:unclassified
    confidential              urn:alert:security@example:confidential
    secret                    urn:alert:security@example:secret
    top secret                urn:alert:security@example:top-secret

The designer should ensure that the new alert-category is orthogonal to all defined standard alert-categories, in that any combination of one of the new URNs with one of the standard URNs is meaningful, in that there could be a message carrying both URNs.

In addition, the set of alert-ind-parts for the new alert-category should be comprehensive and disjoint, in that every message can be described by exactly one of them πŸ”₯

3. General Considerations for Processing Alert-Info πŸŽ€

okay so this section is all about the stuff that comes up when you process Alert-Info, and bestie, you NEED these to get it right πŸ’… they're what keep you conforming to the standards AND give users a good experience ✨ the good news is they don't really depend on the FSM itself, so they got their own little section πŸ¦‹

the UA can have a bunch of different FSMs for processing URNs 🧚 usually there's one FSM for Alert-Info in incoming INVITE requests and another for incoming provisional responses to outgoing INVITE requests. but ANY situation that changes which signals the UA is willing to generate means a different set of signals and URNs, and that means a different FSM πŸ”₯

for example, if a call is active on the UA, all audible signals might become unavailable πŸ™ˆ or audible signals might only be available if urn:alert:priority:high is specified. it's giving "the rules changed mid-call" energy πŸ’€

same deal if the set of signals gets customized by user action or local policy, the generated FSM has to be updated πŸ’– there are two ways to handle that: (1) regenerate it using the method described here, or (2) generate a "generic" FSM and instantiate it based on the signals that are available 🌸 (see Section 7 for a discussion of this πŸ‘‘)

ngl this part is so important: Alert-Info values are allowed to be URIs of any scheme, and within the "urn" scheme they can have any namespace [RFC3261] 🌈 this document doesn't cover processing URIs that aren't "alert" URNs, and [RFC7462] doesn't specify that processing either ✨ but the algorithm designer still has to think about what to do with those URIs if they show up 🧐 the simplest choice is to ignore them 🫢 another option is that the algorithm checks the URI to see if it names an alerting signal or describes how to retrieve one, and if it does, it can choose to render that signal instead of processing the "alert" URNs to pick a signal πŸ’œ either way, the rest of this document assumes two things: (1) the signal gets chosen based on the "alert" URNs in Alert-Info, and (2) all Alert-Info URIs that aren't "alert" URNs have been removed πŸŽ€

the UA might also get "alert" URNs that are semantically invalid in different ways 😭 for example, a URN might only have three components, even though every valid "alert" URN has at least one alert-ind-part and therefore four components πŸ’€ the only useful strategy here is to ignore those URNs (and maybe log them for analysis πŸ“)

and the method described here is robust when it comes to categories and alert-ind-parts the UA doesn't know about πŸ¦‹ so it's also robust if they aren't valid standardized URNs 🌈 which means these error conditions don't need any special handling, periodt πŸ’…βœ¨


4. Constructing the Finite State Machine for a Very Simple Example 🌸

okay now the fun part 😭 building the FSM is a 4-step process πŸ’–

  1. list the URNs expressed by the UA's various signals 🧚
  2. from those expressed URNs, build the finite alphabet of symbols that input URNs get mapped into, and that drive the FSM's state transitions πŸ¦‹
  3. build the states of the FSM and the transitions between them πŸ”₯
  4. pick a signal to associate with each FSM state πŸ‘‘

we'll walk through it with a super simple example πŸŽ€ there are two signals: one expressing "internal source" and one expressing "external source," plus a default signal for when there's no source info to signal 🌈 the "internal source" signal expresses urn:alert:source:internal, and the "external source" signal expresses urn:alert:source:external ✨

4.1. Listing the Expressed URNs

step one is figuring out, for each of the UA's signals, what call characteristics it represents πŸ’… basically, the set of "alert" URNs that make up its information content ✨

    Signal                          URN(s)
    ----------------------------    -------------------------------
    default                         (none)
    internal source                 urn:alert:source:internal
    external source                 urn:alert:source:external

once you've got all the expressed URNs, the designer can figure out which sets of URNs need to be distinguished from each other 🧐 in our simple example, the expressed URNs are:

    urn:alert:source:external
    urn:alert:source:internal

4.2. Constructing the Alphabet of Symbols

okay here's the big idea 🧠 the set of possible "alert" URNs is infinite, so to get a finite alphabet of input symbols that drive the FSM's transitions, the designer has to split the "alert" URNs into a finite set of categories πŸ’–

once you've listed all the expressed URNs, you can list every alert-category that's relevant to the UA's signaling ✨ "alert" URNs in any other alert-category can't affect the signaling, so they can be ignored πŸ™ˆ (the easiest way to ignore the non-relevant URNs is to skip over them during Alert-Info processing 🫢 a more formal approach is to map all of them into one "Other" symbol, and then have, for each state of the FSM, the "Other" symbol transition to that same state πŸ¦‹)

within each relevant alert-category, we define a distinct symbol for every expressed URN AND for all of their "ancestor" URNs (the ones you get by removing one or more trailing alert-ind-parts) 🌸 to name the symbols so they're distinct from the URNs, we remove the initial "urn:alert:" and capitalize each alert-ind-part πŸ’œ so in our example, we get these symbols:

    Source
    Source:External
    Source:Internal

notice there's a "Source" symbol even though there's no matching URN πŸ‘€ (urn:alert:source is not a valid URN -- see Section 7 of [RFC7462] -- although the processing algorithm still has to be ready to screen out such a purported URN if it shows up in the Alert-Info header field 🚫) its existence as a symbol will come in handy later when we build the FSM πŸ”₯

for each of these symbols, we also add a symbol that classifies URNs which extend the symbol's URN with alert-ind-parts that can't be expressed by signals 🧚

    Source:Other
    Source:External:Other
    Source:Internal:Other

the last two classify URNs (like urn:alert:source:external:foo@example) that extend URNs we already have symbols for πŸ’– the first one is for classifying URNs (like urn:alert:source:bar@example) whose first alert-ind-part contradicts all the "source" URNs the UA can signal πŸ’€

so after those steps, we've got this set of symbols:

    Source
    Source:External
    Source:External:Other
    Source:Internal
    Source:Internal:Other
    Source:Other

now we can simplify the set by removing the ones like Source:External:Other and Source:Internal:Other, which add "Other" to a symbol for an expressed URN that isn't ancestral to any other expressed URN ✨ this works because adding more alert-ind-parts to a URN that's a leaf (relative to the set of signals) doesn't change anything 😌 in this example, urn:alert:source:external:foo@example has the same effect as urn:alert:source:external for both (1) causing a signal to be chosen and (2) suppressing the effect of later URNs πŸŽ€

that leaves these symbols for the "source" category:

    Source
    Source:External
    Source:Internal
    Source:Other

to see this visually, picture the infinite tree of possible source "alert" URNs, split into subtrees that each map to one of these symbols 🌳 we mark the expressed URNs with "*" πŸ¦‹

                                urn:alert
                                    |
                                {   |    }
                                { source } --> 1
                                {   |    }
                                    |
               +--------------------+------------------+
               |                    |                  |
          {    |      }        {    |      }        {  |  }
          { external* } --> 2  { internal* } --> 3  { ... } --> 4
          {    |      }        {    |      }        {     }
          {   ...     }        {   ...     }
          {           }        {           }

       1 = Source
       2 = Source:External
       3 = Source:Internal
       4 = Source:Other

and that's the whole map, bestie 🫢✨ the symbols are cute, the tree is clean, and the FSM is about to get built on top of it πŸ’…πŸ‘‘

4.3. Constructing the States and Transitions 🌸

okay so the UA processes the Alert-Info URNs from left to right using an FSM πŸ’…, and every successive URN makes the FSM hop to a new state ✨. Each state keeps track of the info that's been pulled out of the URNs so far. Whatever state the FSM is sitting in after ALL the URNs are processed decides which signal the UA shows the user πŸŽ€

We label each state with a set of symbols, one from each relevant category, that describe the info collected from every URN processed so far 🏷️ The initial state gets the "null" symbols, which are just the category names, because nothing has been recorded yet. In our simple example, the initial state is labeled "Source", since that's the only relevant category.

State: Source (initial state)

Each state also has a corresponding alerting signal, which is the signal the UA will produce when URN processing leaves the FSM in that state πŸ”” The signal is the one that best expresses the info that's been extracted. Usually the designer can pick it easily, but there are certain constraints the choice has to satisfy πŸ’– The big one: the signal's expressed URNs must be semantic supersets of (i.e., identical to or a prefix of) the URNs corresponding to the symbols in the state's label. Like, if the expressed URN of the signal in a certain category is shorter than the state's label, we show that in the state's name by putting parentheses around the trailing part of the symbol that the signal doesn't express. For instance, if the symbol in the label is Source:External but the signal only expresses "Source" (i.e., no "source" URN at all), then the symbol in the label becomes Source:(External).

The reason for this nonintuitive construction is that in some states, the FSM has recorded info that the chosen signal can't express πŸ™ˆ

Note that the parentheses are part of the state name, so sometimes there can be two or more distinct states labeled with the same symbols but with the parentheses placed differently inside the symbols πŸ¦‹ These similar state names matter when the FSM can record info from multiple "alert" URNs but can't express all of them. Depending on the order the URNs show up in, the UA might have to render different signals, so it needs states that record the same info but render different subsets of it.

The initial state's label is the string of null symbols for the relevant categories, so the only allowed signal is the default signal, which expresses no URNs πŸ“

State: Source (initial state)
Signal: default

From each state, we must construct the transition for each possible input symbol. For a particular current state and symbol, we build the label of the next state by combining the input symbol with the symbol in the current state's label for the same category. If one of the symbols is a prefix of the other, we pick the longer one. If not, we pick the symbol in the current state's label.

So in our simple example, the initial state has these transitions:

State: Source (initial state)
Signal: default
Transitions:
    Source:External -> Source:External
    Source:Internal -> Source:Internal
    Source:Other -> Source:Other

In all of these transitions, the input symbol is compatible with the matching label of the current state, "Source", so the next state's label is the full input symbol 🧚

BUT there's a further constraint on the next state: its signal must express URNs that at least contain the expressed URNs of the current state's signal πŸ‘‘ Within that constraint, and while staying compatible with the next state's label, the next state's signal must express the longest URN that any signal can express for the category of the input URN. Iconic behavior, honestly 😭

In our example, this means the next Source:External state gets the "external source" signal, which expresses urn:alert:source:external. Since that signal expresses all of the state's label, it's the chosen state. Same deal for the next Source:Internal state, which gets the "internal source" signal. But for the transition on input Source:Other, the Source:Other state has to use the default signal, because there's no signal that expresses urn:alert:source:[some-unknown-alert-ind-part]. So the next state is Source:(Other), where the parentheses record that the "Other" part of the label isn't expressed by the state's signal πŸ’…

So the current state and the next states it can transition to are:

State: Source (initial state)
Signal: default
Transitions:
    Source:External -> Source:External
    Source:Internal -> Source:Internal
    Source:Other -> Source:(Other)

State: Source:External
Signal: external source (urn:alert:source:external)

State: Source:Internal
Signal: internal source (urn:alert:source:internal)

State: Source:(Other)
Signal: default

Now look at Source:External. It's incompatible with every input symbol other than Source:External, so all of its transitions go back to itself πŸ”

State: Source:External
Signal: external source (urn:alert:source:external)
Transitions:
    Source:External -> Source:External
    Source:Internal -> Source:External
    Source:Other -> Source:External

and similarly:

State: Source:Internal
Signal: internal source (urn:alert:source:internal)
Transitions:
    Source:External -> Source:Internal
    Source:Internal -> Source:Internal
    Source:Other -> Source:Internal

State: Source:(Other)
Signal: default
Transitions:
    Source:External -> Source:(Other)
    Source:Internal -> Source:(Other)
    Source:Other -> Source:(Other)

4.4. Summary πŸ¦‹

The FSM can be built by processing the file "very-simple.txt" with the program "alert-info-fsm.py" in [code]. The program's output shows the stages of the construction, which go like this:

  1. The signals have the meanings:
Signal                          URN(s)
----------------------------    -------------------------------
default                         (none)
internal source                 urn:alert:source:internal
external source                 urn:alert:source:external
  1. The expressed URNs are:
urn:alert:source:external
urn:alert:source:internal
  1. The relevant categories of "alert" URNs are only:
source
  1. So the infinite universe of possible "alert" URNs can be reduced to these symbols, which are the categories of URNs that differ in ways that matter to the resolution process:
Source
Source:External
Source:Internal
Source:Other
  1. The FSM is:
State: Source (initial state)
Signal: default
Transitions:
    Source:External -> Source:External
    Source:Internal -> Source:Internal
    Source:Other -> Source:(Other)

State: Source:External
Signal: external source (urn:alert:source:external)
Transitions:
    Source:External -> Source:External
    Source:Internal -> Source:External
    Source:Other -> Source:External

State: Source:Internal
Signal: internal source (urn:alert:source:internal)
Transitions:
    Source:External -> Source:Internal
    Source:Internal -> Source:Internal
    Source:Other -> Source:Internal

State: Source:(Other)
Signal: default
Transitions:
    Source:External -> Source:(Other)
    Source:Internal -> Source:(Other)
    Source:Other -> Source:(Other)
  • Each state is labeled by a set of symbols that describe the info extracted from the URNs so far.
  • Each state has a signal that is a semantic superset of the state's label, i.e., the signal's expressed URNs match the initial portion of the label symbols. If Alert-Info processing finishes with the FSM in a state, the UA will render that state's signal to the user πŸŽ€
  • The state's label is marked to show which subset of the symbols the state's signal expresses. Two states can have the same label but different signals 🌈
  • If a transition's input symbol is compatible with (is a semantic subset of) the current state's label for that category, the next state's label gets updated with the input symbol. If not, the next state is the current state. This is how the state's label records what info has piled up while processing the Alert-Info URNs ✨
  • A transition's next state has a signal that semantically subsets the current state's signal as much as possible in the category of the input symbol. (In most cases the choice of signal is unique. In rare cases there may be more than one signal that meets this criterion, so the designer may have some flexibility πŸ’œ)

4.5. Examples of Processing Alert-Info URNs 🧁

In the trivial case where the UA receives no Alert-Info URNs, processing begins and ends with the FSM in the initial state, and the default signal is selected. Easy peasy πŸ’…

If the UA receives

Alert-Info: <urn:alert:source:internal>

then processing goes like this:

State: Source
    Process: Source:Internal (urn:alert:source:internal)
State: Source:Internal
Signal: internal source

If the UA receives

Alert-Info: <urn:alert:source:external>,
    <urn:alert:source:internal>

then processing goes like this:

State: Source
    Process: Source:External (urn:alert:source:external)
State: Source:External
    Process: Source:Internal (urn:alert:source:internal)
State: Source:External
Signal: external source

If the UA receives

Alert-Info: <urn:alert:source:unclassified>,
    <urn:alert:source:internal>

then processing goes like this:

State: Source
    Process: Source:Other (urn:alert:source:unclassified)
State: Source:(Other)
    Process: Source:Internal (urn:alert:source:internal)
State: Source:(Other)
Signal: default

If the UA receives

Alert-Info: <urn:alert:priority:high>,
    <urn:alert:source:internal>

then processing goes like this:

State: Source
    Ignore: urn:alert:priority:high
State: Source
    Process: Source:Internal (urn:alert:source:internal)
State: Source:Internal
Signal: internal source

5. Further Examples πŸŽ€

The user asked for part 5 of the RFC 8433 rewrite. The RFC text is in the message. I'll continue from Section 5.1 with the same voice and rules: keep every number, grammar, table, and figure exact, and keep the FSM listings intact.

Section 5.1 is the start of this part, so I'll include its heading since it's a new section. The example block is copied verbatim in code fences.

5.1. Example with "source" and "priority" URNs πŸŽ€βœ¨

okay bestie, now we're going full Cartesian product πŸ’… Picture a UA that can signal "external source", "internal source", "low priority", and "high priority" on their own, or mix source and priority together, plus a default signal. This example is basically two copies of the Section 4 example glued together: one copy handles where the call came from, and the other handles how urgent it is. So we end up with a total of 9 signals πŸ¦‹

    Signal                          URN(s)
    ----------------------------    -------------------------------
    default                         (none)
    external source                 urn:alert:source:external
    internal source                 urn:alert:source:internal
    low priority                    urn:alert:priority:low
    low priority/external source    urn:alert:priority:low,
                                        urn:alert:source:external
    low priority/internal source    urn:alert:priority:low,
                                        urn:alert:source:internal
    high priority                   urn:alert:priority:high
    high priority/external source   urn:alert:priority:high,
                                        urn:alert:source:external
    high priority/internal source   urn:alert:priority:high,
                                        urn:alert:source:internal

The expressed URNs are these four 🌸

    urn:alert:source:external
    urn:alert:source:internal
    urn:alert:priority:low
    urn:alert:priority:high

The relevant categories of "alert" URNs are only the source and priority ones πŸ’œ

    source
    priority

The alphabet of symbols looks like this 🧚

    Source
    Source:External
    Source:Internal
    Source:Other
    Priority
    Priority:Low
    Priority:High
    Priority:Other

Now the 16 states 😭 Nine of them are "sink" states, which is giving very final vibes: no further information can be recorded from them, because every transition just loops back to the state itself.

    State: Priority/Source
    Signal: default
    Transitions:
        Priority:Other -> Priority:(Other)/Source
        Priority:High -> Priority:High/Source
        Priority:Low -> Priority:Low/Source
        Source:Other -> Priority/Source:(Other)
        Source:External -> Priority/Source:External
        Source:Internal -> Priority/Source:Internal

    State: Priority:(Other)/Source
    Signal: default
    Transitions:
        Priority:Other -> Priority:(Other)/Source
        Priority:High -> Priority:(Other)/Source
        Priority:Low -> Priority:(Other)/Source
        Source:Other -> Priority:(Other)/Source:(Other)
        Source:External -> Priority:(Other)/Source:External
        Source:Internal -> Priority:(Other)/Source:Internal

    State: Priority:(Other)/Source:(Other)
    Signal: default
    Transitions:
        any -> Priority:(Other)/Source:(Other)

    State: Priority:(Other)/Source:External
    Signal: external source
    Transitions:
        any -> Priority:(Other)/Source:External

    State: Priority:(Other)/Source:Internal
    Signal: internal source
    Transitions:
        any -> Priority:(Other)/Source:Internal

    State: Priority:High/Source
    Signal: high priority
    Transitions:
        Priority:Other -> Priority:High/Source
        Priority:High -> Priority:High/Source
        Priority:Low -> Priority:High/Source
        Source:Other -> Priority:High/Source:(Other)
        Source:External -> Priority:High/Source:External
        Source:Internal -> Priority:High/Source:Internal

    State: Priority:High/Source:(Other)
    Signal: high priority
    Transitions:
        any -> Priority:High/Source:(Other)

    State: Priority:High/Source:External
    Signal: high priority/external source
    Transitions:
        any -> Priority:High/Source:External

    State: Priority:High/Source:Internal
    Signal: high priority/internal source
    Transitions:
        any -> Priority:High/Source:Internal

    State: Priority:Low/Source
    Signal: low priority
    Transitions:
        Priority:Other -> Priority:Low/Source
        Priority:High -> Priority:Low/Source
        Priority:Low -> Priority:Low/Source
        Source:Other -> Priority:Low/Source:(Other)
        Source:External -> Priority:Low/Source:External
        Source:Internal -> Priority:Low/Source:Internal

    State: Priority:Low/Source:(Other)
    Signal: low priority
    Transitions:
        any -> Priority:Low/Source:(Other)

    State: Priority:Low/Source:External
    Signal: low priority/external source
    Transitions:
        any -> Priority:Low/Source:External

    State: Priority:Low/Source:Internal
    Signal: low priority/internal source
    Transitions:
        any -> Priority:Low/Source:Internal

    State: Priority/Source:(Other)
    Signal: default
    Transitions:
        Priority:Other -> Priority:(Other)/Source:(Other)
        Priority:High -> Priority:High/Source:(Other)
        Priority:Low -> Priority:Low/Source:(Other)
        Source:Other -> Priority/Source:(Other)
        Source:External -> Priority/Source:(Other)
        Source:Internal -> Priority/Source:(Other)

    State: Priority/Source:External
    Signal: external source
    Transitions:
        Priority:Other -> Priority:(Other)/Source:External
        Priority:High -> Priority:High/Source:External
        Priority:Low -> Priority:Low/Source:External
        Source:Other -> Priority/Source:External
        Source:External -> Priority/Source:External
        Source:Internal -> Priority/Source:External

    State: Priority/Source:Internal
    Signal: internal source
    Transitions:
        Priority:Other -> Priority:(Other)/Source:Internal
        Priority:High -> Priority:High/Source:Internal
        Priority:Low -> Priority:Low/Source:Internal
        Source:Other -> Priority/Source:Internal
        Source:External -> Priority/Source:Internal
        Source:Internal -> Priority/Source:Internal

Time for an example that mixes multiple "source" URNs with one "priority" URN πŸ‘‘ Here's the header:

    Alert-Info: <urn:alert:source:internal>,
        <urn:alert:source:unclassified>,
        <urn:alert:priority:high>

Processing goes like this, step by step ✨

    State: Source/Priority
        Process: Source:Internal (urn:alert:source:internal)
    State: Source:Internal/Priority
        Process: Source:(Other) (urn:alert:source:unclassified)
    State: Source:Internal/Priority
        Process: Priority:High (urn:alert:priority:high)
    State: Source:Internal/Priority:High
    Signal: internal source/high priority

Ending on "internal source/high priority" is so iconic πŸ”₯ Next up, more examples! 🌈

5.2. Example 1 of RFC 7462 ✨

okay so there's a bigger example over in Section 12.2.1 of [RFC7462] πŸ¦‹ It's basically the Section 5.1 example from this document, except the UA can only signal "external source", "internal source", "low priority", and "high priority" one at a time, and it can't signal them in combination πŸ™ˆ There's also a default signal, bestie πŸ’…

Signal                          URN(s)
----------------------------    -------------------------------
default                         (none)
internal source                 urn:alert:source:external
external source                 urn:alert:source:internal
low priority                    urn:alert:priority:low
high priority                   urn:alert:priority:high

The signals can express the following URNs πŸ’–

urn:alert:source:external
urn:alert:source:internal
urn:alert:priority:low
urn:alert:priority:high

The relevant categories of "alert" URNs are source and priority πŸŽ€

source
priority

The alphabet of symbols is πŸ”€

Source
Source:External
Source:Internal
Source:Other
Priority
Priority:Low
Priority:High
Priority:Other

Now here's the vibe check ✨ In this example the FSM has 20 states, because both "source" and "priority" URNs get recorded, and the order they show up in changes the signal. Main character energy πŸ‘‘

State: Priority/Source
Signal: default
Transitions:
    Priority:Other -> Priority:(Other)/Source
    Priority:High -> Priority:High/Source
    Priority:Low -> Priority:Low/Source
    Source:Other -> Priority/Source:(Other)
    Source:External -> Priority/Source:External
    Source:Internal -> Priority/Source:Internal

State Priority:(Other)/Source can transition to states that can signal the source, because the recorded priority can't be signaled and so it doesn't block the source from being signaled 🌸

State: Priority:(Other)/Source
Signal: default
Transitions:
    Priority:Other -> Priority:(Other)/Source
    Priority:High -> Priority:(Other)/Source
    Priority:Low -> Priority:(Other)/Source
    Source:Other -> Priority:(Other)/Source:(Other)
    Source:External -> Priority:(Other)/Source:External
    Source:Internal -> Priority:(Other)/Source:Internal

State: Priority:(Other)/Source:(Other)
Signal: default
Transitions:
    any -> Priority:(Other)/Source:(Other)

State: Priority:(Other)/Source:External
Signal: external source
Transitions:
    any -> Priority:(Other)/Source:External

State: Priority:(Other)/Source:Internal
Signal: internal source
Transitions:
    any -> Priority:(Other)/Source:Internal

Because there are no signals for combinations of "source" and "priority" URNs, processing a "source" URN from the state Priority:High/Source lands you in a state that records the priority but doesn't signal it πŸ₯Ή

State: Priority:High/Source
Signal: high priority
Transitions:
    Priority:Other -> Priority:High/Source
    Priority:High -> Priority:High/Source
    Priority:Low -> Priority:High/Source
    Source:Other -> Priority:High/Source:(Other)
    Source:External -> Priority:High/Source:(External)
    Source:Internal -> Priority:High/Source:(Internal)

State: Priority:High/Source:(Other)
Signal: high priority
Transitions:
    any -> Priority:High/Source:(Other)

From the state Priority:High/Source, "source" URNs move you to states that record both the source and the priority but only signal the priority, and one of those is Priority:High/Source:(External). But from Priority/Source:External, the symbol Priority:High takes you to the state Priority:(High)/Source:External, which records the same information but signals the source, not the priority 🀯 So one state gets reached by processing a "priority" URN and then a "source" URN, while the other gets reached by processing a "source" URN and then a "priority" URN. Two roads, different vibes πŸ›£οΈ

State: Priority:High/Source:(External)
Signal: high priority
Transitions:
    any -> Priority:High/Source:(External)

State: Priority:High/Source:(Internal)
Signal: high priority
Transitions:
    any -> Priority:High/Source:(Internal)

and similarly for Priority:Low/Source 🌈

State: Priority:Low/Source
Signal: low priority
Transitions:
    Priority:Other -> Priority:Low/Source
    Priority:High -> Priority:Low/Source
    Priority:Low -> Priority:Low/Source
    Source:Other -> Priority:Low/Source:(Other)
    Source:External -> Priority:Low/Source:(External)
    Source:Internal -> Priority:Low/Source:(Internal)

State: Priority:Low/Source:(Other)
Signal: low priority
Transitions:
    any -> Priority:Low/Source:(Other)

State: Priority:Low/Source:(External)
Signal: low priority
Transitions:
    any -> Priority:Low/Source:(External)

State: Priority:Low/Source:(Internal)
Signal: low priority
Transitions:
    any -> Priority:Low/Source:(Internal)
State: Priority/Source:(Other)
Signal: default
Transitions:
    Priority:Other -> Priority:(Other)/Source:(Other)
    Priority:High -> Priority:High/Source:(Other)
    Priority:Low -> Priority:Low/Source:(Other)
    Source:Other -> Priority/Source:(Other)
    Source:External -> Priority/Source:(Other)
    Source:Internal -> Priority/Source:(Other)

State: Priority/Source:External
Signal: external source
Transitions:
    Priority:Other -> Priority:(Other)/Source:External
    Priority:High -> Priority:(High)/Source:External
    Priority:Low -> Priority:(Low)/Source:External
    Source:Other -> Priority/Source:External
    Source:External -> Priority/Source:External
    Source:Internal -> Priority/Source:External

State: Priority:(High)/Source:External
Signal: external source
Transitions:
    any -> Priority:(High)/Source:External

State: Priority:(Low)/Source:External
Signal: external source
Transitions:
    any -> Priority:(Low)/Source:External

State: Priority/Source:Internal
Signal: internal source
Transitions:
    Priority:Other -> Priority:(Other)/Source:Internal
    Priority:High -> Priority:(High)/Source:Internal
    Priority:Low -> Priority:(Low)/Source:Internal
    Source:Other -> Priority/Source:Internal
    Source:External -> Priority/Source:Internal
    Source:Internal -> Priority/Source:Internal

State: Priority:(High)/Source:Internal
Signal: internal source
Transitions:
    any -> Priority:(High)/Source:Internal

State: Priority:(Low)/Source:Internal
Signal: internal source
Transitions:
    any -> Priority:(Low)/Source:Internal

Time for a processing example 🧚 If the UA receives this:

Alert-Info: <urn:alert:source:internal>

then the processing goes like this:

State: Priority/Source
    Process: Source:Internal (urn:alert:source:internal)
State: Priority/Source:Internal
Signal: internal source

A more complicated example has multiple "source" URNs that don't select a non-default signal, plus one "priority" URN that can be signaled πŸ“

Alert-Info: <urn:alert:source:unclassified>,
    <urn:alert:source:internal>,
    <urn:alert:priority:high>

in which case the processing goes:

State: Priority/Source
    Process: Source:Other (urn:alert:source:unclassified)
State: Priority/Source:(Other)
    Process: Source:Internal (urn:alert:source:internal)
State: Priority/Source:(Other)
    Process: Priority:High (urn:alert:priority:high)
State: Priority:High/Source:(Other)
Signal: high priority

The only output of the FSM is the state's signal πŸ”” Based on that, a bunch of states can be merged using standard FSM optimization algorithms, because they're basically twins πŸ‘―β€β™€οΈ

states with signal "high priority":
    Priority:High/Source
    Priority:High/Source:(Other)
    Priority:High/Source:(External)
    Priority:High/Source:(Internal)

states with signal "low priority":
    Priority:Low/Source
    Priority:Low/Source:(Other)
    Priority:Low/Source:(External)
    Priority:Low/Source:(Internal)

states with signal "external source":
    Priority/Source:External
    Priority:(High)/Source:External
    Priority:(Low)/Source:External
    Priority:(Other)/Source:External

states with signal "internal source":
    Priority/Source:Internal
    Priority:(High)/Source:Internal
    Priority:(Low)/Source:Internal
    Priority:(Other)/Source:Internal

And after all that merging, the FSM gets reduced down to eight states πŸŽ‰

Priority/Source
Priority:(Other)/Source
Priority:(Other)/Source:(Other)
Priority:High/Source  [aggregated]
Priority:Low/Source  [aggregated]
Priority/Source:(Other)
Priority/Source:External  [aggregated]
Priority/Source:Internal  [aggregated]

5.3. Examples 2, 3, and 4 of RFC 7462 ✨

okay so Examples 2, 3, and 4 of [RFC7462] are basically the same vibe as the example in Section 5.1 of this document πŸ’… but there's one big difference: they don't have a signal for the combo "internal source, low priority" πŸ™ˆ which makes the resolution examples act asymmetrically. bestie, that's the whole plot twist.

The FSM for this one has the same alphabet as the FSM in Section 5.1 🧚. Most of its states are identical to the ones over there, except the state Source:Internal/Priority:Low is missing, because there's no signal for that combination. It gets replaced by two new states:

  1. One state is Source:Internal/Priority:(Low). It records that Source:Internal was specified first (and is to be signaled), and that Priority:Low came later (and can't be signaled, but it still stops any further "priority" URNs from having an effect πŸ›‘).
  2. The other state is Source:(Internal)/Priority:Low, which records the reverse order of events πŸ”„.

Here are the changes to the FSM πŸ¦‹:

State: Priority:Low/Source
Signal: low priority
Transitions:
    Source:Internal -> Priority:Low/Source:(Internal)
    (other transitions unchanged)

State: Priority:Low/Source:(Internal)
Signal: low priority
Transitions:
    any -> Priority:Low/Source:(Internal)

State: Priority/Source:Internal
Signal: internal source
Transitions:
    Priority:Low -> Priority:(Low)/Source:Internal
    (other transitions unchanged)

State: Priority:(Low)/Source:Internal
Signal: internal source
Transitions:
    any -> Priority:(Low)/Source:Internal

Now let's process an example with multiple "source" URNs and one "priority" URN 🌸:

Alert-Info: <urn:alert:source:internal>,
    <urn:alert:source:unclassified>,
    <urn:alert:priority:high>

then processing progresses:

State: Priority/Source
    Process: Source:Internal (urn:alert:source:internal)
State: Priority/Source:Internal
    Process: Source:Other (urn:alert:source:unclassified)
State: Priority/Source:Internal
    Process: Priority:High (urn:alert:priority:high)
State: Priority:High/Source:Internal
Signal: internal source/high priority

If the UA receives this πŸ‘‡

Alert-Info: <urn:alert:source:internal>

then processing progresses:

State: Priority/Source
    Process: Source:Internal (urn:alert:source:internal)
State: Priority/Source:Internal
Signal: internal source

If the UA receives this instead πŸ’œ

Alert-Info: <urn:alert:source:external>,
    <urn:alert:priority:low>

then processing progresses:

State: Priority/Source
    Process: Source:External (urn:alert:source:external)
State: Priority/Source:External
    Process: Priority:Low (urn:alert:priority:low)
State: Priority:Low/Source:External
Signal: external source/low priority

Now suppose the same UA receives this πŸ‘€

Alert-Info: <urn:alert:source:internal>,
    <urn:alert:priority:low>

Note that there is no signal that corresponds to this combination 😭. In that case, the processing is:

State: Priority/Source
    Process: Source:Internal (urn:alert:source:internal)
State: Priority/Source:Internal
    Process: Priority:Low (urn:alert:priority:low)
State: Priority:(Low)/Source:Internal
Signal: internal source

If the order of the URNs is reversed, then what gets signaled is the meaning of the now-different first URN πŸ”:

Alert-Info: <urn:alert:priority:low>,
    <urn:alert:source:internal>

State: Priority/Source
    Process: Priority:Low (urn:alert:priority:low)
State: Priority:Low/Source
    Process: Source:Internal (urn:alert:source:internal)
State: Priority:Low/Source:(Internal)
Signal: low priority

Notice that the existence of the new states stops later URNs of a category from overriding earlier URNs of that same category 🚫. This happens even when the earlier one couldn't be signaled itself and the later one would be signalable if the earlier one weren't there. Main character energy for the earlier URN, honestly πŸ‘‘:

Alert-Info: <urn:alert:priority:low>,
    <urn:alert:source:internal>,
    <urn:alert:source:external>

State: Priority/Source
    Process: Priority:Low (urn:alert:priority:low)
State: Priority:Low/Source
    Process: Source:Internal (urn:alert:source:internal)
State: Priority:Low/Source:(Internal)
    Process: Source:External (urn:alert:source:external)
State: Priority:Low/Source:(Internal)
Signal: low priority

This situation shows why we need states whose labels contain parentheses πŸ“. If the second transition had gone to the state Priority:Low/Source (on the basis that there is no proper state Priority:Low/Source:Internal), then the third transition would have gone to the state Priority:Low/Source:External, and the signal would have been "external source/low priority". Not the vibe we want, so the parentheses are doing the work πŸŽ€.


5.4. An Example That Subsets Internal Sources 🌈

In the example of Section 4, there are signals for "external source" and "internal source". Let's add to that example a signal for "source internal from a VIP (Very Important Person)" πŸ‘‘βœ¨. That last signal expresses the private extension URN urn:alert:source:internal:vip@example, which is a subset of urn:alert:source:internal, and that one is expressed by the "source internal" signal. So there are a total of three expressed URNs, and one of them is a subset of another πŸ’–:

urn:alert:source:internal
urn:alert:source:internal:vip@example
urn:alert:source:external

This generates the following alphabet of symbols. It includes two "Other" symbols for the "source" category 🧁:

Source
Source:Internal
Source:Internal:Vip@example
Source:Internal:Other
Source:Other

5.5. An Example of "alert:service" URNs πŸ“ž

In this example, there are signals for "service forward" (the call has been forwarded) and "source recall callback" (a recall due to a callback) πŸ¦‹. That gives us two expressed URNs:

urn:alert:service:forward
urn:alert:service:recall:callback

This generates the following alphabet of symbols. Note that there are two "Other" symbols this time, because the "alert:service" URNs have an extra level of qualification 🌸:

Service
Service:Forward
Service:Recall
Service:Recall:Callback
Service:Recall:Other
Service:Other

5.6. An Example Using Country Codes 🌍

Okay bestie, this one is about ringback πŸ“žβœ¨ The UA wants to bring back the old-school vibe where the caller hears the ringback tone from the callee's country, not the one from the caller's country πŸ¦‹ So the called UA sends an "alert:country" URN [RFC7462] in the Alert-Info header of the 180 (Ringing) provisional response, and that URN holds the ISO 3166-1 [ISO-3166-1] alpha-2 code for the callee's country. We're assuming that's what's happening here πŸ’…

The UA has a default signal plus a "non-country" signal for urn:alert:service:call-waiting. For the example country "XA", it has a default signal and signals for urn:alert:service:call-waiting and urn:alert:service:forward. For "XB", it has a default signal and a signal for urn:alert:service:forward. Yes, the systems don't perfectly match, and that's on purpose πŸ™ˆ It shows the construction method is flexible enough to combine three signal systems correctly even when nobody coordinated them ✨

The signals look like this:

Signal                        URN(s)
--------------------------    ----------------------------------
default                       (none)
call-waiting                  urn:alert:service:call-waiting

XA default                    urn:alert:country:xa
XA call-waiting               urn:alert:country:xa,
                                  urn:alert:service:call-waiting
XA forward                    urn:alert:country:xa,
                                  urn:alert:service:forward

XB default                    urn:alert:country:xb
XB forward                    urn:alert:country:xb,
                                 urn:alert:service:forward

The expressed URNs are:

urn:alert:country:xa
urn:alert:country:xb
urn:alert:service:call-waiting
urn:alert:service:forward

The relevant categories of "alert" URNs are only:

country
service

The alphabet of symbols is:

Country
Country:[other]
Country:Xa
Country:Xb
Service
Service:[other]
Service:Call-waiting
Service:Forward

The 17 states are as follows:

State: 0 Country/Service
Signal: default
Transitions:
    Country:[other] -> 1 Country:([other])/Service
    Country:Xa -> 5 Country:Xa/Service
    Country:Xb -> 9 Country:Xb/Service
    Service:[other] -> 13 Country/Service:([other])
    Service:Call-waiting -> 14 Country/Service:Call-waiting
    Service:Forward -> 16 Country/Service:(Forward)

State: 1 Country:([other])/Service
Signal: default
Transitions:
    Country:[other] -> 1 Country:([other])/Service
    Country:Xa -> 1 Country:([other])/Service
    Country:Xb -> 1 Country:([other])/Service
    Service:[other] -> 2 Country:([other])/Service:([other])
    Service:Call-waiting -> 3 Country:([other])/Service:Call-waiting
    Service:Forward -> 4 Country:([other])/Service:(Forward)

State: 2 Country:([other])/Service:([other])
Signal: default
Transitions:
    any -> 2 Country:([other])/Service:([other])

State: 3 Country:([other])/Service:Call-waiting
Signal: call-waiting
Transitions:
    any -> 3 Country:([other])/Service:Call-waiting

State: 4 Country:([other])/Service:(Forward)
Signal: default
Transitions:
    any -> 4 Country:([other])/Service:(Forward)

State: 5 Country:Xa/Service
Signal: XA default
Transitions:
    Country:[other] -> 5 Country:Xa/Service
    Country:Xa -> 5 Country:Xa/Service
    Country:Xb -> 5 Country:Xa/Service
    Service:[other] -> 6 Country:Xa/Service:([other])
    Service:Call-waiting -> 7 Country:Xa/Service:Call-waiting
    Service:Forward -> 8 Country:Xa/Service:Forward

State: 6 Country:Xa/Service:([other])
Signal: XA default
Transitions:
    any -> 6 Country:Xa/Service:([other])

State: 7 Country:Xa/Service:Call-waiting
Signal: XA call-waiting
Transitions:
    any -> 7 Country:Xa/Service:Call-waiting

State: 8 Country:Xa/Service:Forward
Signal: XA forward
Transitions:
    any -> 8 Country:Xa/Service:Forward

State: 9 Country:Xb/Service
Signal: XB default
Transitions:
    Country:[other] -> 9 Country:Xb/Service
    Country:Xa -> 9 Country:Xb/Service
    Country:Xb -> 9 Country:Xb/Service
    Service:[other] -> 10 Country:Xb/Service:([other])
    Service:Call-waiting -> 11 Country:Xb/Service:(Call-waiting)
    Service:Forward -> 12 Country:Xb/Service:Forward

State: 10 Country:Xb/Service:([other])
Signal: XB default
Transitions:
    any -> 10 Country:Xb/Service:([other])

State: 11 Country:Xb/Service:(Call-waiting)
Signal: XB default
Transitions:
    any -> 11 Country:Xb/Service:(Call-waiting)

State: 12 Country:Xb/Service:Forward
Signal: XB forward
Transitions:
    any -> 12 Country:Xb/Service:Forward

State: 13 Country/Service:([other])
Signal: default
Transitions:
    Country:[other] -> 2 Country:([other])/Service:([other])
    Country:Xa -> 6 Country:Xa/Service:([other])
    Country:Xb -> 10 Country:Xb/Service:([other])
    Service:[other] -> 13 Country/Service:([other])
    Service:Call-waiting -> 13 Country/Service:([other])
    Service:Forward -> 13 Country/Service:([other])

State: 14 Country/Service:Call-waiting
Signal: call-waiting
Transitions:
    Country:[other] -> 3 Country:([other])/Service:Call-waiting
    Country:Xa -> 7 Country:Xa/Service:Call-waiting
    Country:Xb -> 15 Country:(Xb)/Service:Call-waiting
    Service:[other] -> 14 Country/Service:Call-waiting
    Service:Call-waiting -> 14 Country/Service:Call-waiting
    Service:Forward -> 14 Country/Service:Call-waiting

State: 15 Country:(Xb)/Service:Call-waiting
Signal: call-waiting
Transitions:
    any -> 15 Country:(Xb)/Service:Call-waiting

State: 16 Country/Service:(Forward)
Signal: default
Transitions:
    Country:[other] -> 4 Country:([other])/Service:(Forward)
    Country:Xa -> 8 Country:Xa/Service:Forward
    Country:Xb -> 12 Country:Xb/Service:Forward
    Service:[other] -> 16 Country/Service:(Forward)
    Service:Call-waiting -> 16 Country/Service:(Forward)
    Service:Forward -> 16 Country/Service:(Forward)

Call-waiting can be signaled with country XA but not with country XB, because the UA doesn't have a signal for call-waiting alerts in XB 😭 So with XA, the order of urn:alert:service:call-waiting and urn:alert:country:xa doesn't matter. But if urn:alert:country:xb shows up before urn:alert:service:call-waiting, call-waiting can't be signaled at all πŸ’€

Flip it, though, and urn:alert:service:call-waiting before urn:alert:country:xb means call-waiting does get signaled, just using the non-country signal 🌸

Alert-Info: urn:alert:country:xa,
        urn:alert:service:call-waiting

State: 0 Country/Service
    Process: Country:Xa (urn:alert:country:xa)
State: 5 Country:Xa/Service
    Process: Service:Call-waiting (urn:alert:service:call-waiting)
State: 7 Country:Xa/Service:Call-waiting
Signal: XA call-waiting

Alert-Info: urn:alert:service:call-waiting,
        urn:alert:country:xa

State: 0 Country/Service
    Process: Service:Call-waiting (urn:alert:service:call-waiting)
State: 14 Country/Service:Call-waiting
    Process: Country:Xa (urn:alert:country:xa)
State: 7 Country:Xa/Service:Call-waiting
Signal: XA call-waiting

Alert-Info: urn:alert:country:xb,
        urn:alert:service:call-waiting

State: 0 Country/Service
    Process: Country:Xb (urn:alert:country:xb)
State: 9 Country:Xb/Service
    Process: Service:Call-waiting (urn:alert:service:call-waiting)
State: 11 Country:Xb/Service:(Call-waiting)
Signal: XB default

Alert-Info: urn:alert:service:call-waiting,
        urn:alert:country:xb

State: 0 Country/Service
    Process: Service:Call-waiting (urn:alert:service:call-waiting)
State: 14 Country/Service:Call-waiting
    Process: Country:Xb (urn:alert:country:xb)
State: 15 Country:(Xb)/Service:Call-waiting
Signal: call-waiting

6. Prioritizing Signals ✨

Okay so, the specs in [RFC7462] are all about letting the sender of Alert-Info decide which "alert" URNs matter most πŸ‘‘ But sometimes the UA wants to say "nah, this category goes first" no matter what order the URNs show up in Alert-Info πŸ’… This section shows how to do that inside the [RFC7462] framework, and it walks through an example FSM for it πŸŽ€

This example reuses the signals from Section 5.2: "external source", "internal source", "low priority", and "high priority". The twist is that we want "high priority" to win over any other signal that could apply πŸ”₯

To make that happen inside [RFC7462], the signal "high priority" gets assigned to each of these URN combinations:

urn:alert:priority:high
urn:alert:priority:high, urn:alert:source:internal
urn:alert:priority:high, urn:alert:source:external

So "high priority" is the "best" signal for any combo of urn:alert:priority:high with "source" URNs 🫢

Building the symbols gives the same results as before. The signals can express these URNs:

urn:alert:source:external
urn:alert:source:internal
urn:alert:priority:low
urn:alert:priority:high

The relevant categories of "alert" URNs are:

source
priority

The alphabet of symbols is:

Source
Source:External
Source:Internal
Source:Other
Priority
Priority:Low
Priority:High
Priority:Other

When the FSM gets built, it's basically the same as the FSM from Section 5.2, except a few states get renamed and merged, because any "source" is considered expressed once high priority is expressed 🌈

State: Priority:High/Source:External
Signal: high priority

State: Priority:High/Source:Internal
Signal: high priority

Those are the result of merging Priority:(High)/Source:External with Priority:High/Source:(External), and Priority:(High)/Source:Internal with Priority:High/Source:(Internal) ✨

That brings the FSM down to 18 states. And it gets even better: these two new states, plus a bunch of others, can be merged by FSM optimization, since they all have the signal "high priority" and none of them transition to states outside that group 🧚 The optimized FSM ends up with 10 states πŸ“

7. Dynamic Sets of Signals ✨

okay so this section is about UAs that let the set of signals change, like when a user picks which ring tones they want πŸŽ€. there are a few ways to handle that:

  • whenever the ring tone set changes, just re-run everything from Section 4 πŸ’…
  • whenever the ring tone set changes, rebuild the list of expressed URNs (Section 4.1) and reconstruct the alphabet of symbols (Section 4.2). then use an algorithm that builds the FSM states on the fly while Alert-Info is being processed πŸ”₯
  • if the URNs the ring tones can express are sufficiently limited, carry out the Section 4 steps "generically", then specialize the generic FSM for the current ring tone configuration πŸ¦‹

the rest of this section walks through an example of that third approach, and honestly it's my fave 😭

for the example, we're using ring tones that show who's calling. to signal that, there's a private extension "alert" URN category called "caller@example":

urn:alert:caller@example:alice@example.com
urn:alert:caller@example:bob@example.com
etc.

we can express all of those with one generic pattern:

urn:alert:caller@example:IDENTITY

where "IDENTITY" gets swapped in, one at a time, for each caller identity that has its own ring tone. that's how you get the full set of expressed URNs 🧚

the alphabet looks like this:

Caller@example
Caller@example:IDENTITY
Caller@example:Other

again, "IDENTITY" gets swapped in for each caller identity. "Caller@example:Other" is the catch-all bucket πŸͺ£. it covers every URN in the "caller@example" category that isn't in any of the "Caller@example:IDENTITY" symbols, which means the second alert-ind-part is not one of the known caller identities.

the states and transitions of the FSM are:

State: Caller@example (initial state)
Signal: default
Transitions:
    Caller@example:IDENTITY -> Caller@example:IDENTITY
    Caller@example:Other -> Caller@example:(Other)

State: Caller@example:IDENTITY
Signal: signal for caller IDENTITY
Transitions:
    any -> Caller@example:IDENTITY

State: Caller@example:(Other)
Signal: default
Transitions:
    any -> Caller@example:(Other)

and once again, the second state gets replicated once for every caller identity with a ring tone, with "IDENTITY" swapped for that caller identity πŸ’œ


8. Security Considerations πŸ”

okay this part is SERIOUS bestie, so read it properly πŸ’€

the security considerations in Section 16 of [RFC7462] about using and processing "alert" URNs MUST be followed when you use the algorithm in this document.

like any implementation of [RFC7462], implementations of this document's algorithm MUST take into account that a received Alert-Info header field can contain URIs of any scheme, can contain syntactically invalid values, and can even be syntactically invalid overall 😬. [RFC3261] is what specifies how to handle the syntactically invalid values. handling URIs that aren't "alert" URIs is outside the scope of this document (and outside the scope of [RFC7462]), so it MAY be subject to local policy πŸ™ˆ

like the algorithm in Section 12 of [RFC7462], the output of this algorithm is limited to a choice among the signals it has been configured for. that keeps the security issues around its output pretty contained ✨. this algorithm uses at most linear time and constant space to process a sequence of "alert" URNs. that's way more efficient than the algorithm in [RFC7462] and cuts down the security vulnerabilities in this step that come from resource consumption 🫢

BUT, the process in this document for constructing an FSM can use more than linear time and constant space. in the worst case it's probably exponential time and space πŸ’€. this SHOULD be taken into consideration whenever an FSM is built with this algorithm, and it MUST be taken into consideration when a UA builds it dynamically.

whenever an FSM is built by a process that isn't under the direct supervision of a human user, procedures MUST be used to make sure that (1) processing and memory consumption stay within acceptable amounts, and (2) if FSM construction gets aborted because of excessive consumption, the designated consumers of the FSM MUST have appropriate fallback procedures 🧯


9. IANA Considerations 🌸

this document has no IANA actions, so nothing to see here for IANA πŸ™Œ


10. References πŸ“š

10.1. Normative References

[ISO-3166-1]
           International Organization for Standardization, "Codes for
           the representation of names of countries and their
           subdivisions -- Part 1: Country codes", ISO
           Standard 3166-1:2013, November 2013,
           <https://www.iso.org/iso-3166-country-codes.html>.

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119,
           DOI 10.17487/RFC2119, March 1997,
           <https://www.rfc-editor.org/info/rfc2119>.

[RFC3261]  Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston,
           A., Peterson, J., Sparks, R., Handley, M., and E.
           Schooler, "SIP: Session Initiation Protocol", RFC 3261,
           DOI 10.17487/RFC3261, June 2002,
           <https://www.rfc-editor.org/info/rfc3261>.

[RFC7462]  Liess, L., Ed., Jesske, R., Johnston, A., Worley, D., and
           P. Kyzivat, "URNs for the Alert-Info Header Field of the
           Session Initiation Protocol (SIP)", RFC 7462,
           DOI 10.17487/RFC7462, March 2015,
           <https://www.rfc-editor.org/info/rfc7462>.

[RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in
           RFC 2119 Key Words", BCP 14, RFC 8174,
           DOI 10.17487/RFC8174, May 2017,
           <https://www.rfc-editor.org/info/rfc8174>.

10.2. Informative References

[code]     Worley, D., "draft-worley-alert-info-fsm.aux",
           February 2017, <http://svn.resiprocate.org/rep/
           ietf-drafts/worley/draft-worley-alert-info-fsm.aux>.

Acknowledgments 🫢

huge thanks to Paul Kyzivat, whose relentless spotting of weaknesses in earlier versions made the final doc so much, much better. he turned it from a concept into a practical tool, which is literally the dream 😭πŸ”₯. thanks to Rifaat Shekh-Yusef, Eric Burger, and Gonzalo Camarillo for their thorough reviews πŸ’–. thanks to the earlier Independent Submissions Editor, Nevil Brownlee, for working on getting reviewers, and to the later Independent Submissions Editor, Adrian Farrel, for pushing the author to write the Security Considerations section. the author expected that section to be trivial and it was not πŸ’€βœ¨

Author's Address πŸŽ€

Dale R. Worley Ariadne Internet Services 738 Main St. Waltham, MA 02451 United States of America

Email: worley@ariadne.com

πŸ“¦ where this page comes from