diff --git a/packages/core/protocols/schema-bundle.bin b/packages/core/protocols/schema-bundle.bin
index c4b2332..3fabd91 100644
--- a/packages/core/protocols/schema-bundle.bin
+++ b/packages/core/protocols/schema-bundle.bin
@@ -4,12 +4,12 @@ ByteString
 QueryValue´³orµµ±string´³atom³String„„µ±file´³rec´³lit³file„´³tupleµ´³named³filename´³atom³String„„´³named³headers´³refµ„³Headers„„´³named³body´³atom³
 ByteString„„„„„„„„³HostPattern´³orµµ±host´³atom³String„„µ±any´³lit€„„„„³HttpBinding´³rec´³lit³	http-bind„´³tupleµ´³named³host´³refµ„³HostPattern„„´³named³port´³atom³
SignedInteger„„´³named³method´³refµ„³
MethodPattern„„´³named³path´³refµ„³PathPattern„„´³named³handler´³embedded´³refµ„³HttpRequest„„„„„„³HttpContext´³rec´³lit³request„´³tupleµ´³named³req´³refµ„³HttpRequest„„´³named³res´³embedded´³refµ„³HttpResponse„„„„„„³HttpRequest´³rec´³lit³http-request„´³tupleµ´³named³sequenceNumber´³atom³
SignedInteger„„´³named³host´³atom³String„„´³named³port´³atom³
SignedInteger„„´³named³method´³atom³Symbol„„´³named³path´³seqof´³atom³String„„„´³named³headers´³refµ„³Headers„„´³named³query´³dictof´³atom³Symbol„´³seqof´³refµ„³
 QueryValue„„„„´³named³body´³refµ„³RequestBody„„„„„³HttpService´³rec´³lit³http-service„´³tupleµ´³named³host´³refµ„³HostPattern„„´³named³port´³atom³
SignedInteger„„´³named³method´³refµ„³
MethodPattern„„´³named³path´³refµ„³PathPattern„„„„„³PathPattern´³seqof´³refµ„³PathPatternElement„„³RequestBody´³orµµ±present´³atom³
-ByteString„„µ±absent´³lit€„„„„³HttpListener´³rec´³lit³
http-listener„´³tupleµ´³named³port´³atom³
SignedInteger„„„„„³HttpResponse´³orµµ±status´³rec´³lit³status„´³tupleµ´³named³code´³atom³
SignedInteger„„´³named³message´³atom³String„„„„„„µ±header´³rec´³lit³header„´³tupleµ´³named³name´³atom³Symbol„„´³named³value´³atom³String„„„„„„µ±chunk´³rec´³lit³chunk„´³tupleµ´³named³chunk´³refµ„³Chunk„„„„„„µ±done´³rec´³lit³done„´³tupleµ´³named³chunk´³refµ„³Chunk„„„„„„„„³
MethodPattern´³orµµ±any´³lit€„„µ±specific´³atom³Symbol„„„„³PathPatternElement´³orµµ±label´³atom³String„„µ±wildcard´³lit³_„„µ±rest´³lit³...„„„„„³embeddedType€„„µ³noise„´³schema·³version‘³definitions·³Route´³rec´³lit³route„´³tuplePrefixµ´³named³
-transports´³seqof³any„„„´³named³steps´³seqof´³refµ„³	RouteStep„„„„„³Accept´³rec´³lit³accept„´³tupleµ´³named³responderSession´³embedded³any„„„„„³Packet´³orµµ±complete´³atom³
+ByteString„„µ±absent´³lit€„„„„³HttpListener´³rec´³lit³
http-listener„´³tupleµ´³named³port´³atom³
SignedInteger„„„„„³HttpResponse´³orµµ±status´³rec´³lit³status„´³tupleµ´³named³code´³atom³
SignedInteger„„´³named³message´³atom³String„„„„„„µ±header´³rec´³lit³header„´³tupleµ´³named³name´³atom³Symbol„„´³named³value´³atom³String„„„„„„µ±chunk´³rec´³lit³chunk„´³tupleµ´³named³chunk´³refµ„³Chunk„„„„„„µ±done´³rec´³lit³done„´³tupleµ´³named³chunk´³refµ„³Chunk„„„„„„„„³
MethodPattern´³orµµ±any´³lit€„„µ±specific´³atom³Symbol„„„„³PathPatternElement´³orµµ±label´³atom³String„„µ±wildcard´³lit³_„„µ±rest´³lit³...„„„„„³embeddedType€„„µ³noise„´³schema·³version‘³definitions·³Packet´³orµµ±complete´³atom³
 ByteString„„µ±
 fragmented´³seqof´³atom³
