diff --git a/syndicate/protocols/schema-bundle.bin b/syndicate/protocols/schema-bundle.bin
index e9d9294..d46a413 100644
--- a/syndicate/protocols/schema-bundle.bin
+++ b/syndicate/protocols/schema-bundle.bin
@@ -7,8 +7,8 @@ QueryValue
 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„„„„„³	NoiseSpec´³andµ´³dict·³key´³named³key´³atom³
-ByteString„„³service´³named³service´³refµ„³ServiceSelector„„„„´³named³protocol´³refµ„³
NoiseProtocol„„´³named³
preSharedKeys´³refµ„³NoisePreSharedKeys„„„„³
NoiseProtocol´³orµµ±present´³dict·³protocol´³named³protocol´³atom³String„„„„„µ±invalid´³dict·³protocol´³named³protocol³any„„„„µ±absent´³dict·„„„„„³
NoiseStepType´³lit³noise„³SecretKeyField´³orµµ±present´³dict·³	secretKey´³named³	secretKey´³atom³
+ByteString„„„„„³	Initiator´³rec´³lit³	initiator„´³tupleµ´³named³initiatorSession´³embedded´³refµ„³Packet„„„„„„³	NoiseSpec´³andµ´³dict·³key´³named³key´³atom³
+ByteString„„³service´³named³service´³refµ„³ServiceSelector„„„„´³named³protocol´³refµ„³
NoiseProtocol„„´³named³
preSharedKeys´³refµ„³NoisePreSharedKeys„„„„³SessionItem´³orµµ±	Initiator´³refµ„³	Initiator„„µ±Packet´³refµ„³Packet„„„„³
NoiseProtocol´³orµµ±present´³dict·³protocol´³named³protocol´³atom³String„„„„„µ±invalid´³dict·³protocol´³named³protocol³any„„„„µ±absent´³dict·„„„„„³
NoiseStepType´³lit³noise„³SecretKeyField´³orµµ±present´³dict·³	secretKey´³named³	secretKey´³atom³
 ByteString„„„„„µ±invalid´³dict·³	secretKey´³named³	secretKey³any„„„„µ±absent´³dict·„„„„„³DefaultProtocol´³lit±!Noise_NK_25519_ChaChaPoly_BLAKE2s„³NoiseStepDetail´³refµ„³ServiceSelector„³ServiceSelector³any³NoiseServiceSpec´³andµ´³named³base´³refµ„³	NoiseSpec„„´³named³	secretKey´³refµ„³SecretKeyField„„„„³NoisePreSharedKeys´³orµµ±present´³dict·³
preSharedKeys´³named³
preSharedKeys´³seqof´³atom³
 ByteString„„„„„„µ±invalid´³dict·³
preSharedKeys´³named³
preSharedKeys³any„„„„µ±absent´³dict·„„„„„³NoisePathStepDetail´³refµ„³	NoiseSpec„³NoiseDescriptionDetail´³refµ„³NoiseServiceSpec„„³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³
diff --git a/syndicate/protocols/schemas/gatekeeper.prs b/syndicate/protocols/schemas/gatekeeper.prs
index 656ed12..9ba662c 100644
--- a/syndicate/protocols/schemas/gatekeeper.prs
+++ b/syndicate/protocols/schemas/gatekeeper.prs
@@ -13,8 +13,37 @@ Step = <<rec> @stepType symbol [@detail any]> .
 # ---------------------------------------------------------------------------
 # Protocol at dataspaces *associated* with gatekeeper entities
 
-# Assertion. Gatekeeper will compute an appropriate PathStep from `description` pointing at
-# `target`, and will respond with a `Bound` to `observer` (if supplied).
+# ## Handling `Resolve` requests
+#
+# When the gatekeeper entity receives a `Resolve` assertion (call it R1), it
+#
+#  1. asserts a `Resolve` (call it R2) into its associated dataspace that
+#     is the same as R1 except it has a different `observer`; and
+#
+#  2. observes a `Bind` with `description` matching the `step` of R1/R2
+#     according to `stepType` (e.g. treatment of SturdyStepType is not the
+#     same as treatment of NoiseStepType).
+#
+# Normally, an appropriate `Bind` is expected to exist. If the gatekeeper
+# sees the `Bind` first, it takes the `target` from it and does whatever
+# `stepType` mandates before replying to R1's observer.
+#
+# However, if a `Resolved` is asserted to R2's observer before a `Bind`
+# appears, that resolution is relayed on to R1's observer directly, be it
+# positive or negative, and the gatekeeper stops waiting for a `Bind`.
+#
+# 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 could also use `Resolve` requests to synthesize a
+# `Bind` in a "just-in-time" fashion.
+#
+# ## General treatment of `Bind` assertions
+#
+# When the gatekeeper sees a `Bind`, independently of any potential
+# `Resolve` requests, it computes an appropriate PathStep from
+# `description` pointing at `target`, and responds with a `Bound` to
+# `observer` (if supplied).
+#
 Bind = <bind @description Description @target #:any @observer BindObserver> .
 Description = <<rec> @stepType symbol [@detail any]> .
 BindObserver = @present #:Bound / @absent #f .
diff --git a/syndicate/protocols/schemas/noise.prs b/syndicate/protocols/schemas/noise.prs
index 3558b36..90eba05 100644
--- a/syndicate/protocols/schemas/noise.prs
+++ b/syndicate/protocols/schemas/noise.prs
@@ -42,13 +42,30 @@ DefaultProtocol = "Noise_NK_25519_ChaChaPoly_BLAKE2s" .
 # 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
+# ---------------------------------------------------------------------------
+# Handshaking and running a session
+
+# 1. initiator asserts <resolve <noise ServiceSelector> #:A> at Gatekeeper
+# 2. gatekeeper asserts <accepted #:B> at #:A
+# 3. initiator asserts <initiator #:C> at #:B and then sends `Packet`s to #:B
+# 4. responder sends `Packet`s to #:C
+#
+# Sessions begin with introduction of initiator (#:C) and responder (#:B) to each other, and
+# then proceed by sending `Packet`s (from #:C) to #:B and (from #:B) to #:C according to
+# the Noise protocol definition. Each `Packet` represents a complete logical unit of
 # communication; for example, a complete Turn when layering the Syndicate protocol over Noise.
 # Note well the restriction on Noise messages: no individual complete packet or packet fragment
-# may exceed 65535 bytes (N.B. not 65536!). When `fragmented`, each portion of a Packet is a
+# may exceed 65535 bytes (N.B. not 65536!). When `fragmented`, each portion of a `Packet` is a
 # complete Noise "transport message"; when `complete`, the whole thing is likewise a complete
 # "transport message".
+#
+# Retraction of the `Introduction` ends the session from the initiator-side; retraction of the
+# `<accepted ...>` assertion ends the session from the responder-side.
+
+SessionItem = Initiator / Packet .
+# Assertion
+Initiator = <initiator @initiatorSession #:Packet> .
+# Message
 Packet = @complete bytes / @fragmented [bytes ...] .
 
 # When layering Syndicate protocol over noise,
