Gatekeeper
When syndicate-server starts, it creates a gatekeeper service entity, which accepts
resolve assertions requesting conversion of a name (often some kind of 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.
Every gatekeeper entity is configured with a special dataspace which it uses to look up the services it guards. This dataspace is called the gatekeeper's bindspace.
The default gatekeeper is configured to have the server's top-level $config dataspace as its
bindspace.
Gatekeeper protocol
- Relevant schema: [syndicate-protocol]/schemas/gatekeeper.prs
Resolve = <resolve @step Step @observer #:rpc.Result> .
Step = <<rec> @stepType symbol [@detail any]> .
When a request to resolve a given name, a Step, appears at the gatekeeper entity, the
gatekeeper passes the Resolve request on to its bindspace directly. What happens next depends
on the stepType in the Step, and on the stepType resolution handlers installed in the
bindspace.
Generally speaking, a resolution handler uses information from the Step to find a bind
assertion, and uses the information from its description and target to construct a response
to send back to the Resolve's observer.
Bind = <bind @description Description @target #:any @observer BindObserver> .
Description = <<rec> @stepType symbol [@detail any]> .
BindObserver = @present #:Bound / @absent #f .
Bound = <bound @pathStep PathStep> / rpc.Error .
A bind assertion in a bindspace specifies the reference that backs a name, and includes
enough information to check the validity of a presented request to access the target behind
the name.
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 ok record to the observer in the
resolve. If the checks fail, an error 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.
This design also allows for a resolution handler to synthesize a bind in a "just-in-time"
fashion in response to a Resolve.
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. Each gatekeeper
service entity created by syndicate-server always installs a resolution handler for
sturdyrefs in its bindspace.
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.