@tribe-nest/media-protocol 1.0.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.
Files changed (84) hide show
  1. package/build/_fixtures/joinTokenVectors.json +103 -0
  2. package/build/claims.d.ts +327 -0
  3. package/build/claims.d.ts.map +1 -0
  4. package/build/claims.js +91 -0
  5. package/build/claims.js.map +1 -0
  6. package/build/codecs.d.ts +102 -0
  7. package/build/codecs.d.ts.map +1 -0
  8. package/build/codecs.js +138 -0
  9. package/build/codecs.js.map +1 -0
  10. package/build/errorSchema.d.ts +8 -0
  11. package/build/errorSchema.d.ts.map +1 -0
  12. package/build/errorSchema.js +12 -0
  13. package/build/errorSchema.js.map +1 -0
  14. package/build/errors.d.ts +18 -0
  15. package/build/errors.d.ts.map +1 -0
  16. package/build/errors.js +47 -0
  17. package/build/errors.js.map +1 -0
  18. package/build/frames.d.ts +1291 -0
  19. package/build/frames.d.ts.map +1 -0
  20. package/build/frames.js +222 -0
  21. package/build/frames.js.map +1 -0
  22. package/build/grants.d.ts +307 -0
  23. package/build/grants.d.ts.map +1 -0
  24. package/build/grants.js +228 -0
  25. package/build/grants.js.map +1 -0
  26. package/build/index.d.ts +24 -0
  27. package/build/index.d.ts.map +1 -0
  28. package/build/index.js +40 -0
  29. package/build/index.js.map +1 -0
  30. package/build/mp-build/_fixtures/joinTokenVectors.json +103 -0
  31. package/build/mp-build/claims.d.ts +299 -0
  32. package/build/mp-build/claims.d.ts.map +1 -0
  33. package/build/mp-build/claims.js +60 -0
  34. package/build/mp-build/claims.js.map +1 -0
  35. package/build/mp-build/errorSchema.d.ts +8 -0
  36. package/build/mp-build/errorSchema.d.ts.map +1 -0
  37. package/build/mp-build/errorSchema.js +12 -0
  38. package/build/mp-build/errorSchema.js.map +1 -0
  39. package/build/mp-build/errors.d.ts +18 -0
  40. package/build/mp-build/errors.d.ts.map +1 -0
  41. package/build/mp-build/errors.js +47 -0
  42. package/build/mp-build/errors.js.map +1 -0
  43. package/build/mp-build/frames.d.ts +786 -0
  44. package/build/mp-build/frames.d.ts.map +1 -0
  45. package/build/mp-build/frames.js +153 -0
  46. package/build/mp-build/frames.js.map +1 -0
  47. package/build/mp-build/grants.d.ts +245 -0
  48. package/build/mp-build/grants.d.ts.map +1 -0
  49. package/build/mp-build/grants.js +135 -0
  50. package/build/mp-build/grants.js.map +1 -0
  51. package/build/mp-build/index.d.ts +23 -0
  52. package/build/mp-build/index.d.ts.map +1 -0
  53. package/build/mp-build/index.js +39 -0
  54. package/build/mp-build/index.js.map +1 -0
  55. package/build/mp-build/token.d.ts +66 -0
  56. package/build/mp-build/token.d.ts.map +1 -0
  57. package/build/mp-build/token.js +208 -0
  58. package/build/mp-build/token.js.map +1 -0
  59. package/build/mp-build/version.d.ts +9 -0
  60. package/build/mp-build/version.d.ts.map +1 -0
  61. package/build/mp-build/version.js +12 -0
  62. package/build/mp-build/version.js.map +1 -0
  63. package/build/token.d.ts +66 -0
  64. package/build/token.d.ts.map +1 -0
  65. package/build/token.js +208 -0
  66. package/build/token.js.map +1 -0
  67. package/build/version.d.ts +9 -0
  68. package/build/version.d.ts.map +1 -0
  69. package/build/version.js +12 -0
  70. package/build/version.js.map +1 -0
  71. package/package.json +53 -0
  72. package/src/_fixtures/joinTokenVectors.json +103 -0
  73. package/src/_tests/codecs.spec.ts +66 -0
  74. package/src/_tests/publishGrants.spec.ts +109 -0
  75. package/src/_tests/token.spec.ts +272 -0
  76. package/src/claims.ts +116 -0
  77. package/src/codecs.ts +165 -0
  78. package/src/errorSchema.ts +9 -0
  79. package/src/errors.ts +44 -0
  80. package/src/frames.ts +234 -0
  81. package/src/grants.ts +248 -0
  82. package/src/index.ts +23 -0
  83. package/src/token.ts +237 -0
  84. package/src/version.ts +8 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../src/frames.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,kBAAkB;;;;IAK3B;;8CAE0C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmB1C,4DAA4D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuC9D,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;AAEnD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;IAK5B;gFAC4E;;;;;;;;;;;;;IAI9E,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;iEACiE;AACjE,eAAO,MAAM,UAAU;;;;;;;;;;;;EAIrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,gBAAgB;;;;IAKzB,4EAA4E;;;;;;;;;;;;;;;IAG5E;;qCAEiC;;;;;;;;;;;;;;;;;;;;;;;;;;;IAGjC;oCACgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAuBlC,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AAE5C,qCAAqC;AACrC,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;IA7D1B;gFAC4E;;;;;;;;;;;;;;;;;IAsB5E,4EAA4E;;;;;;;;;;;;;;;IAG5E;;qCAEiC;;;;;;;;;;;;;;;;;;;;;;;;;;;IAGjC;oCACgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA6B6C,CAAC;AAClF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,OAAO,GAAI,OAAO,WAAW,KAAG,KAAK,IAAI,UAA8B,CAAC"}
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isEvent = exports.serverFrameSchema = exports.eventFrameSchema = exports.peerSchema = exports.responseFrameSchema = exports.requestFrameSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const errorSchema_1 = require("./errorSchema");
6
+ const grants_1 = require("./grants");
7
+ /**
8
+ * The client <-> node signalling wire.
9
+ *
10
+ * One envelope, stated once. Six phase plans independently invented four
11
+ * different ones (`m` / `type` / `method` / `action`, numeric versus string
12
+ * ids, `result` versus `data`), and every mismatch is a hard parse failure
13
+ * discovered at integration by a different pair of people.
14
+ *
15
+ * ## Shape
16
+ *
17
+ * Requests carry an `id` and get exactly one reply with the same `id`. Events
18
+ * carry no `id` and are never replied to. That is the whole protocol; anything
19
+ * needing more structure is a request that returns a handle.
20
+ *
21
+ * ## What is deliberately NOT here
22
+ *
23
+ * No pipe verb, no node id, no mesh address. A client cannot express "pipe this
24
+ * producer to that node", because piping is a side effect of `consume` and the
25
+ * only way to keep it lazy is for eager piping to be unsayable.
26
+ */
27
+ exports.requestFrameSchema = zod_1.z.discriminatedUnion("method", [
28
+ zod_1.z.object({
29
+ method: zod_1.z.literal("join"),
30
+ id: zod_1.z.number().int().nonnegative(),
31
+ protocolVersion: zod_1.z.number().int(),
32
+ /** The token travels in the first frame, NEVER in the URL: a query string
33
+ * lands in load-balancer access logs, and a join ticket in a log is a join
34
+ * ticket for anyone who can read logs. */
35
+ token: zod_1.z.string().min(1),
36
+ }),
37
+ zod_1.z.object({
38
+ method: zod_1.z.literal("createTransport"),
39
+ id: zod_1.z.number().int().nonnegative(),
40
+ direction: zod_1.z.enum(["send", "recv"]),
41
+ }),
42
+ zod_1.z.object({
43
+ method: zod_1.z.literal("connectTransport"),
44
+ id: zod_1.z.number().int().nonnegative(),
45
+ transportId: zod_1.z.string(),
46
+ dtlsParameters: zod_1.z.unknown(),
47
+ }),
48
+ zod_1.z.object({
49
+ method: zod_1.z.literal("produce"),
50
+ id: zod_1.z.number().int().nonnegative(),
51
+ transportId: zod_1.z.string(),
52
+ kind: zod_1.z.enum(["audio", "video"]),
53
+ /** Opaque to this package: only mediasoup interprets it. */
54
+ rtpParameters: zod_1.z.unknown(),
55
+ appData: zod_1.z.record(zod_1.z.unknown()).optional(),
56
+ }),
57
+ zod_1.z.object({
58
+ method: zod_1.z.literal("consume"),
59
+ id: zod_1.z.number().int().nonnegative(),
60
+ transportId: zod_1.z.string(),
61
+ producerId: zod_1.z.string(),
62
+ rtpCapabilities: zod_1.z.unknown(),
63
+ }),
64
+ zod_1.z.object({
65
+ method: zod_1.z.literal("pauseProducer"),
66
+ id: zod_1.z.number().int().nonnegative(),
67
+ producerId: zod_1.z.string(),
68
+ }),
69
+ zod_1.z.object({
70
+ method: zod_1.z.literal("resumeProducer"),
71
+ id: zod_1.z.number().int().nonnegative(),
72
+ producerId: zod_1.z.string(),
73
+ }),
74
+ zod_1.z.object({
75
+ method: zod_1.z.literal("resumeConsumer"),
76
+ id: zod_1.z.number().int().nonnegative(),
77
+ consumerId: zod_1.z.string(),
78
+ }),
79
+ zod_1.z.object({
80
+ method: zod_1.z.literal("closeProducer"),
81
+ id: zod_1.z.number().int().nonnegative(),
82
+ producerId: zod_1.z.string(),
83
+ }),
84
+ /** A keyframe request. Without it every active-speaker switch shows garbage
85
+ * until the encoder happens to send one. */
86
+ zod_1.z.object({
87
+ method: zod_1.z.literal("requestKeyFrame"),
88
+ id: zod_1.z.number().int().nonnegative(),
89
+ consumerId: zod_1.z.string(),
90
+ }),
91
+ zod_1.z.object({ method: zod_1.z.literal("leave"), id: zod_1.z.number().int().nonnegative() }),
92
+ ]);
93
+ exports.responseFrameSchema = zod_1.z.union([
94
+ zod_1.z.object({ id: zod_1.z.number().int().nonnegative(), ok: zod_1.z.literal(true), data: zod_1.z.unknown() }),
95
+ zod_1.z.object({
96
+ id: zod_1.z.number().int().nonnegative(),
97
+ ok: zod_1.z.literal(false),
98
+ /** The client sees a CODE, never why a token was refused. Telling a caller
99
+ * whether the signature or the room was wrong turns this into an oracle. */
100
+ code: errorSchema_1.mediaErrorCodeSchema,
101
+ message: zod_1.z.string().optional(),
102
+ }),
103
+ ]);
104
+ /** A participant as everyone else sees them. Grants are NOT included: what
105
+ * someone else is permitted to do is not the room's business. */
106
+ exports.peerSchema = zod_1.z.object({
107
+ identity: zod_1.z.string(),
108
+ name: zod_1.z.string().optional(),
109
+ kind: grants_1.participantKindSchema,
110
+ });
111
+ exports.eventFrameSchema = zod_1.z.discriminatedUnion("event", [
112
+ zod_1.z.object({
113
+ event: zod_1.z.literal("joined"),
114
+ identity: zod_1.z.string(),
115
+ room: zod_1.z.string(),
116
+ /** Everything the client needs to build its device before any transport. */
117
+ routerRtpCapabilities: zod_1.z.unknown(),
118
+ peers: zod_1.z.array(exports.peerSchema),
119
+ /** Producers this participant is ALLOWED to see. The barrier is applied to
120
+ * the snapshot, not only at consume: an identity it may not receive must
121
+ * not be discoverable either. */
122
+ producers: zod_1.z.array(zod_1.z.object({ producerId: zod_1.z.string(), identity: zod_1.z.string(), kind: zod_1.z.enum(["audio", "video"]) })),
123
+ iceServers: zod_1.z.array(zod_1.z.object({ urls: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]), username: zod_1.z.string().optional(), credential: zod_1.z.string().optional() })),
124
+ /** True while any `kind: "egress"` participant is present. A consent
125
+ * requirement, not a nicety. */
126
+ recording: zod_1.z.boolean(),
127
+ }),
128
+ zod_1.z.object({ event: zod_1.z.literal("peerJoined"), peer: exports.peerSchema }),
129
+ zod_1.z.object({ event: zod_1.z.literal("peerLeft"), identity: zod_1.z.string() }),
130
+ zod_1.z.object({
131
+ event: zod_1.z.literal("producerAppeared"),
132
+ producerId: zod_1.z.string(),
133
+ identity: zod_1.z.string(),
134
+ kind: zod_1.z.enum(["audio", "video"]),
135
+ }),
136
+ zod_1.z.object({ event: zod_1.z.literal("producerClosed"), producerId: zod_1.z.string() }),
137
+ zod_1.z.object({ event: zod_1.z.literal("producerPaused"), producerId: zod_1.z.string(), paused: zod_1.z.boolean() }),
138
+ /** The active set the node wants this client to consume. How a 1,000-person
139
+ * room stays survivable: the server decides, the client follows. */
140
+ zod_1.z.object({ event: zod_1.z.literal("activeSpeakers"), producerIds: zod_1.z.array(zod_1.z.string()) }),
141
+ zod_1.z.object({ event: zod_1.z.literal("recordingChanged"), recording: zod_1.z.boolean() }),
142
+ /** This node is going away. Reconnect through MEDIA_URL; do not retry here. */
143
+ zod_1.z.object({ event: zod_1.z.literal("draining"), reconnectAfterMs: zod_1.z.number().int().nonnegative() }),
144
+ zod_1.z.object({ event: zod_1.z.literal("roomClosed"), reason: zod_1.z.string() }),
145
+ /** Barrier changes are pushed, because a rule that only applies at join is a
146
+ * rule that does not apply to anyone already in the room. */
147
+ zod_1.z.object({ event: zod_1.z.literal("subscribeRuleChanged"), subscribe: grants_1.subscribeRuleSchema }),
148
+ ]);
149
+ /** Anything arriving from a node. */
150
+ exports.serverFrameSchema = zod_1.z.union([exports.responseFrameSchema, exports.eventFrameSchema]);
151
+ const isEvent = (frame) => "event" in frame;
152
+ exports.isEvent = isEvent;
153
+ //# sourceMappingURL=frames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.js","sourceRoot":"","sources":["../src/frames.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,245 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * What a participant is allowed to do, and - the part that matters most - who
4
+ * they may receive.
5
+ *
6
+ * ## Why the schema is `.strict()`
7
+ *
8
+ * This is the single line that stops the information barrier failing open.
9
+ *
10
+ * Ten phase plans were written in parallel and two of them disagreed on a name:
11
+ * one minted `grants.subscribeOnly`, the other read `grants.subscribe`. The
12
+ * reader would have found nothing, applied its documented default ("an absent
13
+ * rule means everyone"), and two Mandanten in one room would have received each
14
+ * other with nothing anywhere erroring. That is a §203 incident whose start
15
+ * date is unrecoverable from logs.
16
+ *
17
+ * `.strict()` turns that class of mistake from silent permissiveness into a
18
+ * loud verification failure: a token carrying an unknown grant key is rejected
19
+ * with `bad_claims` rather than partially understood. Any future rename fails
20
+ * the same way, at mint time, in a test.
21
+ */
22
+ /**
23
+ * Who a participant may receive, or be received by.
24
+ *
25
+ * A boolean cannot express an information barrier. "A and B are in one room and
26
+ * must not receive each other, while C may receive both" is the actual
27
+ * requirement for a Kanzlei, and it is a per-identity rule rather than a
28
+ * per-room one.
29
+ */
30
+ export declare const subscribeRuleSchema: z.ZodDiscriminatedUnion<"mode", [z.ZodObject<{
31
+ mode: z.ZodLiteral<"all">;
32
+ }, "strict", z.ZodTypeAny, {
33
+ mode: "all";
34
+ }, {
35
+ mode: "all";
36
+ }>, z.ZodObject<{
37
+ mode: z.ZodLiteral<"none">;
38
+ }, "strict", z.ZodTypeAny, {
39
+ mode: "none";
40
+ }, {
41
+ mode: "none";
42
+ }>, z.ZodObject<{
43
+ mode: z.ZodLiteral<"allow">;
44
+ identities: z.ZodArray<z.ZodString, "many">;
45
+ }, "strict", z.ZodTypeAny, {
46
+ mode: "allow";
47
+ identities: string[];
48
+ }, {
49
+ mode: "allow";
50
+ identities: string[];
51
+ }>, z.ZodObject<{
52
+ mode: z.ZodLiteral<"deny">;
53
+ identities: z.ZodArray<z.ZodString, "many">;
54
+ }, "strict", z.ZodTypeAny, {
55
+ mode: "deny";
56
+ identities: string[];
57
+ }, {
58
+ mode: "deny";
59
+ identities: string[];
60
+ }>]>;
61
+ export type SubscribeRule = z.infer<typeof subscribeRuleSchema>;
62
+ export declare const participantKindSchema: z.ZodEnum<["human", "agent", "egress", "sip", "ingress"]>;
63
+ export type ParticipantKind = z.infer<typeof participantKindSchema>;
64
+ /**
65
+ * Transport authority is NOT media authority, which is why this is its own
66
+ * claim rather than something inferred from `kind`.
67
+ *
68
+ * A browser holding a perfectly valid join token must not be able to ask for a
69
+ * `PlainTransport` pointed at an address of its choosing. That is an RTP
70
+ * reflector and an unauthenticated publish path into whatever room the token
71
+ * names. Three phases independently needed to refuse it and none could, because
72
+ * nothing carried the fact.
73
+ */
74
+ export declare const transportKindSchema: z.ZodEnum<["webrtc", "plain", "pipe"]>;
75
+ export type TransportKind = z.infer<typeof transportKindSchema>;
76
+ export declare const mediaGrantsSchema: z.ZodObject<{
77
+ canPublish: z.ZodBoolean;
78
+ canSubscribe: z.ZodBoolean;
79
+ canPublishData: z.ZodBoolean;
80
+ /** Who I may receive. Omitted means `{ mode: "all" }`. */
81
+ subscribe: z.ZodOptional<z.ZodDiscriminatedUnion<"mode", [z.ZodObject<{
82
+ mode: z.ZodLiteral<"all">;
83
+ }, "strict", z.ZodTypeAny, {
84
+ mode: "all";
85
+ }, {
86
+ mode: "all";
87
+ }>, z.ZodObject<{
88
+ mode: z.ZodLiteral<"none">;
89
+ }, "strict", z.ZodTypeAny, {
90
+ mode: "none";
91
+ }, {
92
+ mode: "none";
93
+ }>, z.ZodObject<{
94
+ mode: z.ZodLiteral<"allow">;
95
+ identities: z.ZodArray<z.ZodString, "many">;
96
+ }, "strict", z.ZodTypeAny, {
97
+ mode: "allow";
98
+ identities: string[];
99
+ }, {
100
+ mode: "allow";
101
+ identities: string[];
102
+ }>, z.ZodObject<{
103
+ mode: z.ZodLiteral<"deny">;
104
+ identities: z.ZodArray<z.ZodString, "many">;
105
+ }, "strict", z.ZodTypeAny, {
106
+ mode: "deny";
107
+ identities: string[];
108
+ }, {
109
+ mode: "deny";
110
+ identities: string[];
111
+ }>]>>;
112
+ /**
113
+ * Who may receive ME. Enforced on the PRODUCER side from the owner's stored
114
+ * grants, so a barrier holds even if the other party's token is generous.
115
+ */
116
+ visibleTo: z.ZodOptional<z.ZodDiscriminatedUnion<"mode", [z.ZodObject<{
117
+ mode: z.ZodLiteral<"all">;
118
+ }, "strict", z.ZodTypeAny, {
119
+ mode: "all";
120
+ }, {
121
+ mode: "all";
122
+ }>, z.ZodObject<{
123
+ mode: z.ZodLiteral<"none">;
124
+ }, "strict", z.ZodTypeAny, {
125
+ mode: "none";
126
+ }, {
127
+ mode: "none";
128
+ }>, z.ZodObject<{
129
+ mode: z.ZodLiteral<"allow">;
130
+ identities: z.ZodArray<z.ZodString, "many">;
131
+ }, "strict", z.ZodTypeAny, {
132
+ mode: "allow";
133
+ identities: string[];
134
+ }, {
135
+ mode: "allow";
136
+ identities: string[];
137
+ }>, z.ZodObject<{
138
+ mode: z.ZodLiteral<"deny">;
139
+ identities: z.ZodArray<z.ZodString, "many">;
140
+ }, "strict", z.ZodTypeAny, {
141
+ mode: "deny";
142
+ identities: string[];
143
+ }, {
144
+ mode: "deny";
145
+ identities: string[];
146
+ }>]>>;
147
+ publishKinds: z.ZodOptional<z.ZodArray<z.ZodEnum<["audio", "video", "screen"]>, "many">>;
148
+ /**
149
+ * Bypasses the top-N active-speaker refusal. Mintable ONLY for a non-human
150
+ * kind: a recorder needs every track, a human asking for 1,000 does not.
151
+ */
152
+ subscribeExempt: z.ZodOptional<z.ZodBoolean>;
153
+ /** Hard ceiling on concurrent consumers, for a non-human participant. */
154
+ maxSubscriptions: z.ZodOptional<z.ZodNumber>;
155
+ }, "strict", z.ZodTypeAny, {
156
+ canPublish: boolean;
157
+ canSubscribe: boolean;
158
+ canPublishData: boolean;
159
+ subscribe?: {
160
+ mode: "all";
161
+ } | {
162
+ mode: "none";
163
+ } | {
164
+ mode: "allow";
165
+ identities: string[];
166
+ } | {
167
+ mode: "deny";
168
+ identities: string[];
169
+ } | undefined;
170
+ visibleTo?: {
171
+ mode: "all";
172
+ } | {
173
+ mode: "none";
174
+ } | {
175
+ mode: "allow";
176
+ identities: string[];
177
+ } | {
178
+ mode: "deny";
179
+ identities: string[];
180
+ } | undefined;
181
+ publishKinds?: ("audio" | "video" | "screen")[] | undefined;
182
+ subscribeExempt?: boolean | undefined;
183
+ maxSubscriptions?: number | undefined;
184
+ }, {
185
+ canPublish: boolean;
186
+ canSubscribe: boolean;
187
+ canPublishData: boolean;
188
+ subscribe?: {
189
+ mode: "all";
190
+ } | {
191
+ mode: "none";
192
+ } | {
193
+ mode: "allow";
194
+ identities: string[];
195
+ } | {
196
+ mode: "deny";
197
+ identities: string[];
198
+ } | undefined;
199
+ visibleTo?: {
200
+ mode: "all";
201
+ } | {
202
+ mode: "none";
203
+ } | {
204
+ mode: "allow";
205
+ identities: string[];
206
+ } | {
207
+ mode: "deny";
208
+ identities: string[];
209
+ } | undefined;
210
+ publishKinds?: ("audio" | "video" | "screen")[] | undefined;
211
+ subscribeExempt?: boolean | undefined;
212
+ maxSubscriptions?: number | undefined;
213
+ }>;
214
+ export type MediaGrants = z.infer<typeof mediaGrantsSchema>;
215
+ /**
216
+ * May `consumer` receive `producer`? Two-sided, and deny wins.
217
+ *
218
+ * One-sided is not enough. If only the consumer's rule were consulted, a
219
+ * participant whose own token was minted generously could receive someone who
220
+ * had been explicitly walled off from them. So the producer's `visibleTo` is
221
+ * checked from the OWNER's stored grants, not from anything the consumer
222
+ * presents.
223
+ *
224
+ * **An unreadable owner is `false`.** If the producer's grants cannot be found,
225
+ * this refuses rather than falling through to the permissive default. A missing
226
+ * record is exactly the state a race or a partial failure produces, and in a
227
+ * room with an information barrier the safe reading of "I do not know" is "no".
228
+ * Note this differs deliberately from an ABSENT RULE on a record that WAS
229
+ * found, which means everyone.
230
+ */
231
+ export declare function maySubscribe(input: {
232
+ consumerIdentity: string;
233
+ consumerGrants: MediaGrants;
234
+ producerIdentity: string;
235
+ /** `undefined` when the owner's record could not be read. */
236
+ ownerGrants: MediaGrants | undefined;
237
+ }): boolean;
238
+ /**
239
+ * `subscribeExempt` and `maxSubscriptions` are meaningless for a human and
240
+ * dangerous if mintable for one: exemption is what lets a recorder take every
241
+ * track, and a human with it can pull a thousand streams through a node.
242
+ * Checked at MINT time so a bad token never exists.
243
+ */
244
+ export declare function grantsAreValidForKind(grants: MediaGrants, kind: ParticipantKind): true | string;
245
+ //# sourceMappingURL=grants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAK9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,qBAAqB,2DAAyD,CAAC;AAC5F,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,wCAAsC,CAAC;AACvE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,iBAAiB;;;;IAK1B,0DAA0D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE1D;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAGH;;;OAGG;;IAEH,yEAAyE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGlE,CAAC;AAEZ,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAmB5D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,WAAW,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,6DAA6D;IAC7D,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;CACtC,GAAG,OAAO,CAUV;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,GAAG,MAAM,CAW/F"}
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mediaGrantsSchema = exports.transportKindSchema = exports.participantKindSchema = exports.subscribeRuleSchema = void 0;
4
+ exports.maySubscribe = maySubscribe;
5
+ exports.grantsAreValidForKind = grantsAreValidForKind;
6
+ const zod_1 = require("zod");
7
+ /**
8
+ * What a participant is allowed to do, and - the part that matters most - who
9
+ * they may receive.
10
+ *
11
+ * ## Why the schema is `.strict()`
12
+ *
13
+ * This is the single line that stops the information barrier failing open.
14
+ *
15
+ * Ten phase plans were written in parallel and two of them disagreed on a name:
16
+ * one minted `grants.subscribeOnly`, the other read `grants.subscribe`. The
17
+ * reader would have found nothing, applied its documented default ("an absent
18
+ * rule means everyone"), and two Mandanten in one room would have received each
19
+ * other with nothing anywhere erroring. That is a §203 incident whose start
20
+ * date is unrecoverable from logs.
21
+ *
22
+ * `.strict()` turns that class of mistake from silent permissiveness into a
23
+ * loud verification failure: a token carrying an unknown grant key is rejected
24
+ * with `bad_claims` rather than partially understood. Any future rename fails
25
+ * the same way, at mint time, in a test.
26
+ */
27
+ /**
28
+ * Who a participant may receive, or be received by.
29
+ *
30
+ * A boolean cannot express an information barrier. "A and B are in one room and
31
+ * must not receive each other, while C may receive both" is the actual
32
+ * requirement for a Kanzlei, and it is a per-identity rule rather than a
33
+ * per-room one.
34
+ */
35
+ exports.subscribeRuleSchema = zod_1.z.discriminatedUnion("mode", [
36
+ zod_1.z.object({ mode: zod_1.z.literal("all") }).strict(),
37
+ zod_1.z.object({ mode: zod_1.z.literal("none") }).strict(),
38
+ zod_1.z.object({ mode: zod_1.z.literal("allow"), identities: zod_1.z.array(zod_1.z.string()).max(500) }).strict(),
39
+ zod_1.z.object({ mode: zod_1.z.literal("deny"), identities: zod_1.z.array(zod_1.z.string()).max(500) }).strict(),
40
+ ]);
41
+ exports.participantKindSchema = zod_1.z.enum(["human", "agent", "egress", "sip", "ingress"]);
42
+ /**
43
+ * Transport authority is NOT media authority, which is why this is its own
44
+ * claim rather than something inferred from `kind`.
45
+ *
46
+ * A browser holding a perfectly valid join token must not be able to ask for a
47
+ * `PlainTransport` pointed at an address of its choosing. That is an RTP
48
+ * reflector and an unauthenticated publish path into whatever room the token
49
+ * names. Three phases independently needed to refuse it and none could, because
50
+ * nothing carried the fact.
51
+ */
52
+ exports.transportKindSchema = zod_1.z.enum(["webrtc", "plain", "pipe"]);
53
+ exports.mediaGrantsSchema = zod_1.z
54
+ .object({
55
+ canPublish: zod_1.z.boolean(),
56
+ canSubscribe: zod_1.z.boolean(),
57
+ canPublishData: zod_1.z.boolean(),
58
+ /** Who I may receive. Omitted means `{ mode: "all" }`. */
59
+ subscribe: exports.subscribeRuleSchema.optional(),
60
+ /**
61
+ * Who may receive ME. Enforced on the PRODUCER side from the owner's stored
62
+ * grants, so a barrier holds even if the other party's token is generous.
63
+ */
64
+ visibleTo: exports.subscribeRuleSchema.optional(),
65
+ publishKinds: zod_1.z.array(zod_1.z.enum(["audio", "video", "screen"])).optional(),
66
+ /**
67
+ * Bypasses the top-N active-speaker refusal. Mintable ONLY for a non-human
68
+ * kind: a recorder needs every track, a human asking for 1,000 does not.
69
+ */
70
+ subscribeExempt: zod_1.z.boolean().optional(),
71
+ /** Hard ceiling on concurrent consumers, for a non-human participant. */
72
+ maxSubscriptions: zod_1.z.number().int().positive().max(5000).optional(),
73
+ })
74
+ .strict();
75
+ /** An omitted rule means everyone. Stated once so both sides agree. */
76
+ const DEFAULT_RULE = { mode: "all" };
77
+ function ruleAllows(rule, identity) {
78
+ const r = rule ?? DEFAULT_RULE;
79
+ switch (r.mode) {
80
+ case "all":
81
+ return true;
82
+ case "none":
83
+ return false;
84
+ case "allow":
85
+ return r.identities.includes(identity);
86
+ case "deny":
87
+ return !r.identities.includes(identity);
88
+ }
89
+ }
90
+ /**
91
+ * May `consumer` receive `producer`? Two-sided, and deny wins.
92
+ *
93
+ * One-sided is not enough. If only the consumer's rule were consulted, a
94
+ * participant whose own token was minted generously could receive someone who
95
+ * had been explicitly walled off from them. So the producer's `visibleTo` is
96
+ * checked from the OWNER's stored grants, not from anything the consumer
97
+ * presents.
98
+ *
99
+ * **An unreadable owner is `false`.** If the producer's grants cannot be found,
100
+ * this refuses rather than falling through to the permissive default. A missing
101
+ * record is exactly the state a race or a partial failure produces, and in a
102
+ * room with an information barrier the safe reading of "I do not know" is "no".
103
+ * Note this differs deliberately from an ABSENT RULE on a record that WAS
104
+ * found, which means everyone.
105
+ */
106
+ function maySubscribe(input) {
107
+ if (!input.consumerGrants.canSubscribe)
108
+ return false;
109
+ // Nobody subscribes to themselves; asking is a client bug, not a denial.
110
+ if (input.consumerIdentity === input.producerIdentity)
111
+ return false;
112
+ if (!input.ownerGrants)
113
+ return false;
114
+ return (ruleAllows(input.consumerGrants.subscribe, input.producerIdentity) &&
115
+ ruleAllows(input.ownerGrants.visibleTo, input.consumerIdentity));
116
+ }
117
+ /**
118
+ * `subscribeExempt` and `maxSubscriptions` are meaningless for a human and
119
+ * dangerous if mintable for one: exemption is what lets a recorder take every
120
+ * track, and a human with it can pull a thousand streams through a node.
121
+ * Checked at MINT time so a bad token never exists.
122
+ */
123
+ function grantsAreValidForKind(grants, kind) {
124
+ if (kind === "human" && grants.subscribeExempt) {
125
+ return "subscribeExempt may not be minted for a human participant";
126
+ }
127
+ if (!grants.canSubscribe && (grants.subscribe || grants.subscribeExempt || grants.maxSubscriptions)) {
128
+ return "subscribe rules are meaningless when canSubscribe is false";
129
+ }
130
+ if (!grants.canPublish && grants.publishKinds?.length) {
131
+ return "publishKinds is meaningless when canPublish is false";
132
+ }
133
+ return true;
134
+ }
135
+ //# sourceMappingURL=grants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The contract between the backend, the media plane and every client.
3
+ *
4
+ * The root export is ISOMORPHIC: types, zod schemas, wire frames and the pure
5
+ * `maySubscribe` decision. It pulls in no `node:crypto`, no mediasoup, no DOM,
6
+ * so a browser SDK can import it.
7
+ *
8
+ * Signing and verifying live at `@tribe-nest/media-protocol/token`, which is
9
+ * Node-only. Keeping them apart is what stops importing a frame type from
10
+ * dragging crypto into a browser bundle.
11
+ *
12
+ * This package exists because `apps/backend` and `apps/media-server` may not
13
+ * import each other (there is a test), and six independently written phase
14
+ * plans each invented a different wire envelope for want of somewhere neutral
15
+ * to put one.
16
+ */
17
+ export * from "./version";
18
+ export * from "./errors";
19
+ export * from "./errorSchema";
20
+ export * from "./grants";
21
+ export * from "./claims";
22
+ export * from "./frames";
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * The contract between the backend, the media plane and every client.
19
+ *
20
+ * The root export is ISOMORPHIC: types, zod schemas, wire frames and the pure
21
+ * `maySubscribe` decision. It pulls in no `node:crypto`, no mediasoup, no DOM,
22
+ * so a browser SDK can import it.
23
+ *
24
+ * Signing and verifying live at `@tribe-nest/media-protocol/token`, which is
25
+ * Node-only. Keeping them apart is what stops importing a frame type from
26
+ * dragging crypto into a browser bundle.
27
+ *
28
+ * This package exists because `apps/backend` and `apps/media-server` may not
29
+ * import each other (there is a test), and six independently written phase
30
+ * plans each invented a different wire envelope for want of somewhere neutral
31
+ * to put one.
32
+ */
33
+ __exportStar(require("./version"), exports);
34
+ __exportStar(require("./errors"), exports);
35
+ __exportStar(require("./errorSchema"), exports);
36
+ __exportStar(require("./grants"), exports);
37
+ __exportStar(require("./claims"), exports);
38
+ __exportStar(require("./frames"), exports);
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;GAeG;AACH,4CAA0B;AAC1B,2CAAyB;AACzB,gDAA8B;AAC9B,2CAAyB;AACzB,2CAAyB;AACzB,2CAAyB"}