-ByteString„„„„„³Connect´³rec´³lit³connect„´³tupleµ´³named³serviceSelector³any„´³named³initiatorSession´³embedded³any„„„„„³	NoiseSpec´³andµ´³dict·³key´³named³key´³atom³
-ByteString„„³service´³named³service³any„„„´³named³protocol´³refµ„³
NoiseProtocol„„´³named³
preSharedKeys´³refµ„³NoisePreSharedKeys„„„„³	NoiseStep´³rec´³lit³noise„´³tupleµ´³named³spec´³refµ„³	NoiseSpec„„„„„³	RouteStep´³orµµ±	NoiseStep´³refµ„³	NoiseStep„„µ±GatekeeperStep´³refµ„³GatekeeperStep„„„„³
NoiseProtocol´³orµµ±present´³dict·³protocol´³named³protocol´³atom³String„„„„„µ±invalid´³dict·³protocol´³named³protocol³any„„„„µ±absent´³dict·„„„„„³GatekeeperStep´³refµ³sturdy„³	SturdyRef„³DefaultProtocol´³lit±!Noise_NK_25519_ChaChaPoly_BLAKE2s„³NoisePreSharedKeys´³orµµ±present´³dict·³
preSharedKeys´³named³
preSharedKeys´³seqof´³atom³
+ByteString„„„„„³	NoiseSpec´³andµ´³dict·³key´³named³key´³atom³
+ByteString„„³service´³named³service´³refµ„³ServiceSelector„„„„´³named³protocol´³refµ„³
NoiseProtocol„„´³named³
preSharedKeys´³refµ„³NoisePreSharedKeys„„„„³	NoiseStep´³rec´³lit³noise„´³tupleµ´³named³service´³refµ„³ServiceSelector„„„„„³NoiseService´³rec´³lit³noise„´³tupleµ´³named³spec´³refµ„³NoiseServiceSpec„„„„„³
NoiseProtocol´³orµµ±present´³dict·³protocol´³named³protocol´³atom³String„„„„„µ±invalid´³dict·³protocol´³named³protocol³any„„„„µ±absent´³dict·„„„„„³NoiseRouteStep´³rec´³lit³noise„´³tupleµ´³named³spec´³refµ„³	NoiseSpec„„„„„³SecretKeyField´³orµµ±present´³dict·³	secretKey´³named³	secretKey´³atom³
+ByteString„„„„„µ±invalid´³dict·³	secretKey´³named³	secretKey³any„„„„µ±absent´³dict·„„„„„³DefaultProtocol´³lit±!Noise_NK_25519_ChaChaPoly_BLAKE2s„³ServiceSelector³any³NoiseServiceSpec´³andµ´³named³base´³refµ³noise„³	NoiseSpec„„´³named³	secretKey´³refµ„³SecretKeyField„„„„³NoisePreSharedKeys´³orµµ±present´³dict·³
preSharedKeys´³named³
preSharedKeys´³seqof´³atom³
 ByteString„„„„„„µ±invalid´³dict·³
preSharedKeys´³named³
preSharedKeys³any„„„„µ±absent´³dict·„„„„„„³embeddedType€„„µ³timer„´³schema·³version‘³definitions·³SetTimer´³rec´³lit³	set-timer„´³tupleµ´³named³label³any„´³named³seconds´³atom³Double„„´³named³kind´³refµ„³	TimerKind„„„„„³	LaterThan´³rec´³lit³
 later-than„´³tupleµ´³named³seconds´³atom³Double„„„„„³	TimerKind´³orµµ±relative´³lit³relative„„µ±absolute´³lit³absolute„„µ±clear´³lit³clear„„„„³TimerExpired´³rec´³lit³
