12  CoAP Observe Freshness Contracts

coap
observe
server-push
reliability

12.1 Start With the Stale Alarm

An alarm notification that arrives late can be worse than no notification if the client treats it as current. Observe therefore needs more than “server push”: the client must know which registration a notification belongs to, whether the value is fresh, and what to do when UDP reorders updates.

This page follows the subscription through token binding, Max-Age, liveness checks, sequence comparison, cancellation, and stale-update rejection so the pushed value can be trusted.

12.2 Learning Objectives

After this page, you should be able to:

  • Trace a CoAP Observe registration from GET with Observe: 0 through token-bound notifications and cancellation.
  • Design Max-Age, Confirmable notification, and re-registration policies for best-effort notification streams.
  • Apply the RFC 7641 Observe-value comparison rule to reordered, skipped, and wrapped 24-bit sequence values.
  • Capture the lifecycle, freshness, pacing, and stale-update evidence needed to review an Observe deployment.

12.3 Why This Contract Comes After Observe Server Push

CoAP Observe Server Push teaches the main Observe pattern: replace repeated polling with change-driven notifications. This page narrows in on the deeper operational contract that makes the pattern safe: which token identifies the subscription, how long a value remains fresh, how the server detects unreachable observers, and how the client rejects stale notifications when UDP reorders datagrams.

The CoAP Observe contract therefore keeps the parent idea intact while adding the evidence needed for token binding, freshness, liveness, cancellation, and stale-update rejection.

Use it when an observed resource controls alarms, industrial telemetry, building automation, battery gateways, NAT-crossing clients, or any application where a stale pushed value could be mistaken for current state.

Overview: Register Once, Then Receive Pushes

Observe (RFC 7641) adds a lightweight publish/subscribe relationship on top of plain CoAP request/response, using a single option numbered 6. A client registers by sending an ordinary GET with the Observe option set to 0. The server records the client — keyed by its source endpoint, its Token, and the requested resource — and answers 2.05 Content that itself carries an Observe option holding a sequence number. From that moment the client is an observer.

Whenever the resource's representation changes, the server sends a fresh 2.05 Content notification carrying an incremented Observe value and, crucially, the same Token as the original registration. That Token is how the client knows an unsolicited notification belongs to a subscription it opened. To stop, the client either sends a GET with Observe set to 1 (explicit cancel) or rejects a Confirmable notification with an RST.

A concrete trace shows the contract. If a dashboard registers GET /sensors/temp with Token 0x4a01, the server might answer 2.05 Content, Observe 12, and Max-Age: 60. Fourteen seconds later the pushed update uses a new Message ID but the same Token 0x4a01 and Observe 13. The client must keep that Token reserved while the observation is active; reusing it for another outstanding exchange would make later pushes ambiguous.

sequenceDiagram
    participant C as Client
    participant S as Server
    C->>S: GET (Observe: 0, Token)
    S-->>C: 2.05 Content (Observe: 12, Token)
    Note over S: Resource value changes
    S->>C: 2.05 Content (Observe: 13, same Token)
    S->>C: 2.05 Content (Observe: 14, same Token)

CoAP implementation patterns showing confirmable, non-confirmable, acknowledgement, and reset message types alongside RESTful methods and the Observe pattern for pushed updates.
A CoAP resource keeps observer registry entries keyed by client endpoint, Token, and URI, then fans out 2.05 notifications with increasing Observe values whenever the resource changes.

Register

GET with Observe = 0. Server replies 2.05 Content plus an Observe option (starting sequence number).

Notify

On each change the server sends 2.05 Content with a higher Observe value, reusing the registration Token.

Cancel

GET with Observe = 1, or reject a CON notification with RST, to end the relationship.

Token binding

The Token ties every later notification to the subscription; without it the client cannot match a push.

Practitioner: Notifications Are Best-Effort, So Max-Age and CON Checks Matter

Observe promises eventual consistency, not guaranteed delivery. Notifications are usually Non-confirmable, so any single one may be lost; the model only guarantees that the client eventually converges on the current state through later notifications. That has two practical consequences you must design for: freshness and liveness.

