@cotal-ai/workspace 0.50.1 → 0.51.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.
- package/dist/auth-paths.d.ts +50 -0
- package/dist/auth-paths.d.ts.map +1 -1
- package/dist/auth-paths.js +109 -1
- package/dist/auth-paths.js.map +1 -1
- package/dist/connect.d.ts +4 -1
- package/dist/connect.d.ts.map +1 -1
- package/dist/connect.js +7 -3
- package/dist/connect.js.map +1 -1
- package/dist/control-target.d.ts +1 -0
- package/dist/control-target.d.ts.map +1 -1
- package/dist/control-target.js +2 -0
- package/dist/control-target.js.map +1 -1
- package/dist/flags.d.ts +6 -2
- package/dist/flags.d.ts.map +1 -1
- package/dist/flags.js +10 -2
- package/dist/flags.js.map +1 -1
- 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/join-target.d.ts +173 -0
- package/dist/join-target.d.ts.map +1 -0
- package/dist/join-target.js +335 -0
- package/dist/join-target.js.map +1 -0
- package/dist/mesh-registry.d.ts +29 -5
- package/dist/mesh-registry.d.ts.map +1 -1
- package/dist/mesh-registry.js +64 -20
- package/dist/mesh-registry.js.map +1 -1
- package/dist/mesh-target.d.ts +2 -0
- package/dist/mesh-target.d.ts.map +1 -1
- package/dist/mesh-target.js +1 -0
- package/dist/mesh-target.js.map +1 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +6 -0
- package/dist/preflight.js.map +1 -1
- package/dist/registration-policy.d.ts +69 -0
- package/dist/registration-policy.d.ts.map +1 -0
- package/dist/registration-policy.js +179 -0
- package/dist/registration-policy.js.map +1 -0
- package/dist/render.js +3 -3
- package/dist/render.js.map +1 -1
- package/dist/seat-checkpoint.d.ts +167 -0
- package/dist/seat-checkpoint.d.ts.map +1 -0
- package/dist/seat-checkpoint.js +413 -0
- package/dist/seat-checkpoint.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { assertUserAuthInfo, findMesh, recordMesh } from "./mesh-registry.js";
|
|
2
|
+
import { isLoopbackHost } from "./join-target.js";
|
|
3
|
+
const bad = (message) => ({ ok: false, message });
|
|
4
|
+
const good = (value) => ({ ok: true, value });
|
|
5
|
+
/** Validate a user-auth bundle, fail-loud on every missing pin. The `userAuth` arm goes through
|
|
6
|
+
* {@link assertUserAuthInfo} — the same boundary every provider blob crosses — and additionally
|
|
7
|
+
* requires the pinned exchange URL: for a remote entry the endpoints are a trust position, not a
|
|
8
|
+
* convenience, because no local `up` exists to re-derive them. */
|
|
9
|
+
export function checkUserBundle(raw) {
|
|
10
|
+
let doc;
|
|
11
|
+
try {
|
|
12
|
+
doc = JSON.parse(raw);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return bad("✗ the user-auth bundle is not JSON - export it where the mesh runs and pass the file unmodified");
|
|
16
|
+
}
|
|
17
|
+
if (doc === null || typeof doc !== "object")
|
|
18
|
+
return bad("✗ the user-auth bundle must be a JSON object");
|
|
19
|
+
if (typeof doc.space !== "string" || !doc.space)
|
|
20
|
+
return bad("✗ the user-auth bundle names no space");
|
|
21
|
+
if (typeof doc.server !== "string" || !doc.server)
|
|
22
|
+
return bad("✗ the user-auth bundle names no broker server");
|
|
23
|
+
if (typeof doc.tlsRequired !== "boolean")
|
|
24
|
+
return bad("✗ the user-auth bundle must state tlsRequired explicitly (true or false) - transport strictness is part of what the export pins");
|
|
25
|
+
let policy;
|
|
26
|
+
if (doc.policy !== undefined) {
|
|
27
|
+
const value = doc.policy;
|
|
28
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
29
|
+
return bad("✗ the user-auth bundle policy must be an object with exactly policy.events = \"required\"");
|
|
30
|
+
const fields = Object.keys(value);
|
|
31
|
+
const extra = fields.find((key) => key !== "events");
|
|
32
|
+
if (extra)
|
|
33
|
+
return bad(`✗ the user-auth bundle policy has unsupported field \"policy.${extra}\"`);
|
|
34
|
+
if (value.events !== "required")
|
|
35
|
+
return bad('✗ the user-auth bundle policy.events must be exactly "required"');
|
|
36
|
+
policy = { events: "required" };
|
|
37
|
+
}
|
|
38
|
+
let userAuth;
|
|
39
|
+
try {
|
|
40
|
+
userAuth = assertUserAuthInfo(doc.userAuth);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
return bad(`✗ user-auth bundle: ${e.message}`);
|
|
44
|
+
}
|
|
45
|
+
if (!userAuth.endpoints?.url)
|
|
46
|
+
return bad("✗ the user-auth bundle pins no exchange endpoint (userAuth.endpoints.url) - for a remote registration that URL is trust, and it must come from the export, never be guessed");
|
|
47
|
+
if (userAuth.endpoints.agentProvisioningUrl !== undefined) {
|
|
48
|
+
// The provisioning endpoint receives the login bearer, so it rides the same pinned-fetch
|
|
49
|
+
// scheme rule as every other trust URL in this bundle: https, or a loopback http LITERAL.
|
|
50
|
+
let pu;
|
|
51
|
+
try {
|
|
52
|
+
pu = new URL(userAuth.endpoints.agentProvisioningUrl);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return bad("✗ the user-auth bundle's agent-provisioning endpoint (userAuth.endpoints.agentProvisioningUrl) is not a URL");
|
|
56
|
+
}
|
|
57
|
+
const refusal = assertPinnedFetchUrl(pu, "the bundle's agent-provisioning endpoint (userAuth.endpoints.agentProvisioningUrl)");
|
|
58
|
+
if (refusal)
|
|
59
|
+
return bad(refusal);
|
|
60
|
+
}
|
|
61
|
+
if (typeof doc.sentinelCreds !== "string" || !doc.sentinelCreds)
|
|
62
|
+
return bad("✗ the user-auth bundle carries no sentinelCreds - the sentinel identity is part of the export");
|
|
63
|
+
return good({ space: doc.space, server: doc.server, tlsRequired: doc.tlsRequired, userAuth, ...(policy ? { policy } : {}), sentinelCreds: doc.sentinelCreds });
|
|
64
|
+
}
|
|
65
|
+
/** Budget for the exchange trust probe — same posture as the mode probe above: run once, at
|
|
66
|
+
* registration, where patience is cheaper than a wrong record. */
|
|
67
|
+
const EXCHANGE_PROBE_TIMEOUT_MS = 5_000;
|
|
68
|
+
/**
|
|
69
|
+
* Fetch that CANNOT be walked off HTTPS.
|
|
70
|
+
*
|
|
71
|
+
* `fetch` follows redirects by default and will happily follow `https://` → `http://`, so a
|
|
72
|
+
* 302 from anyone on the path turns a pinned, encrypted fetch into a plaintext one — and the
|
|
73
|
+
* document being fetched IS the trust being adopted. `redirect: "manual"` stops the hop here;
|
|
74
|
+
* a redirect is then reported as the refusal it is, rather than silently followed.
|
|
75
|
+
*
|
|
76
|
+
* Loopback `http://` stays usable for tests and for an exchange on this machine (nothing leaves
|
|
77
|
+
* the box), which is also what keeps the suites honest without a TLS fixture. Everything else
|
|
78
|
+
* must be `https:`.
|
|
79
|
+
*/
|
|
80
|
+
export function assertPinnedFetchUrl(u, what) {
|
|
81
|
+
if (u.protocol === "https:")
|
|
82
|
+
return undefined;
|
|
83
|
+
// The loopback exception is decided by PARSING the host as an address, never by how the text
|
|
84
|
+
// begins: `/^127\./` also matched `127.evil.com`, `127.0.0.1.nip.io` and `127.com`, which anyone
|
|
85
|
+
// can register, and it missed real spellings like `0177.0.0.1`. `isLoopbackHost` is the one
|
|
86
|
+
// authority for the question, canonicalization included. A name that does not parse as an IP is
|
|
87
|
+
// a NAME and gets no exception, however it starts — `localhost` included, since a hosts entry or
|
|
88
|
+
// a poisoned lookup can point it anywhere.
|
|
89
|
+
if (u.protocol === "http:" && isLoopbackHost(u.hostname))
|
|
90
|
+
return undefined;
|
|
91
|
+
// Name the ACTUAL rule. "must be https://" alone misleads the developer whose local flow used
|
|
92
|
+
// http://localhost: it blames the scheme, when the same scheme on 127.0.0.1 would have passed.
|
|
93
|
+
return (`✗ ${what} must be https:// (got ${u.protocol}//) - the pins this registration adopts cannot be fetched over a channel the network can rewrite` +
|
|
94
|
+
(u.protocol === "http:"
|
|
95
|
+
? `. Plain http is accepted only for a loopback LITERAL (127.0.0.1, ::1), where nothing leaves this machine - "${u.hostname}" is a name, and a hosts entry or a poisoned lookup could point it anywhere, so use the literal`
|
|
96
|
+
: ""));
|
|
97
|
+
}
|
|
98
|
+
/** The pinned-fetch policy's verdict for ONE url, as data — so a suite can assert WHY a fetch was
|
|
99
|
+
* refused rather than only that something failed. A cert error, a timeout and a refused redirect
|
|
100
|
+
* are all "it did not work"; only this distinguishes them. Test seam, no production caller. */
|
|
101
|
+
export async function pinnedFetchProbe(target) {
|
|
102
|
+
try {
|
|
103
|
+
await pinnedFetch(target, "the pinned exchange");
|
|
104
|
+
return { refused: false, message: "" };
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
return { refused: true, message: e.message };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** One hop, no downgrade, no redirect-following. */
|
|
111
|
+
export async function pinnedFetch(target, what) {
|
|
112
|
+
const u = new URL(target);
|
|
113
|
+
const bad = assertPinnedFetchUrl(u, what);
|
|
114
|
+
if (bad)
|
|
115
|
+
throw new Error(bad);
|
|
116
|
+
const res = await fetch(u, { redirect: "manual", signal: AbortSignal.timeout(EXCHANGE_PROBE_TIMEOUT_MS) });
|
|
117
|
+
if (res.status >= 300 && res.status < 400)
|
|
118
|
+
throw new Error(`✗ ${what} answered ${res.status} (a redirect to ${JSON.stringify(res.headers.get("location") ?? "")}) - a redirect can move a pinned fetch onto plaintext or onto another host, so it is refused rather than followed; publish the document at the pinned URL itself`);
|
|
119
|
+
return res;
|
|
120
|
+
}
|
|
121
|
+
/** How long a trusted policy refresh stays warm. Pinned to the catalog freshness window by the
|
|
122
|
+
* user-bundle smoke, so the two cannot drift. */
|
|
123
|
+
export const POLICY_FRESH_MS = 5_000;
|
|
124
|
+
function sameRegistrationTrust(entry, bundle) {
|
|
125
|
+
const ua = entry.userAuth;
|
|
126
|
+
return entry.mode === "user" && ua !== undefined &&
|
|
127
|
+
entry.space === bundle.space && entry.server === bundle.server && (entry.tlsRequired === true) === bundle.tlsRequired &&
|
|
128
|
+
ua.idp.url === bundle.userAuth.idp.url && ua.idp.issuer === bundle.userAuth.idp.issuer &&
|
|
129
|
+
ua.idp.audience === bundle.userAuth.idp.audience && ua.endpoints?.url === bundle.userAuth.endpoints?.url;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The registration policy in force for a launch, a join, or a manager start on `target`.
|
|
133
|
+
*
|
|
134
|
+
* A manual user-auth registration made before policies existed learns its policy from its own
|
|
135
|
+
* pinned exchange: the same discovery document `meshes add --from` adopts, fetched with the same
|
|
136
|
+
* pinned rules, accepted only when every trust pin matches the recorded entry, and only `policy`
|
|
137
|
+
* is written. This runs where the policy is CONSUMED, after the command's own local refusals,
|
|
138
|
+
* never as a preparation step every command pays: a read-only `status` or `meshes` must not fail
|
|
139
|
+
* on an exchange that is down, and `supervise` must name a `--server` mismatch or a missing login
|
|
140
|
+
* before it dials anything. A refresh that fails once the warm window has expired refuses the
|
|
141
|
+
* operation, because the entry cannot say whether the space requires events.
|
|
142
|
+
*/
|
|
143
|
+
export async function refreshRegistrationPolicy(target) {
|
|
144
|
+
const entry = findMesh(target.space);
|
|
145
|
+
if (!entry || entry.origin !== "manual" || entry.mode !== "user")
|
|
146
|
+
return target.policy;
|
|
147
|
+
if (entry.policy || !entry.userAuth?.endpoints?.url)
|
|
148
|
+
return entry.policy ?? target.policy;
|
|
149
|
+
const checkedAt = entry.policyCheckedAt ? Date.parse(entry.policyCheckedAt) : Number.NaN;
|
|
150
|
+
if (Number.isFinite(checkedAt) && Date.now() - checkedAt < POLICY_FRESH_MS)
|
|
151
|
+
return target.policy;
|
|
152
|
+
let base;
|
|
153
|
+
try {
|
|
154
|
+
base = new URL(entry.userAuth.endpoints.url);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
throw new Error(`manual registration for "${entry.space}" has an invalid pinned exchange URL`);
|
|
158
|
+
}
|
|
159
|
+
base.pathname = `${base.pathname.replace(/\/$/, "")}/.well-known/cotal-mesh`;
|
|
160
|
+
base.search = "";
|
|
161
|
+
base.hash = "";
|
|
162
|
+
let response;
|
|
163
|
+
try {
|
|
164
|
+
response = await pinnedFetch(base.toString(), `manual registration "${entry.space}" policy refresh at ${entry.userAuth.endpoints.url}`);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
throw new Error(error.message.replace(/^✗ /, ""));
|
|
168
|
+
}
|
|
169
|
+
if (!response.ok)
|
|
170
|
+
throw new Error(`manual registration "${entry.space}" policy refresh at ${entry.userAuth.endpoints.url} answered HTTP ${response.status}`);
|
|
171
|
+
const checked = checkUserBundle(await response.text());
|
|
172
|
+
if (!checked.ok)
|
|
173
|
+
throw new Error(`manual registration "${entry.space}" policy refresh: ${checked.message.replace(/^✗ /, "")}`);
|
|
174
|
+
if (!sameRegistrationTrust(entry, checked.value))
|
|
175
|
+
throw new Error(`manual registration "${entry.space}" policy refresh returned different space, broker, transport, or user-auth trust pins`);
|
|
176
|
+
recordMesh({ ...entry, ...(checked.value.policy ? { policy: checked.value.policy } : {}), policyCheckedAt: new Date().toISOString() });
|
|
177
|
+
return checked.value.policy;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=registration-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration-policy.js","sourceRoot":"","sources":["../src/registration-policy.ts"],"names":[],"mappings":"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"}
|
package/dist/render.js
CHANGED
|
@@ -42,7 +42,7 @@ function renderPreflightFailure(kind, t, pruned) {
|
|
|
42
42
|
case "unreachable":
|
|
43
43
|
// An operator-registered mesh usually runs on ANOTHER machine, so `cotal up` is the wrong
|
|
44
44
|
// remedy here — this machine can only wait for it or stop pointing at it.
|
|
45
|
-
if (t.origin === "manual")
|
|
45
|
+
if (t.origin === "manual" || t.origin === "catalog")
|
|
46
46
|
return `✗ no broker answered at ${t.server} - "${t.space}" is registered here but its mesh is not up; start it where it runs, or \`cotal meshes rm ${t.space}\` to unregister it`;
|
|
47
47
|
// An `up` / pre-origin record is KEPT on a liveness miss. Keep `no mesh running at`
|
|
48
48
|
// so existing attach/mint cells still recognise the classified refusal, and name
|
|
@@ -52,11 +52,11 @@ function renderPreflightFailure(kind, t, pruned) {
|
|
|
52
52
|
// machine only registered: the repair there is the credentials under `--root`, or re-registering
|
|
53
53
|
// the entry — `cotal up` would start a DIFFERENT, local mesh under that name.
|
|
54
54
|
case "registry-creds-rejected":
|
|
55
|
-
return t.origin === "manual"
|
|
55
|
+
return t.origin === "manual" || t.origin === "catalog"
|
|
56
56
|
? `✗ mesh "${t.space}" at ${t.server} rejected the credentials under ${t.root} - re-mint them where that mesh runs, or re-register it with \`cotal meshes add ${t.space} --server <url> --root <dir> --force\``
|
|
57
57
|
: `✗ mesh "${t.space}" at ${t.server} no longer matches its registry entry (credentials rejected - port reused?) - re-run \`cotal up\` from ${t.root}, or \`cotal meshes\` to see what's live`;
|
|
58
58
|
case "registry-open-now-auth":
|
|
59
|
-
return t.origin === "manual"
|
|
59
|
+
return t.origin === "manual" || t.origin === "catalog"
|
|
60
60
|
? `✗ "${t.space}" is registered as an open mesh, but the broker at ${t.server} requires auth - copy that mesh's account + creds under ${t.root} and re-register with \`cotal meshes add ${t.space} --server ${t.server} --mode auth --force\``
|
|
61
61
|
: `✗ open mesh "${t.space}" at ${t.server} no longer matches its registry entry (broker now requires auth - port reused?) - re-run \`cotal up\` from ${t.root}, or \`cotal meshes\` to see what's live`;
|
|
62
62
|
case "creds-rejected":
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAmBA,+EAA+E;AAC/E,MAAM,UAAU,oBAAoB,CAAC,CAAiB;IACpD,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACpC,KAAK,WAAW;YACd,OAAO,sBAAsB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;QAC/D,KAAK,WAAW;YACd,OAAO,eAAe,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAED,qGAAqG;AACrG,SAAS,iBAAiB,CAAC,GAAoB;IAC7C,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;IACtB,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;QACjB,KAAK,WAAW;YACd,OAAO,qEAAqE,CAAC;QAC/E,KAAK,eAAe;YAClB,OAAO,oBAAoB,CAAC,CAAC,SAAS,qCAAqC,CAAC;QAC9E,KAAK,kBAAkB;YACrB,OAAO,+BAA+B,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,gFAAgF,CAAC;QACvJ,KAAK,kBAAkB;YACrB,OAAO,oBAAoB,CAAC,CAAC,KAAK,oBAAoB,CAAC,CAAC,MAAM,yDAAyD,CAAC,CAAC,KAAK,eAAe,CAAC;QAChJ,KAAK,iBAAiB;YACpB,0FAA0F;YAC1F,sFAAsF;YACtF,OAAO,CAAC,CAAC,OAAO,KAAK,KAAK;gBACxB,CAAC,CAAC,qBAAqB,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,IAAI,4BAA4B,CAAC,CAAC,KAAK,8CAA8C,CAAC,CAAC,KAAK,+EAA+E,CAAC,CAAC,KAAK,IAAI;gBACrO,CAAC,CAAC,qBAAqB,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,IAAI,4BAA4B,CAAC,CAAC,KAAK,wEAAwE,CAAC;QACnK,KAAK,iBAAiB;YACpB,OAAO,YAAY,CAAC,CAAC,KAAK,4BAA4B,CAAC,CAAC,IAAI,mBAAmB,GAAG,CAAC,OAAO,mEAAmE,CAAC;QAChK,KAAK,sBAAsB;YACzB,gFAAgF;YAChF,yFAAyF;YACzF,yFAAyF;YACzF,OAAO,YAAY,CAAC,CAAC,KAAK,6HAA6H,CAAC,CAAC,IAAI,IAAI,iBAAiB,qHAAqH,CAAC;IAC5S,CAAC;AACH,CAAC;AAED,+FAA+F;AAC/F,SAAS,sBAAsB,CAAC,IAAsB,EAAE,CAAa,EAAE,MAAe;IACpF,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,aAAa;YAChB,0FAA0F;YAC1F,0EAA0E;YAC1E,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export declare const SEAT_CHECKPOINT_FORMAT: "cotal-seat-checkpoint/v1";
|
|
2
|
+
export declare const SEAT_CHECKPOINT_RECORD = "checkpoint.json";
|
|
3
|
+
/** The record is a bounded document like the resume document it carries, and for the same reason:
|
|
4
|
+
* a reader must be able to size its buffer before it trusts anything in the file. */
|
|
5
|
+
export declare const MAX_SEAT_CHECKPOINT_BYTES: number;
|
|
6
|
+
/**
|
|
7
|
+
* Where one cut's seat checkpoints live: `.cotal/maintenance/v<N>/checkpoints/<attempt>/<seat>/`.
|
|
8
|
+
*
|
|
9
|
+
* Scoped by the preservation attempt, not shared across cuts. The writer refuses a destination
|
|
10
|
+
* that already exists, which is what makes a checkpoint immutable once sealed; without the attempt
|
|
11
|
+
* in the path the second cut in a root would refuse on the first seat's leftover directory, and
|
|
12
|
+
* deleting the old one to make room would destroy the artifact a rollback still needs.
|
|
13
|
+
*/
|
|
14
|
+
export declare function seatCheckpointDir(root: string, attemptId: string, version?: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* Where a captured session file is put back on the destination.
|
|
17
|
+
*
|
|
18
|
+
* An anchor plus a relative path, never an absolute one. The destination's workspace root, account
|
|
19
|
+
* home and seat working tree are its own, and a record that carried the source host's absolute
|
|
20
|
+
* spelling would either miss them or write outside them.
|
|
21
|
+
*/
|
|
22
|
+
export interface SeatCheckpointDestination {
|
|
23
|
+
readonly anchor: "workspace-root" | "home" | "cwd";
|
|
24
|
+
/** Relative to the anchor. No absolute spelling and no `..` segment, so a record cannot name a
|
|
25
|
+
* path outside the directory it is anchored on. */
|
|
26
|
+
readonly path: string;
|
|
27
|
+
}
|
|
28
|
+
/** One captured file, addressed by its name inside the checkpoint directory. */
|
|
29
|
+
export interface SeatCheckpointFile {
|
|
30
|
+
/** Basename within the checkpoint directory. Never a path, so a record cannot name a file
|
|
31
|
+
* outside the directory it was read from. */
|
|
32
|
+
readonly path: string;
|
|
33
|
+
readonly size: number;
|
|
34
|
+
readonly sha256: string;
|
|
35
|
+
readonly kind: "bundle" | "index-diff" | "worktree-diff" | "untracked" | "session-store" | "session-pointer";
|
|
36
|
+
/** Where a restore puts this file back. Carried by the two session kinds and by nothing else:
|
|
37
|
+
* the repository artifacts are consumed by git and tar rather than copied to a path. */
|
|
38
|
+
readonly restore?: SeatCheckpointDestination;
|
|
39
|
+
}
|
|
40
|
+
/** The repository half: a bundle anchored on a named base, plus the delta the bundle cannot carry. */
|
|
41
|
+
export interface SeatCheckpointRepository {
|
|
42
|
+
/** Full object id. A destination refuses a bundle that does not contain this commit. */
|
|
43
|
+
readonly base: string;
|
|
44
|
+
/** The exact selection rule the untracked set was produced under. A destination that cannot
|
|
45
|
+
* reproduce this rule refuses rather than restoring a silently thinner tree. */
|
|
46
|
+
readonly untrackedSelection: string;
|
|
47
|
+
/** `git status --porcelain` as the source read it, under the same selection rule: the untracked
|
|
48
|
+
* entries the rule excludes are excluded here too, so this describes the state the captured
|
|
49
|
+
* bytes can actually reproduce. A restore re-reads it in the promoted tree and refuses a
|
|
50
|
+
* difference, which is the only check that sees a restore that applied and still landed a
|
|
51
|
+
* different index. */
|
|
52
|
+
readonly status: string;
|
|
53
|
+
readonly bundle: SeatCheckpointFile;
|
|
54
|
+
readonly indexDiff: SeatCheckpointFile;
|
|
55
|
+
readonly worktreeDiff: SeatCheckpointFile;
|
|
56
|
+
readonly untracked: SeatCheckpointFile;
|
|
57
|
+
}
|
|
58
|
+
/** The harness half: an opaque file set plus the session id it is expected to reopen. Cotal does
|
|
59
|
+
* not parse a transcript, so the store travels as bytes and nothing here interprets it. */
|
|
60
|
+
export interface SeatCheckpointSession {
|
|
61
|
+
/** What `sessionContinuityClass` returned for this seat's connector. A destination refuses to
|
|
62
|
+
* promise more than the class allows. */
|
|
63
|
+
readonly continuity: "exact" | "fork" | "fresh" | "drain-only";
|
|
64
|
+
/** The harness-declared session id the store is expected to reopen, when the connector declares
|
|
65
|
+
* one. Absent for a class that does not reopen a session. */
|
|
66
|
+
readonly sessionId?: string;
|
|
67
|
+
/** The connector's session pointer file, captured beside the store. */
|
|
68
|
+
readonly pointer?: SeatCheckpointFile;
|
|
69
|
+
readonly store: readonly SeatCheckpointFile[];
|
|
70
|
+
}
|
|
71
|
+
/** The applied launch profile revision, so a destination can tell whether it is resuming the same
|
|
72
|
+
* intent rather than silently applying a newer profile to a recovered transcript. */
|
|
73
|
+
export interface SeatCheckpointProfile {
|
|
74
|
+
readonly configSha256: string;
|
|
75
|
+
readonly manifestSha256?: string;
|
|
76
|
+
readonly hash?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface SeatCheckpoint<Entry = unknown> {
|
|
79
|
+
readonly format: typeof SEAT_CHECKPOINT_FORMAT;
|
|
80
|
+
/** The manager's resume entry, unchanged and first. The checkpoint restates none of it. */
|
|
81
|
+
readonly entry: Entry;
|
|
82
|
+
readonly space: string;
|
|
83
|
+
readonly name: string;
|
|
84
|
+
readonly lifecycleUid: string;
|
|
85
|
+
/** The per-seat writer generation this cut was taken at. A destination advances past it. */
|
|
86
|
+
readonly generation: number;
|
|
87
|
+
/** Absolute instant of the cut, compared against the destination's clock at gate 3. */
|
|
88
|
+
readonly capturedAt: string;
|
|
89
|
+
/** How long after `capturedAt` this checkpoint is admitted without an explicit override. A
|
|
90
|
+
* property of the deployment, stated here so a destination and an operator cannot disagree
|
|
91
|
+
* about what was promised. */
|
|
92
|
+
readonly recencyHorizonMs: number;
|
|
93
|
+
readonly profile: SeatCheckpointProfile;
|
|
94
|
+
readonly repository: SeatCheckpointRepository;
|
|
95
|
+
readonly session: SeatCheckpointSession;
|
|
96
|
+
}
|
|
97
|
+
/** Thrown by every refusal in this module. Each message names what the reader actually saw. */
|
|
98
|
+
export declare class SeatCheckpointError extends Error {
|
|
99
|
+
constructor(message: string);
|
|
100
|
+
}
|
|
101
|
+
export interface SeatCheckpointWriter {
|
|
102
|
+
readonly directory: string;
|
|
103
|
+
/** Copy one file into the checkpoint, returning its content address. `restore` is where the
|
|
104
|
+
* destination puts it back, required for a file a restore has to place rather than consume. */
|
|
105
|
+
captureFile(name: string, kind: SeatCheckpointFile["kind"], source: string, restore?: SeatCheckpointDestination): SeatCheckpointFile;
|
|
106
|
+
/** Write the record. Last, after every digest above was computed over bytes that landed. */
|
|
107
|
+
seal<Entry>(checkpoint: Omit<SeatCheckpoint<Entry>, "format">): SeatCheckpoint<Entry>;
|
|
108
|
+
/** Remove an unsealed directory this writer exclusively created. */
|
|
109
|
+
cleanup(): void;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Open a checkpoint destination. The disposition is `createArtifactWriter`'s in
|
|
113
|
+
* `implementations/cli/src/lib/backup-artifact.ts`: refuse a destination that already exists, and
|
|
114
|
+
* harden the directory to 0700 before anything lands in it.
|
|
115
|
+
*/
|
|
116
|
+
export declare function createSeatCheckpointWriter(destination: string): SeatCheckpointWriter;
|
|
117
|
+
/** Parse a checkpoint record, refusing anything a gate would otherwise have to guess about. */
|
|
118
|
+
export declare function parseSeatCheckpoint(value: unknown): SeatCheckpoint;
|
|
119
|
+
/** Every file a checkpoint names, in one list, so gate 1 cannot miss one by walking the shape. */
|
|
120
|
+
export declare function seatCheckpointFiles(record: SeatCheckpoint): readonly SeatCheckpointFile[];
|
|
121
|
+
/** Read and parse the record itself, under the same discipline the resume document reader uses:
|
|
122
|
+
* no symlink, bounded read, and a re-stat after the read so a file that moved under us is a
|
|
123
|
+
* refusal rather than a value. */
|
|
124
|
+
export declare function readSeatCheckpoint(directory: string): SeatCheckpoint;
|
|
125
|
+
/**
|
|
126
|
+
* Gate 1, integrity. Every file the checkpoint names must be present, be a regular non-symlink
|
|
127
|
+
* file, match its recorded byte size, and match its recorded sha256. The file is re-stat'd after
|
|
128
|
+
* the read and a move is a refusal, which is the standard `readMaintenanceResumeDocument` holds
|
|
129
|
+
* itself to. Failure here is a refusal and no other gate is consulted.
|
|
130
|
+
*/
|
|
131
|
+
export declare function assertSeatCheckpointIntegrity(directory: string, record: SeatCheckpoint): void;
|
|
132
|
+
export interface SeatIdentityAdmission {
|
|
133
|
+
/** The destination's own space. A checkpoint from another space is refused. */
|
|
134
|
+
readonly space: string;
|
|
135
|
+
/** Whether the recorded lifecycle uid belongs to a live incarnation here, judged by the caller
|
|
136
|
+
* from the same two sources `resumePreserved` uses: the manager-local retirement hold and the
|
|
137
|
+
* roster with the confirmed-predecessor exception. */
|
|
138
|
+
readonly lifecycleUidIsLive: boolean;
|
|
139
|
+
/** The destination's current profile revision for this seat, when it has one. */
|
|
140
|
+
readonly profileConfigSha256?: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Gate 2, identity. Authority, not usefulness: this gate has no override, which is the only reason
|
|
144
|
+
* gate 3 may have one.
|
|
145
|
+
*/
|
|
146
|
+
export declare function assertSeatCheckpointIdentity(record: SeatCheckpoint, admission: SeatIdentityAdmission): void;
|
|
147
|
+
export interface SeatRecencyAdmission {
|
|
148
|
+
/** The destination's clock reading. Passed in so the decision is testable and so a caller that
|
|
149
|
+
* does not trust its clock can refuse before it gets here. */
|
|
150
|
+
readonly now: number;
|
|
151
|
+
/** Explicit operator consent to admit a checkpoint past its horizon, because a stale checkpoint
|
|
152
|
+
* is sometimes the only checkpoint. Recorded by the caller when it is used. */
|
|
153
|
+
readonly acceptStale?: boolean;
|
|
154
|
+
}
|
|
155
|
+
/** What gate 3 decided, so a caller can record an override that was actually exercised rather than
|
|
156
|
+
* one that was merely offered. */
|
|
157
|
+
export interface SeatRecencyVerdict {
|
|
158
|
+
readonly ageMs: number;
|
|
159
|
+
readonly admitted: "inside-horizon" | "override";
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Gate 3, recency. Whether the transcript and the tree are close enough to the world for the
|
|
163
|
+
* resumed seat's next turn to be sensible. Refused by default outside the horizon, with all three
|
|
164
|
+
* values in the message; an explicit operator flag overrides and the caller records that it did.
|
|
165
|
+
*/
|
|
166
|
+
export declare function admitSeatCheckpointRecency(record: SeatCheckpoint, admission: SeatRecencyAdmission): SeatRecencyVerdict;
|
|
167
|
+
//# sourceMappingURL=seat-checkpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seat-checkpoint.d.ts","sourceRoot":"","sources":["../src/seat-checkpoint.ts"],"names":[],"mappings":"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"}
|