Summary

  • RFC 3341 selected the most specific matching owner/actor access entry, so deleting an exact entry could reveal a broader wildcard entry that still granted some actions.
  • Revocation sometimes required retaining a more-specific all:none entry; the deleted row, successful mutation reply, owner notification, effective decision and enforced outcome were different receipts.

RFC 3341 gives the example in plain terms. One access entry for a named actor permits core data delivery plus presence subscription and watching. Another entry for every actor in the same domain permits core data delivery. If the named actor’s entry is deleted, the actor loses the presence permissions but keeps data permission through the domain-wide rule.

The deletion succeeded. The intended row is gone. Yet the effective authorization is not empty because the matching set changed. The narrower winner disappeared and the broader candidate became the winner. To remove every permission, the RFC says the exact entry should instead be modified to carry the special action all:none. A denial has to remain in the table precisely because absence invites fallback.

Policy was a selection result

An RFC 3341 access entry contained an owner, an actor, one or more actions, and a service-maintained lastUpdate time. The owner was the endpoint or subaddress whose policy was being described. The actor named the entity or group receiving permission in that owner’s context. Actions were service/operation pairs: core:data, for example, controlled use of the relaying mesh to send data to the owner.

Actors could include limited wildcards in both local and domain parts. Several entries might therefore match one concrete actor. Selection first kept entries for the requested owner, then kept those whose actor pattern matched. Domain exactness was the primary ordering key; local-part exactness was secondary. Exact matches won, otherwise the shorter wildcard match was considered more specific.

That algorithm means a permission cannot be read from one row in isolation. It is the result of a candidate set, a precedence rule and the requested action. A screenshot showing that an exact grant disappeared proves only a database fact. It does not prove the next policy evaluation will deny the actor.

Defaults made the distinction still sharper. For each owner, the owner itself and same-domain APEX services received broad default access, APEX services in any domain received core data permission, and other global actors received all:none. An explicit entry displaced only a default with the same actor value. Effective state therefore included both stored rows and defined defaults.

Why all:none had to be present

Deletion was expressed by sending a set operation whose access entry omitted the actions attribute. The access service removed that owner/actor entry, returned a success reply to the originator, and sent the owner a separate set notification with no actions so the owner could know the entry had been deleted.

RFC 3341 then warns that wildcarding can make deletion alter permission rather than remove it. Its solution is not a magical global revocation bit. all:none is itself an action value meaning no operation whatsoever. The exact actor entry remains and continues to outrank the wider wildcard; what changes is its payload from allowed actions to explicit denial.

This is a compact example of a negative policy fact that must be stored. Absence does not carry the same information. A tombstone-like denial says the actor was evaluated at this specificity and must not inherit the broader permission. If that explicit record is later garbage-collected without considering fallback, access can reappear even though nobody created a new grant.

A version check protected the mutation, not the whole outcome

RFC 3341 required an application to retrieve an entry before modifying it. The successful get returned lastUpdate; a replacement or deletion supplied that current value. If no exact entry existed while lastUpdate was present, or if the supplied value was not semantically identical to the selected entry’s value, the service returned code 555. Creation, by contrast, omitted lastUpdate.

This was a compare-before-replace mechanism. It prevented a writer from silently overwriting an entry that had changed since retrieval. The service assigned a new timestamp after creation or update, and it was supposed to differ from the replaced value.

The timestamp did not prove more than that mutation check. It did not prove every relay had observed the policy, that a notification reached the owner, or that a later query used the intended candidate set. The precise receipt chain is: retrieved entry and version; submitted mutation; service acceptance or conflict; new stored entry; notification emitted; notification delivered; decision recomputed; enforcement point updated; attempted operation allowed or denied.

Querying policy was itself authorized

The access service did not disclose policy simply because an application could address its well-known endpoint. For a query, it first checked that the subject belonged to the administrative domain and named a valid address. Then it consulted the subject’s access entry matching the query originator and required an access:query token. Only afterward did it select the entry matching the actor under examination and determine whether all requested actions were present.

That creates two actor roles: the originator asking to inspect a decision, and the actor whose permission is being tested. Their evidence must not be collapsed. Permission to ask is not permission to perform. Likewise, an allow reply says that all listed actions were contained in the selected entry at that service evaluation; it is not proof that a later data operation reached the same policy version or succeeded.

The RFC required implementations to keep access entries in persistent storage and maintained them whether or not the owner address was currently attached to the relay mesh. Disconnecting an endpoint did not erase its policy. Persistence made policy outlive presence, but it did not itself certify durability, replication, freshness or enforcement.

Historic status and a durable lesson

RFC 3341 was published on the Standards Track in July 2002 alongside the APEX core, option and presence RFCs. On 29 July 2012, the IETF recorded why RFCs 3340 through 3343 were reclassified as Historic: to the best of its knowledge, no implementations had been deployed, and APEX functionality was being supplied by widely deployed XMPP under RFC 6120 and RFC 6121.

That statement is the historical adoption boundary. It does not say wildcard fallback caused non-deployment. Nor does it make the access rule example a deployed vulnerability. Its value is conceptual and exact: removing one policy object, revoking an effective permission and enforcing denial are three separate events.

Any layered authorization system can be audited with the same discipline without pretending it implements APEX. Record the full candidate set and matching algorithm, not merely the edited row. Preserve the explicit denial when precedence depends on its presence. After a change, query the effective decision and test the enforcement point. A change receipt tells us what the service accepted. Revocation requires evidence of what the actor can no longer do.

Sources