When syndicate-server starts, it creates a gatekeeper service entity, which accepts resolve assertions requesting conversion of a long-lived "sturdyref" to a live reference. The gatekeeper is the default object, available as OID 0 to peers at the other end of relay listener connections.

Gatekeeper protocol

Resolve = <resolve @sturdyref sturdy.SturdyRef @observer #!#!any>.

When a request to resolve a given sturdyref appears, the gatekeeper entity queries a dataspace (by default, the server's top-level $config dataspace) for bind assertions:

Bind = <bind @oid any @key bytes @target #!any>.

Each bind assertion matching the requested sturdyref is checked against the credentials provided in the sturdyref, and if the checks pass, the target entity from the bind is asserted to the observer in the resolve.


A "sturdyref" is a long-lived certificate including a cryptographic signature that can be upgraded by a gatekeeper entity to a live reference to the entity named in the sturdyref. The current sturdyref implementation is based on the design of Macaroons.

The following definitions are taken from the sturdy.prs schema.

SturdyRef = <ref @oid any @caveatChain [Attenuation ...] @sig bytes>.

Within a ref record, the oid field is a free-form value that the targeted service chooses to name itself. The sig is an iterated keyed-HMAC construction, just as in macaroons. First, the service's secret key is used to key an HMAC of the oid. Then, the result is used to key an HMAC of the first Attenuation in caveatChain. Each Attenuation's HMAC becomes the key for the next in the caveatChain. The final result is equal to the sig field in a valid sturdyref.

Attenuation of authority

When it comes to publishing assertions or sending messages to the entity denoted by a sturdyref, the caveatChain is used to attenuate the authority denoted by the sturdyref by filtering and/or rewriting assertion and message bodies. The caveatChain is run right to left, with newer rewrites-and-filters at the right-hand end of the chain and older ones at the left-hand end. Of course, an empty caveatChain is an unattenuated reference.

Attenuation = [Caveat ...].

Each individual Attenuation in a caveatChain is a sequence of Caveats. The term "caveat" is shamelessly taken from macaroons, though our caveats presently embody only what in the Macaroons paper are called "first-party caveats" over assertion structure; future versions of the server may add "third-party caveats" and other, richer, predicates over assertions.

Each Attenuation's Caveats are run in right to left order. The structure and interpretation of Caveats is described fully in the relevant section of the Syndicate network protocol specification.