|
| 1 | +# FMSG-006 Indicators Standard |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +| Revision | Date | Summary | |
| 6 | +|----------|------------|---------------| |
| 7 | +| v0.1.0 | 2026-09-02 | Initial draft | |
| 8 | + |
| 9 | +This standard defines indicators: short-lived, unreliable signals such as |
| 10 | +"composing a reply" that a participant of a message shows to the message's |
| 11 | +other participants. Indicators travel host to host as UDP datagrams, are never |
| 12 | +acknowledged, and are never stored. |
| 13 | + |
| 14 | +## Requirements Language |
| 15 | + |
| 16 | +The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, |
| 17 | +**SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **NOT RECOMMENDED**, **MAY**, and |
| 18 | +**OPTIONAL** in this document are to be interpreted as described in BCP 14 |
| 19 | +([RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and |
| 20 | +[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174)) when, and only when, they |
| 21 | +appear in all capitals. |
| 22 | + |
| 23 | +## Scope |
| 24 | + |
| 25 | +This standard defines: |
| 26 | + |
| 27 | +- the indicator datagram and its UDP binding; |
| 28 | +- how the indicating participant is identified without placing an address on |
| 29 | + the wire; |
| 30 | +- the verification a receiving host performs before accepting a datagram; |
| 31 | +- the sending host's fan-out and rate limits; |
| 32 | +- how a participant's indicator state is derived and when it expires; and |
| 33 | +- a recommended vocabulary of indicator emoji. |
| 34 | + |
| 35 | +It does not change the fmsg host-to-host protocol or the message format, does |
| 36 | +not define a client API, and does not require a host to implement it. A host |
| 37 | +that does not implement this standard is unaffected: it does not listen, and |
| 38 | +datagrams sent to it are lost, which is the defined outcome. Client API |
| 39 | +exposure of indicators is expected in a future revision of |
| 40 | +[FMSG-003](fmsg-003-webapi.md). |
| 41 | + |
| 42 | +## Normative References |
| 43 | + |
| 44 | +- [fmsg Specification](../SPECIFICATION.md) v0.6.0 or later |
| 45 | +- [FMSG-001 TCP+TLS Transport and Binding Standard](fmsg-001-transport-and-binding.md) |
| 46 | +- [FMSG-005 Reactions Standard](fmsg-005-reactions.md) |
| 47 | +- [RFC 768: User Datagram Protocol](https://www.rfc-editor.org/rfc/rfc768) |
| 48 | +- [RFC 2827 (BCP 38): Network Ingress Filtering](https://www.rfc-editor.org/rfc/rfc2827) |
| 49 | +- [UTS #51: Unicode Emoji](https://www.unicode.org/reports/tr51/) |
| 50 | + |
| 51 | +## Terminology |
| 52 | + |
| 53 | +**subject** is the message an indicator refers to via its message hash. It may |
| 54 | +be a thread's first message, a reply, or an add-to batch message. |
| 55 | + |
| 56 | +**indicating participant** is the participant of the subject whose state the |
| 57 | +indicator conveys. |
| 58 | + |
| 59 | +**indicator state** is the emoji, if any, that an indicating participant |
| 60 | +currently shows on a subject, determined per |
| 61 | +[Indicator State](#indicator-state). |
| 62 | + |
| 63 | +**participant index** is the position of an address among the subject's wire |
| 64 | +fields, defined in [Participant Index](#participant-index). |
| 65 | + |
| 66 | +Other terms have the meanings given in the fmsg Specification. |
| 67 | + |
| 68 | +## Introduction |
| 69 | + |
| 70 | +This section is non-normative. |
| 71 | + |
| 72 | +Messaging clients show that someone is composing a reply, that an agent is |
| 73 | +working on one, or that a file is being attached. These signals are useful for |
| 74 | +a few seconds and worthless afterwards. Carrying them as fmsg messages would |
| 75 | +give them delivery guarantees, storage, and hashes they do not need and would |
| 76 | +fill every host's store with noise. Indicators are instead sent as single UDP |
| 77 | +datagrams: fire and forget, verified enough to prevent forgery, fanned out to |
| 78 | +whoever is connected at that moment, and then discarded. |
| 79 | + |
| 80 | +An indicator is the ephemeral twin of a reaction. [FMSG-005](fmsg-005-reactions.md) |
| 81 | +defines a participant's durable state on a message as a single emoji, empty to |
| 82 | +clear, latest wins. An indicator is the same shape with a lifetime: a single |
| 83 | +emoji that is a participant's transient state on a message, expiring unless |
| 84 | +refreshed. A client that recognises ✍️ shows the familiar composing bubble; one |
| 85 | +that does not still shows the emoji beside the participant's name. New kinds of |
| 86 | +indicator therefore need no change to this standard. |
| 87 | + |
| 88 | +No address appears in a datagram. The indicating participant is named by their |
| 89 | +position among the subject's participants, which only a host holding the |
| 90 | +subject can resolve. The subject's hash is likewise meaningful only to hosts |
| 91 | +that hold it. A datagram observed on the network reveals which two hosts are |
| 92 | +exchanging one and nothing more. |
| 93 | + |
| 94 | +## Datagram |
| 95 | + |
| 96 | +All integers are little-endian. Fields are read sequentially. |
| 97 | + |
| 98 | +| # | Field | Type | Notes | |
| 99 | +|---|-------|------|-------| |
| 100 | +| 1 | version | uint8 | 1 for this revision. | |
| 101 | +| 2 | hash | 32 bytes | Message hash of the subject, per the Specification's Computing Message Hash. | |
| 102 | +| 3 | index | uint16 | Participant index of the indicating participant, see [Participant Index](#participant-index). | |
| 103 | +| 4 | ttl | uint8 | Seconds the state remains valid unless refreshed. 0 clears the state immediately. | |
| 104 | +| 5 | data | uint8 length + UTF-8 | The indicator emoji, or empty to clear. Length MUST NOT exceed 64. | |
| 105 | + |
| 106 | +A datagram is exactly these fields; any trailing bytes make it invalid. A |
| 107 | +receiving host MUST silently drop a datagram it cannot parse, whose version it |
| 108 | +does not support, whose data length exceeds 64, or whose non-empty data is not |
| 109 | +a single emoji per [Emoji](#emoji). |
| 110 | + |
| 111 | +An empty data field with any _ttl_ clears the indicating participant's state. |
| 112 | +A _ttl_ of 0 with non-empty data is treated the same as empty data: the state |
| 113 | +is cleared. |
| 114 | + |
| 115 | +### Emoji |
| 116 | + |
| 117 | +Non-empty _data_ MUST be the UTF-8 encoding of exactly one element of the |
| 118 | +Unicode `RGI_Emoji` set defined by UTS #51, subject to the same rules and the |
| 119 | +same leniency as FMSG-005 [Emoji](fmsg-005-reactions.md#emoji). Implementations |
| 120 | +compare emoji as exact byte strings; ✍ and ✍️ are different indicators. |
| 121 | + |
| 122 | +### Participant Index |
| 123 | + |
| 124 | +The participants of the subject are the addresses in its wire fields, in |
| 125 | +field order: |
| 126 | + |
| 127 | +1. _from_; |
| 128 | +2. each address in _to_, in order; |
| 129 | +3. if the subject has _has add to_ set, _add to from_; |
| 130 | +4. if the subject has _has add to_ set, each address in _add to_, in order. |
| 131 | + |
| 132 | +The participant index is the zero-based position in that sequence: _from_ is |
| 133 | +0, the first _to_ address is 1, and so on. Because _add to from_ repeats an |
| 134 | +address already in _from_ or _to_, and an address MAY appear in both _to_ and |
| 135 | +_add to_, one address can have several indices. They are equivalent; a sender |
| 136 | +SHOULD use the lowest. A receiving host MUST drop a datagram whose index is |
| 137 | +out of range for the subject. |
| 138 | + |
| 139 | +A host holds every recipient of a stored message exactly as transmitted (the |
| 140 | +Specification's Verifying Message Stored), so any host holding the subject can |
| 141 | +resolve an index. A host that does not hold the subject cannot, and drops the |
| 142 | +datagram. |
| 143 | + |
| 144 | +## UDP Binding |
| 145 | + |
| 146 | +Indicators are sent as UDP datagrams to port 4930, the same port number |
| 147 | +FMSG-001 assigns to TCP. A host implementing this standard MUST listen on UDP |
| 148 | +port 4930 on every address in its `fmsg.<domain>` A and AAAA record set, and |
| 149 | +MUST send indicators from an address in that set, because receivers verify the |
| 150 | +source address against it. |
| 151 | + |
| 152 | +The Sending Host resolves `fmsg.<domain>` per the Specification's Domain |
| 153 | +Resolution and sends one datagram to one resolved address. It MAY alternate |
| 154 | +between addresses on successive datagrams. Nothing is ever sent in reply. A |
| 155 | +sending host MUST ignore ICMP errors beyond using them to pause sending to |
| 156 | +that host. |
| 157 | + |
| 158 | +Datagrams are not encrypted. Implementations MUST NOT place anything other |
| 159 | +than the fields above in a datagram. |
| 160 | + |
| 161 | +## Sending |
| 162 | + |
| 163 | +A participant's client tells its host that the participant has a state on a |
| 164 | +subject. The host is the Sending Host only when the indicating participant's |
| 165 | +address belongs to its domain; a host MUST NOT send an indicator for an address |
| 166 | +it does not host. |
| 167 | + |
| 168 | +The Sending Host MUST send the datagram to every unique participant domain of |
| 169 | +the subject other than its own domain. The subject's participant domains are |
| 170 | +the domains of every address in the sequence defined by |
| 171 | +[Participant Index](#participant-index). Participants on the Sending Host's own |
| 172 | +domain are informed through the host's client API directly, never over the |
| 173 | +wire. |
| 174 | + |
| 175 | +### Refresh and Clear |
| 176 | + |
| 177 | +A client SHOULD refresh a continuing state before its _ttl_ lapses, and SHOULD |
| 178 | +send a clearing datagram when the state ends, for example when the composed |
| 179 | +reply is sent or discarded. Both are best effort: a receiving host expires the |
| 180 | +state on its own when no refresh arrives. |
| 181 | + |
| 182 | +### Rate Limits |
| 183 | + |
| 184 | +Indicators are cheap to send and cheap to drop, so limits are enforced on the |
| 185 | +sending side, per receiving host, before anything reaches the network. A |
| 186 | +Sending Host MUST: |
| 187 | + |
| 188 | +- send at most one datagram per (subject, participant index) every |
| 189 | + INDICATOR_MIN_INTERVAL seconds, coalescing more frequent state changes into |
| 190 | + the latest; and |
| 191 | +- send at most INDICATOR_HOST_RATE datagrams per second to any one receiving |
| 192 | + domain, dropping the excess. |
| 193 | + |
| 194 | +| Variable | Description | Recommended | |
| 195 | +|----------|-------------|-------------| |
| 196 | +| INDICATOR_MIN_INTERVAL | Minimum seconds between datagrams for one participant on one subject. | 2 | |
| 197 | +| INDICATOR_HOST_RATE | Maximum datagrams per second to one receiving domain. | 20 | |
| 198 | +| INDICATOR_TTL_MAX | Longest _ttl_ a host accepts; larger values are clamped. | 120 | |
| 199 | + |
| 200 | +A clearing datagram (empty data) SHOULD be exempt from INDICATOR_MIN_INTERVAL |
| 201 | +so that an ended state clears promptly. |
| 202 | + |
| 203 | +## Receiving |
| 204 | + |
| 205 | +A Receiving Host performs the following steps for each datagram and MUST drop |
| 206 | +the datagram silently at the first step that fails. Steps are ordered so the |
| 207 | +cheapest checks run first. |
| 208 | + |
| 209 | +1. Enforce a per-source-address rate limit; drop the excess. |
| 210 | +2. Parse the datagram per [Datagram](#datagram). |
| 211 | +3. Look up the subject by _hash_ among the messages the host has stored, |
| 212 | + including add-to batch messages by batch hash (the Specification's |
| 213 | + Verifying Message Stored). Not found: drop. |
| 214 | +4. Resolve _index_ per [Participant Index](#participant-index). Out of range: |
| 215 | + drop. |
| 216 | +5. Resolve `fmsg.<domain>` for the domain of the address at _index_, per the |
| 217 | + Specification's Domain Resolution, and verify the datagram's source address |
| 218 | + is in the resolved set. DNS results SHOULD be cached. Not in set: drop. |
| 219 | +6. Clamp _ttl_ to INDICATOR_TTL_MAX. |
| 220 | +7. Deliver the indicator to every participant of the subject hosted on this |
| 221 | + domain other than the indicating participant, through the host's client |
| 222 | + API, and then discard it. |
| 223 | + |
| 224 | +A Receiving Host MUST NOT store an indicator beyond what delivery to currently |
| 225 | +connected clients requires, MUST NOT send anything in response, and MUST NOT |
| 226 | +generate a push notification or any other durable notification from an |
| 227 | +indicator. |
| 228 | + |
| 229 | +Because the hash is known only to hosts holding the subject and the source |
| 230 | +address must match the indicating participant's domain, forging an indicator |
| 231 | +requires both possession of the message and a spoofed source address that |
| 232 | +survives ingress filtering (BCP 38). That is the level of assurance an |
| 233 | +unacknowledged, unencrypted, disposable signal warrants, and no more. |
| 234 | + |
| 235 | +## Indicator State |
| 236 | + |
| 237 | +An indicating participant has at most one state on a subject. The most |
| 238 | +recently received datagram from that participant on that subject determines |
| 239 | +it: its _data_ is the state, empty means none, and the state expires _ttl_ |
| 240 | +seconds after receipt unless a later datagram replaces it. Datagrams may be |
| 241 | +lost or reordered; a client displays whatever the latest received datagram |
| 242 | +says and relies on expiry to correct anything stale. |
| 243 | + |
| 244 | +State is held wherever it is displayed, normally in the client. Hosts need not |
| 245 | +track it. |
| 246 | + |
| 247 | +## Recommended Indicators |
| 248 | + |
| 249 | +This section is non-normative. Any single emoji is a valid indicator; the |
| 250 | +following meanings are recommended so that clients interoperate for the common |
| 251 | +cases and can style them specially. Suggested TTLs assume the sender refreshes |
| 252 | +at INDICATOR_MIN_INTERVAL. |
| 253 | + |
| 254 | +| Emoji | Meaning | Suggested TTL | |
| 255 | +|-------|---------|---------------| |
| 256 | +| ✍️ | Composing a reply to the subject | 6 s | |
| 257 | +| ⏳ | Processing: an agent is working on a reply | 60 s | |
| 258 | +| 🎙️ | Recording a voice reply | 10 s | |
| 259 | +| 📎 | Attaching a file to a reply | 15 s | |
| 260 | +| 👀 | Viewing the subject | 30 s | |
| 261 | + |
| 262 | +Clients SHOULD show an indicator they do not recognise as the emoji itself |
| 263 | +beside the participant's address, and SHOULD render ✍️ as a composing |
| 264 | +indication. Clients SHOULD treat sending 👀 as opt-in, since it discloses |
| 265 | +attention rather than activity. |
| 266 | + |
| 267 | +## Interoperability |
| 268 | + |
| 269 | +| Peer | Behaviour | |
| 270 | +|------|-----------| |
| 271 | +| Host implementing this standard | Verifies and fans out to its connected participants. | |
| 272 | +| Host not implementing this standard | Does not listen on UDP 4930; datagrams are discarded by the network stack. The sender neither knows nor needs to. | |
| 273 | +| Host holding the subject but not the indicating participant's message hash form | Cannot occur: a host that holds the subject holds its hash. | |
| 274 | + |
| 275 | +Indicators require no change to the Specification or to FMSG-001. Firewalls |
| 276 | +in front of a host implementing this standard need UDP 4930 open inbound on |
| 277 | +the addresses in `fmsg.<domain>`, and outbound from them. |
| 278 | + |
| 279 | +## Security Considerations |
| 280 | + |
| 281 | +- **Forgery:** mitigated by the hash-as-capability and source-address checks |
| 282 | + in [Receiving](#receiving). A host on the same domain as a participant could |
| 283 | + forge that participant's indicators; it could also forge their messages, so |
| 284 | + this adds no new trust. |
| 285 | +- **Amplification and reflection:** none. A host never responds to a datagram, |
| 286 | + never resolves DNS before the cheaper hash lookup fails, and rate-limits per |
| 287 | + source address. |
| 288 | +- **Disclosure:** the wire carries a hash, an index and an emoji. An observer |
| 289 | + learns that two hosts exchanged an indicator, which the TCP connection for |
| 290 | + the eventual reply would reveal anyway. A host that later obtains the |
| 291 | + subject could correlate captured datagrams with it; the emoji is the only |
| 292 | + content exposed, and senders SHOULD NOT convey anything sensitive through |
| 293 | + their choice of it. |
| 294 | +- **Presence leakage:** 👀 and similar states disclose attention. Clients |
| 295 | + SHOULD require the user to opt in to sending them. |
| 296 | +- **Resource use:** the per-datagram work before the hash lookup is parsing |
| 297 | + only; the lookup is one indexed query. Hosts SHOULD bound the number of |
| 298 | + indicators fanned out per second per client. |
| 299 | + |
| 300 | +## Examples |
| 301 | + |
| 302 | +This section is non-normative. |
| 303 | + |
| 304 | +Alice at `example.com` sent a message to Bob at `example.edu` and Carol at |
| 305 | +`example.org`. Bob starts composing a reply. His host sends, to |
| 306 | +`fmsg.example.com` and `fmsg.example.org`, a datagram whose fields are: |
| 307 | + |
| 308 | +``` |
| 309 | +version 1 |
| 310 | +hash <message hash of Alice's message> |
| 311 | +index 1 (Bob is the first address in to) |
| 312 | +ttl 6 |
| 313 | +data "✍️" (length 6, bytes E2 9C 8D EF B8 8F) |
| 314 | +``` |
| 315 | + |
| 316 | +Alice's host looks up the hash, finds the message, resolves index 1 to Bob, |
| 317 | +resolves `fmsg.example.edu`, confirms the source address, and forwards |
| 318 | +"Bob ✍️" to Alice's connected clients. Bob's host repeats the datagram every |
| 319 | +two seconds while he types. When he sends the reply, his host sends the same |
| 320 | +datagram with an empty data field and Alice's client removes the indication; |
| 321 | +had that datagram been lost, the indication would have lapsed six seconds |
| 322 | +after the last refresh. |
0 commit comments