Gatekeeper
When syndicate-server starts, it creates a gatekeeper service entity, which accepts
resolve assertions requesting conversion of a long-lived credential 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
- Relevant schema: [syndicate-protocol]/schemas/gatekeeper.prs
Resolve = <resolve @step Step @observer #:Resolved> .
Resolved = <accepted @responderSession #:any> / Rejected .
Step = <<rec> @stepType symbol [@detail any]> .
Rejected = <rejected @detail any> .
When a request to resolve a given credential, a Step, appears, the gatekeeper entity queries a
dataspace (by default, the server's top-level $config dataspace) for bind assertions:
Bind = <bind @description Description @target #:any @observer BindObserver> .
Description = <<rec> @stepType symbol [@detail any]> .
BindObserver = @present #:Bound / @absent #f .
Bound = <bound @pathStep PathStep> / Rejected .
A bind assertion specifies the reference that backs a long-lived credential, and gives
instructions for checking the validity of a presented credential. Each bind assertion
matching a requested Step is checked using the stepType-specific detail in the
Description combined with the detail from the Step. If the checks pass, the target
entity from the bind is asserted in an accepted record to the observer in the resolve.
If the checks fail, a rejected record is asserted to the observer. If no bind matching a
particular Step exists, the system just waits; this allows it to be relaxed about ordering of
events.
However, besides waiting for a bind, the gatekeeper asserts a resolve of its own into its
associated dataspace, with the same Step that it received but a different observer. If,
before an appropriate bind appears, a Resolved assertion is sent to this resolve's
observer, the gatekeeper stops waiting for a bind and relays the response on to the
ultimate requester directly. This way, entities can keep an eye out for resolve requests that
will never complete, and answer rejected to them even when no matching bind exists.
Entities can also use resolve requests to synthesize a bind in a "just-in-time" fashion.
Sturdyrefs
- Relevant schema: [syndicate-protocol]/schemas/sturdy.prs
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.
Example. The sturdyref <ref {oid: "syndicate" sig: #[acowDB2/oI+6aSEC3YIxGg==]}> is valid
for the associated Bind assertion <bind <ref {oid: "syndicate" key: #[]}> $ds #f>.
The following definitions are taken from the sturdy.prs schema. For further detail, see the reference.
SturdyStepType = =ref .
SturdyStepDetail = Parameters .
SturdyRef = <ref @parameters Parameters> .
First, when used as a Step, a sturdyref uses ref as its stepType and Parameters as its
detail. A sturdyref as a whole, then, is just the combination of the type and parameters in a
record.
Parameters = {
oid: any,
sig: bytes,
} & @caveats CaveatsField .
CaveatsField =
/ @present { caveats: [Caveat ...] }
/ @invalid { caveats: any }
/ @absent {} .
The Parameters of a sturdyref are the oid field, which is a free-form value that the
targeted service chooses to name itself, and the sig, which is an iterated keyed-HMAC
construction, just as in macaroons. The sig is derived from the oid and the service's
secret key:
SturdyDescriptionDetail = {
oid: any,
key: bytes,
} .
In a Bind with stepType of ref, the detail in the Description should be a
SturdyDescriptionDetail value. The key is the secret key used to compute sigs on
sturdyrefs; the oid connects references with their defining Binds.
To compute a sig for a sturdyref, the service's secret key is first used to key an HMAC of
the oid. Then, the result is used to key an HMAC of the (canonical
form of the) first Caveat in the ref's caveats,
if any. Each Caveat's HMAC becomes the key for the next in the caveatChain. The sig is
the final result.
When validating sturdyrefs, compute the sig fresh, starting from the key and oid, and
compare the final result to the presented sig.
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. The structure and interpretation of Caveats is described fully in
the relevant section of the Syndicate network protocol
specification.
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.