Freshness is governed by Max-Age. Each notification says how long its value can be considered current. A well-behaved server sends the next notification before the previous one's Max-Age expires, so a live resource never looks stale; if the client's Max-Age lapses with no new push, the client should re-GET (re-register) rather than trust an aged value. Liveness is checked with the occasional Confirmable notification: if its retransmissions all go unanswered, the server concludes the observer is gone and removes it, which is how "ghost observers" behind a closed NAT binding get cleaned up.

For example, a battery gateway observing /tank/level might send ordinary NON notifications only when the level changes by at least 2% or every 60 seconds, whichever comes first, with Max-Age: 120. The client treats silence for 120 seconds as stale and re-registers instead of assuming the last level is still safe. The server might make every tenth notification Confirmable, or use a five-minute CON probe, so closed NAT bindings and rebooted clients are detected without turning every update into an ACK exchange. If the sensor chatters around the 2% threshold, the server should coalesce changes and publish the newest value at the pacing limit; Observe reduces polling traffic only when push frequency is also controlled.

Mechanism
Purpose
What the client should do
Evidence to capture
Max-Age on notifications
Bounds how long a value is fresh
Re-register if Max-Age lapses with no push
Trace shows notify interval below Max-Age
Occasional CON notify
Confirms observer is still reachable
ACK it to stay registered
Server log shows CON, then observer kept or dropped
Rate limiting / pacing
Prevents flooding on fast-changing data
Tolerate coalesced updates
Notify rate stays within link and client budget

Under the Hood: The Observe Value Orders Notifications Despite UDP Reordering

Because CoAP runs over UDP, notifications can arrive out of order: a client may receive an older reading after a newer one. If it simply trusted arrival order it would display stale data. The Observe option value — a number up to 24 bits wide that the server increments per notification — exists to solve exactly this, and RFC 7641 defines a precise comparison so wraparound does not fool the client.

Let V1 be the value of the notification the client currently accepts, received at time T1, and V2 the value of a newly arrived notification at time T2. The new notification is treated as fresher only if (V1 < V2 and V2 − V1 < 2^23) or (V1 > V2 and V1 − V2 > 2^23) — the two cases that correctly handle counter wraparound — or if more than 128 seconds have elapsed (T2 > T1 + 128 s), after which the later arrival simply wins regardless of value. Anything that fails all three tests is a reordered straggler and is discarded.

Suppose the client has accepted Observe 120 and then receives 124; because the difference is small and positive, 124 becomes the new displayed state. If 122 arrives two seconds later, it is discarded as a late datagram. Near wraparound, a server can move from 16777214 to 1; the large reverse numeric difference tells the client that 1 is actually newer, not older. After a long quiet period above 128 seconds, the client accepts the later arrival as a new baseline because old ordering evidence has expired.

This is why the Observe value must be read as a reordering sequence number, not as a reliable event counter. Lost Non-confirmable notifications mean the numbers a client sees can skip; the guarantee is ordering and eventual convergence, not that every increment is delivered.

Observe value

Up to 24 bits, incremented per notification, used purely to decide which of two notifications is newer.

Wraparound window

The 2^23 threshold distinguishes a genuine increment from a wrapped-around older value.

128-second rule

After 128 s, the later arrival is accepted regardless of the numeric comparison.

Straggler drop

A notification failing all freshness tests is a reordered duplicate and is silently discarded.

12.4 Release Checklist

Before shipping a CoAP Observe contract, verify these records:

  • Registration state is keyed by client endpoint, request Token, and resource URI, and the Token stays reserved while the observation is active.
  • Notification policy names which updates are Non-confirmable, which are occasional Confirmable liveness probes, and when failed probes remove the observer.
  • Freshness policy sets Max-Age for each observed resource and says what the client does when no notification arrives before that value expires.
  • Ordering policy implements the RFC 7641 24-bit Observe-value comparison, including the 2^23 wraparound window and the 128-second fallback.
  • Pacing policy documents change thresholds, coalescing, maximum notification rate, and evidence that Observe reduces traffic instead of creating a push flood.
  • Recovery behavior covers explicit Observe: 1 cancellation, RST cancellation, client reboot with token loss, NAT binding expiry, resource deletion, and re-registration.

12.5 See Also

12.6 Next

Return to CoAP Observe Server Push, then continue to CoAP Advanced Features for block-wise transfer and large-payload considerations.