timer-expired„´³tupleµ´³named³label³any„´³named³seconds´³atom³Double„„„„„„³embeddedType€„„µ³trace„´³schema·³version‘³definitions·³Oid³any³Name´³orµµ±	anonymous´³rec´³lit³	anonymous„´³tupleµ„„„„µ±named´³rec´³lit³named„´³tupleµ´³named³name³any„„„„„„„³Target´³rec´³lit³entity„´³tupleµ´³named³actor´³refµ„³ActorId„„´³named³facet´³refµ„³FacetId„„´³named³oid´³refµ„³Oid„„„„„³TaskId³any³TurnId³any³ActorId³any³FacetId³any³	TurnCause´³orµµ±turn´³rec´³lit³	caused-by„´³tupleµ´³named³id´³refµ„³TurnId„„„„„„µ±cleanup´³rec´³lit³cleanup„´³tupleµ„„„„µ±linkedTaskRelease´³rec´³lit³linked-task-release„´³tupleµ´³named³id´³refµ„³TaskId„„´³named³reason´³refµ„³LinkedTaskReleaseReason„„„„„„µ±periodicActivation´³rec´³lit³periodic-activation„´³tupleµ´³named³period´³atom³Double„„„„„„µ±delay´³rec´³lit³delay„´³tupleµ´³named³causingTurn´³refµ„³TurnId„„´³named³amount´³atom³Double„„„„„„µ±external´³rec´³lit³external„´³tupleµ´³named³description³any„„„„„„„³	TurnEvent´³orµµ±assert´³rec´³lit³assert„´³tupleµ´³named³	assertion´³refµ„³AssertionDescription„„´³named³handle´³refµ³protocol„³Handle„„„„„„µ±retract´³rec´³lit³retract„´³tupleµ´³named³handle´³refµ³protocol„³Handle„„„„„„µ±message´³rec´³lit³message„´³tupleµ´³named³body´³refµ„³AssertionDescription„„„„„„µ±sync´³rec´³lit³sync„´³tupleµ´³named³peer´³refµ„³Target„„„„„„µ±	breakLink´³rec´³lit³
 break-link„´³tupleµ´³named³source´³refµ„³ActorId„„´³named³handle´³refµ³protocol„³Handle„„„„„„„„³
@@ -26,9 +26,12 @@ ByteString
 TAttenuate´³refµ„³
 TAttenuate„„µ±TRef´³refµ„³TRef„„µ±Lit´³refµ„³Lit„„µ±	TCompound´³refµ„³	TCompound„„„„³	PCompound´³orµµ±rec´³rec´³lit³rec„´³tupleµ´³named³label³any„´³named³fields´³seqof´³refµ„³Pattern„„„„„„„µ±arr´³rec´³lit³arr„´³tupleµ´³named³items´³seqof´³refµ„³Pattern„„„„„„„µ±dict´³rec´³lit³dict„´³tupleµ´³named³entries´³dictof³any´³refµ„³Pattern„„„„„„„„„³	PEmbedded´³lit³Embedded„³	SturdyRef´³rec´³lit³ref„´³tupleµ´³named³oid³any„´³named³caveatChain´³seqof´³refµ„³Caveat„„„´³named³sig´³atom³
 ByteString„„„„„³	TCompound´³orµµ±rec´³rec´³lit³rec„´³tupleµ´³named³label³any„´³named³fields´³seqof´³refµ„³Template„„„„„„„µ±arr´³rec´³lit³arr„´³tupleµ´³named³items´³seqof´³refµ„³Template„„„„„„„µ±dict´³rec´³lit³dict„´³tupleµ´³named³entries´³dictof³any´³refµ„³Template„„„„„„„„„³
-TAttenuate´³rec´³lit³	attenuate„´³tupleµ´³named³template´³refµ„³Template„„´³named³attenuation´³seqof´³refµ„³Caveat„„„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³worker„´³schema·³version‘³definitions·³Instance´³rec´³lit³Instance„´³tupleµ´³named³name´³atom³String„„´³named³argument³any„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³service„´³schema·³version‘³definitions·³State´³orµµ±started´³lit³started„„µ±ready´³lit³ready„„µ±failed´³lit³failed„„µ±complete´³lit³complete„„µ±userDefined³any„„„³
+SturdyStep´³refµ„³	SturdyRef„³
+TAttenuate´³rec´³lit³	attenuate„´³tupleµ´³named³template´³refµ„³Template„„´³named³attenuation´³seqof´³refµ„³Caveat„„„„„„³
SturdyService´³rec´³lit³ref„´³tupleµ´³named³oid³any„´³named³key´³atom³
+ByteString„„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³worker„´³schema·³version‘³definitions·³Instance´³rec´³lit³Instance„´³tupleµ´³named³name´³atom³String„„´³named³argument³any„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³service„´³schema·³version‘³definitions·³State´³orµµ±started´³lit³started„„µ±ready´³lit³ready„„µ±failed´³lit³failed„„µ±complete´³lit³complete„„µ±userDefined³any„„„³
 RunService´³rec´³lit³run-service„´³tupleµ´³named³serviceName³any„„„„³ServiceState´³rec´³lit³
