@blockcast/mmt-container 1.0.1 → 2.0.0-main.609e08d7e29f

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 (101) hide show
  1. package/README.md +9 -7
  2. package/dist/browser-runtime.js +6972 -6693
  3. package/dist/browser-runtime.js.map +4 -4
  4. package/dist/carp-catalog.d.ts +7 -7
  5. package/dist/carp-catalog.d.ts.map +1 -1
  6. package/dist/carp-catalog.js +65 -230
  7. package/dist/carp-catalog.js.map +3 -3
  8. package/dist/catalog-fec-params.js.map +1 -1
  9. package/dist/catalog-network-source.js.map +1 -1
  10. package/dist/delivery-catalog.js +3 -0
  11. package/dist/delivery-catalog.js.map +2 -2
  12. package/dist/delta-fallback-config.js.map +1 -1
  13. package/dist/fec-manager.d.ts +25 -1
  14. package/dist/fec-manager.d.ts.map +1 -1
  15. package/dist/fec-manager.js +208 -15
  16. package/dist/fec-manager.js.map +2 -2
  17. package/dist/fec-state.js.map +1 -1
  18. package/dist/fec-worker-setup.js.map +1 -1
  19. package/dist/held-layer-admission.d.ts +8 -0
  20. package/dist/held-layer-admission.d.ts.map +1 -1
  21. package/dist/held-layer-admission.js +22 -1
  22. package/dist/held-layer-admission.js.map +2 -2
  23. package/dist/iso-block-locator.js.map +1 -1
  24. package/dist/mfu-reassembler.js.map +1 -1
  25. package/dist/mmtp-container-parser.js.map +1 -1
  26. package/dist/mmtp-container-parser.no-modules.js +146 -332
  27. package/dist/mmtp-container-parser.no-modules.js.map +4 -4
  28. package/dist/mmtp-decode-pipeline.js.map +1 -1
  29. package/dist/mmtp-fec-session.d.ts.map +1 -1
  30. package/dist/mmtp-fec-session.js +61 -15
  31. package/dist/mmtp-fec-session.js.map +3 -3
  32. package/dist/mmtp-router.d.ts +11 -6
  33. package/dist/mmtp-router.d.ts.map +1 -1
  34. package/dist/mmtp-router.js +47 -10
  35. package/dist/mmtp-router.js.map +3 -3
  36. package/dist/multicast-auth-catalog.d.ts +8 -8
  37. package/dist/multicast-auth-catalog.d.ts.map +1 -1
  38. package/dist/multicast-auth-catalog.js +171 -75
  39. package/dist/multicast-auth-catalog.js.map +4 -4
  40. package/dist/multicast-input.d.ts +5 -5
  41. package/dist/multicast-input.d.ts.map +1 -1
  42. package/dist/multicast-input.js +126 -49
  43. package/dist/multicast-input.js.map +3 -3
  44. package/dist/multicast-manager.d.ts +11 -4
  45. package/dist/multicast-manager.d.ts.map +1 -1
  46. package/dist/multicast-manager.js +7914 -7659
  47. package/dist/multicast-manager.js.map +4 -4
  48. package/dist/multicast-transport.d.ts +16 -1
  49. package/dist/multicast-transport.d.ts.map +1 -1
  50. package/dist/multicast-transport.js +120 -5
  51. package/dist/multicast-transport.js.map +4 -4
  52. package/dist/multicast-unwrap.js +2 -2
  53. package/dist/multicast-unwrap.js.map +2 -2
  54. package/dist/no-modules.js +100 -288
  55. package/dist/no-modules.js.map +4 -4
  56. package/dist/repair-fetch.js.map +1 -1
  57. package/dist/signaling.js.map +1 -1
  58. package/dist/timestamp-codec.js.map +1 -1
  59. package/dist/wasm-mfu-reassembler.js.map +1 -1
  60. package/dist/wasm-mmtp-container-parser.js.map +1 -1
  61. package/package.json +5 -5
  62. package/schemas/catalog.schema.json +15 -7
  63. package/src/carp-catalog.ts +14 -13
  64. package/src/fec-manager.ts +89 -14
  65. package/src/held-layer-admission.ts +38 -1
  66. package/src/mmtp-fec-session.ts +20 -12
  67. package/src/mmtp-router.ts +65 -16
  68. package/src/multicast-auth-catalog.ts +43 -36
  69. package/src/multicast-input.ts +28 -8
  70. package/src/multicast-manager.ts +38 -34
  71. package/src/multicast-transport.ts +74 -7
  72. package/test-vectors/bc-provenance-v2.json +614 -175
  73. package/test-vectors/catalog/README.md +24 -8
  74. package/test-vectors/catalog/negative/multicast-auth-asm-missing-sender-address.json +0 -2
  75. package/test-vectors/catalog/negative/{multicast-auth-manifest-port-equals-port.json → multicast-auth-draft01-manifest-port.json} +1 -1
  76. package/test-vectors/catalog/negative/multicast-auth-duplicate-first-epoch.json +0 -1
  77. package/test-vectors/catalog/negative/multicast-auth-duplicate-kid.json +0 -1
  78. package/test-vectors/catalog/negative/multicast-auth-endpoint-auth-without-object-auth.json +0 -1
  79. package/test-vectors/catalog/negative/multicast-auth-endpoint-missing-bandwidth.json +0 -1
  80. package/test-vectors/catalog/negative/multicast-auth-manifest-timer-out-of-range.json +0 -1
  81. package/test-vectors/catalog/negative/multicast-auth-sender-address-on-ssm.json +0 -1
  82. package/test-vectors/catalog/negative/multicast-auth-shared-sg-port.json +73 -0
  83. package/test-vectors/catalog/negative/multicast-auth-version-1.json +0 -1
  84. package/test-vectors/catalog/negative/multicast-auth-version-3.json +0 -1
  85. package/test-vectors/catalog/negative/multicast-auth-version-missing.json +0 -1
  86. package/test-vectors/catalog/negative/multicast-endpoint-address-family-mismatch.json +55 -0
  87. package/test-vectors/catalog/negative/multicast-endpoint-address-host-name.json +55 -0
  88. package/test-vectors/catalog/negative/multicast-endpoint-address-leading-zero.json +55 -0
  89. package/test-vectors/catalog/negative/multicast-endpoint-address-ls32-leading-zero.json +55 -0
  90. package/test-vectors/catalog/negative/multicast-endpoint-address-mapped-family-mismatch.json +55 -0
  91. package/test-vectors/catalog/negative/multicast-endpoint-address-zone-id.json +55 -0
  92. package/test-vectors/catalog/positive/multicast-auth-asm-sender.json +0 -2
  93. package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-above-max.json → positive/multicast-auth-max-auth-delay-above-max-clamped.json} +0 -1
  94. package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-below-min.json → positive/multicast-auth-max-auth-delay-below-min-clamped.json} +0 -1
  95. package/test-vectors/catalog/positive/multicast-auth-max-auth-delay-string.json +55 -0
  96. package/test-vectors/catalog/positive/multicast-auth-rotation.json +0 -1
  97. package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +0 -1
  98. package/test-vectors/catalog/section-codes.json +8 -3
  99. package/test-vectors/catalog-multicast-auth-v1.json +0 -1
  100. package/test-vectors/catalog-validation.json +3 -3
  101. package/test-vectors/generate-bc-provenance-v2.mjs +94 -14
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * `multicast.auth` and an endpoint's `auth`: bc-provenance version 2, per
3
- * draft-ramadan-moq-multicast-01 7.2.6 at
4
- * Blockcast/moqcast-draft@fd962fd73d21ed281b60219c11ea61cc9e4e91c4.
3
+ * draft-ramadan-moq-multicast-02 7.2.6 at Blockcast/moqcast-draft@5f49172.
4
+ * Records ride in band on the flow's own `port` (7.2.3); draft -01's separate
5
+ * record port is gone.
5
6
  *
