Summary

  • A key change between reading the JMAP Session and creating a push subscription can make verification fail. The server must not retry that rejected verification; the client must inspect the returned Session state.
  • A July 2026 technical erratum, still marked Reported when checked, challenges the RFC's optional final-notification mechanism. Recovery planning should not treat that passage as a guaranteed wake-up.

The subscription exists. The verification has not arrived. Waiting looks reasonable, because waiting is what clients often do after asking a remote service to act. In this particular race, it is exactly the wrong assumption: the sender is not going to try that verification again.

RFC 9749, published in March 2025, makes the boundary explicit. JMAP's push notifications use VAPID authentication. If the application server rotates its key between a client's reading of the Session object and its creation of a push subscription, the push service can reject the verification because the subscription was bound to the old public key. The client must check the returned sessionState against the Session object and the key it expected. Recovery belongs on the side able to discover that changed assumption.

This is a protocol-defined possibility, not a newly discovered outage. The sources reviewed here establish neither a vendor failure nor a count of missed notifications. Their value is more exact: they identify a case in which a seemingly sensible operational habit—waiting for the server to retry—does not match the contract.

Two clocks in one creation request

The advertised key and the subscription do not come into existence in one indivisible operation. A client first learns the application's current public key. It then uses a push endpoint associated with that key when registering its subscription with JMAP. A rotation in between leaves two participants acting on different moments.

The restriction is intentional. Under RFC 8292, a restricted push subscription requires authentication using the private key corresponding to the public key supplied at creation. Accepting whichever key a sender now prefers would discard that restriction. A replacement application key consequently requires a new subscription, not merely a different signature on an old destination.

The resulting 403 is not, by itself, a diagnosis. Invalid signatures, an incorrect audience and token-expiry problems can also produce an authentication rejection. An operator who classifies every 403 as rotation would risk rebuilding subscriptions while leaving the actual fault untouched. Timing, key cohort and Session-state evidence must agree before the race is treated as the explanation.

There are also distinct meanings of “key.” The signing key identifies the application server; payload encryption has a different purpose and must use different private key material for its key exchange. Neither a valid signature nor a changed signing key proves that a notification has been received, read or acted upon. Security controls lose their usefulness when their successful execution is reported as an outcome they do not establish.

Acceptance is not verification

JMAP first verifies that the client can access data sent to the supplied URL. Further requests wait until the client returns the correct verification code. RFC 8620's push-subscription rules therefore create a meaningful intermediate state: registration is not yet an operational notification channel.

The registration method offers no collection-level ifInState condition, and its result has no oldState or newState fields. The relevant sessionState sits in the outer API response. A recovery design borrowed from an ordinary data-collection update can therefore check the wrong thing while looking perfectly disciplined.

RFC 9749 requires the client to examine that Session information. If the expected key does not match, it permits another creation attempt and permits destruction of the unsuccessful subscription. It does not instruct the client to hammer the same request indefinitely. Nor does its prohibition on retrying this rejected PushVerification prohibit every retry of every push message. The useful distinction is between repeating an obsolete assumption and rebuilding from a newly established one.

For a product team, this means a progress indicator called “subscribed” is underspecified. Does it mean a creation response arrived, a verification code came back, or a usable subscription now belongs to the current key cohort? Those stages need not all be exposed to the reader. They do need to remain distinguishable to whoever operates the service.

A support instruction to reinstall the application may occasionally clear local state. It is not evidence that the protocol recovery path works. It can also transfer diagnosis and reconstruction to the customer without teaching the operator which transition failed. That is an organisational risk to test, not a claim that a particular provider uses such advice.

A warning with an unresolved interface problem

The original RFC offers an optional final StateChange notification when retiring certain old-key subscriptions. It describes that notification as prompting a PushSubscription/changes call and subsequent discovery of the new Session key. There is a reason not to build a guarantee on that paragraph.

Erratum 9055, visible in the RFC Editor's RFC 9749 search, was reported by Neil Jenkins on 31 July 2026. Its status was Reported, not Verified, when checked on 7 September. It proposes deleting the paragraph: PushSubscription has no account association for the specified StateChange structure, and RFC 8620 defines no PushSubscription/changes method.

Those interface observations can be checked against RFC 8620 itself. The erratum's status nevertheless matters. A reported correction is not a silently amended standard, and it is not evidence that every implementation is defective. The responsible conclusion is narrower: do not represent the disputed call chain as an established interoperability mechanism or a dependable final warning.

Even a valid warning would face the delivery limits in RFC 8030. Push messages have a retention lifetime, services can shorten it, and a zero-lifetime message will not wait for an unavailable device. A sleeping client is not made reachable by writing “final” into an operator's plan. Recovery must survive a return to activity without assuming that a last message was seen.

The IANA JMAP capability registry records the VAPID extension's identifier. That is coordination evidence, not certification that installed clients implement a particular recovery sequence. Procurement and release reviews should ask for demonstrated behavior on the relevant client versions, not treat presence in a registry as a substitute.

Keep the evidence without keeping the secret

A useful recovery record can identify a key generation, the creation attempt, verification completion and eventual retirement. It does not need to retain the full push URL or encryption secrets. RFC 8620 requires secure erasure of those values when a subscription is destroyed and does not allow PushSubscription/get to return them.

There is an important limit here for observability programmes. “We keep everything for debugging” is not a neutral choice when the deleted material is itself sensitive. Non-secret cohort identifiers and bounded event timestamps can support an audit without recreating a second lifetime for retired endpoint credentials.

Subscription lifetime is also tied to the API credentials that created it. An expired or revoked credential is not simply another notification retry problem. Likewise, an application should not repair one device by destroying subscriptions it does not recognize as its own. A reconstruction procedure is only a recovery procedure if it preserves the boundaries around other clients.