@claudexor/daemon 3.3.16 → 3.4.1
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/command-store.d.ts +3 -0
- package/dist/command-store.d.ts.map +1 -1
- package/dist/command-store.js +4 -0
- package/dist/command-store.js.map +1 -1
- package/dist/daemon-listen.d.ts +10 -0
- package/dist/daemon-listen.d.ts.map +1 -0
- package/dist/daemon-listen.js +36 -0
- package/dist/daemon-listen.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/interactions.d.ts +3 -0
- package/dist/interactions.d.ts.map +1 -1
- package/dist/interactions.js +4 -0
- package/dist/interactions.js.map +1 -1
- package/dist/journal-manager-lifecycle.d.ts +8 -0
- package/dist/journal-manager-lifecycle.d.ts.map +1 -0
- package/dist/journal-manager-lifecycle.js +2 -0
- package/dist/journal-manager-lifecycle.js.map +1 -0
- package/dist/journal-manager.d.ts +21 -11
- package/dist/journal-manager.d.ts.map +1 -1
- package/dist/journal-manager.js +234 -198
- package/dist/journal-manager.js.map +1 -1
- package/dist/journal-prepared-operation.d.ts +53 -0
- package/dist/journal-prepared-operation.d.ts.map +1 -0
- package/dist/journal-prepared-operation.js +158 -0
- package/dist/journal-prepared-operation.js.map +1 -0
- package/dist/journal-projection-slot.d.ts +28 -0
- package/dist/journal-projection-slot.d.ts.map +1 -0
- package/dist/journal-projection-slot.js +71 -0
- package/dist/journal-projection-slot.js.map +1 -0
- package/dist/journal-projection.d.ts +2 -0
- package/dist/journal-projection.d.ts.map +1 -1
- package/dist/journal-recovery-files.d.ts +19 -1
- package/dist/journal-recovery-files.d.ts.map +1 -1
- package/dist/journal-recovery-files.js +271 -26
- package/dist/journal-recovery-files.js.map +1 -1
- package/dist/journal-recovery-operation.d.ts +27 -0
- package/dist/journal-recovery-operation.d.ts.map +1 -0
- package/dist/journal-recovery-operation.js +68 -0
- package/dist/journal-recovery-operation.js.map +1 -0
- package/dist/project-partition-preparation.d.ts +75 -0
- package/dist/project-partition-preparation.d.ts.map +1 -0
- package/dist/project-partition-preparation.js +233 -0
- package/dist/project-partition-preparation.js.map +1 -0
- package/dist/project-partitions.d.ts +18 -16
- package/dist/project-partitions.d.ts.map +1 -1
- package/dist/project-partitions.js +98 -99
- package/dist/project-partitions.js.map +1 -1
- package/dist/project-thread-listing.d.ts +14 -0
- package/dist/project-thread-listing.d.ts.map +1 -0
- package/dist/project-thread-listing.js +37 -0
- package/dist/project-thread-listing.js.map +1 -0
- package/dist/quota-projection.d.ts +9 -0
- package/dist/quota-projection.d.ts.map +1 -0
- package/dist/quota-projection.js +10 -0
- package/dist/quota-projection.js.map +1 -0
- package/dist/quota-registry.d.ts +3 -5
- package/dist/quota-registry.d.ts.map +1 -1
- package/dist/quota-registry.js +9 -10
- package/dist/quota-registry.js.map +1 -1
- package/dist/root-authority.d.ts +68 -0
- package/dist/root-authority.d.ts.map +1 -0
- package/dist/root-authority.js +380 -0
- package/dist/root-authority.js.map +1 -0
- package/dist/rpc-followers.d.ts +17 -0
- package/dist/rpc-followers.d.ts.map +1 -0
- package/dist/rpc-followers.js +46 -0
- package/dist/rpc-followers.js.map +1 -0
- package/dist/server.d.ts +4 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +42 -62
- package/dist/server.js.map +1 -1
- package/dist/serving-admission.d.ts +21 -0
- package/dist/serving-admission.d.ts.map +1 -0
- package/dist/serving-admission.js +17 -0
- package/dist/serving-admission.js.map +1 -0
- package/dist/terminate.d.ts +18 -7
- package/dist/terminate.d.ts.map +1 -1
- package/dist/terminate.js +213 -69
- package/dist/terminate.js.map +1 -1
- package/dist/token.d.ts +6 -0
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js +8 -0
- package/dist/token.js.map +1 -1
- package/dist/writer-lease.d.ts +76 -4
- package/dist/writer-lease.d.ts.map +1 -1
- package/dist/writer-lease.js +346 -56
- package/dist/writer-lease.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent root-authority barrier for a shared data root (issue #165).
|
|
3
|
+
*
|
|
4
|
+
* D1: before ANY journal recovery or destructive startup work, the fixed
|
|
5
|
+
* daemon publishes a permanent, legacy-opaque barrier at the canonical writer
|
|
6
|
+
* address. The barrier is a lease DIRECTORY carrying `root-authority-v2.json`
|
|
7
|
+
* and NO top-level owner.json, so a pre-fix claimant's owner parse fails
|
|
8
|
+
* closed forever — it can neither adopt nor quarantine the address. The
|
|
9
|
+
* barrier is never removed, including on clean shutdown: a failed fixed
|
|
10
|
+
* candidate leaves a protected recovery/offline state instead of reopening
|
|
11
|
+
* the process-reaping race.
|
|
12
|
+
*
|
|
13
|
+
* D2: the barrier record persists two SEPARATE facts — the writer protocol
|
|
14
|
+
* epoch (pre-fix and newer-than-supported epochs are refused) and the
|
|
15
|
+
* semantic version floor of the last fixed runtime that PROVED it could
|
|
16
|
+
* serve (strictly lower fixed versions are refused; equal versions contend
|
|
17
|
+
* normally — build SHA/entry path are evidence, not ordering).
|
|
18
|
+
*
|
|
19
|
+
* D3: an already-running pre-fix contender that passed its dead-owner check
|
|
20
|
+
* before this barrier existed cannot be retroactively revoked — a LIVE flat
|
|
21
|
+
* owner refuses this candidate through the shared lease machinery. Migration
|
|
22
|
+
* of an owned flat generation is an in-place atomic owner replacement: the
|
|
23
|
+
* owner record is atomically swapped for an unparseable migration sentinel,
|
|
24
|
+
* so the address is never absent and never carries a dead parseable owner.
|
|
25
|
+
* This module makes no stronger claim.
|
|
26
|
+
*
|
|
27
|
+
* D4: authority follows the data root. The barrier anchors at the canonical
|
|
28
|
+
* DEFAULT socket address of the daemon root even when the serving socket
|
|
29
|
+
* spelling differs, so every fixed runtime sharing one
|
|
30
|
+
* CLAUDEXOR_CONFIG_DIR/daemon root contends on one root authority.
|
|
31
|
+
*
|
|
32
|
+
* This module builds ON the writer-lease owner (single-writer acquisition,
|
|
33
|
+
* owner classification, stale quarantine) and never re-implements liveness.
|
|
34
|
+
*/
|
|
35
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
36
|
+
import { lstatSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
37
|
+
import { join } from "node:path";
|
|
38
|
+
import { compareRuntimeSemver, isRuntimeSemver } from "@claudexor/util";
|
|
39
|
+
import { canonicalDefaultSocketPath } from "./token.js";
|
|
40
|
+
import { acquireDaemonWriterLease, inspectDaemonWriterLease, resolveWriterLeaseFilesystem, writerLeaseAnchorPath, ROOT_AUTHORITY_MARKER_FILE, } from "./writer-lease.js";
|
|
41
|
+
export const ROOT_AUTHORITY_SCHEMA_VERSION = 2;
|
|
42
|
+
/** First fixed writer-protocol epoch. Pre-fix runtimes have no epoch at all;
|
|
43
|
+
* a record with a lower OR higher epoch than this build supports is refused. */
|
|
44
|
+
export const ROOT_AUTHORITY_EPOCH = 2;
|
|
45
|
+
function rootAuthorityRefusal(code, message) {
|
|
46
|
+
return Object.assign(new Error(message), { code, status: 409 });
|
|
47
|
+
}
|
|
48
|
+
function parseRootAuthorityRecord(raw) {
|
|
49
|
+
let value;
|
|
50
|
+
try {
|
|
51
|
+
value = JSON.parse(raw);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return "marker is not valid JSON";
|
|
55
|
+
}
|
|
56
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
57
|
+
return "marker is not a JSON object";
|
|
58
|
+
}
|
|
59
|
+
const record = value;
|
|
60
|
+
if (record.schemaVersion !== ROOT_AUTHORITY_SCHEMA_VERSION) {
|
|
61
|
+
return `marker schemaVersion is not ${ROOT_AUTHORITY_SCHEMA_VERSION}`;
|
|
62
|
+
}
|
|
63
|
+
if (!Number.isSafeInteger(record.epoch) || record.epoch <= 0) {
|
|
64
|
+
return "marker epoch is not a positive integer";
|
|
65
|
+
}
|
|
66
|
+
if (record.state !== "vacant" && record.state !== "served") {
|
|
67
|
+
return "marker state is not vacant/served";
|
|
68
|
+
}
|
|
69
|
+
if (record.floor !== undefined && !isRuntimeSemver(record.floor)) {
|
|
70
|
+
return "marker floor is not an x.y.z semver";
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
schemaVersion: ROOT_AUTHORITY_SCHEMA_VERSION,
|
|
74
|
+
epoch: record.epoch,
|
|
75
|
+
state: record.state,
|
|
76
|
+
...(record.floor !== undefined ? { floor: record.floor } : {}),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Fail-closed barrier read: only a physically missing marker is `absent`.
|
|
80
|
+
* C9c: the marker is validated like the startup-diagnostics target — a
|
|
81
|
+
* symlink, a multi-linked file, a foreign uid, or group/other-accessible
|
|
82
|
+
* permissions cannot carry root authority. */
|
|
83
|
+
export function readRootAuthority(anchorPath) {
|
|
84
|
+
const markerPath = join(anchorPath, ROOT_AUTHORITY_MARKER_FILE);
|
|
85
|
+
let raw;
|
|
86
|
+
try {
|
|
87
|
+
const stat = lstatSync(markerPath);
|
|
88
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
89
|
+
return { status: "invalid", markerPath, reason: "marker is not a regular file" };
|
|
90
|
+
}
|
|
91
|
+
if (stat.nlink !== 1) {
|
|
92
|
+
return { status: "invalid", markerPath, reason: "marker is not singly linked" };
|
|
93
|
+
}
|
|
94
|
+
if (process.platform !== "win32") {
|
|
95
|
+
const uid = process.getuid?.();
|
|
96
|
+
if (uid !== undefined && stat.uid !== uid) {
|
|
97
|
+
return { status: "invalid", markerPath, reason: "marker has a foreign uid" };
|
|
98
|
+
}
|
|
99
|
+
if ((stat.mode & 0o077) !== 0) {
|
|
100
|
+
return { status: "invalid", markerPath, reason: "marker permissions are not private" };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
raw = readFileSync(markerPath, "utf8");
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error?.code === "ENOENT") {
|
|
107
|
+
return { status: "absent", markerPath };
|
|
108
|
+
}
|
|
109
|
+
return { status: "invalid", markerPath, reason: "marker is unreadable" };
|
|
110
|
+
}
|
|
111
|
+
const record = parseRootAuthorityRecord(raw);
|
|
112
|
+
if (typeof record === "string")
|
|
113
|
+
return { status: "invalid", markerPath, reason: record };
|
|
114
|
+
return { status: "valid", markerPath, record };
|
|
115
|
+
}
|
|
116
|
+
/** D2 admission: refuse foreign epochs and strictly lower semantic versions. */
|
|
117
|
+
export function assertRootAuthorityAdmits(record, version) {
|
|
118
|
+
if (record.epoch !== ROOT_AUTHORITY_EPOCH) {
|
|
119
|
+
throw rootAuthorityRefusal("root_authority_epoch_unsupported", record.epoch < ROOT_AUTHORITY_EPOCH
|
|
120
|
+
? `root authority epoch ${record.epoch} predates the supported writer epoch ${ROOT_AUTHORITY_EPOCH}`
|
|
121
|
+
: `root authority epoch ${record.epoch} is newer than the supported writer epoch ${ROOT_AUTHORITY_EPOCH}`);
|
|
122
|
+
}
|
|
123
|
+
if (record.floor === undefined)
|
|
124
|
+
return;
|
|
125
|
+
if (!isRuntimeSemver(version)) {
|
|
126
|
+
// Fail closed: a candidate that cannot be ORDERED against the proven
|
|
127
|
+
// floor cannot prove it is not a regression.
|
|
128
|
+
throw rootAuthorityRefusal("root_authority_floor_regression", `candidate version '${version}' cannot be ordered against the proven serving floor ${record.floor}`);
|
|
129
|
+
}
|
|
130
|
+
if (compareRuntimeSemver(version, record.floor) < 0) {
|
|
131
|
+
throw rootAuthorityRefusal("root_authority_floor_regression", `candidate version ${version} is below the proven serving floor ${record.floor}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** Atomic (tmp + rename), link-refusing marker write inside the barrier dir. */
|
|
135
|
+
function writeRootAuthorityRecord(anchorPath, record) {
|
|
136
|
+
const markerPath = join(anchorPath, ROOT_AUTHORITY_MARKER_FILE);
|
|
137
|
+
const temp = join(anchorPath, `.${ROOT_AUTHORITY_MARKER_FILE}.${process.pid}-${randomUUID()}`);
|
|
138
|
+
writeFileSync(temp, `${JSON.stringify(record)}\n`, { mode: 0o600, flag: "wx" });
|
|
139
|
+
try {
|
|
140
|
+
renameSync(temp, markerPath);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
rmSync(temp, { force: true });
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Migration sentinel that atomically replaces the owned flat owner record.
|
|
148
|
+
* It parses as NO lease owner anywhere (no pid/token), so from the instant it
|
|
149
|
+
* lands, pre-fix AND fixed claimants alike fail closed on the flat address;
|
|
150
|
+
* only this module recognizes it and completes the interrupted migration. */
|
|
151
|
+
const MIGRATION_SENTINEL = { rootAuthority: "migrating" };
|
|
152
|
+
function isMigrationSentinel(anchorPath) {
|
|
153
|
+
try {
|
|
154
|
+
const value = JSON.parse(readFileSync(join(anchorPath, "owner.json"), "utf8"));
|
|
155
|
+
return value?.rootAuthority === MIGRATION_SENTINEL.rootAuthority;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Publish the marker over an already-neutralized generation, then retire the
|
|
162
|
+
* sentinel. Every intermediate state keeps the address occupied and opaque. */
|
|
163
|
+
function completeBarrierInstallation(anchorPath) {
|
|
164
|
+
writeRootAuthorityRecord(anchorPath, {
|
|
165
|
+
schemaVersion: ROOT_AUTHORITY_SCHEMA_VERSION,
|
|
166
|
+
epoch: ROOT_AUTHORITY_EPOCH,
|
|
167
|
+
state: "vacant",
|
|
168
|
+
});
|
|
169
|
+
rmSync(join(anchorPath, "owner.json"), { force: true });
|
|
170
|
+
}
|
|
171
|
+
/** In-place preservation name for a stale flat owner record (C4). The name
|
|
172
|
+
* never parses as the owner record, marks a migration as begun for crash
|
|
173
|
+
* completion, and keeps the dead owner's bytes as evidence (authority
|
|
174
|
+
* artifacts are never deleted). */
|
|
175
|
+
function staleFlatOwnerPreservationName(owner) {
|
|
176
|
+
const digest = createHash("sha256")
|
|
177
|
+
.update(String(owner.pid))
|
|
178
|
+
.update("\0")
|
|
179
|
+
.update(owner.token)
|
|
180
|
+
.digest("hex");
|
|
181
|
+
return `owner.stale-${owner.pid}-${digest}.json`;
|
|
182
|
+
}
|
|
183
|
+
function hasStaleFlatOwnerPreservation(anchorPath) {
|
|
184
|
+
try {
|
|
185
|
+
return readdirSync(anchorPath).some((name) => name.startsWith("owner.stale-") && name.endsWith(".json"));
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** Atomically install the migration sentinel as owner.json (temp + rename). */
|
|
192
|
+
function installMigrationSentinel(anchorPath, fs) {
|
|
193
|
+
const temp = join(anchorPath, `.owner.json.${process.pid}-${randomUUID()}`);
|
|
194
|
+
fs.writeOwner(temp, `${JSON.stringify(MIGRATION_SENTINEL)}\n`);
|
|
195
|
+
fs.rename(temp, join(anchorPath, "owner.json"));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* C4: convert a PROVEN-STALE flat legacy generation into the barrier IN
|
|
199
|
+
* PLACE, before any claim. The shared lease machinery's stale quarantine
|
|
200
|
+
* renames the WHOLE directory to the tombstone and recreates it — an absence
|
|
201
|
+
* window a newly arriving legacy claimant wins, beyond D3's accepted
|
|
202
|
+
* already-running limitation. On this migration path the directory is never
|
|
203
|
+
* absent and owner.json is never a parseable dead owner after our first
|
|
204
|
+
* mutation:
|
|
205
|
+
* 1. atomically rename owner.json to the in-dir stale-owner preservation
|
|
206
|
+
* file (a legacy claimant fails closed on the missing owner record and
|
|
207
|
+
* never writes one into an existing directory);
|
|
208
|
+
* 2. atomically install the migration sentinel as owner.json;
|
|
209
|
+
* 3. publish the marker and retire the sentinel.
|
|
210
|
+
* A crash between 1 and 2 leaves {preservation file, no owner.json} — a
|
|
211
|
+
* state only this module produces — and the next fixed candidate completes
|
|
212
|
+
* the installation deterministically; a crash between 2 and 3 is the
|
|
213
|
+
* existing sentinel-completion path. Nested-slot (#159) semantics are
|
|
214
|
+
* untouched: this runs only on the barrier-less flat address.
|
|
215
|
+
*/
|
|
216
|
+
function migrateStaleFlatGenerationInPlace(anchorPath, canonical, deps) {
|
|
217
|
+
const status = inspectDaemonWriterLease(canonical, deps);
|
|
218
|
+
if (status.path !== anchorPath)
|
|
219
|
+
return; // a barrier already resolved the nested slot
|
|
220
|
+
const fs = resolveWriterLeaseFilesystem(deps);
|
|
221
|
+
try {
|
|
222
|
+
if (status.status === "owned" && status.capability.status === "proven_stale") {
|
|
223
|
+
fs.rename(join(anchorPath, "owner.json"), join(anchorPath, staleFlatOwnerPreservationName(status.owner)));
|
|
224
|
+
installMigrationSentinel(anchorPath, fs);
|
|
225
|
+
completeBarrierInstallation(anchorPath);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (status.status === "unknown" &&
|
|
229
|
+
status.reason === "owner_missing" &&
|
|
230
|
+
hasStaleFlatOwnerPreservation(anchorPath)) {
|
|
231
|
+
// A previous migration crashed after preserving the stale owner.
|
|
232
|
+
installMigrationSentinel(anchorPath, fs);
|
|
233
|
+
completeBarrierInstallation(anchorPath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (error?.code === "ENOENT") {
|
|
238
|
+
// A concurrent fixed contender is migrating the same generation; the
|
|
239
|
+
// claim below arbitrates through the shared single-writer machinery.
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
throw rootAuthorityRefusal("root_authority_unreadable", `root authority barrier could not be installed over the stale flat generation at ${anchorPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Stage-1 startup admission: validate/install the permanent barrier, apply
|
|
247
|
+
* the epoch + floor refusals, and claim single-writer authority for this
|
|
248
|
+
* root. On a barrier-less root the flat legacy address is claimed through
|
|
249
|
+
* the shared lease machinery first (live owners refuse, proven-stale owners
|
|
250
|
+
* are quarantined), then the owned generation becomes the barrier in place:
|
|
251
|
+
* owner record atomically swapped for the migration sentinel, marker
|
|
252
|
+
* published, sentinel retired, claim reacquired in the nested slot.
|
|
253
|
+
*/
|
|
254
|
+
export function acquireRootAuthority(input) {
|
|
255
|
+
// C9b: a candidate that cannot be ORDERED against serving floors must not
|
|
256
|
+
// serve — even on a fresh floorless root, where it would otherwise be
|
|
257
|
+
// admitted and then leave the root floorless forever.
|
|
258
|
+
if (!isRuntimeSemver(input.version)) {
|
|
259
|
+
throw rootAuthorityRefusal("root_authority_candidate_invalid", `candidate version '${input.version}' is not an x.y.z semantic version; refusing to serve this data root`);
|
|
260
|
+
}
|
|
261
|
+
const canonical = input.canonicalSocketPath ?? canonicalDefaultSocketPath();
|
|
262
|
+
const anchorPath = writerLeaseAnchorPath(canonical);
|
|
263
|
+
const effectiveDeps = {
|
|
264
|
+
identity: input.identity,
|
|
265
|
+
...(input.deps ?? {}),
|
|
266
|
+
};
|
|
267
|
+
const acquireClaim = (socketPath) => acquireDaemonWriterLease(socketPath, { identity: input.identity }, input.deps ?? {});
|
|
268
|
+
const before = readRootAuthority(anchorPath);
|
|
269
|
+
if (before.status === "invalid") {
|
|
270
|
+
throw rootAuthorityRefusal("root_authority_unreadable", `root authority barrier at ${before.markerPath} is unusable (${before.reason}); refusing to serve this data root`);
|
|
271
|
+
}
|
|
272
|
+
let record = null;
|
|
273
|
+
if (before.status === "valid") {
|
|
274
|
+
assertRootAuthorityAdmits(before.record, input.version);
|
|
275
|
+
record = before.record;
|
|
276
|
+
}
|
|
277
|
+
else if (isMigrationSentinel(anchorPath)) {
|
|
278
|
+
// A previous fixed candidate crashed between neutralizing the owner and
|
|
279
|
+
// publishing the marker. The address stayed opaque throughout; finish the
|
|
280
|
+
// interrupted installation deterministically.
|
|
281
|
+
completeBarrierInstallation(anchorPath);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// C4: a PROVEN-STALE flat generation migrates in place BEFORE any claim,
|
|
285
|
+
// so the shared quarantine's rename-away/recreate absence window never
|
|
286
|
+
// opens on the legacy-visible address.
|
|
287
|
+
migrateStaleFlatGenerationInPlace(anchorPath, canonical, effectiveDeps);
|
|
288
|
+
}
|
|
289
|
+
// Claim the canonical root address. With a barrier installed the lease
|
|
290
|
+
// machinery resolves the nested active slot; without one it wins (or
|
|
291
|
+
// refuses with the shared typed errors) the flat legacy address.
|
|
292
|
+
let rootClaim = acquireClaim(canonical);
|
|
293
|
+
if (rootClaim.path === anchorPath) {
|
|
294
|
+
// First migration (D1/D3): we own the FLAT generation with a live owner
|
|
295
|
+
// record. Convert it in place — the directory never disappears and its
|
|
296
|
+
// owner record is either this live pid or the unparseable sentinel, so a
|
|
297
|
+
// pre-fix claimant fails closed at every instant.
|
|
298
|
+
try {
|
|
299
|
+
const temp = join(anchorPath, `.owner.json.${process.pid}-${randomUUID()}`);
|
|
300
|
+
writeFileSync(temp, `${JSON.stringify(MIGRATION_SENTINEL)}\n`, { mode: 0o600, flag: "wx" });
|
|
301
|
+
renameSync(temp, join(anchorPath, "owner.json"));
|
|
302
|
+
completeBarrierInstallation(anchorPath);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
throw rootAuthorityRefusal("root_authority_unreadable", `root authority barrier could not be installed at ${anchorPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
306
|
+
}
|
|
307
|
+
rootClaim = acquireClaim(canonical);
|
|
308
|
+
}
|
|
309
|
+
if (record === null) {
|
|
310
|
+
const installed = readRootAuthority(anchorPath);
|
|
311
|
+
if (installed.status !== "valid") {
|
|
312
|
+
rootClaim.release();
|
|
313
|
+
throw rootAuthorityRefusal("root_authority_unreadable", `root authority barrier at ${anchorPath} did not settle after installation`);
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
assertRootAuthorityAdmits(installed.record, input.version);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
rootClaim.release();
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
record = installed.record;
|
|
323
|
+
}
|
|
324
|
+
// D4: a custom socket spelling still serves under the canonical root
|
|
325
|
+
// authority; its own socket-adjacent lease is claimed in addition.
|
|
326
|
+
const socketLease = input.socketPath === canonical ? null : acquireClaim(input.socketPath);
|
|
327
|
+
const lease = socketLease ?? rootClaim;
|
|
328
|
+
let released = false;
|
|
329
|
+
return {
|
|
330
|
+
lease,
|
|
331
|
+
record,
|
|
332
|
+
anchorPath,
|
|
333
|
+
advanceFloor: () => {
|
|
334
|
+
// C9a generation fence: only the LIVE writer of the root address may
|
|
335
|
+
// promote the floor. A released grant, or one superseded by a newer
|
|
336
|
+
// generation, refuses instead of clobbering the successor's authority.
|
|
337
|
+
if (released) {
|
|
338
|
+
throw rootAuthorityRefusal("root_authority_grant_stale", "root authority grant was released; refusing to advance the serving floor");
|
|
339
|
+
}
|
|
340
|
+
// C9a: missing/corrupt current state is a typed refusal, never a
|
|
341
|
+
// silent rewrite — the barrier record is authority evidence. Checked
|
|
342
|
+
// BEFORE the lease fence: a vanished marker also breaks the nested
|
|
343
|
+
// address resolution, and the honest cause is the marker itself.
|
|
344
|
+
const current = readRootAuthority(anchorPath);
|
|
345
|
+
if (current.status !== "valid") {
|
|
346
|
+
throw rootAuthorityRefusal("root_authority_unreadable", current.status === "absent"
|
|
347
|
+
? `root authority marker at ${current.markerPath} disappeared; refusing to recreate it during floor advancement`
|
|
348
|
+
: `root authority marker at ${current.markerPath} is unusable (${current.reason}); refusing to overwrite it during floor advancement`);
|
|
349
|
+
}
|
|
350
|
+
const claim = inspectDaemonWriterLease(canonical, effectiveDeps);
|
|
351
|
+
if (claim.status !== "owned" ||
|
|
352
|
+
claim.path !== rootClaim.path ||
|
|
353
|
+
claim.owner.pid !== rootClaim.owner.pid ||
|
|
354
|
+
claim.owner.token !== rootClaim.owner.token) {
|
|
355
|
+
throw rootAuthorityRefusal("root_authority_grant_stale", `root authority grant is no longer the live writer of ${rootClaim.path}; refusing to advance the serving floor`);
|
|
356
|
+
}
|
|
357
|
+
// Monotonic floor: input.version is a proven semver (C9b admission).
|
|
358
|
+
const floor = current.record.floor !== undefined &&
|
|
359
|
+
compareRuntimeSemver(current.record.floor, input.version) >= 0
|
|
360
|
+
? current.record.floor
|
|
361
|
+
: input.version;
|
|
362
|
+
const next = {
|
|
363
|
+
schemaVersion: ROOT_AUTHORITY_SCHEMA_VERSION,
|
|
364
|
+
epoch: ROOT_AUTHORITY_EPOCH,
|
|
365
|
+
state: "served",
|
|
366
|
+
floor,
|
|
367
|
+
};
|
|
368
|
+
writeRootAuthorityRecord(anchorPath, next);
|
|
369
|
+
return next;
|
|
370
|
+
},
|
|
371
|
+
release: () => {
|
|
372
|
+
if (released)
|
|
373
|
+
return;
|
|
374
|
+
released = true;
|
|
375
|
+
socketLease?.release();
|
|
376
|
+
rootClaim.release();
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=root-authority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-authority.js","sourceRoot":"","sources":["../src/root-authority.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Socket } from "node:net";
|
|
2
|
+
/**
|
|
3
|
+
* Lifecycle owner for local RPC follower sockets. A disconnected follower is
|
|
4
|
+
* dropped instead of crashing the daemon: readline re-emits asynchronous
|
|
5
|
+
* socket write failures (such as EPIPE) on its Interface, so the Interface
|
|
6
|
+
* and the socket share one cleanup, and sends skip sockets that can no
|
|
7
|
+
* longer receive.
|
|
8
|
+
*/
|
|
9
|
+
export declare class RpcFollowers {
|
|
10
|
+
private readonly sockets;
|
|
11
|
+
attach(sock: Socket, onLine: (line: string) => void): void;
|
|
12
|
+
send(sock: Socket, obj: unknown): void;
|
|
13
|
+
/** Destroy every follower so server.close() cannot hang on live sockets. */
|
|
14
|
+
destroyAll(): void;
|
|
15
|
+
private drop;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=rpc-followers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-followers.d.ts","sourceRoot":"","sources":["../src/rpc-followers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAWzD;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAUrC;IAED,4EAA4E;IAC5E,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,CAAC,IAAI;CAIb"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
/**
|
|
3
|
+
* Lifecycle owner for local RPC follower sockets. A disconnected follower is
|
|
4
|
+
* dropped instead of crashing the daemon: readline re-emits asynchronous
|
|
5
|
+
* socket write failures (such as EPIPE) on its Interface, so the Interface
|
|
6
|
+
* and the socket share one cleanup, and sends skip sockets that can no
|
|
7
|
+
* longer receive.
|
|
8
|
+
*/
|
|
9
|
+
export class RpcFollowers {
|
|
10
|
+
sockets = new Set();
|
|
11
|
+
attach(sock, onLine) {
|
|
12
|
+
this.sockets.add(sock);
|
|
13
|
+
const rl = createInterface({ input: sock });
|
|
14
|
+
const cleanup = () => {
|
|
15
|
+
rl.close();
|
|
16
|
+
this.drop(sock);
|
|
17
|
+
};
|
|
18
|
+
rl.on("line", onLine);
|
|
19
|
+
rl.on("error", cleanup);
|
|
20
|
+
sock.on("error", cleanup);
|
|
21
|
+
sock.on("close", cleanup);
|
|
22
|
+
}
|
|
23
|
+
send(sock, obj) {
|
|
24
|
+
if (sock.destroyed || !sock.writable) {
|
|
25
|
+
this.drop(sock);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
sock.write(JSON.stringify(obj) + "\n");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
this.drop(sock);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Destroy every follower so server.close() cannot hang on live sockets. */
|
|
36
|
+
destroyAll() {
|
|
37
|
+
for (const socket of this.sockets)
|
|
38
|
+
socket.destroy();
|
|
39
|
+
}
|
|
40
|
+
drop(sock) {
|
|
41
|
+
this.sockets.delete(sock);
|
|
42
|
+
if (!sock.destroyed)
|
|
43
|
+
sock.destroy();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=rpc-followers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-followers.js","sourceRoot":"","sources":["../src/rpc-followers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACN,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAE7C,MAAM,CAAC,IAAY,EAAE,MAA8B;QACjD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtB,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1B,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC,IAAY,EAAE,GAAY;QAC7B,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,UAAU;QACR,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IACtD,CAAC;IAEO,IAAI,CAAC,IAAY;QACvB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IACtC,CAAC;CACF"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type CommandAuthority } from "./command-authority.js";
|
|
2
2
|
import { type DelegationAdmissionAuthority } from "./delegation-admission.js";
|
|
3
3
|
import { JOB_STATES, jobStateFromResult, type JobRecord } from "./job-record.js";
|
|
4
|
+
import { type DaemonServingModeSnapshot } from "./serving-admission.js";
|
|
4
5
|
import { socketAlive } from "./socket-probe.js";
|
|
5
6
|
import { type RuntimeReplacementAuthority } from "./daemon-shutdown-rpc.js";
|
|
6
7
|
import { type TurnEnqueueProblem as TurnEnqueueProblemValue } from "@claudexor/schema";
|
|
@@ -37,6 +38,8 @@ export interface DaemonOptions extends RuntimeReplacementAuthority {
|
|
|
37
38
|
onTurnEnqueueFailed?: (turnId: string, problem: TurnEnqueueProblemValue) => void;
|
|
38
39
|
onShutdownRequested?: () => Promise<void>;
|
|
39
40
|
onRuntimeReplacementRequested?: () => Promise<void>;
|
|
41
|
+
/** Issue #165 D5 admission snapshot; absent embedders always serve normal. */
|
|
42
|
+
servingMode?: DaemonServingModeSnapshot;
|
|
40
43
|
/** Test-only barriers around command authority acquisition. */
|
|
41
44
|
startupBarrier?: (barrier: "before_registry_load" | "after_registry_load") => void | Promise<void>;
|
|
42
45
|
}
|
|
@@ -44,7 +47,7 @@ export interface DaemonOptions extends RuntimeReplacementAuthority {
|
|
|
44
47
|
export declare class DaemonServer {
|
|
45
48
|
private readonly opts;
|
|
46
49
|
private server?;
|
|
47
|
-
private readonly
|
|
50
|
+
private readonly followers;
|
|
48
51
|
private readonly queue;
|
|
49
52
|
private readonly cancelled;
|
|
50
53
|
private readonly controllers;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|