6
7
  * The block lives in the `multicast` object, never the root: the root dotted
7
8
  * key `"multicast.auth"` was draft -00's (libmmt called its `tracks[]` shape
@@ -48,15 +49,13 @@ export interface MulticastAuthConfig {
48
49
  }
49
50
  /** An endpoint's `auth` (7.2.6). A protected endpoint also declares `bandwidth`. */
50
51
  export interface EndpointAuthConfig {
51
- /** Destination port of the record datagrams on the endpoint's `(S, G)`. */
52
- manifestPort: number;
53
52
  /** u32 chosen per channel generation. */
54
53
  streamId: number;
55
54
  /** `S` of a protected ASM endpoint; never on SSM. */
56
55
  senderAddress?: string;
57
- /** 50..1000, default 200. */
56
+ /** 50..1000, default 200; a receiver clamps one outside the range. */
58
57
  maxAuthDelayMs?: number;
59
- /** At least 1, default 20. */
58
+ /** At least 1, default 20; a receiver ignores the endpoint below 1. */
60
59
  manifestTimerMs?: number;
61
60
  /** MoQ track mirroring the records. */
62
61
  manifestTrack?: string;
@@ -68,12 +67,13 @@ export declare function isMulticastAuthConfig(obj: unknown): obj is MulticastAut
68
67
  export interface ResolvedMulticastAuthFlow {
69
68
  /** Index of the endpoint in `multicast.endpoints`. */
70
69
  endpointIndex: number;
71
- /** `S`: `sourceAddress` on SSM, `auth.senderAddress` on ASM. */
70
+ /** `S`: `sourceAddress` on SSM, `auth.senderAddress` on ASM; canonical (`canonicalIpAddress`). */
72
71
  source: string;
72
+ /** Canonical (`canonicalIpAddress`). */
73
73
  groupAddress: string;
74
+ /** The flow's port; its records ride in band on it (7.2.3). */
74
75
  port: number;
75
76
  bandwidth: number;
76
- manifestPort: number;
77
77
  streamId: number;
78
78
  maxAuthDelayMs: number;
79
79
  manifestTimerMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"multicast-auth-catalog.d.ts","sourceRoot":"","sources":["../src/multicast-auth-catalog.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"multicast-auth-catalog.d.ts","sourceRoot":"","sources":["../src/multicast-auth-catalog.ts"],"names":[],"mappings":"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"}
@@ -13,13 +13,98 @@ var FEC_BLOCK_PROFILES = Object.freeze([
13
13
  "iso-ssbg1-v1"
14
14
  ]);
15
15
 
16
+ // ../mmt-base/dist/ip-address.js
17
+ var DEC_OCTET = /^(?:0|[1-9]\d{0,2})$/;
18
+ var H16 = /^[0-9A-Fa-f]{1,4}$/;
19
+ function parseIpv4(text2) {
20
+ const fields = text2.split(".");
21
+ if (fields.length !== 4)
22
+ return void 0;
23
+ const octets = [];
24
+ for (const field of fields) {
25
+ if (!DEC_OCTET.test(field))
26
+ return void 0;
27
+ const value = Number(field);
28
+ if (value > 255)
29
+ return void 0;
30
+ octets.push(value);
31
+ }
32
+ return octets;
33
+ }
34
+ function parseIpv6(text2) {
35
+ const halves = text2.split("::");
36
+ if (halves.length > 2)
37
+ return void 0;
38
+ const groupsOf = (part) => part === "" ? [] : part.split(":");
39
+ const left = groupsOf(halves[0]);
40
+ const right = halves.length === 2 ? groupsOf(halves[1]) : [];
41
+ const words = [];
42
+ const all = [...left, ...right];
43
+ const last = halves.length === 2 && right.length === 0 ? -1 : all.length - 1;
44
+ for (let index = 0; index < all.length; index++) {
45
+ const group = all[index];
46
+ if (index === last && group.includes(".")) {
47
+ const v4 = parseIpv4(group);
48
+ if (!v4)
49
+ return void 0;
50
+ words.push([v4[0] << 8 | v4[1], v4[2] << 8 | v4[3]]);
51
+ } else if (H16.test(group)) {
52
+ words.push([Number.parseInt(group, 16)]);
53
+ } else {
54
+ return void 0;
55
+ }
56
+ }
57
+ const leftWords = words.slice(0, left.length).flat();
58
+ const rightWords = words.slice(left.length).flat();
59
+ const missing = 8 - leftWords.length - rightWords.length;
60
+ if (halves.length === 2 ? missing < 1 : missing !== 0)
61
+ return void 0;
62
+ const out = new Uint8Array(16);
63
+ [...leftWords, ...new Array(missing).fill(0), ...rightWords].forEach((word, index) => {
64
+ out[index * 2] = word >> 8;
65
+ out[index * 2 + 1] = word & 255;
66
+ });
67
+ return out;
68
+ }
69
+ function parseIpAddressLiteral(text2) {
70
+ if (typeof text2 !== "string")
71
+ return void 0;
72
+ if (text2.includes(":"))
73
+ return parseIpv6(text2);
74
+ const v4 = parseIpv4(text2);
75
+ return v4 ? Uint8Array.from(v4) : void 0;
76
+ }
77
+ function canonicalIpAddress(text2) {
78
+ const bytes = parseIpAddressLiteral(text2);
79
+ if (!bytes)
80
+ return void 0;
81
+ if (bytes.length === 4)
82
+ return bytes.join(".");
83
+ const words = Array.from({ length: 8 }, (_, index) => bytes[index * 2] << 8 | bytes[index * 2 + 1]);
84
+ let bestStart = -1;
85
+ let bestLength = 1;
86
+ for (let start = 0; start < 8; ) {
87
+ let end = start;
88
+ while (end < 8 && words[end] === 0)
89
+ end++;
90
+ if (end - start > bestLength) {
91
+ bestStart = start;
92
+ bestLength = end - start;
93
+ }
94
+ start = end === start ? start + 1 : end;
95
+ }
96
+ const hex = words.map((word) => word.toString(16));
97
+ if (bestStart < 0)
98
+ return hex.join(":");
99
+ return `${hex.slice(0, bestStart).join(":")}::${hex.slice(bestStart + bestLength).join(":")}`;
100
+ }
101
+
16
102
  // ../mmt-base/dist/multicast-catalog.js
17
103
  var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
18
104
  var UINT32_MAX = 4294967295;
19
105
  var AUTH_MEMBERS = /* @__PURE__ */ new Set(["scheme", "version", "sig", "keys", "minEpoch"]);
20
106
  var KEY_MEMBERS = /* @__PURE__ */ new Set(["kid", "publicKey", "firstEpoch"]);
21
107
  var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
22
- "manifestPort",
23
108
  "streamId",
24
109
  "senderAddress",
25
110
  "maxAuthDelayMs",
@@ -29,17 +114,32 @@ var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
29
114
  function isObject(value) {
30
115
  return typeof value === "object" && value !== null && !Array.isArray(value);
31
116
  }
32
- function isNonEmptyString(value) {
33
- return typeof value === "string" && value.trim().length > 0;
34
- }
35
117
  function isIntegerIn(value, min, max) {
36
118
  return Number.isSafeInteger(value) && value >= min && value <= max;
37
119
  }
120
+ var BCP_DEFAULT_MAX_AUTH_DELAY_MS = 200;
121
+ var BCP_DEFAULT_MANIFEST_TIMER_MS = 20;
122
+ function isFiniteNumber(value) {
123
+ return typeof value === "number" && Number.isFinite(value);
124
+ }
125
+ function endpointAuthTiming(auth) {
126
+ const record = isObject(auth) ? auth : {};
127
+ const delay = record["maxAuthDelayMs"];
128
+ const timer = record["manifestTimerMs"];
129
+ const track = record["manifestTrack"];
130
+ const maxAuthDelayMs = !isFiniteNumber(delay) ? BCP_DEFAULT_MAX_AUTH_DELAY_MS : delay < 50 ? 50 : delay > 1e3 ? 1e3 : Number.isInteger(delay) ? delay : BCP_DEFAULT_MAX_AUTH_DELAY_MS;
131
+ const manifestTimerMs = !isFiniteNumber(timer) ? BCP_DEFAULT_MANIFEST_TIMER_MS : timer < 1 || Number.isSafeInteger(timer) ? timer : BCP_DEFAULT_MANIFEST_TIMER_MS;
132
+ return {
133
+ maxAuthDelayMs,
134
+ manifestTimerMs,
135
+ ...typeof track === "string" && track.length > 0 ? { manifestTrack: track } : {}
136
+ };
137
+ }
38
138
  function multicastEndpointSource(endpoint) {
39
139
  if (!isObject(endpoint))
40
140
  return void 0;
41
141
  const sourceAddress = endpoint["sourceAddress"];
42
- if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress))
142
+ if (sourceAddress !== void 0 && !parseIpAddressLiteral(sourceAddress))
43
143
  return void 0;
44
144
  const mode = sourceAddress === void 0 ? "asm" : "ssm";
45
145
  const protocol = endpoint["protocol"];
@@ -51,7 +151,7 @@ function multicastEndpointSource(endpoint) {
51
151
  return senderAddress === void 0 ? { mode, source: sourceAddress } : void 0;
52
152
  if (auth === void 0)
53
153
  return { mode };
54
- return isNonEmptyString(senderAddress) ? { mode, source: senderAddress } : void 0;
154
+ return parseIpAddressLiteral(senderAddress) ? { mode, source: senderAddress } : void 0;
55
155
  }
56
156
  function authIssues(auth) {
57
157
  const base = "$.multicast.auth";
@@ -62,12 +162,12 @@ function authIssues(auth) {
62
162
  return issue("", "auth must be an object");
63
163
  const unknown = Object.keys(auth).filter((key) => !AUTH_MEMBERS.has(key));
64
164
  if (unknown.length > 0) {
65
- return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -01 7.2.6 defines scheme, version, sig, keys and minEpoch`);
165
+ return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -02 7.2.6 defines scheme, version, sig, keys and minEpoch`);
66
166
  }
67
167
  if (auth["scheme"] !== "bc-provenance")
68
168
  return issue(".scheme", 'scheme must be "bc-provenance"');
69
169
  if (auth["version"] !== 2)
70
- return issue(".version", "version must be 2 (bc-provenance version 2, draft -01 7.2)");
170
+ return issue(".version", "version must be 2 (bc-provenance version 2, draft -02 7.2)");
71
171
  if (auth["sig"] !== "ed25519")
72
172
  return issue(".sig", 'sig must be "ed25519"');
73
173
  const keys = auth["keys"];
@@ -110,26 +210,17 @@ function endpointAuthIssue(endpoint, mode, at) {
110
210
  const unknown = Object.keys(auth).filter((key) => !ENDPOINT_AUTH_MEMBERS.has(key));
111
211
  if (unknown.length > 0)
112
212
  return { path: `${at}.auth`, message: `auth has unknown member(s) ${unknown.join(", ")}` };
113
- if (!isIntegerIn(auth["manifestPort"], 1, 65535)) {
114
- return { path: `${at}.auth.manifestPort`, message: "manifestPort must be an integer from 1 to 65535" };
115
- }
116
213
  if (!isIntegerIn(auth["streamId"], 0, UINT32_MAX)) {
117
214
  return { path: `${at}.auth.streamId`, message: "streamId must be an unsigned 32-bit integer" };
118
215
  }
119
216
  if (mode === "ssm" && auth["senderAddress"] !== void 0) {
120
217
  return { path: `${at}.auth.senderAddress`, message: "senderAddress must not appear on an SSM endpoint; S is sourceAddress" };
121
218
  }
122
- if (mode === "asm" && !isNonEmptyString(auth["senderAddress"])) {
123
- return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress" };
219
+ if (mode === "asm" && !parseIpAddressLiteral(auth["senderAddress"])) {
220
+ return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress, an RFC 3986 IP address literal" };
124
221
  }
125
- if (auth["maxAuthDelayMs"] !== void 0 && !isIntegerIn(auth["maxAuthDelayMs"], 50, 1e3)) {
126
- return { path: `${at}.auth.maxAuthDelayMs`, message: "maxAuthDelayMs must be an integer from 50 to 1000" };
127
- }
128
- if (auth["manifestTimerMs"] !== void 0 && !isIntegerIn(auth["manifestTimerMs"], 1, Number.MAX_SAFE_INTEGER)) {
129
- return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be an integer of at least 1" };
130
- }
131
- if (auth["manifestTrack"] !== void 0 && !isNonEmptyString(auth["manifestTrack"])) {
132
- return { path: `${at}.auth.manifestTrack`, message: "manifestTrack must be a non-empty string" };
222
+ if (endpointAuthTiming(auth).manifestTimerMs < 1) {
223
+ return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be at least 1" };
133
224
  }
134
225
  if (!isIntegerIn(endpoint["bandwidth"], 1, Number.MAX_SAFE_INTEGER)) {
135
226
  return { path: `${at}.bandwidth`, message: "a protected endpoint requires bandwidth, which bounds the receiver's hold queues" };
@@ -145,7 +236,7 @@ function multicastCatalogIssues(catalog, options = {}) {
145
236
  issues.push({
146
237
  code: "multicast_auth",
147
238
  path: '$["multicast.auth"]',
148
- message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -01 7.2.6)'
239
+ message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -02 7.2.6)'
149
240
  });
150
241
  }
151
242
  const multicast = catalog["multicast"];
@@ -179,7 +270,7 @@ function multicastCatalogIssues(catalog, options = {}) {
179
270
  }
180
271
  const protocol = endpoint["protocol"];
181
272
  if (protocol !== void 0 && protocol !== "ssm" && protocol !== "asm") {
182
- fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -01 4.2)`);
273
+ fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -02 4.2)`);
183
274
  continue;
184
275
  }
185
276
  const hasAuth = endpoint["auth"] !== void 0;
@@ -188,8 +279,18 @@ function multicastCatalogIssues(catalog, options = {}) {
188
279
  continue;
189
280
  }
190
281
  const sourceAddress = endpoint["sourceAddress"];
191
- if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress)) {
192
- fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress must be a non-empty string");
282
+ const sourceBytes = sourceAddress === void 0 ? void 0 : parseIpAddressLiteral(sourceAddress);
283
+ if (sourceAddress !== void 0 && !sourceBytes) {
284
+ fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress must be an RFC 3986 IPv4address or IPv6address literal");
285
+ continue;
286
+ }
287
+ const groupBytes = parseIpAddressLiteral(endpoint["groupAddress"]);
288
+ if (!groupBytes) {
289
+ fail("multicast_endpoint", `${at}.groupAddress`, "groupAddress must be an RFC 3986 IPv4address or IPv6address literal");
290
+ continue;
291
+ }
292
+ if (sourceBytes && sourceBytes.length !== groupBytes.length) {
293
+ fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress and groupAddress are of different address families");
193
294
  continue;
194
295
  }
195
296
  const mode = sourceAddress === void 0 ? "asm" : "ssm";
@@ -208,7 +309,13 @@ function multicastCatalogIssues(catalog, options = {}) {
208
309
  continue;
209
310
  }
210
311
  }
211
- sources[index] = multicastEndpointSource(endpoint);
312
+ const source = multicastEndpointSource(endpoint);
313
+ const senderBytes = source?.mode === "asm" && source.source !== void 0 ? parseIpAddressLiteral(source.source) : void 0;
314
+ if (senderBytes && senderBytes.length !== groupBytes.length) {
315
+ fail("multicast_endpoint_auth", `${at}.auth.senderAddress`, "senderAddress and groupAddress are of different address families");
316
+ continue;
317
+ }
318
+ sources[index] = source;
212
319
  const refs = Array.isArray(endpoint["tracks"]) ? endpoint["tracks"] : [];
213
320
  for (let refIndex = 0; refIndex < refs.length; refIndex++) {
214
321
  const ref = refs[refIndex];
@@ -224,38 +331,23 @@ function multicastCatalogIssues(catalog, options = {}) {
224
331
  }
225
332
  }
226
333
  }
