@ccmsg/protocol 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/control/peers.ts +11 -0
- package/src/envelope.ts +29 -3
- package/src/messaging/direct-delivery.ts +8 -3
package/package.json
CHANGED
package/src/control/peers.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Static, Type } from "@sinclair/typebox";
|
|
2
|
+
import { InstanceInfo } from "../common/hello.ts";
|
|
2
3
|
import { topicFrame } from "../envelope.ts";
|
|
3
4
|
import { InstanceId, Sid, Timestamp } from "../identifiers.ts";
|
|
4
5
|
import { SessionMetaFields } from "../session-meta.ts";
|
|
@@ -189,5 +190,15 @@ export const PeersFrame = topicFrame(
|
|
|
189
190
|
Type.Object({
|
|
190
191
|
peers: Type.Array(PeerInfo),
|
|
191
192
|
last_live: Type.Array(LastLiveSession),
|
|
193
|
+
/** The instances the sending one can see, itself included, as `hello`
|
|
194
|
+
* answers it. Carried here so that a link going down is something a
|
|
195
|
+
* subscriber learns from the topic it is already on, rather than by
|
|
196
|
+
* greeting again to find out. It is the sender's own view like the two
|
|
197
|
+
* lists above — `reachable` says whether that instance can reach the one it
|
|
198
|
+
* names, which two instances may legitimately disagree about.
|
|
199
|
+
*
|
|
200
|
+
* Absent from an instance that states no view; a client then keeps what it
|
|
201
|
+
* last knew rather than reading the omission as nothing being reachable. */
|
|
202
|
+
instances: Type.Optional(Type.Array(InstanceInfo)),
|
|
192
203
|
}),
|
|
193
204
|
);
|
package/src/envelope.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Static, type TSchema, Type } from "@sinclair/typebox";
|
|
2
2
|
import { ErrorBody } from "./errors.ts";
|
|
3
|
-
import { InstanceId } from "./identifiers.ts";
|
|
3
|
+
import { InstanceId, Role, Sid } from "./identifiers.ts";
|
|
4
4
|
|
|
5
5
|
/** The generation of this wire contract. Within a generation, only optional
|
|
6
6
|
* fields and whole new ops may be added; a removal or a change of meaning
|
|
@@ -8,14 +8,27 @@ import { InstanceId } from "./identifiers.ts";
|
|
|
8
8
|
* connections and on mesh links alike. */
|
|
9
9
|
export const PROTOCOL_VERSION = 2;
|
|
10
10
|
|
|
11
|
+
/** The identity a forwarded request is dispatched as: the connection the
|
|
12
|
+
* forwarding instance received it on, in the two fields that decide anything —
|
|
13
|
+
* the role the attribute table is read against, and the session it speaks for. */
|
|
14
|
+
export const CallerIdentity = Type.Object(
|
|
15
|
+
{
|
|
16
|
+
role: Role,
|
|
17
|
+
/** Present exactly when the role is `session`, as in `hello`. */
|
|
18
|
+
sid: Type.Optional(Sid),
|
|
19
|
+
},
|
|
20
|
+
{ $id: "CallerIdentity" },
|
|
21
|
+
);
|
|
22
|
+
export type CallerIdentity = Static<typeof CallerIdentity>;
|
|
23
|
+
|
|
11
24
|
/** Fields a request carries in addition to its own arguments.
|
|
12
25
|
*
|
|
13
26
|
* `request_id` pairs a reply with its request so one connection can run its
|
|
14
27
|
* requests concurrently instead of in arrival order. Uniqueness only has to
|
|
15
28
|
* hold among one connection's in-flight requests.
|
|
16
29
|
*
|
|
17
|
-
* The
|
|
18
|
-
*
|
|
30
|
+
* The mesh fields are the whole of the mesh plane: an op forwarded to another
|
|
31
|
+
* instance is the same op in the same shape, wrapped in these. */
|
|
19
32
|
export const RequestEnvelope = Type.Object(
|
|
20
33
|
{
|
|
21
34
|
request_id: Type.String({ minLength: 1 }),
|
|
@@ -28,6 +41,19 @@ export const RequestEnvelope = Type.Object(
|
|
|
28
41
|
/** Instances this request has already passed through, in order. A request
|
|
29
42
|
* that would revisit an instance is dropped rather than looped. */
|
|
30
43
|
hops: Type.Optional(Type.Array(InstanceId)),
|
|
44
|
+
/** Who the forwarding instance says made this request.
|
|
45
|
+
*
|
|
46
|
+
* The destination runs every stage of dispatch again against this identity
|
|
47
|
+
* — the role check, the capability check, the whole of it — rather than
|
|
48
|
+
* taking the forwarder's outcome for it. What it does take is the identity
|
|
49
|
+
* itself: the forwarder is an authenticated peer, so what it says about who
|
|
50
|
+
* called is believed, which is the assumption that holds inside one
|
|
51
|
+
* deployment and nowhere else.
|
|
52
|
+
*
|
|
53
|
+
* A forwarded request that names none is dispatched as the `instance` role
|
|
54
|
+
* it arrived on, which the attribute table already answers: an
|
|
55
|
+
* instance-local op called by an instance is `forbidden`. */
|
|
56
|
+
caller: Type.Optional(CallerIdentity),
|
|
31
57
|
},
|
|
32
58
|
{ $id: "RequestEnvelope" },
|
|
33
59
|
);
|
|
@@ -23,9 +23,14 @@ export const DIRECT_DELIVERY_TAG = "cross-session-message";
|
|
|
23
23
|
*
|
|
24
24
|
* Not the sender's session and not a `uds:` path. Those are the addresses the
|
|
25
25
|
* harness itself dials, and dialing one that has gone ends the recipient's turn
|
|
26
|
-
* in failure
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* in failure. A name asks for no answer, so there is nothing to dangle — the
|
|
27
|
+
* way back is the reply line, which the recipient runs rather than the harness.
|
|
28
|
+
*
|
|
29
|
+
* The frame this wrapper is written in is a separate address: there the
|
|
30
|
+
* instance may name a `uds:` socket of its own, which is where the receiving
|
|
31
|
+
* harness writes what became of the delivery. Those receipts are negative only
|
|
32
|
+
* — `refused`, `denied`, `dropped`, `expired`, `held` — so silence within the
|
|
33
|
+
* window is what says the message was taken. */
|
|
29
34
|
export const DIRECT_DELIVERY_FROM = "ccmsg";
|
|
30
35
|
|
|
31
36
|
/** What the recipient is told the sender is doing. `prompting` is what a peer
|