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 sig
s on
sturdyrefs; the oid
connects references with their defining Bind
s.
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 Caveat
s 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.