service-state„´³tupleµ´³named³serviceName³any„´³named³state´³refµ„³State„„„„„³
ServiceObject´³rec´³lit³service-object„´³tupleµ´³named³serviceName³any„´³named³object³any„„„„³RequireService´³rec´³lit³require-service„´³tupleµ´³named³serviceName³any„„„„³RestartService´³rec´³lit³restart-service„´³tupleµ´³named³serviceName³any„„„„³ServiceDependency´³rec´³lit³
 depends-on„´³tupleµ´³named³depender³any„´³named³dependee´³refµ„³ServiceState„„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³protocol„´³schema·³version‘³definitions·³Oid´³atom³
SignedInteger„³Sync´³rec´³lit³sync„´³tupleµ´³named³peer´³embedded´³lit�„„„„„„³Turn´³seqof´³refµ„³	TurnEvent„„³Error´³rec´³lit³error„´³tupleµ´³named³message´³atom³String„„´³named³detail³any„„„„³Event´³orµµ±Assert´³refµ„³Assert„„µ±Retract´³refµ„³Retract„„µ±Message´³refµ„³Message„„µ±Sync´³refµ„³Sync„„„„³Assert´³rec´³lit³assert„´³tupleµ´³named³	assertion´³refµ„³	Assertion„„´³named³handle´³refµ„³Handle„„„„„³Handle´³atom³
SignedInteger„³Packet´³orµµ±Turn´³refµ„³Turn„„µ±Error´³refµ„³Error„„µ±	Extension´³refµ„³	Extension„„„„³Message´³rec´³lit³message„´³tupleµ´³named³body´³refµ„³	Assertion„„„„„³Retract´³rec´³lit³retract„´³tupleµ´³named³handle´³refµ„³Handle„„„„„³	Assertion³any³	Extension´³rec´³named³label³any„´³named³fields´³seqof³any„„„³	TurnEvent´³tupleµ´³named³oid´³refµ„³Oid„„´³named³event´³refµ„³Event„„„„„³embeddedType€„„µ³	dataspace„´³schema·³version‘³definitions·³Observe´³rec´³lit³Observe„´³tupleµ´³named³pattern´³refµ³dataspacePatterns„³Pattern„„´³named³observer´³embedded³any„„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³
-gatekeeper„´³schema·³version‘³definitions·³Bind´³rec´³lit³bind„´³tupleµ´³named³oid³any„´³named³key´³atom³
-ByteString„„´³named³target´³embedded³any„„„„„³Resolve´³rec´³lit³resolve„´³tupleµ´³named³	sturdyref´³refµ³sturdy„³	SturdyRef„„´³named³observer´³embedded´³embedded³any„„„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³transportAddress„´³schema·³version‘³definitions·³Tcp´³rec´³lit³tcp„´³tupleµ´³named³host´³atom³String„„´³named³port´³atom³
SignedInteger„„„„„³Unix´³rec´³lit³unix„´³tupleµ´³named³path´³atom³String„„„„„³Stdio´³rec´³lit³stdio„´³tupleµ„„„³	WebSocket´³rec´³lit³ws„´³tupleµ´³named³url´³atom³String„„„„„„³embeddedType€„„µ³dataspacePatterns„´³schema·³version‘³definitions·³DLit´³rec´³lit³lit„´³tupleµ´³named³value´³refµ„³AnyAtom„„„„„³DBind´³rec´³lit³bind„´³tupleµ´³named³pattern´³refµ„³Pattern„„„„„³AnyAtom´³orµµ±bool´³atom³Boolean„„µ±float´³atom³Float„„µ±double´³atom³Double„„µ±int´³atom³
SignedInteger„„µ±string´³atom³String„„µ±bytes´³atom³
+gatekeeper„´³schema·³version‘³definitions·³Bind´³rec´³lit³bind„´³tupleµ´³named³description³any„´³named³target´³embedded³any„„´³named³observer´³refµ„³BindObserver„„„„„³Bound´³orµµ±bound´³rec´³lit³bound„´³tupleµ´³named³step³any„„„„„µ±Rejected´³refµ„³Rejected„„„„³Route´³rec´³lit³route„´³tuplePrefixµ´³named³
+transports´³seqof³any„„„´³named³	pathSteps´³seqof³any„„„„³Resolve´³rec´³lit³resolve„´³tupleµ´³named³step³any„´³named³observer´³embedded´³refµ„³Resolved„„„„„„³Rejected´³rec´³lit³rejected„´³tupleµ´³named³detail³any„„„„³Resolved´³orµµ±accepted´³rec´³lit³accepted„´³tupleµ´³named³responderSession´³embedded³any„„„„„„µ±Rejected´³refµ„³Rejected„„„„³ResolvePath´³rec´³lit³resolve-path„´³tupleµ´³named³route´³refµ„³Route„„´³named³addr³any„´³named³control´³embedded´³refµ„³TransportControl„„„´³named³resolved´³refµ„³Resolved„„„„„³BindObserver´³orµµ±present´³embedded´³refµ„³Bound„„„µ±absent´³lit€„„„„³ForceDisconnect´³rec´³lit³force-disconnect„´³tupleµ„„„³ResolvePathStep´³orµµ±base´³rec´³lit³connect-transport„´³tupleµ´³named³addr³any„´³named³observer´³embedded´³refµ„³Resolved„„„„„„„µ±step´³rec´³lit³resolve-path-step„´³tupleµ´³named³
+gatekeeper´³embedded´³refµ„³Resolve„„„´³named³pathStep³any„´³named³observer´³embedded´³refµ„³Resolved„„„„„„„„„³TransportControl´³refµ„³ForceDisconnect„„³embeddedType´³refµ³	EntityRef„³Cap„„„µ³transportAddress„´³schema·³version‘³definitions·³Tcp´³rec´³lit³tcp„´³tupleµ´³named³host´³atom³String„„´³named³port´³atom³
SignedInteger„„„„„³Unix´³rec´³lit³unix„´³tupleµ´³named³path´³atom³String„„„„„³Stdio´³rec´³lit³stdio„´³tupleµ„„„³	WebSocket´³rec´³lit³ws„´³tupleµ´³named³url´³atom³String„„„„„„³embeddedType€„„µ³dataspacePatterns„´³schema·³version‘³definitions·³DLit´³rec´³lit³lit„´³tupleµ´³named³value´³refµ„³AnyAtom„„„„„³DBind´³rec´³lit³bind„´³tupleµ´³named³pattern´³refµ„³Pattern„„„„„³AnyAtom´³orµµ±bool´³atom³Boolean„„µ±float´³atom³Float„„µ±double´³atom³Double„„µ±int´³atom³
SignedInteger„„µ±string´³atom³String„„µ±bytes´³atom³
 ByteString„„µ±symbol´³atom³Symbol„„µ±embedded´³embedded³any„„„„³Pattern´³orµµ±DDiscard´³refµ„³DDiscard„„µ±DBind´³refµ„³DBind„„µ±DLit´³refµ„³DLit„„µ±	DCompound´³refµ„³	DCompound„„„„³DDiscard´³rec´³lit³_„´³tupleµ„„„³	DCompound´³orµµ±rec´³rec´³lit³rec„´³tupleµ´³named³label³any„´³named³fields´³seqof´³refµ„³Pattern„„„„„„„µ±arr´³rec´³lit³arr„´³tupleµ´³named³items´³seqof´³refµ„³Pattern„„„„„„„µ±dict´³rec´³lit³dict„´³tupleµ´³named³entries´³dictof³any´³refµ„³Pattern„„„„„„„„„„³embeddedType´³refµ³	EntityRef„³Cap„„„„„