334
+ const addressKey = (text2) => {
335
+ const bytes = text2 === void 0 ? void 0 : parseIpAddressLiteral(text2);
336
+ return bytes ? `${bytes.length}:${bytes.join(".")}` : "-";
337
+ };
227
338
  const tupleOf = (index) => {
228
339
  const endpoint = endpoints[index];
229
- const auth = endpoint["auth"];
230
- return {
231
- sg: JSON.stringify([sources[index].source ?? null, endpoint["groupAddress"]]),
232
- port: endpoint["port"],
233
- manifestPort: isObject(auth) ? auth["manifestPort"] : void 0
234
- };
340
+ return `${addressKey(sources[index].source)}|${addressKey(endpoint["groupAddress"])}|${endpoint["port"]}`;
235
341
  };
236
342
  for (let index = 0; index < endpoints.length; index++) {
237
343
  if (!sources[index])
238
344
  continue;
239
345
  const own = tupleOf(index);
240
- const at = `$.multicast.endpoints[${index}]`;
241
- if (own.manifestPort !== void 0 && own.manifestPort === own.port) {
242
- issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: "manifestPort must differ from the endpoint's own port", endpointIndex: index });
243
- continue;
244
- }
245
346
  for (let other = 0; other < endpoints.length; other++) {
246
- if (other === index || !sources[other])
247
- continue;
248
- const peer = tupleOf(other);
249
- if (peer.sg !== own.sg)
347
+ if (other === index || !sources[other] || tupleOf(other) !== own)
250
348
  continue;
251
- if (peer.port === own.port) {
252
- issues.push({ code: "multicast_endpoint", path: `${at}.port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
253
- break;
254
- }
255
- if (own.manifestPort !== void 0 && (own.manifestPort === peer.port || own.manifestPort === peer.manifestPort)) {
256
- issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: `manifestPort is a port or manifestPort of endpoint ${other} on the same (S, G)`, endpointIndex: index });
257
- break;
258
- }
349
+ issues.push({ code: "multicast_endpoint", path: `$.multicast.endpoints[${index}].port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
350
+ break;
259
351
  }
260
352
  }
261
353
  return issues;
@@ -620,17 +712,23 @@ function lp16(value) {
620
712
  return concat([u16(bytes.byteLength), bytes]);
621
713
  }
622
714
  function bcProvenanceV2ParamsHash(params) {
623
- const fec = params.fec;
715
+ const fec = [...params.fec ?? []].sort((a, b) => (a.instanceId ?? 0) - (b.instanceId ?? 0));
716
+ if (fec.some((instance) => instance.scheme === "none")) {
717
+ throw new Error('FEC scheme "none" is reserved (fec-core-00 15.2)');
718
+ }
719
+ const tuples = fec.length === 0 ? [lp16("none"), u32(0), u32(0), u32(0), u32(0)] : fec.flatMap((instance) => [
720
+ lp16(instance.scheme),
721
+ u32(instance.sourceSymbols),
722
+ u32(instance.repairSymbols),
723
+ u32(instance.symbolSize),
724
+ u32(instance.interleaveDepthMs ?? 0)
725
+ ]);
624
726
  return sha2562(concat([
625
727
  text.encode("BCPV2-params"),
626
728
  lp16(params.codec),
627
729
  u32(params.timescale),
628
730
  u32(params.groupDurationTicks),
629
- lp16(fec ? fec.algorithm : "none"),
630
- u32(fec?.sourceSymbols ?? 0),
631
- u32(fec?.repairSymbols ?? 0),
632
- u32(fec?.symbolSize ?? 0),
633
- u32(fec?.interleaveDepthMs ?? 0)
731
+ ...tuples
634
732
  ]));
635
733
  }
636
734
  function bcProvenanceV2Ctx(namespace, packetIds, params) {
@@ -649,8 +747,8 @@ function bcProvenanceV2Ctx(namespace, packetIds, params) {
649
747
 
650
748
  // src/multicast-auth-catalog.ts
651
749
  var RAW_ED25519_KEY_LENGTH = 32;
652
- var DEFAULT_MAX_AUTH_DELAY_MS = 200;
653
- var DEFAULT_MANIFEST_TIMER_MS = 20;
750
+ var DEFAULT_MAX_AUTH_DELAY_MS = BCP_DEFAULT_MAX_AUTH_DELAY_MS;
751
+ var DEFAULT_MANIFEST_TIMER_MS = BCP_DEFAULT_MANIFEST_TIMER_MS;
654
752
  var BASE64URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
655
753
  function encodeBase64Url(bytes) {
656
754
  let output = "";
@@ -746,15 +844,13 @@ function resolveMulticastAuthCatalog(catalog, options = {}) {
746
844
  }
747
845
  flows.push({
748
846
  endpointIndex,
749
- source: endpoint["sourceAddress"] ?? flowAuth.senderAddress,
750
- groupAddress: endpoint["groupAddress"],
847
+ source: canonicalIpAddress(endpoint["sourceAddress"] ?? flowAuth.senderAddress),
848
+ groupAddress: canonicalIpAddress(endpoint["groupAddress"]),
751
849
  port: endpoint["port"],
752
850
  bandwidth: endpoint["bandwidth"],
753
- manifestPort: flowAuth.manifestPort,
754
851
  streamId: flowAuth.streamId,
755
- maxAuthDelayMs: flowAuth.maxAuthDelayMs ?? DEFAULT_MAX_AUTH_DELAY_MS,
756
- manifestTimerMs: flowAuth.manifestTimerMs ?? DEFAULT_MANIFEST_TIMER_MS,
757
- ...flowAuth.manifestTrack === void 0 ? {} : { manifestTrack: flowAuth.manifestTrack },
852
+ // Clamped, or the defaults where absent (7.2.6).
853
+ ...endpointAuthTiming(flowAuth),
758
854
  tracks,
759
855
  ...ctx === void 0 ? {} : { ctx }
760
856
  });
@@ -770,32 +866,32 @@ function positiveInteger(value) {
770
866
  }
771
867
  function ctxParams(catalogTracks, listed) {
772
868
  const byName = new Map(catalogTracks.map((track) => [track["name"], track]));
869
+ const instancesOf = (track) => Array.isArray(track["fec"]) ? track["fec"].filter((f) => typeof f === "object" && f !== null) : [];
773
870
  const ordered = [...listed].sort((a, b) => a.packetId - b.packetId).map(({ name }) => byName.get(name));
774
871
  let entry = ordered.find((track) => track !== void 0 && track["packaging"] !== "fec-repair");
775
872
  if (entry === void 0) {
776
873
  const repair = ordered.find((track) => track !== void 0);
777
874
  if (repair === void 0) return void 0;
778
- const depends = repair["depends"];
779
- entry = Array.isArray(depends) && depends.length === 1 ? byName.get(depends[0]) : catalogTracks.find((track) => track["fec"]?.["repairTrack"] === repair["name"]);
875
+ entry = typeof repair["source"] === "string" ? byName.get(repair["source"]) : catalogTracks.find((track) => instancesOf(track).some((f) => f["repairTrack"] === repair["name"]));
780
876
  if (entry === void 0) return void 0;
781
877
  }
878
+ const instances = instancesOf(entry);
879
+ if (instances.some((f) => f["scheme"] === "none")) return void 0;
782
880
  const timescale = positiveInteger(entry["timescale"]);
783
881
  const ms = positiveInteger(entry["groupDurationMs"]);
784
882
  const groupDurationTicks = positiveInteger(entry["groupDurationTicks"]) || positiveInteger(timescale && ms * timescale % 1e3 === 0 ? ms * timescale / 1e3 : 0);
785
- const fec = entry["fec"];
786
883
  return {
787
884
  codec: typeof entry["codec"] === "string" ? entry["codec"] : "",
788
885
  timescale,
789
886
  groupDurationTicks,
790
- ...fec === void 0 || fec === null ? {} : {
791
- fec: {
792
- algorithm: typeof fec["algorithm"] === "string" ? fec["algorithm"] : "",
793
- sourceSymbols: positiveInteger(fec["sourceSymbols"]),
794
- repairSymbols: positiveInteger(fec["repairSymbols"]),
795
- symbolSize: positiveInteger(fec["symbolSize"]),
796
- interleaveDepthMs: positiveInteger(fec["interleaveDepthMs"])
797
- }
798
- }
887
+ fec: instances.map((f) => ({
888
+ instanceId: typeof f["instanceId"] === "number" ? f["instanceId"] : 0,
889
+ scheme: typeof f["scheme"] === "string" ? f["scheme"] : "",
890
+ sourceSymbols: positiveInteger(f["sourceSymbols"]),
891
+ repairSymbols: positiveInteger(f["repairSymbols"]),
892
+ symbolSize: positiveInteger(f["symbolSize"]),
893
+ interleaveDepthMs: positiveInteger(f["interleaveDepthMs"])
894
+ }))
799
895
  };
800
896
  }
801
897
  function multicastAuthKeyFor(resolved, kid, epoch) {