@ccmsg/cli 0.11.3 → 0.11.5
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/README.md +8 -12
- package/package.json +1 -1
- package/src/auth/auth.ts +23 -23
- package/src/auth/http.ts +3 -3
- package/src/auth/records.ts +2 -2
- package/src/auth/webauthn.ts +1 -1
- package/src/cli.ts +10 -10
- package/src/daemon/control.ts +1 -1
- package/src/daemon/registry.ts +11 -11
- package/src/daemon/snapshot.ts +1 -1
- package/src/daemon/supervise.ts +4 -4
- package/src/dispatch/caller.ts +1 -1
- package/src/dispatch/dispatch.ts +4 -4
- package/src/dispatch/handler.ts +4 -4
- package/src/dispatch/identity.ts +1 -1
- package/src/dispatch/result.ts +2 -2
- package/src/files/containment.ts +2 -2
- package/src/harness/index.ts +1 -1
- package/src/instance/client.ts +1 -1
- package/src/instance/config.ts +13 -13
- package/src/instance/instance.ts +42 -42
- package/src/instance/lock.ts +2 -2
- package/src/instance/log.ts +1 -1
- package/src/instance/paths.ts +6 -6
- package/src/instance/socket.ts +1 -1
- package/src/kv/store.ts +1 -1
- package/src/mesh/instances.ts +2 -2
- package/src/mesh/keys.ts +7 -7
- package/src/mesh/mesh.ts +67 -67
- package/src/mesh/relay.ts +8 -8
- package/src/mesh/wire.ts +2 -2
- package/src/messaging/delivery.ts +24 -24
- package/src/messaging/direct.ts +23 -23
- package/src/messaging/inbox.ts +6 -6
- package/src/messaging/notify.ts +5 -5
- package/src/plugin/codex.ts +1 -1
- package/src/sessions/classify.ts +6 -6
- package/src/sessions/dump.ts +1 -1
- package/src/sessions/handlers.ts +2 -2
- package/src/sessions/harness.ts +12 -12
- package/src/sessions/last-live.ts +4 -4
- package/src/sessions/registry.ts +32 -32
- package/src/sessions/status.ts +7 -7
- package/src/sessions/terminals.ts +1 -1
- package/src/transcript/files.ts +4 -4
- package/src/transcript/fold.ts +9 -9
- package/src/transcript/items/classify.ts +56 -8
- package/src/transcript/read.ts +1 -1
- package/src/transcript/tail.ts +3 -3
- package/src/transcript/transcripts.ts +7 -7
- package/src/translate/helper.ts +2 -2
- package/src/transport/conn.ts +3 -3
- package/src/transport/dial.ts +1 -1
- package/src/transport/driver.ts +2 -2
- package/src/transport/entry.ts +1 -1
- package/src/transport/framing.ts +1 -1
- package/src/transport/listener.ts +3 -3
- package/src/transport/uds.ts +2 -2
- package/src/transport/ws.ts +2 -2
- package/src/upstream/events.ts +3 -3
- package/src/upstream/gateway.ts +6 -6
- package/src/upstream/json.ts +1 -1
- package/src/upstream/requests.ts +6 -6
- package/src/upstream/stats.ts +1 -1
- package/src/upstream/status.ts +4 -4
- package/src/upstream/usage.ts +1 -1
- package/src/upstream/webhook.ts +1 -1
package/src/transport/conn.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
/** One accepted connection, as the layers above transport see it.
|
|
9
9
|
*
|
|
10
10
|
* UDS and WS produce this same type, so nothing above transport can tell them
|
|
11
|
-
* apart (
|
|
11
|
+
* apart (DESIGN §2.1). Everything that differs between the two — how a line
|
|
12
12
|
* reaches the peer, how a blocked write is retried — is settled behind `send`. */
|
|
13
13
|
export interface Conn extends Requester {
|
|
14
14
|
/** Distinguishes connections within one process run. It is not an identity:
|
|
@@ -34,7 +34,7 @@ export interface Conn extends Requester {
|
|
|
34
34
|
* (mesh-peer-auth §8.1). Transports that carry no such code ignore it. */
|
|
35
35
|
close(code?: number, reason?: string): void;
|
|
36
36
|
/** Run when the connection is gone. Anything held per connection — the
|
|
37
|
-
* subscriptions of §6.3 once they exist — is released here, because a closed
|
|
37
|
+
* subscriptions of DESIGN §6.3 once they exist — is released here, because a closed
|
|
38
38
|
* connection is the only end a subscription has. */
|
|
39
39
|
onClose(listener: () => void): void;
|
|
40
40
|
}
|
|
@@ -101,7 +101,7 @@ export class BaseConn implements Conn {
|
|
|
101
101
|
|
|
102
102
|
/** The connections one instance currently holds.
|
|
103
103
|
*
|
|
104
|
-
* It exists so shutdown (§8.5 step 3) can reach every connection before any
|
|
104
|
+
* It exists so shutdown (DESIGN §8.5 step 3) can reach every connection before any
|
|
105
105
|
* listener is closed, and so tests can see that a closed connection is gone. */
|
|
106
106
|
export class ConnRegistry {
|
|
107
107
|
readonly #conns = new Set<Conn>();
|
package/src/transport/dial.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface DialOptions {
|
|
|
16
16
|
|
|
17
17
|
/** A connection this instance opened, as the same `Conn` an accepted one is.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
19
|
+
* DESIGN §2.1 has one connection type above transport, and a mesh link is a
|
|
20
20
|
* connection of that layer whichever end dialled it: the two differ in who
|
|
21
21
|
* opened the socket and in nothing the layers above can see. */
|
|
22
22
|
export async function dialWs(options: DialOptions): Promise<Conn> {
|
package/src/transport/driver.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface FrameHandler {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** The ops whose reply settles the connection's identity. Transport knows these
|
|
12
|
-
* three op names because binding the identity is its job (
|
|
12
|
+
* three op names because binding the identity is its job (DESIGN §2.1);
|
|
13
13
|
* every other op is opaque to it. */
|
|
14
14
|
|
|
15
15
|
/** Drive one connection: a line in, a frame answered on the same connection.
|
|
@@ -36,7 +36,7 @@ export function createDriver(conn: Conn, handle: FrameHandler) {
|
|
|
36
36
|
settleIfHello(conn, frame, result);
|
|
37
37
|
conn.send(responseOf(result));
|
|
38
38
|
// Whatever the implementation queued for after its reply — the
|
|
39
|
-
// snapshot of a fresh subscription (§6.1) — goes out here.
|
|
39
|
+
// snapshot of a fresh subscription (DESIGN §6.1) — goes out here.
|
|
40
40
|
conn.flushDeferred();
|
|
41
41
|
},
|
|
42
42
|
(cause: unknown) => {
|
package/src/transport/entry.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Who is allowed to reach this instance at all (
|
|
1
|
+
/** Who is allowed to reach this instance at all (DESIGN §2.1, "入口の許可").
|
|
2
2
|
*
|
|
3
3
|
* Two questions, because they are asked of different things. `allowRequest`
|
|
4
4
|
* runs for every HTTP request the listener takes, the routed ones included, and
|
package/src/transport/framing.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Newline-delimited JSON, in one place for both transports (
|
|
1
|
+
/** Newline-delimited JSON, in one place for both transports (DESIGN §2.1).
|
|
2
2
|
*
|
|
3
3
|
* The limit and the backpressure handling live here rather than in the UDS and
|
|
4
4
|
* WS listeners, so the two cannot drift into two framings. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** One bound address, and the way to give it up.
|
|
2
2
|
*
|
|
3
|
-
* `kind` exists for the stop order of
|
|
3
|
+
* `kind` exists for the stop order of DESIGN §8.5: the UDS is released after
|
|
4
4
|
* everything else, because a client reads "the unix socket refuses" as the
|
|
5
5
|
* instance having finished leaving, and a successor may take the resources it
|
|
6
6
|
* sees freed before that. */
|
|
@@ -15,7 +15,7 @@ export interface Listener {
|
|
|
15
15
|
close(): Promise<void>;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
/** The listeners one instance holds, closed in the order §8.5 requires. */
|
|
18
|
+
/** The listeners one instance holds, closed in the order DESIGN §8.5 requires. */
|
|
19
19
|
export class Transport {
|
|
20
20
|
readonly #listeners: Listener[] = [];
|
|
21
21
|
|
|
@@ -29,7 +29,7 @@ export class Transport {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/** Release every address, the unix socket last. Callers do the steps that
|
|
32
|
-
* come before this one (§8.5 1-4: refuse new work, stop upstream watches,
|
|
32
|
+
* come before this one (DESIGN §8.5 1-4: refuse new work, stop upstream watches,
|
|
33
33
|
* tell the connections, settle what is persisted). */
|
|
34
34
|
async close(): Promise<void> {
|
|
35
35
|
const held = [...this.#listeners];
|
package/src/transport/uds.ts
CHANGED
|
@@ -13,7 +13,7 @@ interface UdsState {
|
|
|
13
13
|
export interface UdsOptions {
|
|
14
14
|
/** The socket path.
|
|
15
15
|
*
|
|
16
|
-
* §8.5 asks that closing leave the path alone, because a successor's socket
|
|
16
|
+
* DESIGN §8.5 asks that closing leave the path alone, because a successor's socket
|
|
17
17
|
* may already stand there. Bun's listener unlinks it in `stop()` regardless
|
|
18
18
|
* (measured against Bun 1.3.13: the path is gone the moment `stop` returns),
|
|
19
19
|
* so this layer cannot honour that on its own — what it can do is not add a
|
|
@@ -29,7 +29,7 @@ export interface UdsOptions {
|
|
|
29
29
|
*
|
|
30
30
|
* `socket.write` hands the bytes to sendto(2) and returns a short count when
|
|
31
31
|
* the socket buffer is full, so the unsent tail is kept by the queue and
|
|
32
|
-
* written again on `drain` — the difference from WS that §
|
|
32
|
+
* written again on `drain` — the difference from WS that DESIGN §2.1 puts in this
|
|
33
33
|
* layer. */
|
|
34
34
|
export function listenUds(options: UdsOptions): Listener {
|
|
35
35
|
const server = Bun.listen<UdsState>({
|
package/src/transport/ws.ts
CHANGED
|
@@ -37,7 +37,7 @@ export interface WsOptions {
|
|
|
37
37
|
*
|
|
38
38
|
* It shares this listener rather than opening a second one: a producer that
|
|
39
39
|
* posts to this instance reaches it at the address it already has, and the
|
|
40
|
-
* entry check of §
|
|
40
|
+
* entry check of DESIGN §2.1 runs before this is asked, so a route cannot be
|
|
41
41
|
* reached by anyone the WebSocket could not be. Answering `undefined` leaves
|
|
42
42
|
* the request to the upgrade, which refuses it.
|
|
43
43
|
*
|
|
@@ -54,7 +54,7 @@ export interface WsOptions {
|
|
|
54
54
|
* newline-delimited framing as the unix socket: one line is one frame, whether
|
|
55
55
|
* a message holds one line or several. Backpressure differs from UDS — a send
|
|
56
56
|
* is either buffered whole by Bun or dropped whole — and the queue absorbs
|
|
57
|
-
* that difference here (§
|
|
57
|
+
* that difference here (DESIGN §2.1). */
|
|
58
58
|
export function serveWs(options: WsOptions): Listener {
|
|
59
59
|
const path = options.path ?? ENTRY_PATH;
|
|
60
60
|
const entry = options.entry ?? OPEN;
|
package/src/upstream/events.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { LlmRequestInfo, Sid, Timestamp } from "@ccmsg/protocol";
|
|
|
4
4
|
* instance decides whether its series is the session's main one.
|
|
5
5
|
*
|
|
6
6
|
* `main` is a verdict about a session's several series, so it needs the other
|
|
7
|
-
* series to be made and cannot be read off one event (§
|
|
7
|
+
* series to be made and cannot be read off one event (DESIGN §2.4 renames, it does not
|
|
8
8
|
* derive). `instance` is stamped by whoever publishes, since an event says
|
|
9
9
|
* nothing about which instance received it. */
|
|
10
10
|
export type LlmRequestObservation = Omit<LlmRequestInfo, "main" | "instance">;
|
|
@@ -68,7 +68,7 @@ export type GatewayItem =
|
|
|
68
68
|
/** The name of the lifetime this request promised, when it promised one.
|
|
69
69
|
* Kept beside the observation rather than inside it: it is how two
|
|
70
70
|
* notices of the gateway's are matched to each other, and nothing a
|
|
71
|
-
* client reads (§
|
|
71
|
+
* client reads (DESIGN §2.4). */
|
|
72
72
|
readonly notice?: string;
|
|
73
73
|
}
|
|
74
74
|
| { readonly kind: "response"; readonly info: LlmResponseObservation }
|
|
@@ -95,7 +95,7 @@ const SAME_NAME_NUMBERS = [
|
|
|
95
95
|
|
|
96
96
|
/** The instants the gateway names without the suffix this contract requires of
|
|
97
97
|
* every field that is a point in time. Renamed here, at the boundary, so
|
|
98
|
-
* nothing downstream sees the gateway's spelling (§
|
|
98
|
+
* nothing downstream sees the gateway's spelling (DESIGN §2.4). */
|
|
99
99
|
const RENAMED_INSTANTS = [
|
|
100
100
|
["cache_since", "cache_since_at"],
|
|
101
101
|
["cache_until", "cache_until_at"],
|
package/src/upstream/gateway.ts
CHANGED
|
@@ -45,14 +45,14 @@ export interface GatewaySetup {
|
|
|
45
45
|
readonly statsUrl?: string;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/** Read the upstream section (§8.2).
|
|
48
|
+
/** Read the upstream section (DESIGN §8.2).
|
|
49
49
|
*
|
|
50
50
|
* A setting that is there and cannot be honoured ends the start rather than
|
|
51
|
-
* leaving the feature it asked for silently off (
|
|
51
|
+
* leaving the feature it asked for silently off (DR-0004): an operator who named
|
|
52
52
|
* a webhook source wants the route, and an instance that came up without it
|
|
53
53
|
* looks identical to one nobody is posting to. A section that is absent is not
|
|
54
54
|
* broken — it states that this instance has no gateway, which costs the rows
|
|
55
|
-
* one attribute and nothing else (§
|
|
55
|
+
* one attribute and nothing else (DESIGN §4.3). */
|
|
56
56
|
export function gatewaySetup(config: UpstreamConfig, file: string, env: Env): GatewaySetup {
|
|
57
57
|
const name = config.gateway_webhook_source;
|
|
58
58
|
const url = config.gateway_url;
|
|
@@ -112,7 +112,7 @@ export interface GatewayDeps {
|
|
|
112
112
|
readonly setup: GatewaySetup;
|
|
113
113
|
readonly publish: (topic: string, data: unknown) => void;
|
|
114
114
|
/** The gateway saw something happen for a session, which is an input of the
|
|
115
|
-
* sessions domain (§
|
|
115
|
+
* sessions domain (DESIGN §4.2) rather than of either topic. */
|
|
116
116
|
readonly onActivity?: () => void;
|
|
117
117
|
/** A session already known to be running was seen again: its clock moved,
|
|
118
118
|
* and the row that carries it is what says so. */
|
|
@@ -173,7 +173,7 @@ export class Gateway {
|
|
|
173
173
|
return this.status ?? SILENT;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
/** When the gateway last saw inference for a session (§
|
|
176
|
+
/** When the gateway last saw inference for a session (DESIGN §4.2). */
|
|
177
177
|
activeAt(sid: Sid, now?: Timestamp): Timestamp | undefined {
|
|
178
178
|
return this.requests.activeAt(sid, now);
|
|
179
179
|
}
|
|
@@ -184,7 +184,7 @@ export class Gateway {
|
|
|
184
184
|
return handleWebhook(request, this.#source, this.deps.log);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
/** Stop what is pending. Called from the stop order (§8.5). */
|
|
187
|
+
/** Stop what is pending. Called from the stop order (DESIGN §8.5). */
|
|
188
188
|
close(): void {
|
|
189
189
|
this.status?.stop();
|
|
190
190
|
}
|
package/src/upstream/json.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Timestamp } from "@ccmsg/protocol";
|
|
2
2
|
|
|
3
|
-
/** What every read of an upstream document is built from (§
|
|
3
|
+
/** What every read of an upstream document is built from (DESIGN §2.4).
|
|
4
4
|
*
|
|
5
5
|
* The gateway's three documents are read the same way — a field is taken only
|
|
6
6
|
* at the type this contract states for it, and anything else is absent — so the
|
package/src/upstream/requests.ts
CHANGED
|
@@ -17,10 +17,10 @@ import type {
|
|
|
17
17
|
|
|
18
18
|
export interface LlmRequestsDeps {
|
|
19
19
|
readonly self: InstanceId;
|
|
20
|
-
/** The one way a value reaches subscribers (§6.1). */
|
|
20
|
+
/** The one way a value reaches subscribers (DESIGN §6.1). */
|
|
21
21
|
readonly publish: (topic: string, data: unknown) => void;
|
|
22
22
|
/** An event moved when a session was last seen running inference, which is
|
|
23
|
-
* an input of the sessions domain (§
|
|
23
|
+
* an input of the sessions domain (DESIGN §4.2) and not of this topic. Told when
|
|
24
24
|
* the window opened, which is the moment the classification can change. */
|
|
25
25
|
readonly onActivity?: () => void;
|
|
26
26
|
/** The same session seen again inside a window already open: one attribute
|
|
@@ -32,7 +32,7 @@ export interface LlmRequestsDeps {
|
|
|
32
32
|
|
|
33
33
|
/** Which of a session's gateway facts moved.
|
|
34
34
|
*
|
|
35
|
-
* `live` is the moment the classification of §
|
|
35
|
+
* `live` is the moment the classification of DESIGN §4.2 can change, because the
|
|
36
36
|
* window either opened or closed, and the sessions domain recomputes for it.
|
|
37
37
|
* `clock` is the same session seen again inside a window that was already open
|
|
38
38
|
* — the value of an attribute, not a section anything is in — so what it asks
|
|
@@ -209,7 +209,7 @@ export class LlmRequests implements UpstreamResource {
|
|
|
209
209
|
else if (move === "clock") this.deps.onMoved?.(sid);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
/** When the gateway last saw inference for a session (§
|
|
212
|
+
/** When the gateway last saw inference for a session (DESIGN §4.2). Undefined once
|
|
213
213
|
* that is old enough to say nothing about whether the session is alive. */
|
|
214
214
|
activeAt(sid: Sid, now: Timestamp = Date.now()): Timestamp | undefined {
|
|
215
215
|
const at = this.#activeAt.get(sid);
|
|
@@ -237,7 +237,7 @@ export class LlmRequests implements UpstreamResource {
|
|
|
237
237
|
}));
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
// --- UpstreamResource (§6.3). There is nothing to start: the events are
|
|
240
|
+
// --- UpstreamResource (DESIGN §6.3). There is nothing to start: the events are
|
|
241
241
|
// pushed to this instance whether or not anyone is listening, because the
|
|
242
242
|
// sessions domain reads the same arrivals for a value of its own.
|
|
243
243
|
|
|
@@ -258,7 +258,7 @@ export class LlmRequests implements UpstreamResource {
|
|
|
258
258
|
* A session already inside its window moves its clock and nothing else, so
|
|
259
259
|
* the row it lands on is restated on its own: inference is observed several
|
|
260
260
|
* times a second, and recomputing the domain for each would spend the whole
|
|
261
|
-
* of that work on one attribute of one row (§
|
|
261
|
+
* of that work on one attribute of one row (DESIGN §4.3). */
|
|
262
262
|
private active(sid: Sid, at: Timestamp): GatewayMove {
|
|
263
263
|
const held = this.#activeAt.get(sid);
|
|
264
264
|
if (held !== undefined && held >= at) return "none";
|
package/src/upstream/stats.ts
CHANGED
|
@@ -47,7 +47,7 @@ export async function readStats(
|
|
|
47
47
|
return stats;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/** Read the gateway's document as this contract's answer (§
|
|
50
|
+
/** Read the gateway's document as this contract's answer (DESIGN §2.4). The dates are
|
|
51
51
|
* the gateway's own keys and are not reinterpreted: a day here means whatever
|
|
52
52
|
* it means there. */
|
|
53
53
|
export function statsOf(value: unknown): LlmStatsReadResult | undefined {
|
package/src/upstream/status.ts
CHANGED
|
@@ -59,7 +59,7 @@ export interface LlmStatusDeps {
|
|
|
59
59
|
|
|
60
60
|
/** The gateway's report on the services behind it.
|
|
61
61
|
*
|
|
62
|
-
* `per_instance_whole` (§6.2): a frame replaces what this instance last said
|
|
62
|
+
* `per_instance_whole` (DESIGN §6.2): a frame replaces what this instance last said
|
|
63
63
|
* and leaves other instances' reports alone, because the report is one document
|
|
64
64
|
* the gateway behind this instance assembles and half of it means nothing on
|
|
65
65
|
* its own.
|
|
@@ -85,7 +85,7 @@ export class LlmStatus implements UpstreamResource {
|
|
|
85
85
|
this.#settling = undefined;
|
|
86
86
|
void this.read();
|
|
87
87
|
}, this.deps.settleMs ?? TROUBLE_SETTLE_MS);
|
|
88
|
-
// The instance must be able to leave while this is pending (§8.5).
|
|
88
|
+
// The instance must be able to leave while this is pending (DESIGN §8.5).
|
|
89
89
|
this.#settling.unref?.();
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -99,7 +99,7 @@ export class LlmStatus implements UpstreamResource {
|
|
|
99
99
|
await this.#reading;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
// --- UpstreamResource (§6.3)
|
|
102
|
+
// --- UpstreamResource (DESIGN §6.3)
|
|
103
103
|
|
|
104
104
|
start(): void {
|
|
105
105
|
this.#listening = true;
|
|
@@ -156,7 +156,7 @@ const OFFICIAL_STATES: readonly LlmStatusOfficialState[] = [
|
|
|
156
156
|
];
|
|
157
157
|
const OBSERVED_STATES: readonly LlmStatusObservedState[] = ["reachable", "failing", "unknown"];
|
|
158
158
|
|
|
159
|
-
/** Read the gateway's document as this contract's report (§
|
|
159
|
+
/** Read the gateway's document as this contract's report (DESIGN §2.4).
|
|
160
160
|
*
|
|
161
161
|
* The gateway already answers in Unix ms under these names, so nothing is
|
|
162
162
|
* converted — but nothing is passed through unread either: every field is
|
package/src/upstream/usage.ts
CHANGED
|
@@ -64,7 +64,7 @@ export async function readUsage(
|
|
|
64
64
|
return usage;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/** Read the gateway's document as this contract's answer (§
|
|
67
|
+
/** Read the gateway's document as this contract's answer (DESIGN §2.4).
|
|
68
68
|
*
|
|
69
69
|
* The names that differ are the two the gateway spells its own way — `reset`
|
|
70
70
|
* and `window_seconds` — and both are already Unix ms and seconds, so the
|
package/src/upstream/webhook.ts
CHANGED
|
@@ -94,7 +94,7 @@ export async function handleWebhook(
|
|
|
94
94
|
/** Whether the request presents exactly this source's token.
|
|
95
95
|
*
|
|
96
96
|
* Compared in constant time. The route is reachable only by whoever the entry
|
|
97
|
-
* check of §
|
|
97
|
+
* check of DESIGN §2.1 already let through, but a comparison that leaks its prefix
|
|
98
98
|
* through timing is the kind of thing that quietly stops being enough once an
|
|
99
99
|
* instance is bound past loopback. */
|
|
100
100
|
function authorized(header: string | null, token: string): boolean {
|