\ No newline at end of file
diff --git a/packages/core/protocols/schemas/gatekeeper.prs b/packages/core/protocols/schemas/gatekeeper.prs
index 43da79a..ddc1efd 100644
--- a/packages/core/protocols/schemas/gatekeeper.prs
+++ b/packages/core/protocols/schemas/gatekeeper.prs
@@ -1,5 +1,51 @@
 version 1 .
 embeddedType EntityRef.Cap .
 
-Resolve = <resolve @sturdyref sturdy.SturdyRef @observer #!#!any>.
-Bind = <bind @oid any @key bytes @target #!any>.
+;---------------------------------------------------------------------------
+; Protocol at *gatekeeper* entities
+
+; Assertion. Gatekeeper will attempt to resolve `step`, responding with a `Resolved` to
+; `observer`.
+Resolve = <resolve @step any @observer #!Resolved> .
+Resolved = <accepted @responderSession #!any> / Rejected .
+
+;---------------------------------------------------------------------------
+; Protocol at dataspaces *associated* with gatekeeper entities
+
+; Assertion. Gatekeeper will compute an appropriate `step` from `description` pointing at
+; `target`, and will respond with a `Bound` to `observer`, if supplied.
+Bind = <bind @description any @target #!any @observer BindObserver> .
+BindObserver = @present #!Bound / @absent #f .
+Bound = <bound @step any> / Rejected .
+
+;---------------------------------------------------------------------------
+; Protocol at client-side dataspaces, for resolution utilities
+
+; Assertion. In response to observation of this with appropriate captures/wildcards in `addr`
+; and `resolved`, respondent will follow `route.pathSteps` starting from one of the
+; `route.transports`, asserting `ResolvePath` with the final `Resolved` as well as the selected
+; transport `addr` and a `control` for it.
+ResolvePath = <resolve-path @route Route @addr any @control #!TransportControl @resolved Resolved> .
+ResolvePathStep =
+/ @base <connect-transport @addr any @observer #!Resolved>
+/ @step <resolve-path-step @gatekeeper #!Resolve @pathStep any @observer #!Resolved>
+.
+
+; A `Route` describes a network path that can be followed to reach some target entity.
+;
+; It starts with zero or more possible non-Syndicate `transports`, in preference order. These
+; could be `transportAddress.Tcp` values or similar. They are just suggestions; it's quite
+; possible the endpoint is reachable by some means not listed. The network outside Syndicate
+; is, after all, pretty diverse! In particular, *zero* `transports` may be provided, in which
+; case some out-of-band means has to be used to make that first connection.
+;
+; The `transports` give instructions for contacting the first entity in the `Route` path. Often
+; this will be a `gatekeeper`, or a `noise` protocol endpoint, or both. Occasionally, it may
+; even be the desired target entity. Subsequent `pathSteps` describe how to proceed from the
+; initial entity to the target.
+Route = <route @transports [any ...] @pathSteps any ...> .
+
+TransportControl = ForceDisconnect .
+ForceDisconnect = <force-disconnect> .
+
+Rejected = <rejected @detail any> .
diff --git a/packages/core/protocols/schemas/noise.prs b/packages/core/protocols/schemas/noise.prs
index f14031e..5b52404 100644
--- a/packages/core/protocols/schemas/noise.prs
+++ b/packages/core/protocols/schemas/noise.prs
@@ -2,11 +2,43 @@ version 1 .
 
 ; https://noiseprotocol.org/
 
