@ccmsg/cli 0.11.2 → 0.11.4

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 (67) hide show
  1. package/README.md +8 -12
  2. package/package.json +1 -1
  3. package/src/auth/auth.ts +23 -23
  4. package/src/auth/http.ts +3 -3
  5. package/src/auth/records.ts +2 -2
  6. package/src/auth/webauthn.ts +1 -1
  7. package/src/cli.ts +78 -23
  8. package/src/daemon/control.ts +1 -1
  9. package/src/daemon/registry.ts +11 -11
  10. package/src/daemon/snapshot.ts +1 -1
  11. package/src/daemon/supervise.ts +4 -4
  12. package/src/dispatch/caller.ts +1 -1
  13. package/src/dispatch/dispatch.ts +4 -4
  14. package/src/dispatch/handler.ts +4 -4
  15. package/src/dispatch/identity.ts +1 -1
  16. package/src/dispatch/result.ts +2 -2
  17. package/src/files/containment.ts +2 -2
  18. package/src/harness/index.ts +1 -1
  19. package/src/instance/client.ts +1 -1
  20. package/src/instance/config.ts +13 -13
  21. package/src/instance/instance.ts +42 -42
  22. package/src/instance/lock.ts +2 -2
  23. package/src/instance/log.ts +1 -1
  24. package/src/instance/paths.ts +6 -6
  25. package/src/instance/socket.ts +1 -1
  26. package/src/kv/store.ts +1 -1
  27. package/src/mesh/instances.ts +2 -2
  28. package/src/mesh/keys.ts +7 -7
  29. package/src/mesh/mesh.ts +67 -67
  30. package/src/mesh/relay.ts +8 -8
  31. package/src/mesh/wire.ts +2 -2
  32. package/src/messaging/delivery.ts +24 -24
  33. package/src/messaging/direct.ts +23 -23
  34. package/src/messaging/inbox.ts +6 -6
  35. package/src/messaging/notify.ts +5 -5
  36. package/src/plugin/codex.ts +1 -1
  37. package/src/sessions/classify.ts +6 -6
  38. package/src/sessions/dump.ts +1 -1
  39. package/src/sessions/handlers.ts +2 -2
  40. package/src/sessions/harness.ts +12 -12
  41. package/src/sessions/last-live.ts +4 -4
  42. package/src/sessions/registry.ts +32 -32
  43. package/src/sessions/status.ts +7 -7
  44. package/src/sessions/terminals.ts +1 -1
  45. package/src/transcript/files.ts +4 -4
  46. package/src/transcript/fold.ts +9 -9
  47. package/src/transcript/items/classify.ts +56 -8
  48. package/src/transcript/read.ts +1 -1
  49. package/src/transcript/tail.ts +3 -3
  50. package/src/transcript/transcripts.ts +7 -7
  51. package/src/translate/helper.ts +2 -2
  52. package/src/transport/conn.ts +3 -3
  53. package/src/transport/dial.ts +1 -1
  54. package/src/transport/driver.ts +2 -2
  55. package/src/transport/entry.ts +1 -1
  56. package/src/transport/framing.ts +1 -1
  57. package/src/transport/listener.ts +3 -3
  58. package/src/transport/uds.ts +2 -2
  59. package/src/transport/ws.ts +2 -2
  60. package/src/upstream/events.ts +3 -3
  61. package/src/upstream/gateway.ts +6 -6
  62. package/src/upstream/json.ts +1 -1
  63. package/src/upstream/requests.ts +6 -6
  64. package/src/upstream/stats.ts +1 -1
  65. package/src/upstream/status.ts +4 -4
  66. package/src/upstream/usage.ts +1 -1
  67. package/src/upstream/webhook.ts +1 -1
@@ -10,7 +10,7 @@ export class CallerError extends Error {}
10
10
  * The role is read here because it decides which fields the value has to
11
11
  * carry — `sid` is present exactly when the role is `session` — and not
12
12
  * whether anything is allowed: what the identity may do is decided afterwards,
13
- * by dispatch, against the attribute table (§3.2). It is the same question
13
+ * by dispatch, against the attribute table (DESIGN §2.2). It is the same question
14
14
  * `hello` asks of a greeting, whose shape depends on its role in the same way
15
15
  * and for the same reason: one schema covers all three roles, so the schema
16
16
  * cannot state the rule and the instance does.
@@ -20,7 +20,7 @@ export interface DispatchDeps {
20
20
  readonly capabilities: ReadonlySet<Capability>;
21
21
  /** The instance that owns the subject of an `instance-local` op, or
22
22
  * `undefined` when no other instance owns it and we answer ourselves.
23
- * The routing table behind this is the `peers` topic (daemon-v2 §7.3). */
23
+ * The routing table behind this is the `peers` topic (DESIGN §7.3). */
24
24
  readonly resolveInstance: (op: OpName, frame: Record<string, unknown>) => InstanceId | undefined;
25
25
  readonly handlers: Handlers;
26
26
  }
