@statewalker/httpeers-libp2p 0.1.3
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/LICENSE +21 -0
- package/README.md +173 -0
- package/dist/browser.d.ts +33 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +77 -0
- package/dist/duplex.d.ts +86 -0
- package/dist/duplex.d.ts.map +1 -0
- package/dist/duplex.js +101 -0
- package/dist/hop-reserve.d.ts +105 -0
- package/dist/hop-reserve.d.ts.map +1 -0
- package/dist/hop-reserve.js +269 -0
- package/dist/hub-link.d.ts +131 -0
- package/dist/hub-link.d.ts.map +1 -0
- package/dist/hub-link.js +217 -0
- package/dist/hub-relay.d.ts +129 -0
- package/dist/hub-relay.d.ts.map +1 -0
- package/dist/hub-relay.js +154 -0
- package/dist/identity.d.ts +84 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +91 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/link.d.ts +49 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +88 -0
- package/dist/multiaddr-parts.d.ts +21 -0
- package/dist/multiaddr-parts.d.ts.map +1 -0
- package/dist/multiaddr-parts.js +27 -0
- package/dist/node.d.ts +33 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +60 -0
- package/dist/reservation.d.ts +245 -0
- package/dist/reservation.d.ts.map +1 -0
- package/dist/reservation.js +396 -0
- package/dist/serve-peer.d.ts +91 -0
- package/dist/serve-peer.d.ts.map +1 -0
- package/dist/serve-peer.js +68 -0
- package/dist/timers.d.ts +36 -0
- package/dist/timers.d.ts.map +1 -0
- package/dist/timers.js +38 -0
- package/dist/transport.d.ts +314 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +410 -0
- package/package.json +73 -0
- package/src/browser.ts +118 -0
- package/src/duplex.ts +172 -0
- package/src/hop-reserve.ts +295 -0
- package/src/hub-link.ts +281 -0
- package/src/hub-relay.ts +188 -0
- package/src/identity.ts +155 -0
- package/src/index.ts +114 -0
- package/src/link.ts +143 -0
- package/src/multiaddr-parts.ts +29 -0
- package/src/node.ts +63 -0
- package/src/reservation.ts +564 -0
- package/src/serve-peer.ts +141 -0
- package/src/timers.ts +47 -0
- package/src/transport.ts +538 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A mount table, served to the mesh.
|
|
3
|
+
*
|
|
4
|
+
* THE SEAM THREE ASSEMBLIES COLLAPSE INTO. The prototype builds a peer three
|
|
5
|
+
* times — the Node hub, the browser hub and the test hub — each wiring a node,
|
|
6
|
+
* a reservation, identity registration, a router and mounts in its own order,
|
|
7
|
+
* and the coverage audit found that the one symbol every assembly turns on was
|
|
8
|
+
* exported nowhere. This is it.
|
|
9
|
+
*
|
|
10
|
+
* WHAT IT DOES NOT DO, and the omission is the design. It does not verify a
|
|
11
|
+
* token, evaluate a policy, or know what a hub is. `access` is a middleware
|
|
12
|
+
* the caller supplies — `withAccess` from `@statewalker/httpeers-access`, or
|
|
13
|
+
* nothing at all for a peer that trusts its transport — because this package
|
|
14
|
+
* must not learn about Biscuit and `httpeers-access` must not learn about
|
|
15
|
+
* libp2p. They meet at a `FetchHandler`, which is the only contract either
|
|
16
|
+
* needs.
|
|
17
|
+
*
|
|
18
|
+
* IDENTITY IS REGISTERED BEFORE ANYTHING DOWNSTREAM RUNS. `serveTransport`
|
|
19
|
+
* gives every inbound stream its own `Duplex` closing over that stream's
|
|
20
|
+
* Noise-proven `remotePeer`, and calls `registerPeer` on the request before
|
|
21
|
+
* the handler sees it. That ordering is the whole basis of the binding check:
|
|
22
|
+
* a token's subject is compared against what the TRANSPORT proved, never
|
|
23
|
+
* against anything the caller said.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { Libp2p } from "@libp2p/interface";
|
|
27
|
+
import type { FetchHandler, Mounts, PeerIdStr } from "@statewalker/httpeers-core";
|
|
28
|
+
import { createPeerRouter } from "@statewalker/httpeers-core";
|
|
29
|
+
import type { CallOnLimitedConnection } from "./link.js";
|
|
30
|
+
import { createRemote, PROTOCOL, serveTransport } from "./transport.js";
|
|
31
|
+
|
|
32
|
+
export interface ServePeerInit {
|
|
33
|
+
node: Libp2p;
|
|
34
|
+
/**
|
|
35
|
+
* What this peer serves. A value, or a factory taking the peer's own id —
|
|
36
|
+
* a hub's endpoints need to know which mesh they are, and the alternative
|
|
37
|
+
* is for this package to learn what a hub is.
|
|
38
|
+
*/
|
|
39
|
+
mounts: Mounts | ((self: PeerIdStr) => Mounts);
|
|
40
|
+
/**
|
|
41
|
+
* Everything between the transport and the mounts: identity binding and
|
|
42
|
+
* policy. Omitted, requests reach the mounts with only the transport's
|
|
43
|
+
* proof — which is a legitimate shape for a peer whose mounts are public,
|
|
44
|
+
* and a serious mistake for one whose are not.
|
|
45
|
+
*
|
|
46
|
+
* Passed through to the router, which applies it to LOCAL handling only.
|
|
47
|
+
* That distinction is load-bearing and easy to lose: wrapping the whole
|
|
48
|
+
* router instead would run policy over FORWARDED requests too, and an
|
|
49
|
+
* outbound call is not this peer's business to authorize.
|
|
50
|
+
*/
|
|
51
|
+
access?: (next: FetchHandler) => FetchHandler;
|
|
52
|
+
/**
|
|
53
|
+
* May this request be forwarded to `target`? **Deny by default**, and the
|
|
54
|
+
* default is inherited rather than restated here.
|
|
55
|
+
*
|
|
56
|
+
* Relaying is a distinct capability, not a side effect of knowing how to
|
|
57
|
+
* route: without it any peer can make this one dial a third party and pump
|
|
58
|
+
* a stream on its behalf. The hole is invisible for a while because it
|
|
59
|
+
* fails CLOSED at the far end — the third party rejects the tokenless
|
|
60
|
+
* request, so every observable outcome looks right. The damage is the work
|
|
61
|
+
* done, not the answer given.
|
|
62
|
+
*/
|
|
63
|
+
allowForward?: (request: Request, target: PeerIdStr) => Promise<boolean>;
|
|
64
|
+
protocol?: string;
|
|
65
|
+
/** Forwarded to the transport; see `transport.ts` for what each bounds. */
|
|
66
|
+
maxInboundStreams?: number;
|
|
67
|
+
maxOutboundStreams?: number;
|
|
68
|
+
drainTimeoutMs?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Accept streams on limited connections (relay circuits) -- SERVING ONLY. A
|
|
71
|
+
* HUB sets it, so members that can reach it only through the public relay
|
|
72
|
+
* are served; a member never does, so the hub's relay limits still bound
|
|
73
|
+
* member-to-member traffic.
|
|
74
|
+
*/
|
|
75
|
+
serveOnLimitedConnection?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Open streams on limited connections to the peers this allows -- CALLING
|
|
78
|
+
* ONLY, and it covers every outbound path of this peer (`call`, and the
|
|
79
|
+
* router's forwarding behind `dispatch`). A member in relay mode passes
|
|
80
|
+
* `(peerId) => peerId === hubPeerId`. See `CallOnLimitedConnection`.
|
|
81
|
+
*/
|
|
82
|
+
callOnLimitedConnection?: CallOnLimitedConnection;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface Peer {
|
|
86
|
+
peerId: PeerIdStr;
|
|
87
|
+
/** Every multiaddr this node is reachable on, right now. */
|
|
88
|
+
addrs(): string[];
|
|
89
|
+
/** Inbound, after identity binding and policy. Exposed so an edge can reuse it locally. */
|
|
90
|
+
dispatch: FetchHandler;
|
|
91
|
+
/** Call another peer. The request's URL path is what the far side routes on. */
|
|
92
|
+
call(peerId: PeerIdStr, request: Request): Promise<Response>;
|
|
93
|
+
stop(): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function servePeer(init: ServePeerInit): Promise<Peer> {
|
|
97
|
+
const peerId = init.node.peerId.toString();
|
|
98
|
+
const protocol = init.protocol ?? PROTOCOL;
|
|
99
|
+
|
|
100
|
+
const mounts = typeof init.mounts === "function" ? init.mounts(peerId) : init.mounts;
|
|
101
|
+
|
|
102
|
+
const remote = createRemote({
|
|
103
|
+
node: init.node,
|
|
104
|
+
protocol,
|
|
105
|
+
maxOutboundStreams: init.maxOutboundStreams,
|
|
106
|
+
callOnLimitedConnection: init.callOnLimitedConnection,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const dispatch = createPeerRouter({
|
|
110
|
+
selfPeerId: peerId,
|
|
111
|
+
mounts,
|
|
112
|
+
remote,
|
|
113
|
+
access: init.access,
|
|
114
|
+
allowForward: init.allowForward,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const stopServing = await serveTransport({
|
|
118
|
+
node: init.node,
|
|
119
|
+
dispatch,
|
|
120
|
+
protocol,
|
|
121
|
+
maxInboundStreams: init.maxInboundStreams,
|
|
122
|
+
maxOutboundStreams: init.maxOutboundStreams,
|
|
123
|
+
drainTimeoutMs: init.drainTimeoutMs,
|
|
124
|
+
runOnLimitedConnection: init.serveOnLimitedConnection,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
let stopped = false;
|
|
128
|
+
return {
|
|
129
|
+
peerId,
|
|
130
|
+
addrs: () => init.node.getMultiaddrs().map((a) => a.toString()),
|
|
131
|
+
dispatch,
|
|
132
|
+
call: async (target, request) => remote(target, request),
|
|
133
|
+
async stop() {
|
|
134
|
+
// Idempotent: an assembly that tears down twice — a page unloading while
|
|
135
|
+
// a test also cleans up — must not see the second call throw.
|
|
136
|
+
if (stopped) return;
|
|
137
|
+
stopped = true;
|
|
138
|
+
await stopServing();
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
package/src/timers.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The timer seam: the four calls anything long-lived in this package schedules
|
|
3
|
+
* with, behind an interface instead of the global functions.
|
|
4
|
+
*
|
|
5
|
+
* WHY, AND IT IS NOT TESTABILITY. A browser throttles `setTimeout` and
|
|
6
|
+
* `setInterval` in a background tab -- to once a minute, and on some engines
|
|
7
|
+
* far less -- and a hub running in a tab is a hub whose relay reservation is
|
|
8
|
+
* renewed on the browser's schedule rather than on the relay's. The fix is
|
|
9
|
+
* `worker-timers`, which schedules from a Web Worker and is not throttled; the
|
|
10
|
+
* fix is NOT to shorten the interval, which changes nothing about what the
|
|
11
|
+
* browser does to it. That wiring is deliberately not here yet (the Node hub is
|
|
12
|
+
* what runs today); this seam is what makes it a one-line change later:
|
|
13
|
+
*
|
|
14
|
+
* import { clearInterval, clearTimeout, setInterval, setTimeout } from "worker-timers";
|
|
15
|
+
* const timers: Timers = { setTimeout, clearTimeout, setInterval, clearInterval };
|
|
16
|
+
*
|
|
17
|
+
* Tests get the seam for free, and that is the cheaper of the two reasons.
|
|
18
|
+
*
|
|
19
|
+
* `TimerHandle` IS `unknown` ON PURPOSE. Node's timers return an object,
|
|
20
|
+
* browsers return a number, `worker-timers` returns a number of its own that
|
|
21
|
+
* only its own `clearTimeout` understands. An implementation must therefore
|
|
22
|
+
* only ever hand its OWN handles back to its OWN clear functions -- which is
|
|
23
|
+
* exactly what an opaque type states and a `number` alias would quietly
|
|
24
|
+
* permit callers to violate.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/** A handle from one `Timers` implementation, meaningful only to that same one. */
|
|
28
|
+
export type TimerHandle = unknown;
|
|
29
|
+
|
|
30
|
+
export interface Timers {
|
|
31
|
+
setTimeout(handler: () => void, delayMs: number): TimerHandle;
|
|
32
|
+
clearTimeout(handle: TimerHandle): void;
|
|
33
|
+
setInterval(handler: () => void, delayMs: number): TimerHandle;
|
|
34
|
+
clearInterval(handle: TimerHandle): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The global timers, which is what everything uses unless it is told otherwise. */
|
|
38
|
+
export const globalTimers: Timers = {
|
|
39
|
+
setTimeout: (handler, delayMs) => setTimeout(handler, delayMs),
|
|
40
|
+
clearTimeout: (handle) => {
|
|
41
|
+
if (handle !== undefined) clearTimeout(handle as ReturnType<typeof setTimeout>);
|
|
42
|
+
},
|
|
43
|
+
setInterval: (handler, delayMs) => setInterval(handler, delayMs),
|
|
44
|
+
clearInterval: (handle) => {
|
|
45
|
+
if (handle !== undefined) clearInterval(handle as ReturnType<typeof setInterval>);
|
|
46
|
+
},
|
|
47
|
+
};
|