-; Assertion.
-Connect = <connect @serviceSelector any @initiatorSession #!any> .
+;---------------------------------------------------------------------------
+; Binding and connection
 
-; Assertion (to initiatorSession).
-Accept = <accept @responderSession #!any> .
+; A `step` for use with `gatekeeper.Resolve`.
+NoiseStep = <noise @service ServiceSelector> .
+
+; A `routeStep` for use in a `gatekeeper.Route`.
+NoiseRouteStep = <noise @spec NoiseSpec> .
+
+; A `description` for use with `gatekeeper.Bind` etc.
+NoiseService = <noise @spec NoiseServiceSpec> .
+
+;---------------------------------------------------------------------------
+; Specification of target and bind addresses
+
+ServiceSelector = any .
+
+NoiseSpec = {
+  ; The `serviceSelector` to use in a `NoiseStep` for `gatekeeper.Resolve`.
+  service: ServiceSelector,
+  ; The responder's static public key. If not required (uncommon!), supply the empty ByteString.
+  key: bytes,
+}
+& @protocol NoiseProtocol
+& @preSharedKeys NoisePreSharedKeys
+.
+
+NoiseServiceSpec = @base noise.NoiseSpec & @secretKey SecretKeyField .
+SecretKeyField = @present { secretKey: bytes } / @invalid { secretKey: any } / @absent {} .
+
+; If absent, a default of DefaultProtocol is used. Most services will speak the default.
+NoiseProtocol = @present { protocol: string } / @invalid { protocol: any } / @absent {} .
+DefaultProtocol = "Noise_NK_25519_ChaChaPoly_BLAKE2s" .
+
+; If present, Noise pre-shared-keys (PSKs) are drawn from the sequence as required; if the
+; sequence is exhausted or not supplied, an all-zeros key is used each time a PSK is needed.
+NoisePreSharedKeys = @present { preSharedKeys: [bytes ...] } / @invalid { preSharedKeys: any } / @absent {} .
 
 ; Sessions proceed by sending Packets to the initiatorSession and responderSession according to
 ; the Noise protocol definition. Each Packet represents a complete logical unit of