@@ -31,7 +31,7 @@ function isOpName(op: string): op is OpName {
31
31
 
32
32
  /** Decide one frame.
33
33
  *
34
- * The six steps of daemon-v2 §3.2 are written once, here, and read the op
34
+ * The six steps of DESIGN §2.2 are written once, here, and read the op
35
35
  * attribute table for every op. Adding an op is a row in the table plus a
36
36
  * schema and an implementation — never a check in this function (M1). */
37
37
  export async function dispatch(
@@ -101,7 +101,7 @@ export async function dispatch(
101
101
  //
102
102
  // A request that has already been here is dropped before that: a cycle in
103
103
  // the routing would otherwise send it round the same instances until every
104
- // deadline expired (§7.3). It is answered rather than left unanswered,
104
+ // deadline expired (DESIGN §7.3). It is answered rather than left unanswered,
105
105
  // because the caller learns the same thing sooner and the code is the one
106
106
  // the contract gives a destination that could not be reached.
107
107
  const hops = fields["hops"];
@@ -123,7 +123,7 @@ export async function dispatch(
123
123
  args: fields,
124
124
  conn,
125
125
  identity: identity.state === "settled" ? identity : undefined,
126
- // The one route by which a role reaches an implementation (§3.2).
126
+ // The one route by which a role reaches an implementation (DESIGN §2.2).
127
127
  role: attrs.scope === "role" && identity.state === "settled" ? identity.role : undefined,
128
128
  });
129
129
  return reply(requestId, body);
@@ -5,7 +5,7 @@ import type { ConnIdentity, SettledIdentity } from "./identity.ts";
5
5
  *
6
6
  * It is the `Conn` transport accepted, narrowed to what an op may do with it:
7
7
  * read the identity, push frames, and learn that the connection is gone. A
8
- * subscription is held by a connection and ends with it (daemon-v2 §6.3), so
8
+ * subscription is held by a connection and ends with it (DESIGN §6.3), so
9
9
  * this is what the topic mechanism keys its subscribers on. Declared here
10
10
  * rather than imported from transport because dispatch sits below it. */
11
11
  export interface Requester {
@@ -25,12 +25,12 @@ export interface Requester {
25
25
  /** What an op implementation receives.
26
26
  *
27
27
  * The arguments are already validated and the caller is already allowed
28
- * (daemon-v2 §3.2): a handler starts from "this may be run", so it holds no
28
+ * (DESIGN §2.2): a handler starts from "this may be run", so it holds no
29
29
  * check of its own. */
30
30
  export interface HandlerInput {
31
31
  readonly op: OpName;
32
32
  /** The connection the request arrived on. Ops that hold something for the
33
- * length of a connection — the subscriptions of daemon-v2 §6.3 — need it;
33
+ * length of a connection — the subscriptions of DESIGN §6.3 — need it;
34
34
  * ops that only answer ignore it. */
35
35
  readonly conn: Requester;
36
36
  /** The whole request frame, validated against the op's request schema. */
@@ -40,7 +40,7 @@ export interface HandlerInput {
40
40
  /** Set only for ops the attribute table marks `scope: "role"`, where the role
41
41
  * changes what the reply may contain rather than whether the call is allowed.
42
42
  * This is the only route by which a role reaches an implementation
43
- * (daemon-v2 §3.2). */
43
+ * (DESIGN §2.2). */
44
44
  readonly role?: Role;
45
45
  }
46
46
 
@@ -2,7 +2,7 @@ import type { Role, Sid } from "@ccmsg/protocol";
2
2
 
3
3
  /** What dispatch knows about the connection a frame arrived on.
4
4
  *
5
- * transport settles this (daemon-v2 §3.1): a connection starts anonymous and
5
+ * transport settles this (DESIGN §2.1): a connection starts anonymous and
6
6
  * becomes `settled` when `hello` binds a role, and a session's `sid`, to it.
7
7
  * Nothing else about the connection reaches dispatch — the authorization steps
8
8
  * read the op attribute table, not the connection. */
@@ -3,7 +3,7 @@ import type { ErrorCode, ErrorResponse, InstanceId } from "@ccmsg/protocol";
3
3
  /** What dispatch decided about one frame.
4
4
  *
5
5
  * `forward` is the only outcome that is not an answer: the op belongs to
6
- * another instance and mesh has to carry it there (daemon-v2 §3.2 step 6).
6
+ * another instance and mesh has to carry it there (DESIGN §2.2 step 6).
7
7
  * There is no mesh yet, so dispatch names the destination and stops. */
8
8
  export type DispatchResult =
9
9
  /** The frame was not a request and has no answer. The mesh handshake's own
@@ -32,7 +32,7 @@ export class OpError extends Error {
32
32
  }
33
33
 
34
34
  /** The reply envelope, built here and nowhere else so the wire shape stays in
35
- * one place (daemon-v2 §11.1). */
35
+ * one place (DESIGN §9.1). */
36
36
  export function reply(
37
37
  requestId: string,
38
38
  body: unknown,
@@ -53,13 +53,13 @@ export interface Located {
53
53
  * range a caller has are decided in one place and cannot come apart.
54
54
  *
55
55
  * `Viewer` is that visible range, and it is where the role of a `scope: "role"`
56
- * op arrives (§3.2): a session reads its own session's files, a person reads
56
+ * op arrives (DESIGN §2.2): a session reads its own session's files, a person reads
57
57
  * any session's, and a role the rule does not name reaches nothing rather than
58
58
  * being guessed at. An op the attribute table gives no `scope` states no role
59
59
  * here, and needs none — dispatch has already settled who may call it. */
60
60
  export interface Viewer {
61
61
  /** Present only for an op the attribute table marks `scope: "role"`, which
62
- * is the one route by which a role reaches an implementation (§3.2). */
62
+ * is the one route by which a role reaches an implementation (DESIGN §2.2). */
63
63
  readonly role?: Role;
64
64
  /** The session the connection speaks for, when it speaks for one. */
65
65
  readonly sid?: Sid;
@@ -72,7 +72,7 @@ export const HARNESS: Record<Harness, HarnessFacts> = {
72
72
  * thread to the commands of its own turn. The narrower claim is the truer one,
73
73
  * so it is asked first. The reverse nesting — a Claude Code session started
74
74
  * from inside a Codex turn — reads as Codex. `--sid` overrides only the sid a
75
- * command speaks as, and not which instance it speaks to (§3.8).
75
+ * command speaks as, and not which instance it speaks to (DESIGN §4.1).
76
76
  *
77
77
  * A process no session runs inside — a person at a terminal, a supervisor —
78
78
  * matches nothing here, and the caller falls back to what it would have done
@@ -1,5 +1,5 @@
1
1
  /** Where a request came from, when something in front of us is forwarding it
2
- * (§3.1).
2
+ * (DESIGN §2.1).
3
3
  *
4
4
  * The address the listener observed is the one thing here that cannot be
5
5
  * claimed, and behind a reverse proxy it is always the proxy's. `X-Forwarded-*`
@@ -14,7 +14,7 @@ import { parseCidr } from "./client.ts";
14
14
 
15
15
  /** Where the instance accepts WebSocket connections, and from whom.
16
16
  *
17
- * The two allowlists are the entry check of §3.1: what transport asks before a
17
+ * The two allowlists are the entry check of DESIGN §2.1: what transport asks before a
18
18
  * request is upgraded. They are config-driven because who may reach an
19
19
  * instance is a deployment fact, not a property of the code. */
20
20
  export interface EntryConfig {
@@ -111,14 +111,14 @@ export interface DumpConfig {
111
111
  }
112
112
 
113
113
  export interface InstanceConfig {
114
- /** Which harness this config home runs (§3.8).
114
+ /** Which harness this config home runs (DESIGN §4.1).
115
115
  *
116
116
  * A setting rather than something discovered, because it decides where the
117
117
  * instance looks before there is anything there to look at: an empty config
118
118
  * home says nothing about the program it belongs to, and an instance that
119
119
  * guessed would walk the wrong tree for the whole of its first session. */
120
120
  readonly harness: Harness;
121
- /** Every instance of the mesh, this one among them (§7.1).
121
+ /** Every instance of the mesh, this one among them (DESIGN §7.1).
122
122
  *
123
123
  * Data, and the same data on every host: a settings function is handed it
124
124
  * and may read it — an instance that wants to know who else there is has it
@@ -127,7 +127,7 @@ export interface InstanceConfig {
127
127
  * settles `endpoint` below. */
128
128
  readonly endpoints: readonly EndpointRow[];
129
129
  /** Where peers and people reach this instance: its own row of the mesh
130
- * (§7.1).
130
+ * (DESIGN §7.1).
131
131
  *
132
132
  * Not something a settings file states — the row is, and two places to write
133
133
  * one address is one place for it to be wrong. An instance behind a reverse
@@ -140,7 +140,7 @@ export interface InstanceConfig {
140
140
  readonly entry?: EntryConfig;
141
141
  readonly upstream: UpstreamConfig;
142
142
  /** Whether delivery tries the harness's messaging socket before the `inbox`
143
- * topic (§4.1 condition 0). On, because the protocol has been read off a
143
+ * topic (DESIGN §6.5 condition 0). On, because the protocol has been read off a
144
144
  * running harness; off is for a harness generation that turns out to speak
145
145
  * something else, and costs only the reach route (b) never had. */
146
146
  readonly direct_delivery: boolean;
@@ -157,7 +157,7 @@ export interface InstanceConfig {
157
157
  *
158
158
  * Its own class so startup can tell "the operator wrote something wrong" from
159
159
  * any other failure, and refuse to run rather than continuing with the feature
160
- * that setting was for silently off (§8.3, DV-Q9). */
160
+ * that setting was for silently off (DESIGN §8.3, DR-0004). */
161
161
  export class ConfigError extends Error {
162
162
  constructor(
163
163
  readonly file: string,
@@ -184,7 +184,7 @@ export const DEFAULT_CONFIG: InstanceConfig = {
184
184
 
185
185
  /** The file every instance's settings start from, and the directory holding
186
186
  * one file per instance. Both are read from the config home a person edits
187
- * (§8.2). The names are held here alone, so what the files are called is one
187
+ * (DESIGN §8.2). The names are held here alone, so what the files are called is one
188
188
  * edit rather than a search. */
189
189
  export const CONFIG_FILE = "config_v2.ts";
190
190
  export const INSTANCES_DIR = "instances";
@@ -216,10 +216,10 @@ const INSTANCE_FIELDS = ["dir", "name"] as const;
216
216
  * instance cannot work out for itself — which address of the several a host
217
217
  * has is the one its peers dial, and which of the entries is this instance.
218
218
  * Both are answered by the row carrying its own id, which is what settles
219
- * `self` (§7.1) without asking the network anything.
219
+ * `self` (DESIGN §7.1) without asking the network anything.
220
220
  *
221
221
  * Every instance of the mesh is in it, this host's and the others', so one
222
- * file can be copied to every host unchanged (§8.2). */
222
+ * file can be copied to every host unchanged (DESIGN §8.2). */
223
223
  export const ENDPOINTS_FILE = "endpoints.json";
224
224
 
225
225
  /** Which of them this host starts. An id here and not in the endpoints is a
@@ -233,7 +233,7 @@ export const SUPERVISOR_FILE = "supervisor.json";
233
233
  * Apart from the files a person edits because the two answer different
234
234
  * questions: what is being written, and what is running. A config that does
235
235
  * not check out never reaches here, which is what lets a broken edit be
236
- * reported without taking the host down (§8.3). */
236
+ * reported without taking the host down (DESIGN §8.3). */
237
237
  export const STATE_CONFIG_DIR = "config";
238
238
  export const SATISFIED_FILE = "satisfied.json";
239
239
  export const REJECTED_DIR = "config.rejected";
@@ -292,7 +292,7 @@ export function instanceFileName(id: string): string {
292
292
  }
293
293
 
294
294
  /** Read everything a person edits, call what has to be called, and check the
295
- * whole of it (DV-Q8, §8.3).
295
+ * whole of it (DR-0004, DESIGN §8.3).
296
296
  *
297
297
  * One pass rather than a check per file, because what makes a config right is
298
298
  * mostly between files: an id the supervisor starts has to be an entry of the
@@ -400,7 +400,7 @@ function readEndpoints(
400
400
  if (rows.some((row) => row.id === id)) at(file, `${where}.id repeats ${id}`);
401
401
  else if (rows.some((row) => row.endpoint === endpoint)) {
402
402
  // Two entries at one address would each be this instance to whoever
403
- // dialled it, and neither could be told from the other (§7.1).
403
+ // dialled it, and neither could be told from the other (DESIGN §7.1).
404
404
  at(file, `${where}.endpoint repeats ${endpoint}`);
405
405
  } else rows.push({ id, endpoint: endpoint as Endpoint });
406
406
  }
@@ -468,7 +468,7 @@ async function instanceOf(
468
468
  if (read === undefined) return undefined;
469
469
  // Where this instance is reached: its own row of the mesh. An instance the
470
470
  // data does not name could not be dialled by anybody and could not settle
471
- // what a handshake calls it (§7.1), so it is a config error rather than an
471
+ // what a handshake calls it (DESIGN §7.1), so it is a config error rather than an
472
472
  // instance with no address.
473
473
  const mine = endpoints.find((row) => row.id === id);
474
474
  if (mine === undefined) {
@@ -111,7 +111,7 @@ export interface StartOptions {
111
111
  *
112
112
  * Passed by value rather than through the environment, because the
113
113
  * environment is read for a different question: which session the process
114
- * runs inside, and therefore which config home *that* means (§3.8). A
114
+ * runs inside, and therefore which config home *that* means (DESIGN §4.1). A
115
115
  * `daemon run <dir>` started from inside a session of another harness would
116
116
  * otherwise answer for the config home of whoever started it. Absent means
117
117
  * the environment decides, which is what a process nobody named a directory
@@ -120,7 +120,7 @@ export interface StartOptions {
120
120
  /** Mirror the log to stderr. A foreground run wants it; a test does not. */
121
121
  readonly echoLog?: boolean;
122
122
  /** Whether this start is the one that reads the edited files and writes down
123
- * what checked out (§8.2).
123
+ * what checked out (DESIGN §8.2).
124
124
  *
125
125
  * A supervisor does that for the instances it starts, so its children read
126
126
  * what it applied and write nothing: one writer means no two processes
@@ -144,19 +144,19 @@ export interface StartOptions {
144
144
  }
145
145
 
146
146
  /** The mesh intervals a caller may shorten. The values themselves, and why they
147
- * are what they are, belong to the mesh (§8.2, §8.3). */
147
+ * are what they are, belong to the mesh (DESIGN §8.2, §8.3). */
148
148
  export interface MeshTiming {
149
149
  readonly heartbeatMs?: number;
150
150
  readonly heartbeatTimeoutMs?: number;
151
151
  readonly reconnectMinMs?: number;
152
152
  readonly forwardTimeoutMs?: number;
153
- /** The clock the retention window of §7.5 is read against, so a test can
153
+ /** The clock the retention window of DESIGN §7.5 is read against, so a test can
154
154
  * pass it without waiting a week. */
155
155
  readonly now?: () => Timestamp;
156
156
  }
157
157
 
158
158
  /** Startup found another instance already serving this config home. Nothing
159
- * was created and nothing has to be undone (§8.3 step 2). */
159
+ * was created and nothing has to be undone (DESIGN §8.3 step 2). */
160
160
  export interface AlreadyRunning {
161
161
  readonly kind: "already_running";
162
162
  readonly pid: number;
@@ -168,7 +168,7 @@ export function isRunning(outcome: StartOutcome): outcome is Instance {
168
168
  return outcome instanceof Instance;
169
169
  }
170
170
 
171
- /** Start one instance, in the order of §8.3.
171
+ /** Start one instance, in the order of DESIGN §8.3.
172
172
  *
173
173
  * The order is the point of this function: the lock before anything is
174
174
  * created, the config before anything is derived from it, the pid before the
@@ -191,15 +191,15 @@ export async function start(options: StartOptions = {}): Promise<StartOutcome> {
191
191
  const log = new Log(paths.logFile, options.echoLog ?? true);
192
192
  try {
193
193
  // 3. the config. A broken one ends the start rather than turning the
194
- // setting it carried silently off (DV-Q9).
194
+ // setting it carried silently off (DR-0004).
195
195
  const config = await configFor(paths, log, options.settle ?? true);
196
196
  // What the config says of the gateway, resolved before anything is built
197
197
  // from it: a webhook source whose secret cannot be read ends the start
198
- // here, for the same reason a broken config does (DV-Q9).
198
+ // here, for the same reason a broken config does (DR-0004).
199
199
  const gateway = gatewaySetup(config.upstream, paths.configFile, env);
200
200
  // The translation helper, checked the same way and for the same reason: a
201
201
  // program that was named and cannot be run is a setting that cannot be
202
- // honoured (DV-Q9).
202
+ // honoured (DR-0004).
203
203
  const helper = translateSetup(config.upstream, paths.configFile);
204
204
  // 4. this instance's identity, written the first time it is asked for.
205
205
  //
@@ -211,7 +211,7 @@ export async function start(options: StartOptions = {}): Promise<StartOutcome> {
211
211
  // 5. the mesh, for an instance the data names an address for.
212
212
  //
213
213
  // Which entry of the list is this instance is its own row, so nothing has
214
- // to be asked of the network to settle it (§7.1). The WebSocket is still
214
+ // to be asked of the network to settle it (DESIGN §7.1). The WebSocket is still
215
215
  // bound here and handed over, because the instance does not exist yet and
216
216
  // a peer may dial the moment the address is up.
217
217
  const mesh = meshFor(id, config, log, options.meshTiming);
@@ -246,7 +246,7 @@ export async function start(options: StartOptions = {}): Promise<StartOutcome> {
246
246
  * the same place: what this instance runs with is a value that checked out.
247
247
  * A config that does not hold leaves the applied one standing and is written
248
248
  * to the log, because an instance that was serving a session is not something
249
- * a typo should take down (§8.3).
249
+ * a typo should take down (DESIGN §8.3).
250
250
  *
251
251
  * A config home nothing states settings for runs the built-in ones, which is
252
252
  * the unix socket and no mesh: `daemon run` on a directory nobody registered
@@ -299,7 +299,7 @@ function meshFor(
299
299
  *
300
300
  * The connection registry is shared rather than copied: a connection accepted
301
301
  * during self-identification is one of the instance's, and two registries would
302
- * mean the stop order (§8.5 step 3) reaching only one of them. */
302
+ * mean the stop order (DESIGN §8.5 step 3) reaching only one of them. */
303
303
  export interface MeshWiring {
304
304
  readonly conns: ConnRegistry;
305
305
  readonly ws: Listener;
@@ -343,11 +343,11 @@ async function bindForMesh(config: InstanceConfig, mesh: Mesh): Promise<MeshWiri
343
343
  }
344
344
 
345
345
  /** One running instance: the layers wired together, and the two lifecycle
346
- * orders of §8.3 and §8.5. */
346
+ * orders of DESIGN §8.3 and §8.5. */
347
347
  export class Instance {
348
348
  readonly startedAt: Timestamp = Date.now();
349
349
  readonly #conns: ConnRegistry;
350
- /** The mesh, on an instance configured for one (§7). */
350
+ /** The mesh, on an instance configured for one (DESIGN §7). */
351
351
  readonly #mesh: Mesh | undefined;
352
352
  /** The WebSocket listener, when it had to be bound before this instance
353
353
  * existed so that self-identification could reach it. */
@@ -372,7 +372,7 @@ export class Instance {
372
372
  readonly #proxies: readonly Cidr[];
373
373
  readonly #handlers: Handlers;
374
374
  readonly #capabilities: ReadonlySet<Capability>;
375
- /** Set the moment shutdown starts, which is the re-entry guard of §8.5 step
375
+ /** Set the moment shutdown starts, which is the re-entry guard of DESIGN §8.5 step
376
376
  * 1: a request arriving after it is refused rather than half-served. */
377
377
  #stopping = false;
378
378
  #stopped: Promise<void> | undefined;
@@ -425,7 +425,7 @@ export class Instance {
425
425
  ]);
426
426
  // The mesh is the rest of the cluster as the topic mechanism sees it: what
427
427
  // the peers have stated, and where a local subscription has to travel to
428
- // (§7.4). An instance without one has no other instance to hear from.
428
+ // (DESIGN §7.4). An instance without one has no other instance to hear from.
429
429
  this.#topics = new Topics(this.self, this.#capabilities, this.#mesh);
430
430
  this.#mesh?.bind({
431
431
  handle: (frame, conn) => this.handle(frame, conn),
@@ -441,7 +441,7 @@ export class Instance {
441
441
  if (Array.isArray(stated)) this.#auth.merge(stated);
442
442
  },
443
443
  // The mesh view is this instance's own, so the topic that carries it is
444
- // restated when that view moves (§7.5).
444
+ // restated when that view moves (DESIGN §7.5).
445
445
  changed: () => {
446
446
  this.#instances.refresh();
447
447
  this.#linkMoved();
@@ -449,7 +449,7 @@ export class Instance {
449
449
  });
450
450
 
451
451
  // What the gateway saw. It feeds two topics and one input of the sessions
452
- // domain (§5.1), so it is built before both.
452
+ // domain (DESIGN §4.2), so it is built before both.
453
453
  this.#gateway = new Gateway({
454
454
  self: this.self,
455
455
  setup,
@@ -480,7 +480,7 @@ export class Instance {
480
480
 
481
481
  // The transcript tails and their folds. Built before the sessions domain
482
482
  // and reading from it lazily: the fold is one of the sessions domain's
483
- // inputs (§5.1) while the path to follow is one of its outputs, and the
483
+ // inputs (DESIGN §4.2) while the path to follow is one of its outputs, and the
484
484
  // two meet at the moment a tail starts rather than at construction.
485
485
  this.#transcripts = new Transcripts({
486
486
  self: this.self,
@@ -521,7 +521,7 @@ export class Instance {
521
521
  onChanged: () => {
522
522
  this.#status.refresh();
523
523
  // A session that is live again is one route (a) can be tried against,
524
- // which is what the inbox is waiting for (§4.3).
524
+ // which is what the inbox is waiting for (DESIGN §6.7).
525
525
  void this.#delivery.retry();
526
526
  },
527
527
  ...(pollMs === undefined ? {} : { pollMs }),
@@ -530,7 +530,7 @@ export class Instance {
530
530
  // The topics whose value is the fold's error state, over the sessions the
531
531
  // instance holds. They are the other thing that keeps a tail running: a
532
532
  // subscriber watching the list of stopped sessions is watching every
533
- // session's fold, and the tails behind it run only while it does (§6.3).
533
+ // session's fold, and the tails behind it run only while it does (DESIGN §6.3).
534
534
  this.#status = new SessionStatus({
535
535
  self: this.self,
536
536
  sessions: () => this.#sessions.connectedSids(),
@@ -549,9 +549,9 @@ export class Instance {
549
549
 
550
550
  const inbox = new Inbox(inboxPath(paths.stateDir));
551
551
  inbox.load();
552
- // Route (a) is the harness's own way in (§4.1): Claude Code's messaging
552
+ // Route (a) is the harness's own way in (DESIGN §6.5): Claude Code's messaging
553
553
  // socket, Codex's thread queue. Which one an instance speaks follows the
554
- // config home it answers for (§3.8), and the flag turns the route off for
554
+ // config home it answers for (DESIGN §4.1), and the flag turns the route off for
555
555
  // either.
556
556
  this.#direct = !config.direct_delivery
557
557
  ? new DisabledDirectRoute()
@@ -597,7 +597,7 @@ export class Instance {
597
597
  this.#topics.attach("llm.status", this.#gateway.statusResource);
598
598
 
599
599
  // The one thing here that is written down and is nobody's derived value
600
- // (§3.6): what a person saved through a client, which no other party holds
600
+ // (DESIGN §2.5): what a person saved through a client, which no other party holds
601
601
  // a copy of. It owns `kv:<ns>` and is the only publisher of it.
602
602
  const kv = new KvStore(join(paths.stateDir, KV_DIR), this.self, (topic, data) => {
603
603
  this.#topics.publish(topic, data);
@@ -606,7 +606,7 @@ export class Instance {
606
606
 
607
607
  // The credentials, tokens and removals the cluster shares (DR-0001 §2.6).
608
608
  // Written down beside the store and for the same reason: none of it is
609
- // derived from anything else this instance holds (§3.6).
609
+ // derived from anything else this instance holds (DESIGN §2.5).
610
610
  const records = new AuthRecords({
611
611
  dir: recordsDir(paths.stateDir),
612
612
  self: this.self,
@@ -695,7 +695,7 @@ export class Instance {
695
695
  });
696
696
  }
697
697
 
698
- /** 7-8 of §8.3: the pid, then the listeners with the unix socket first, then
698
+ /** 7-8 of DESIGN §8.3: the pid, then the listeners with the unix socket first, then
699
699
  * the peers. */
700
700
  async listen(): Promise<void> {
701
701
  // Before any listener: a client that can connect can always find the
@@ -742,13 +742,13 @@ export class Instance {
742
742
  this.accepted(conn, info);
743
743
  },
744
744
  // The gateway posts to the address this instance already serves,
745
- // behind the same entry check (§3.1).
745
+ // behind the same entry check (DESIGN §2.1).
746
746
  route: (request, source) => this.route(request, source),
747
747
  }),
748
748
  );
749
749
  }
750
750
  // 8. the peers. Every instance dials every one of them, and one that is not
751
- // there is retried rather than waited for (§7.2).
751
+ // there is retried rather than waited for (DESIGN §7.2).
752
752
  this.#mesh?.connect();
753
753
  await Promise.resolve();
754
754
  this.log.write("started", {
@@ -810,17 +810,17 @@ export class Instance {
810
810
  }
811
811
 
812
812
  /** Whether the sessions watch is running. It is driven by subscription
813
- * (§6.3), so this is how "the upstream watches stopped" is observable from
813
+ * (DESIGN §6.3), so this is how "the upstream watches stopped" is observable from
814
814
  * outside the domain that owns them. */
815
815
  get watching(): boolean {
816
816
  return this.#sessions.watching;
817
817
  }
818
818
 
819
- /** When the gateway last saw inference for a session (§5.1).
819
+ /** When the gateway last saw inference for a session (DESIGN §4.2).
820
820
  *
821
821
  * The one input of the classification that arrives from outside this host,
822
822
  * and the only place it is observable from: it is an attribute of a row
823
- * rather than a state (§5.2), so nothing on the wire carries it yet. */
823
+ * rather than a state (DESIGN §4.3), so nothing on the wire carries it yet. */
824
824
  gatewayActiveAt(sid: Sid): Timestamp | undefined {
825
825
  return this.#gateway.activeAt(sid);
826
826
  }
@@ -845,7 +845,7 @@ export class Instance {
845
845
  * the answer is read from: a peer that answers is the link working, and
846
846
  * every configured peer silent at once is the link gone. Nothing else is
847
847
  * probed — an instance does not dial the internet to have an opinion about
848
- * it, and the peers are already being dialled for their own reasons (§8.3).
848
+ * it, and the peers are already being dialled for their own reasons (DESIGN §8.3).
849
849
  *
850
850
  * Two cases state no verdict rather than guessing one. `off` is an instance
851
851
  * with no mesh: nothing here watches the link at all. `unknown` is a mesh
@@ -872,7 +872,7 @@ export class Instance {
872
872
  for (const conn of this.#conns) conn.send(event);
873
873
  }
874
874
 
875
- /** One frame, from either transport. The re-entry guard of §8.5 step 1 sits
875
+ /** One frame, from either transport. The re-entry guard of DESIGN §8.5 step 1 sits
876
876
  * here because this is the single door every request comes through. */
877
877
  async handle(frame: unknown, conn: Requester): Promise<DispatchResult> {
878
878
  if (this.#stopping) {
@@ -934,7 +934,7 @@ export class Instance {
934
934
  });
935
935
  if (decided.kind !== "forward") return decided;
936
936
  // The op belongs to another instance. Mesh carries it and brings the
937
- // answer back under the id the caller used (§7.3); without a mesh there is
937
+ // answer back under the id the caller used (DESIGN §7.3); without a mesh there is
938
938
  // nothing that can reach it, which the driver names.
939
939
  //
940
940
  // Who it is forwarded as is stated here rather than copied from the
@@ -950,7 +950,7 @@ export class Instance {
950
950
  * The subject is the session an op names, and an op that names none is about
951
951
  * this instance and stays here. A session this instance holds is its own
952
952
  * whatever the cluster last said; one it does not hold is looked for in the
953
- * routing table the `peers` topic is (§7.3). */
953
+ * routing table the `peers` topic is (DESIGN §7.3). */
954
954
  #owner(fields: Record<string, unknown>): InstanceId | undefined {
955
955
  const sid = fields["sid"];
956
956
  if (typeof sid !== "string" || this.#mesh === undefined) return undefined;
@@ -958,7 +958,7 @@ export class Instance {
958
958
  return this.#mesh.ownerOf(sid as Sid);
959
959
  }
960
960
 
961
- /** Stop, in the order of §8.5. Repeating it waits for the first one. */
961
+ /** Stop, in the order of DESIGN §8.5. Repeating it waits for the first one. */
962
962
  stop(): Promise<void> {
963
963
  this.#stopped ??= this.#stop().finally(() => {
964
964
  this.#done.resolve();
@@ -976,7 +976,7 @@ export class Instance {
976
976
  // 1. refuse new work
977
977
  this.#stopping = true;
978
978
  // 2. stop the upstream watches. They run only while something is
979
- // subscribed (§6.3), so dropping the subscriptions is what stops them.
979
+ // subscribed (DESIGN §6.3), so dropping the subscriptions is what stops them.
980
980
  for (const conn of this.#conns) this.#topics.dropAll(conn);
981
981
  // A tail may also be held for a value this instance states rather than for
982
982
  // a subscriber, and those holds end here.
@@ -988,23 +988,23 @@ export class Instance {
988
988
  // with it rather than outliving the daemon that has its pipe.
989
989
  this.#translate?.stop();
990
990
  // Route (a) holds a socket of its own, bound where the sessions' sockets
991
- // are so their receipts can reach it (§4.1). It has a name on disk, so it
991
+ // are so their receipts can reach it (DESIGN §6.5). It has a name on disk, so it
992
992
  // is taken down here rather than left for the next run to find.
993
993
  this.#direct.close();
994
994
  // The mesh's links and its timers, let go here for the same reason: they
995
- // are this instance's and do not outlive it (§7).
995
+ // are this instance's and do not outlive it (DESIGN §7).
996
996
  this.#mesh?.stop();
997
997
  // 3. tell the connections, while they can still be told
998
998
  const restarting: RestartingEvent = { ev: "restarting", instance: this.self };
999
999
  for (const conn of this.#conns) conn.send(restarting);
1000
1000
  // 4. settle what is persisted. `last_live` and the inbox are written as
1001
1001
  // they change rather than at exit, so there is nothing held back to flush;
1002
- // the log's writer is synchronous for the same reason (§3.6).
1002
+ // the log's writer is synchronous for the same reason (DESIGN §2.5).
1003
1003
  this.log.write("stopping", { instance: this.self });
1004
1004
  // 5. let the resources go, the unix socket last. Closing takes the path
1005
1005
  // this process bound, and only that one: the stable address is a symlink
1006
1006
  // nothing here touches, because a successor may have already pointed it at
1007
- // itself (§8.5).
1007
+ // itself (DESIGN §8.5).
1008
1008
  try {
1009
1009
  await this.#transport.close();
1010
1010
  } catch (cause) {
@@ -1023,7 +1023,7 @@ export class Instance {
1023
1023
  }
1024
1024
  }
1025
1025
 
1026
- /** Who may reach the WebSocket at all (§3.1): an address the operator named.
1026
+ /** Who may reach the WebSocket at all (DESIGN §2.1): an address the operator named.
1027
1027
  *
1028
1028
  * An empty `source_ips` leaves the addresses to the bind, which for the default
1029
1029
  * loopback host is this machine. The `Origin` a request carries is not read:
@@ -2,9 +2,9 @@ import { linkSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "no
2
2
  import { dirname } from "node:path";
3
3
  import { randomUUID } from "node:crypto";
4
4
 
5
- /** The right to be the instance for one config home (§8.3 step 2).
5
+ /** The right to be the instance for one config home (DESIGN §8.3 step 2).
6
6
  *
7
- * A handle, not state (§3.6): it says who is running right now and means
7
+ * A handle, not state (DESIGN §2.5): it says who is running right now and means
8
8
  * nothing once the process is gone. */
9
9
  export interface Lock {
10
10
  release(): void;
@@ -2,7 +2,7 @@ import { appendFileSync, mkdirSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
3
 
4
4
  /** The instance's log: one writer, and every line on disk before the call
5
- * returns (§3.6).
5
+ * returns (DESIGN §2.5).
6
6
  *
7
7
  * The reason to read a log is to find out why a process stopped, so the line
8
8
  * that matters most is the last one written before it did. A buffered writer