@cotal-ai/manager 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ import type { KV } from "@nats-io/kv";
2
+ /** Which guard refused. The command prints this verbatim, and the smoke asserts on it, so a refusal
3
+ * can never be confused with a different refusal — the failure mode where an operator reads
4
+ * "refused" and force-retries the wrong thing. */
5
+ export type GateReconcileCondition =
6
+ /** No gate row at the coordinate — nothing to reconcile (a typo'd endpoint/instanceId). */
7
+ "no-gate"
8
+ /** The gate is `open` or `retired`: this repairs a frozen gate only. */
9
+ | "not-frozen"
10
+ /** Frozen under a takeover/activation/retirement op — this completes a REGISTRATION's obligation. */
11
+ | "wrong-op-kind"
12
+ /** The freeze-holder still holds a live connection. The freeze is protecting a live op. */
13
+ | "holder-alive"
14
+ /** The sweep under-reported (no responder, truncation, unproven single-server mode). May be live. */
15
+ | "holder-unknown"
16
+ /** The oracle could not be consulted at all, or answered unusably (unreachable, unprovisioned,
17
+ * garbled, or a reply that did not echo the principal asked about). */
18
+ | "liveness-unestablishable"
19
+ /** A holder's eviction was not VERIFIED gone — the barrier's own fail-closed step. */
20
+ | "eviction-unverified"
21
+ /** The token-pinned reopen lost its CAS: a newer barrier moved the gate. Re-observe. */
22
+ | "raced";
23
+ /** A refusal, carrying the condition as DATA rather than only as prose. */
24
+ export declare class GateReconcileRefused extends Error {
25
+ readonly condition: GateReconcileCondition;
26
+ constructor(condition: GateReconcileCondition, message: string);
27
+ }
28
+ /** The freeze-holder liveness verdict as the reconciler consumes it. `unestablishable` is kept
29
+ * DISTINCT from `unknown`: both refuse, but they tell the operator to fix different things (start
30
+ * the daemon vs. re-observe a mesh that could not be swept completely). */
31
+ export type HolderLiveness = {
32
+ state: "live";
33
+ detail: string;
34
+ } | {
35
+ state: "gone";
36
+ detail: string;
37
+ } | {
38
+ state: "unknown";
39
+ detail: string;
40
+ } | {
41
+ state: "unestablishable";
42
+ detail: string;
43
+ };
44
+ /** What the reconciliation found and did — returned on success, and logged either way. */
45
+ export interface GateReconcileReport {
46
+ endpoint: string;
47
+ instanceId: string;
48
+ holderPrincipal: string;
49
+ opId: string;
50
+ freezeToken: number;
51
+ /** The coordinate before the repair; the reopen advances `generation` by one and NOTHING else. */
52
+ before: {
53
+ generation: number;
54
+ processEpoch: number;
55
+ registrationRevision: number;
56
+ nameAuthorityRevision: number;
57
+ };
58
+ liveness: HolderLiveness;
59
+ familyRows: number;
60
+ revoked: string[];
61
+ evicted: string[];
62
+ reopenedAtGeneration: number;
63
+ }
64
+ /**
65
+ * Reconcile ONE frozen gate. Every seam the guard depends on is injected, so the command wires the
66
+ * real ones and the smoke drives the real barrier against an ephemeral broker.
67
+ *
68
+ * `probeHolder` is the AFFIRMATIVE check. It must return `gone` only on a proven-absent verdict;
69
+ * everything else — including any form of "we did not hear back" — must be `unknown` or
70
+ * `unestablishable`. It is a parameter and not an inlined call so that this ordering (probe, then
71
+ * refuse, THEN mutate) is structural: the barrier is not even constructed until the probe passed.
72
+ */
73
+ export declare function reconcileEndpointGate(opts: {
74
+ kv: KV;
75
+ space: string;
76
+ endpoint: string;
77
+ instanceId: string;
78
+ probeHolder: (principal: string) => Promise<HolderLiveness>;
79
+ evict: (holderPrincipal: string) => Promise<boolean>;
80
+ log: (line: string) => void;
81
+ }): Promise<GateReconcileReport>;
82
+ //# sourceMappingURL=reconcile-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile-gate.d.ts","sourceRoot":"","sources":["../src/reconcile-gate.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC;;mDAEmD;AACnD,MAAM,MAAM,sBAAsB;AAChC,2FAA2F;AACzF,SAAS;AACX,wEAAwE;GACtE,YAAY;AACd,qGAAqG;GACnG,eAAe;AACjB,2FAA2F;GACzF,cAAc;AAChB,qGAAqG;GACnG,gBAAgB;AAClB;wEACwE;GACtE,0BAA0B;AAC5B,sFAAsF;GACpF,qBAAqB;AACvB,wFAAwF;GACtF,OAAO,CAAC;AAEZ,2EAA2E;AAC3E,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,SAAS,EAAE,sBAAsB;gBAAjC,SAAS,EAAE,sBAAsB,EAC1C,OAAO,EAAE,MAAM;CAKlB;AAED;;4EAE4E;AAC5E,MAAM,MAAM,cAAc,GACtB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,0FAA0F;AAC1F,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,kGAAkG;IAClG,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAC;QAAC,qBAAqB,EAAE,MAAM,CAAA;KAAE,CAAC;IAClH,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAC5D,KAAK,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4F/B"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * GUARDED RECONCILIATION of an endpoint issuance gate left FROZEN by a crashed re-registration
3
+ * (Cotal #391; SPEC 13.1 "the gate is left frozen for reconciliation").
4
+ *
5
+ * THE STATE THIS REPAIRS, and the only one it accepts: a manager restart was killed between the
6
+ * barrier's freeze and the successor's completion. The gate is `frozen` under a `registration` op
7
+ * whose holder no longer exists, so the freeze token's owner can never resume. Fail-closed is
8
+ * correct and stays correct — but in THAT state the freeze protects nothing while blocking every
9
+ * restart, and before this the operator's only exits were hand-driving internals or discarding
10
+ * state.
11
+ *
12
+ * The repair itself is not new: it is the SHIPPED composition (`endpointRegistrationBarrier` +
13
+ * a verified evictor), driven in the §13.1 order, with no raw writes. What is new is the GUARD in
14
+ * front of it, which is the whole point of the command:
15
+ *
16
+ * 1. VERIFY THE HOLDER IS GONE, AFFIRMATIVELY. Not "the holder did not answer", not "a timeout
17
+ * elapsed" — a COMPLETE, single-server-proven CONNZ sweep that PROVES no connection attributes
18
+ * to the freeze-holder. Absence of evidence is `unknown`, and `unknown` REFUSES.
19
+ * 2. LOG WHAT WAS FOUND, before mutating anything.
20
+ * 3. REFUSE LOUD, NAMING WHICH CONDITION FAILED — an operator staring at a wedged mesh needs to
21
+ * know whether the holder is alive, whether liveness could not be established, or whether the
22
+ * gate simply is not in the state this repairs. "It refused" is not actionable.
23
+ *
24
+ * The probe sits ON TOP OF the barrier's own fail-closed eviction, never in place of it: BOTH must
25
+ * pass. There is deliberately NO force flag and no path that discards gate state — a recovery path
26
+ * whose failure mode is killing a live manager does not get an override.
27
+ */
28
+ import { endpointRegistrationBarrier, parseEndpointGate, epgateKey, } from "@cotal-ai/core";
29
+ /** A refusal, carrying the condition as DATA rather than only as prose. */
30
+ export class GateReconcileRefused extends Error {
31
+ condition;
32
+ constructor(condition, message) {
33
+ super(message);
34
+ this.condition = condition;
35
+ this.name = "GateReconcileRefused";
36
+ }
37
+ }
38
+ /**
39
+ * Reconcile ONE frozen gate. Every seam the guard depends on is injected, so the command wires the
40
+ * real ones and the smoke drives the real barrier against an ephemeral broker.
41
+ *
42
+ * `probeHolder` is the AFFIRMATIVE check. It must return `gone` only on a proven-absent verdict;
43
+ * everything else — including any form of "we did not hear back" — must be `unknown` or
44
+ * `unestablishable`. It is a parameter and not an inlined call so that this ordering (probe, then
45
+ * refuse, THEN mutate) is structural: the barrier is not even constructed until the probe passed.
46
+ */
47
+ export async function reconcileEndpointGate(opts) {
48
+ const { kv, space, endpoint, instanceId, log } = opts;
49
+ // ---- 1. Observe the gate RAW: the freeze token IS the row revision, and the dead op's opId is
50
+ // on the row. Both are needed to complete that op's obligation rather than start a new one.
51
+ const key = epgateKey(endpoint, instanceId);
52
+ const entry = await kv.get(key);
53
+ if (!entry || entry.operation !== "PUT")
54
+ throw new GateReconcileRefused("no-gate", `no endpoint gate at ${key} — nothing to reconcile (check the endpoint and instanceId)`);
55
+ const row = parseEndpointGate(entry.value, key);
56
+ const freezeToken = entry.revision;
57
+ log(`gate ${key}: state=${row.state} generation=${row.generation} processEpoch=${row.processEpoch} registrationRevision=${row.registrationRevision} nameAuthorityRevision=${row.nameAuthorityRevision} op=${JSON.stringify(row.op ?? null)} holder=${row.principal} revision=${freezeToken}`);
58
+ if (row.state !== "frozen")
59
+ throw new GateReconcileRefused("not-frozen", `the gate at ${key} is "${row.state}", not "frozen" — this reconciles a frozen gate only, and an open gate needs no repair`);
60
+ if (row.op?.kind !== "registration")
61
+ throw new GateReconcileRefused("wrong-op-kind", `the gate at ${key} is frozen under a "${row.op?.kind}" op, not a registration — this completes a crashed REGISTRATION's §13.1 obligation only; refusing to reinterpret another op's intent`);
62
+ const opId = row.op.opId;
63
+ // ---- 2. THE GUARD. Affirmative liveness, before anything is mutated.
64
+ const liveness = await opts.probeHolder(row.principal);
65
+ log(`freeze-holder ${row.principal}: ${liveness.state} — ${liveness.detail}`);
66
+ if (liveness.state === "live")
67
+ throw new GateReconcileRefused("holder-alive", `the freeze-holder "${row.principal}" is ALIVE (${liveness.detail}) — the freeze is protecting an op that is still running. Refusing: reconciling here would revoke and evict a live incarnation's credential family. Stop that process first, then re-run.`);
68
+ if (liveness.state === "unknown")
69
+ throw new GateReconcileRefused("holder-unknown", `the liveness of freeze-holder "${row.principal}" is UNKNOWN (${liveness.detail}) — the sweep could not prove it absent, and absence of evidence is not evidence of absence. Refusing: a holder that may still be live is treated as live.`);
70
+ if (liveness.state === "unestablishable")
71
+ throw new GateReconcileRefused("liveness-unestablishable", `liveness for freeze-holder "${row.principal}" CANNOT BE ESTABLISHED (${liveness.detail}) — without the oracle this repair has no affirmative check to stand on, and it will not infer death from silence.`);
72
+ // ---- 3. Only now: the shipped composition, in the §13.1 order, over the DEAD op's own id.
73
+ const barrier = endpointRegistrationBarrier(kv, space, { endpoint, instanceId, opId, evict: opts.evict });
74
+ const rows = await barrier.enumerate();
75
+ log(`family: ${rows.length} ledger row(s)`);
76
+ const revoked = [];
77
+ for (const r of rows) {
78
+ if (r.state === "active") {
79
+ await barrier.revoke(r); // deny-new BEFORE kill-live — the precondition eviction's name carries
80
+ revoked.push(r.credentialId);
81
+ log(` revoked ${r.credentialId} (${r.holderPrincipal})`);
82
+ }
83
+ else
84
+ log(` ${r.state}: ${r.credentialId} (${r.holderPrincipal})`);
85
+ }
86
+ // Evict every distinct family holder, and the freeze-holder itself even when it staged no rows —
87
+ // the probe proved it gone, and the barrier's verify is what makes that durable rather than a
88
+ // snapshot. An unverified eviction aborts and LEAVES THE GATE FROZEN (unchanged §13.1 posture).
89
+ const holders = [...new Set([row.principal, ...rows.map((r) => r.holderPrincipal)])];
90
+ const evicted = [];
91
+ for (const h of holders) {
92
+ if (!(await barrier.evict(h)))
93
+ throw new GateReconcileRefused("eviction-unverified", `eviction of "${h}" was NOT verified gone — the gate stays frozen (fail-closed, SPEC 13.1). Nothing was reopened; the revocations above are deny-new and safe to leave.`);
94
+ evicted.push(h);
95
+ log(` verified evicted: ${h}`);
96
+ }
97
+ // ---- 4. Token-pinned abort-reopen at the UNCHANGED coordinate. The dead op wrote nothing
98
+ // forward, so only `generation` advances; the successor's normal takeover then runs end-to-end.
99
+ const reopenedAtGeneration = row.generation + 1;
100
+ const ok = await barrier.reopen(freezeToken, {
101
+ generation: reopenedAtGeneration,
102
+ processEpoch: row.processEpoch,
103
+ registrationRevision: row.registrationRevision,
104
+ nameAuthorityRevision: row.nameAuthorityRevision,
105
+ });
106
+ if (!ok)
107
+ throw new GateReconcileRefused("raced", `the token-pinned reopen of ${key} lost its CAS — a newer barrier moved the gate while this repair ran. Nothing was reopened; re-observe before retrying.`);
108
+ log(`✓ gate ${key} reopened at generation=${reopenedAtGeneration}, processEpoch unchanged (${row.processEpoch}); family revoked (${revoked.length}) and verify-evicted (${evicted.length} holder(s))`);
109
+ return {
110
+ endpoint, instanceId, holderPrincipal: row.principal, opId, freezeToken,
111
+ before: {
112
+ generation: row.generation, processEpoch: row.processEpoch,
113
+ registrationRevision: row.registrationRevision, nameAuthorityRevision: row.nameAuthorityRevision,
114
+ },
115
+ liveness, familyRows: rows.length, revoked, evicted, reopenedAtGeneration,
116
+ };
117
+ }
118
+ //# sourceMappingURL=reconcile-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile-gate.js","sourceRoot":"","sources":["../src/reconcile-gate.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cotal-ai/manager",
3
- "description": "Cotal agent supervisor: spawns and manages mesh nodes via a pluggable runtime (pty/tmux/cmux/orca).",
4
- "version": "0.17.0",
3
+ "description": "Cotal agent supervisor: spawns and manages mesh nodes via a pluggable runtime (pty/tmux/cmux/orca/herdr).",
4
+ "version": "0.19.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -27,8 +27,8 @@
27
27
  "ws": "^8.21.0",
28
28
  "yaml": "^2.9.0",
29
29
  "zod": "^4.4.3",
30
- "@cotal-ai/core": "0.17.0",
31
- "@cotal-ai/workspace": "0.17.0"
30
+ "@cotal-ai/core": "0.19.0",
31
+ "@cotal-ai/workspace": "0.19.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@nats-io/jetstream": "^3.4.0",
@@ -37,7 +37,8 @@
37
37
  "@nats-io/transport-node": "^3.4.0",
38
38
  "@types/ws": "^8.18.1",
39
39
  "esbuild": "^0.28.1",
40
- "@cotal-ai/connector-core": "0.17.0"
40
+ "@cotal-ai/smoke-kit": "0.0.0",
41
+ "@cotal-ai/connector-core": "0.19.0"
41
42
  },
42
43
  "files": [
43
44
  "dist"