@irtio/protocol 0.3.0 → 0.5.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/dist/index.d.ts +49 -1
- package/dist/index.js +26 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -384,4 +384,52 @@ declare const relaySchema: Schema<{
|
|
|
384
384
|
declare const RELAY_HASH8: Uint8Array;
|
|
385
385
|
declare function isRelayHash8(hash8: Uint8Array): boolean;
|
|
386
386
|
|
|
387
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Browser-origin policy, shared by every component that has to answer "may this page connect?"
|
|
389
|
+
*
|
|
390
|
+
* Three components ask that question about the same project and must answer it identically: the
|
|
391
|
+
* router (from the control plane's fresh list), the guest supervisor (from the list frozen into
|
|
392
|
+
* its boot env), and `irtio dev` (from `IRT_ORIGINS`). It lived only in the supervisor until the
|
|
393
|
+
* router was given the list it had been fetching and discarding, at which point one copy of the
|
|
394
|
+
* rules became the only honest arrangement.
|
|
395
|
+
*
|
|
396
|
+
* The rules, in the order they are applied:
|
|
397
|
+
*
|
|
398
|
+
* 1. No `Origin` header at all — bots, native clients, tests, `curl` — is NOT a browser and is
|
|
399
|
+
* allowed unless the caller opts out. Origin locking is a same-origin-policy backstop; it
|
|
400
|
+
* protects a browser user from a page they did not open, and there is no such user here.
|
|
401
|
+
* 2. `*` in the list allows every origin.
|
|
402
|
+
* 3. Localhost is ALWAYS allowed, on any port and any scheme. A project's list starts blank,
|
|
403
|
+
* and a developer running `vite dev` against hosted rooms must not have to register a port
|
|
404
|
+
* number before anything works. Nothing is protected by refusing it: an attacker who can
|
|
405
|
+
* serve the victim a page from their own machine has already won.
|
|
406
|
+
* 4. Otherwise the origin must appear in the list verbatim.
|
|
407
|
+
*/
|
|
408
|
+
/**
|
|
409
|
+
* Is `origin` a page served from the connecting developer's own machine?
|
|
410
|
+
*
|
|
411
|
+
* Matches on the HOSTNAME only, so every port passes — a dev server's port is an implementation
|
|
412
|
+
* detail that changes per tool and per run. `*.localhost` (which resolves to loopback in every
|
|
413
|
+
* current browser) counts too. A `file://` page sends the literal `null`, which does not parse
|
|
414
|
+
* as a URL and is therefore not local: it is indistinguishable from a sandboxed iframe on a
|
|
415
|
+
* hostile site, and lumping the two together would silently widen rule 3 to the open internet.
|
|
416
|
+
*/
|
|
417
|
+
declare function isLocalhostOrigin(origin: string): boolean;
|
|
418
|
+
/**
|
|
419
|
+
* `origins: ['*']` allows every browser origin; an empty list allows only localhost. A missing
|
|
420
|
+
* `Origin` (bots, native clients, tests) is allowed unless `allowNoOrigin` is explicitly `false`.
|
|
421
|
+
*/
|
|
422
|
+
declare function originAllowed(origins: readonly string[], allowNoOrigin: boolean, origin: string | undefined): boolean;
|
|
423
|
+
/**
|
|
424
|
+
* Splits an `IRT_ORIGINS`-shaped value into a list. The distinction that matters is UNSET vs
|
|
425
|
+
* EMPTY, and it is the caller's to make: unset means "no policy configured, allow everything"
|
|
426
|
+
* (`['*']`, what a hand-run supervisor and every pre-blank-default deployment expect), while an
|
|
427
|
+
* empty string means "a policy IS configured and it lists nothing" — localhost only.
|
|
428
|
+
*
|
|
429
|
+
* Getting that backwards fails OPEN, which is why it is one function with one test rather than a
|
|
430
|
+
* `?? '*'` at each call site. `deploy/host/rootfs/tenant-init` has the same trap in shell:
|
|
431
|
+
* `${IRT_ORIGINS:-*}` treats empty as unset, `${IRT_ORIGINS-*}` does not.
|
|
432
|
+
*/
|
|
433
|
+
declare function parseOriginList(raw: string | undefined): readonly string[];
|
|
434
|
+
|
|
435
|
+
export { type Call, type ClientCallable, type Credential, ERROR_CATALOGUE, type ErrorCatalogueEntry, ErrorCode, type ErrorCodeDef, type ErrorCodeName, type ErrorPayload, type Frame, FrameType, type Hello, type Msg, type MsgTarget, PRESENCE_COLLECTION, PROTOCOL_VERSION, type Ping, type Pong, type PresenceRecord, RELAY_HASH8, type Reply, type Welcome, builtinRpcs, correctPayload, decodeCall, decodeErrorPayload, decodeFrame, decodeHello, decodeMsg, decodePing, decodePong, decodeReply, decodeWelcome, deltaPayload, encodeCall, encodeCorrectFrame, encodeDeltaFrame, encodeErrorPayload, encodeFrame, encodeHello, encodeMsg, encodePing, encodePong, encodeReply, encodeWelcome, encodeWriteFrame, errorByCode, formatError, isFrameType, isLocalhostOrigin, isRelayHash8, originAllowed, parseOriginList, presenceEntity, readCorrectAppliedTick, readCorrectClientTick, relaySchema, requestOwnership, rpcByIdOf, rpcIdOf, rpcTable, withBuiltins, writePayload };
|
package/dist/index.js
CHANGED
|
@@ -405,6 +405,29 @@ function isRelayHash8(hash8) {
|
|
|
405
405
|
for (const b of hash8) if (b !== 0) return false;
|
|
406
406
|
return true;
|
|
407
407
|
}
|
|
408
|
+
|
|
409
|
+
// src/origin.ts
|
|
410
|
+
var LOCAL_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]", "0.0.0.0"]);
|
|
411
|
+
function isLocalhostOrigin(origin) {
|
|
412
|
+
let url;
|
|
413
|
+
try {
|
|
414
|
+
url = new URL(origin);
|
|
415
|
+
} catch {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
const host = url.hostname.toLowerCase();
|
|
419
|
+
return LOCAL_HOSTNAMES.has(host) || host.endsWith(".localhost");
|
|
420
|
+
}
|
|
421
|
+
function originAllowed(origins, allowNoOrigin, origin) {
|
|
422
|
+
if (origin === void 0 || origin === "") return allowNoOrigin;
|
|
423
|
+
if (origins.includes("*")) return true;
|
|
424
|
+
if (isLocalhostOrigin(origin)) return true;
|
|
425
|
+
return origins.includes(origin);
|
|
426
|
+
}
|
|
427
|
+
function parseOriginList(raw) {
|
|
428
|
+
if (raw === void 0) return ["*"];
|
|
429
|
+
return raw.split(",").map((o) => o.trim()).filter((o) => o.length > 0);
|
|
430
|
+
}
|
|
408
431
|
export {
|
|
409
432
|
ERROR_CATALOGUE,
|
|
410
433
|
ErrorCode,
|
|
@@ -439,7 +462,10 @@ export {
|
|
|
439
462
|
errorByCode,
|
|
440
463
|
formatError,
|
|
441
464
|
isFrameType,
|
|
465
|
+
isLocalhostOrigin,
|
|
442
466
|
isRelayHash8,
|
|
467
|
+
originAllowed,
|
|
468
|
+
parseOriginList,
|
|
443
469
|
presenceEntity,
|
|
444
470
|
readCorrectAppliedTick,
|
|
445
471
|
readCorrectClientTick,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irtio/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "irtio wire protocol: frames, framing, session payloads, error codes, built-in presence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@irtio/schema": "0.
|
|
23
|
+
"@irtio/schema": "0.5.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsup",
|