@@ -28,39 +60,4 @@ Packet = @complete bytes / @fragmented [bytes ...] .
 ;
 ; In summary, each noise.Packet, once (reassembled and) decrypted, will be a sequence of zero
 ; or more machine-encoded protocol.Packets, followed by zero or more 0x80 bytes.
-
-; A `Route` describes a network path that can be followed to reach some target entity.
-;
-; It starts with zero or more possible non-Syndicate `transports`, in preference order. These
-; could be `transportAddress.Tcp` values or similar. They are just suggestions; it's quite
-; possible the endpoint is reachable by some means not listed. The network outside Syndicate
-; is, after all, pretty diverse! In particular, *zero* `transports` may be provided, in which
-; case some out-of-band means has to be used to make that first connection.
-;
-; The `transports` give instructions for contacting the first entity in the `Route` path. Often
-; this will be a `gatekeeper`, or a `noise` protocol endpoint, or both. Occasionally, it may
-; even be the desired target entity. Subsequent `steps` describe how to proceed from the
-; initial entity to the target.
-Route = <route @transports [any ...] @steps RouteStep ...> .
-RouteStep = NoiseStep / GatekeeperStep .
-
-GatekeeperStep = sturdy.SturdyRef .
-
-NoiseStep = <noise @spec NoiseSpec> .
-NoiseSpec = {
-  ; The `serviceSelector` to use in a `Connect`.
-  service: any,
-  ; The responder's static public key. If not required (uncommon!), supply the empty ByteString.
-  key: bytes,
-}
-& @protocol NoiseProtocol
-& @preSharedKeys NoisePreSharedKeys
 .
-
-; If absent, a default of DefaultProtocol is used. Most services will speak the default.
-NoiseProtocol = @present { protocol: string } / @invalid { protocol: any } / @absent {} .
-DefaultProtocol = "Noise_NK_25519_ChaChaPoly_BLAKE2s" .
-
-; If present, Noise pre-shared-keys (PSKs) are drawn from the sequence as required; if the
-; sequence is exhausted or not supplied, an all-zeros key is used each time a PSK is needed.
-NoisePreSharedKeys = @present { preSharedKeys: [bytes ...] } / @invalid { preSharedKeys: any } / @absent {} .
diff --git a/packages/core/protocols/schemas/sturdy.prs b/packages/core/protocols/schemas/sturdy.prs
index 6d36735..04daab3 100644
--- a/packages/core/protocols/schemas/sturdy.prs
+++ b/packages/core/protocols/schemas/sturdy.prs
@@ -1,19 +1,24 @@
 version 1 .
 embeddedType EntityRef.Cap .
 
+; `step` and `description` for use with `gatekeeper.Resolve` and `gatekeeper.Bind` etc.
+SturdyStep = SturdyRef .
+SturdyService = <ref @oid any @key bytes> .
+
 ; The sequence of Caveats is run RIGHT-TO-LEFT.
 ; That is, the newest Caveats are at the right.
 ;
-; Let f = HMAC-BLAKE2s, e = canonical machine-oriented serialization of some preserves value,
-; and k = the original secret key for the ref.
+; Let f(k,d) = HMAC-BLAKE2s-256(k,d)[0..16),
+;     e = canonical machine-oriented serialization of some preserves value, and
+;     k = the original secret key for the ref.
 ;
-; The `sig` is then f(f(f(f(k, e(oid)), ...), Caveat), ...).
+; The `sig` is then f(f(f(f(k, e(oid)), ...), e(Caveat)), ...).
 ;
 SturdyRef = <ref @oid any @caveatChain [Caveat ...] @sig bytes>.
 
 ; embodies 1st-party caveats over assertion structure, but nothing else
 ; can add 3rd-party caveats and richer predicates later
-Caveat = Rewrite / Alts / Reject /@unknown any .
+Caveat = Rewrite / Alts / Reject / @unknown any .
 Rewrite = <rewrite @pattern Pattern @template Template> .
 Reject = <reject @pattern Pattern> .
 Alts = <or @alternatives [Rewrite ...]>.
