@cotal-ai/delivery 0.16.0 → 0.18.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/delivery.d.ts.map +1 -1
- package/dist/delivery.js +46 -10
- package/dist/delivery.js.map +1 -1
- package/dist/evict-exec.d.ts +45 -3
- package/dist/evict-exec.d.ts.map +1 -1
- package/dist/evict-exec.js +61 -17
- package/dist/evict-exec.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/membership.d.ts +12 -1
- package/dist/membership.d.ts.map +1 -1
- package/dist/membership.js +57 -27
- package/dist/membership.js.map +1 -1
- package/package.json +3 -3
package/dist/delivery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AACA,OAAO,EAWL,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAA2F,MAAM,qBAAqB,CAAC;AAMlJ;;;yBAGyB;AACzB,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAuF9B;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA8LtF"}
|
package/dist/delivery.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { basename, dirname, join, resolve } from "node:path";
|
|
2
|
-
import { CotalEndpoint, DEFAULT_SERVER, LEASE_TTL_MS, credsClaims, idFromCreds, isReachable, mintCreds, newIdentity, } from "@cotal-ai/core";
|
|
2
|
+
import { CotalEndpoint, DEFAULT_SERVER, LEASE_TTL_MS, accountFromCreds, credsClaims, idFromCreds, isReachable, mintCreds, newIdentity, } from "@cotal-ai/core";
|
|
3
3
|
import { DELIVERY_CREDS_KEY, FsSecretStore, authDir, findCotalRoot, loadSpaceAuth, soleSpaceOf, workspaceSecretStore } from "@cotal-ai/workspace";
|
|
4
4
|
import { startMembership } from "./membership.js";
|
|
5
|
-
import { executeEviction, executePlaneLiveness } from "./evict-exec.js";
|
|
5
|
+
import { executeEviction, executePlaneLiveness, executePrincipalLiveness } from "./evict-exec.js";
|
|
6
6
|
/** Re-exported for hosted compositions: the {@link SecretStore} key a store injected into
|
|
7
7
|
* {@link runDelivery} must hold the cred under. Defined once in workspace (the key↔filename
|
|
8
8
|
* convention is the workspace layout's) so the writer, the renewal owner, and this reader can
|
|
@@ -118,13 +118,30 @@ export async function runDelivery(args, store) {
|
|
|
118
118
|
const server = v.server ?? DEFAULT_SERVER;
|
|
119
119
|
const creds = await loadDeliveryCreds(credsSrc, v); // pre-minted scoped cred; NO signer/loadSpaceAuth in this path
|
|
120
120
|
let latestCreds = creds.initial; // freshest renewal — the broker-reachability poll below presents it
|
|
121
|
-
|
|
121
|
+
// REQUIRE TLS when the operator said to. This daemon holds a STANDING credential and reconnects
|
|
122
|
+
// unattended, so a downgrade here is not a one-shot exposure like a human running `cotal status`
|
|
123
|
+
// - it is repeated, on every reconnect, with nobody watching. `tls: true` makes the client refuse
|
|
124
|
+
// rather than fall back, which is the only behaviour that survives a forged plaintext INFO.
|
|
125
|
+
// Boolean flags arrive as presence in this Values map (`Record<string, string | undefined>`),
|
|
126
|
+
// matching how `--dev-mint` is read below. Comparing to `true` would silently never match.
|
|
127
|
+
const tls = v.tls !== undefined;
|
|
128
|
+
if (!(await isReachable(server, { creds: latestCreds, ...(tls ? { tls: true } : {}) }))) {
|
|
122
129
|
console.error(`✗ delivery: can't reach NATS at ${server}. Run: cotal up`);
|
|
123
130
|
process.exit(1);
|
|
124
131
|
}
|
|
132
|
+
// PIN THE SCAN TARGET ONCE, HERE. The $SYS sweeps below resolve an ACCOUNT, and a complete sweep
|
|
133
|
+
// of the WRONG account is indistinguishable from "the principal is gone" — a healthy-looking
|
|
134
|
+
// answer that authorizes eviction and gate reconciliation. Resolving the root per request from
|
|
135
|
+
// `process.cwd()` let that drift; and a daemon started in a foreign mesh root would sweep a
|
|
136
|
+
// foreign tenant while looking entirely well. So: the root is fixed at start, and the account on
|
|
137
|
+
// disk is cross-checked against the account this daemon's OWN credential authenticates as — a
|
|
138
|
+
// fact that does not come from that root, which is what makes it an independent check.
|
|
139
|
+
const scanTarget = { root: findCotalRoot(), expectedAccount: accountFromCreds(creds.initial) };
|
|
140
|
+
console.error(`• delivery: $SYS sweeps bound to ${join(scanTarget.root, ".cotal")} (account ${scanTarget.expectedAccount})`);
|
|
125
141
|
const ep = new CotalEndpoint({
|
|
126
142
|
space,
|
|
127
143
|
servers: server,
|
|
144
|
+
...(tls ? { tls: true } : {}),
|
|
128
145
|
// The RELOAD seam (D5 slice 5 class 2): the endpoint re-invokes the source at 75% of each JWT's
|
|
129
146
|
// lifetime and swaps the connection onto the re-signed file — bounded delivery creds renew with
|
|
130
147
|
// no daemon restart. The explicit card.id pins the daemon's nkey across renewals.
|
|
@@ -153,6 +170,10 @@ export async function runDelivery(args, store) {
|
|
|
153
170
|
// Broker-sourced graph membership handle — declared BEFORE Plane-3 so the delivery-admin reload
|
|
154
171
|
// hook below can close over it (it starts further down; the closure reads it live).
|
|
155
172
|
let membership;
|
|
173
|
+
// WHY the feed is down, carried from `startMembership` so the adoption refusal below names the real
|
|
174
|
+
// fault instead of its symptom. Without it, an expired $SYS observer cred surfaces to the operator
|
|
175
|
+
// only as "membership feed is not running" (#338).
|
|
176
|
+
let membershipDown;
|
|
156
177
|
// Host Plane-3 (fan-out writer + trusted reader) AND serve the ctl.delivery runtime durable ops. The
|
|
157
178
|
// reader re-authorizes each entry against the durable ACL registry, read FRESH per entry. The
|
|
158
179
|
// delivery-admin rail's `reloadCreds` (explicit class-2 adoption) also reloads the membership feed's
|
|
@@ -164,18 +185,22 @@ export async function runDelivery(args, store) {
|
|
|
164
185
|
// membership-rw.creds but the feed that must adopt it is not running), else n/a — a
|
|
165
186
|
// delivery-only renewal must never be falsely failed by an unprovisioned feed.
|
|
166
187
|
if (expected !== undefined)
|
|
167
|
-
throw new Error(
|
|
168
|
-
return { skipped:
|
|
188
|
+
throw new Error(`membership feed is not running, but a membership generation was expected - nothing adopted${membershipDown ? `: ${membershipDown}` : ""}`);
|
|
189
|
+
return { skipped: `membership feed not running (nothing to reload)${membershipDown ? `: ${membershipDown}` : ""}` };
|
|
169
190
|
}
|
|
170
191
|
// Symmetric with delivery: claim broker acceptance (the preflight), not verified resident reauth.
|
|
171
192
|
return { brokerAccepted: await membership.reloadRwCreds(expected), residentSwap: "best-effort" };
|
|
172
193
|
},
|
|
173
194
|
// Live-eviction executor (D5 slice 6): per-call $SYS observer/evictor connections; refuses
|
|
174
195
|
// loudly on a pre-evictor space. Rare repair/flip step — never a standing $SYS conn here.
|
|
175
|
-
evictPrincipal: (principal) => executeEviction(server, principal),
|
|
196
|
+
evictPrincipal: (principal) => executeEviction(server, scanTarget, principal),
|
|
176
197
|
// Plane-claim liveness oracle (#29 HIGH 3): read-only $SYS CONNZ per call; the auth plane's
|
|
177
198
|
// stale-claim reclaim gates on this verdict (any refusal/unknown blocks takeover, fail-closed).
|
|
178
|
-
planeConnLiveness: (query) => executePlaneLiveness(server, query),
|
|
199
|
+
planeConnLiveness: (query) => executePlaneLiveness(server, scanTarget, query),
|
|
200
|
+
// Freeze-holder liveness probe (#391): read-only $SYS CONNZ per call — the READ half of
|
|
201
|
+
// evictPrincipal, so gate reconciliation can refuse on a live holder's behalf rather than
|
|
202
|
+
// killing it to discover it was alive (any refusal/unknown blocks the repair, fail-closed).
|
|
203
|
+
principalLiveness: (principal) => executePrincipalLiveness(server, scanTarget, principal),
|
|
179
204
|
});
|
|
180
205
|
// Flip the lease to READY only now — after the loops + ctl.delivery responder are bound — so readiness
|
|
181
206
|
// waiters (ensureDelivery) and the cotal_channels health surface see "ready" iff the responder is up,
|
|
@@ -192,10 +217,12 @@ export async function runDelivery(args, store) {
|
|
|
192
217
|
// Pass the INJECTED store (hosted KMS/Vault), NOT credsSrc.store — that one may be an FS store over
|
|
193
218
|
// an arbitrary `--creds` path, whereas membership-rw lives under the workstation `.cotal/` key. With
|
|
194
219
|
// no injected store, startMembership falls back to the workstation FS store.
|
|
195
|
-
|
|
220
|
+
// Fail-soft, but never fault-FORGETFUL: whichever way the feed fails to come up, keep the reason.
|
|
221
|
+
({ handle: membership, down: membershipDown } = await startMembership({ space, server }, store));
|
|
196
222
|
}
|
|
197
223
|
catch (e) {
|
|
198
|
-
|
|
224
|
+
membershipDown = e.message;
|
|
225
|
+
console.error(`! membership: failed to start (${membershipDown}); graph membership degraded, delivery unaffected`);
|
|
199
226
|
}
|
|
200
227
|
let stopping = false;
|
|
201
228
|
const shutdown = (code) => {
|
|
@@ -242,7 +269,16 @@ export async function runDelivery(args, store) {
|
|
|
242
269
|
const brokerWatch = setInterval(() => {
|
|
243
270
|
if (stopping)
|
|
244
271
|
return;
|
|
245
|
-
|
|
272
|
+
// THE SAME TRANSPORT AS EVERY OTHER DIAL IN THIS PROCESS. This poll carries `latestCreds` — a
|
|
273
|
+
// standing credential — and `isReachable` performs a real authenticated connect whenever creds
|
|
274
|
+
// are supplied, every 2 seconds, for the life of the daemon. It is the most repeated credential
|
|
275
|
+
// presentation in the system, and it was the one dial here that did not name its transport.
|
|
276
|
+
//
|
|
277
|
+
// The poll predates TLS and is identical on `main`, where nothing is encrypted and it is at
|
|
278
|
+
// least consistent. What is new is the ASYMMETRY: with the two dials above upgraded, an operator
|
|
279
|
+
// who enables TLS would get a protected main path and an unprotected watchdog. An inconsistent
|
|
280
|
+
// guarantee is worse than a uniformly absent one, because the operator now believes something.
|
|
281
|
+
void isReachable(server, { creds: latestCreds, ...(tls ? { tls: true } : {}) })
|
|
246
282
|
.then((ok) => {
|
|
247
283
|
if (ok) {
|
|
248
284
|
lastReachable = Date.now();
|
package/dist/delivery.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,SAAS,EACT,WAAW,GAIZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAClJ,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"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"}
|
package/dist/evict-exec.d.ts
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
|
-
import { type EvictionResult, type PlaneLivenessResult } from "@cotal-ai/core";
|
|
1
|
+
import { type EvictionResult, type PlaneLivenessResult, type PrincipalLivenessResult } from "@cotal-ai/core";
|
|
2
|
+
/**
|
|
3
|
+
* THE SCAN TARGET, resolved fail-closed and BOUND TO THE DAEMON'S OWN TENANCY.
|
|
4
|
+
*
|
|
5
|
+
* Every executor below sweeps an ACCOUNT, and a complete, well-formed sweep of the WRONG account is
|
|
6
|
+
* indistinguishable from "the principal is gone" — it is a healthy-looking answer that authorizes
|
|
7
|
+
* eviction and gate reconciliation. The account used to be read from `findCotalRoot()` at REQUEST
|
|
8
|
+
* time with nothing tying it to the space the daemon serves, so a daemon whose working directory
|
|
9
|
+
* resolved a different mesh root (a parent mesh, a home mesh, a leftover scratch root, a service
|
|
10
|
+
* unit with the wrong WorkingDirectory) would sweep a foreign tenant and answer `gone` for a
|
|
11
|
+
* principal that was alive on its own.
|
|
12
|
+
*
|
|
13
|
+
* Two independent guards, because either alone leaves a real case open:
|
|
14
|
+
* - the root is PINNED AT DAEMON START, so it cannot drift with `process.cwd()` between requests;
|
|
15
|
+
* - the account on disk is CROSS-CHECKED against the account the daemon's own credential
|
|
16
|
+
* authenticates as. Pinning alone cannot notice a daemon that started in the wrong root to
|
|
17
|
+
* begin with; the tenancy check can, because the credential is not read from that root.
|
|
18
|
+
*
|
|
19
|
+
* A mismatch REFUSES LOUD and names both sides. Note the asymmetry that makes this necessary: an
|
|
20
|
+
* observer scoped to account A with accountId B under-reports (broker-denied → `unknown`, safe),
|
|
21
|
+
* but observer A with accountId A while the GATE lives on B is internally consistent and answers a
|
|
22
|
+
* confident, wrong `gone`.
|
|
23
|
+
*/
|
|
24
|
+
export interface ScanTarget {
|
|
25
|
+
/** The mesh root resolved ONCE at daemon start — never re-resolved per request. */
|
|
26
|
+
root: string;
|
|
27
|
+
/** The account the daemon's own delivery credential authenticates as. */
|
|
28
|
+
expectedAccount: string;
|
|
29
|
+
}
|
|
2
30
|
/**
|
|
3
31
|
* The delivery daemon's LIVE-EVICTION executor (D5 slice 6, on the privileged delivery-admin rail):
|
|
4
32
|
* force-drop a denied principal's live connections via the core scan→KICK→verify primitive. The two
|
|
@@ -7,7 +35,7 @@ import { type EvictionResult, type PlaneLivenessResult } from "@cotal-ai/core";
|
|
|
7
35
|
* $SYS connection in the daemon). A space provisioned before the evictor existed refuses loudly with
|
|
8
36
|
* the regeneration step — deny-new-only (durable reauth) remains its honest posture.
|
|
9
37
|
*/
|
|
10
|
-
export declare function executeEviction(server: string, principal: string): Promise<EvictionResult>;
|
|
38
|
+
export declare function executeEviction(server: string, target: ScanTarget, principal: string): Promise<EvictionResult>;
|
|
11
39
|
/**
|
|
12
40
|
* The delivery daemon's PLANE-LIVENESS oracle executor (#29 HIGH 3, on the privileged
|
|
13
41
|
* delivery-admin rail): answer whether the auth plane's two claimed sealed-scanner connections are
|
|
@@ -17,5 +45,19 @@ export declare function executeEviction(server: string, principal: string): Prom
|
|
|
17
45
|
* refuses with the regeneration step — the auth plane treats that refusal as UNKNOWN and never
|
|
18
46
|
* reclaims over it (fail-closed).
|
|
19
47
|
*/
|
|
20
|
-
export declare function executePlaneLiveness(server: string, query: unknown): Promise<PlaneLivenessResult>;
|
|
48
|
+
export declare function executePlaneLiveness(server: string, target: ScanTarget, query: unknown): Promise<PlaneLivenessResult>;
|
|
49
|
+
/**
|
|
50
|
+
* The delivery daemon's FREEZE-HOLDER LIVENESS probe (#391, on the privileged delivery-admin rail):
|
|
51
|
+
* answer whether ONE principal still holds a live connection, via the $SYS CONNZ observer cred
|
|
52
|
+
* (opened per call; READ-ONLY — the KICK evictor cred never enters this path, and is not even read
|
|
53
|
+
* from disk here).
|
|
54
|
+
*
|
|
55
|
+
* This is the READ half of {@link executeEviction}, and it exists because the write half cannot
|
|
56
|
+
* serve as its own precheck: a repair that must REFUSE while the holder is alive would, using
|
|
57
|
+
* `evictPrincipal` to find out, kill the holder before it could refuse. Same observer cred, same
|
|
58
|
+
* sweep, same refusal posture as the plane oracle — a space provisioned before the observer existed
|
|
59
|
+
* refuses with the regeneration step, and the caller treats that refusal as UNKNOWN and never
|
|
60
|
+
* repairs over it (fail-closed).
|
|
61
|
+
*/
|
|
62
|
+
export declare function executePrincipalLiveness(server: string, target: ScanTarget, principal: unknown): Promise<PrincipalLivenessResult>;
|
|
21
63
|
//# sourceMappingURL=evict-exec.d.ts.map
|
package/dist/evict-exec.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evict-exec.d.ts","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"evict-exec.d.ts","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,eAAe,EAAE,MAAM,CAAC;CACzB;AAsBD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAwBpH;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAkB3H;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAuBvI"}
|
package/dist/evict-exec.js
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { evictDeniedPrincipalWithCreds, isPlaneConnTuple, isPrincipalOwnerToken, observePlaneLivenessWithCreds, parsePrincipalKey, } from "@cotal-ai/core";
|
|
3
|
+
import { evictDeniedPrincipalWithCreds, isPlaneConnTuple, isPrincipalOwnerToken, observePlaneLivenessWithCreds, observePrincipalLivenessWithCreds, parsePrincipalKey, } from "@cotal-ai/core";
|
|
4
4
|
import { findCotalRoot } from "@cotal-ai/workspace";
|
|
5
|
+
function resolveScan(target, verb) {
|
|
6
|
+
const live = findCotalRoot();
|
|
7
|
+
if (live !== target.root)
|
|
8
|
+
throw new Error(`${verb}: the mesh root resolved at this request (${live}) is not the one this daemon started in (${target.root}); ` +
|
|
9
|
+
"the scan account would be read from a different workspace than the one this daemon serves. Refusing — a sweep of the wrong account looks exactly like a dead principal");
|
|
10
|
+
const dir = join(target.root, ".cotal");
|
|
11
|
+
const cfgPath = join(dir, "membership.json");
|
|
12
|
+
if (!existsSync(cfgPath))
|
|
13
|
+
throw new Error(`${verb}: ${cfgPath} is missing, so the account to scan is unknown`);
|
|
14
|
+
const accountId = JSON.parse(readFileSync(cfgPath, "utf8")).accountId;
|
|
15
|
+
if (!accountId)
|
|
16
|
+
throw new Error(`${verb}: ${cfgPath} has no accountId`);
|
|
17
|
+
if (accountId !== target.expectedAccount)
|
|
18
|
+
throw new Error(`${verb}: ${cfgPath} names account ${accountId}, but this daemon's own credential authenticates as ${target.expectedAccount}. ` +
|
|
19
|
+
"That disk material belongs to a different mesh, and sweeping it would return a confident, WRONG answer (a complete sweep of the wrong account is indistinguishable from a gone principal). Refusing — start the daemon from the workspace root whose account it serves");
|
|
20
|
+
return { dir, accountId };
|
|
21
|
+
}
|
|
5
22
|
/**
|
|
6
23
|
* The delivery daemon's LIVE-EVICTION executor (D5 slice 6, on the privileged delivery-admin rail):
|
|
7
24
|
* force-drop a denied principal's live connections via the core scan→KICK→verify primitive. The two
|
|
@@ -10,7 +27,7 @@ import { findCotalRoot } from "@cotal-ai/workspace";
|
|
|
10
27
|
* $SYS connection in the daemon). A space provisioned before the evictor existed refuses loudly with
|
|
11
28
|
* the regeneration step — deny-new-only (durable reauth) remains its honest posture.
|
|
12
29
|
*/
|
|
13
|
-
export async function executeEviction(server, principal) {
|
|
30
|
+
export async function executeEviction(server, target, principal) {
|
|
14
31
|
// Fail-closed principal validation — the KICK targets come from the observer's own CONNZ scan,
|
|
15
32
|
// but the FILTER must be a REAL principal: syntax alone is not enough, because CONNZ attribution
|
|
16
33
|
// only ever surfaces owners that pass isPrincipalOwnerToken (`local` / derived `u_…`), so a
|
|
@@ -20,15 +37,11 @@ export async function executeEviction(server, principal) {
|
|
|
20
37
|
const parsed = parsePrincipalKey(principal);
|
|
21
38
|
if (!parsed || !isPrincipalOwnerToken(parsed.owner))
|
|
22
39
|
throw new Error(`evictPrincipal: "${principal}" is not a real owner.actor principal (owner must be \`local\` or a derived \`u_…\` token — the only shapes CONNZ attribution can surface)`);
|
|
23
|
-
const dir =
|
|
40
|
+
const { dir, accountId } = resolveScan(target, "evictPrincipal");
|
|
24
41
|
const obsPath = join(dir, "membership-observer.creds");
|
|
25
42
|
const evPath = join(dir, "connection-evictor.creds");
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
throw new Error("evictPrincipal: the $SYS observer/evictor creds are not provisioned here (a space created before live eviction) — regenerate the space auth (`cotal down` + fresh `cotal up`); until then removal is deny-new-only (durable reauth)");
|
|
29
|
-
const accountId = JSON.parse(readFileSync(cfgPath, "utf8")).accountId;
|
|
30
|
-
if (!accountId)
|
|
31
|
-
throw new Error("evictPrincipal: .cotal/membership.json has no accountId");
|
|
43
|
+
if (!existsSync(obsPath) || !existsSync(evPath))
|
|
44
|
+
throw new Error("evictPrincipal: the $SYS observer/evictor creds are not provisioned here (a space created before live eviction); mint them with `cotal down` then `cotal up --rotate-sys`. Until then removal is deny-new-only (durable reauth)");
|
|
32
45
|
return evictDeniedPrincipalWithCreds({
|
|
33
46
|
servers: server,
|
|
34
47
|
observerCreds: readFileSync(obsPath, "utf8"),
|
|
@@ -46,20 +59,16 @@ export async function executeEviction(server, principal) {
|
|
|
46
59
|
* refuses with the regeneration step — the auth plane treats that refusal as UNKNOWN and never
|
|
47
60
|
* reclaims over it (fail-closed).
|
|
48
61
|
*/
|
|
49
|
-
export async function executePlaneLiveness(server, query) {
|
|
62
|
+
export async function executePlaneLiveness(server, target, query) {
|
|
50
63
|
const q = query;
|
|
51
64
|
if (q === undefined || q === null || typeof q !== "object" ||
|
|
52
65
|
Object.keys(q).some((k) => k !== "ledger" && k !== "records") || // closed top-level shape
|
|
53
66
|
!isPlaneConnTuple(q.ledger) || !isPlaneConnTuple(q.records))
|
|
54
67
|
throw new Error("planeConnLiveness: the query must be exactly { ledger, records } connection tuples ({ serverId, cid, userNkey }); refusing a malformed or wider query");
|
|
55
|
-
const dir =
|
|
68
|
+
const { dir, accountId } = resolveScan(target, "planeConnLiveness");
|
|
56
69
|
const obsPath = join(dir, "membership-observer.creds");
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
throw new Error("planeConnLiveness: the $SYS observer creds are not provisioned here (a space created before live observation) — regenerate the space auth (`cotal down` + fresh `cotal up`)");
|
|
60
|
-
const accountId = JSON.parse(readFileSync(cfgPath, "utf8")).accountId;
|
|
61
|
-
if (!accountId)
|
|
62
|
-
throw new Error("planeConnLiveness: .cotal/membership.json has no accountId");
|
|
70
|
+
if (!existsSync(obsPath))
|
|
71
|
+
throw new Error("planeConnLiveness: the $SYS observer creds are not provisioned here (a space created before live observation); mint them with `cotal down` then `cotal up --rotate-sys`");
|
|
63
72
|
return observePlaneLivenessWithCreds({
|
|
64
73
|
servers: server,
|
|
65
74
|
observerCreds: readFileSync(obsPath, "utf8"),
|
|
@@ -67,4 +76,39 @@ export async function executePlaneLiveness(server, query) {
|
|
|
67
76
|
query: { ledger: q.ledger, records: q.records },
|
|
68
77
|
});
|
|
69
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The delivery daemon's FREEZE-HOLDER LIVENESS probe (#391, on the privileged delivery-admin rail):
|
|
81
|
+
* answer whether ONE principal still holds a live connection, via the $SYS CONNZ observer cred
|
|
82
|
+
* (opened per call; READ-ONLY — the KICK evictor cred never enters this path, and is not even read
|
|
83
|
+
* from disk here).
|
|
84
|
+
*
|
|
85
|
+
* This is the READ half of {@link executeEviction}, and it exists because the write half cannot
|
|
86
|
+
* serve as its own precheck: a repair that must REFUSE while the holder is alive would, using
|
|
87
|
+
* `evictPrincipal` to find out, kill the holder before it could refuse. Same observer cred, same
|
|
88
|
+
* sweep, same refusal posture as the plane oracle — a space provisioned before the observer existed
|
|
89
|
+
* refuses with the regeneration step, and the caller treats that refusal as UNKNOWN and never
|
|
90
|
+
* repairs over it (fail-closed).
|
|
91
|
+
*/
|
|
92
|
+
export async function executePrincipalLiveness(server, target, principal) {
|
|
93
|
+
// Same fail-closed principal boundary the eviction filter applies, for the same reason: CONNZ
|
|
94
|
+
// attribution only ever surfaces `local` / derived `u_…` owners, so a syntactically-valid
|
|
95
|
+
// non-principal would sweep completely, match nothing, and read as a healthy `gone` — false
|
|
96
|
+
// confidence for a typo'd or old-shape holder, on the exact verdict that authorizes the repair.
|
|
97
|
+
if (typeof principal !== "string" || principal.trim().length === 0)
|
|
98
|
+
throw new Error("principalLiveness: a principal (owner.actor dot-form) is required");
|
|
99
|
+
const wanted = principal.trim();
|
|
100
|
+
const parsed = parsePrincipalKey(wanted);
|
|
101
|
+
if (!parsed || !isPrincipalOwnerToken(parsed.owner))
|
|
102
|
+
throw new Error(`principalLiveness: "${wanted}" is not a real owner.actor principal (owner must be \`local\` or a derived \`u_…\` token — the only shapes CONNZ attribution can surface)`);
|
|
103
|
+
const { dir, accountId } = resolveScan(target, "principalLiveness");
|
|
104
|
+
const obsPath = join(dir, "membership-observer.creds");
|
|
105
|
+
if (!existsSync(obsPath))
|
|
106
|
+
throw new Error("principalLiveness: the $SYS observer creds are not provisioned here (a space created before live observation); mint them with `cotal down` then `cotal up --rotate-sys`");
|
|
107
|
+
return observePrincipalLivenessWithCreds({
|
|
108
|
+
servers: server,
|
|
109
|
+
observerCreds: readFileSync(obsPath, "utf8"),
|
|
110
|
+
accountId,
|
|
111
|
+
principal: wanted,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
70
114
|
//# sourceMappingURL=evict-exec.js.map
|
package/dist/evict-exec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evict-exec.js","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,6BAA6B,EAC7B,gBAAgB,EAChB,qBAAqB,EACrB,6BAA6B,EAC7B,iBAAiB,
|
|
1
|
+
{"version":3,"file":"evict-exec.js","sourceRoot":"","sources":["../src/evict-exec.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AA4CzD,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const deliveryCommands = [
|
|
|
17
17
|
flags: [
|
|
18
18
|
{ name: "space", type: "string", value: "<s>", description: "space to serve (required; the scoped cred doesn't encode it)" },
|
|
19
19
|
{ name: "server", type: "string", value: "<url>", description: "broker URL (default: the local mesh)" },
|
|
20
|
+
{ name: "tls", type: "boolean", description: "REQUIRE TLS to the broker - refuse to connect if it is not offered" },
|
|
20
21
|
{ name: "creds", type: "string", value: "<file>", description: "pre-minted scoped delivery cred" },
|
|
21
22
|
{ name: "shard", type: "string", value: "<n>", description: "shard index (N=1 only; non-zero is rejected)" },
|
|
22
23
|
{ name: "shards", type: "string", value: "<n>", description: "shard count (N=1 only; >1 is rejected)" },
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/membership.d.ts
CHANGED
|
@@ -18,8 +18,19 @@ import { type MembershipFeedHandle, type SecretStore } from "@cotal-ai/core";
|
|
|
18
18
|
* They are a static $SYS cred and non-secret config, respectively — the renewable rw kind is what 3b
|
|
19
19
|
* migrates; the manager re-signs only it.
|
|
20
20
|
*/
|
|
21
|
+
/** Why the feed is, or is not, running. `down` carries the DIAGNOSIS to the caller, because the
|
|
22
|
+
* daemon's log line is not the only place it is needed: an adoption reply that says only "the feed is
|
|
23
|
+
* not running" sends an operator hunting for a feed fault when the real one is an expired $SYS cred
|
|
24
|
+
* three layers down (the failure reported in #338). Exactly one of the two members is set. */
|
|
25
|
+
export type MembershipStart = {
|
|
26
|
+
handle: MembershipFeedHandle;
|
|
27
|
+
down?: undefined;
|
|
28
|
+
} | {
|
|
29
|
+
handle?: undefined;
|
|
30
|
+
down: string;
|
|
31
|
+
};
|
|
21
32
|
export declare function startMembership(opts: {
|
|
22
33
|
space: string;
|
|
23
34
|
server: string;
|
|
24
|
-
}, store?: SecretStore): Promise<
|
|
35
|
+
}, store?: SecretStore): Promise<MembershipStart>;
|
|
25
36
|
//# sourceMappingURL=membership.d.ts.map
|
package/dist/membership.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuD,KAAK,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGlI;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;+FAG+F;AAC/F,MAAM,MAAM,eAAe,GAAG;IAAE,MAAM,EAAE,oBAAoB,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,MAAM,CAAC,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAExH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CA0F5H"}
|
package/dist/membership.js
CHANGED
|
@@ -1,26 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { startMembershipFeed } from "@cotal-ai/core";
|
|
3
|
+
import { credsClaims, inspectCredHealth, startMembershipFeed } from "@cotal-ai/core";
|
|
4
4
|
import { findCotalRoot, MEMBERSHIP_RW_CREDS_KEY, workspaceSecretStore } from "@cotal-ai/workspace";
|
|
5
|
-
/**
|
|
6
|
-
* The delivery daemon's thin composition root for the broker-sourced graph-membership feed. It loads the
|
|
7
|
-
* DATA account id + the SYSTEM-account observer cred from `.cotal/` (written by `cotal up`; the daemon
|
|
8
|
-
* never holds the signer) and reads the DATA-account rw cred through the {@link SecretStore} seam — the
|
|
9
|
-
* SAME seam the renewal owner (the manager) re-signs into — so a hosted composition can renew the feed's
|
|
10
|
-
* writer end-to-end (KMS/Vault) without a daemon restart. `store` is that hosted injection point; with
|
|
11
|
-
* none, the workstation FS store (keys = the filenames under `.cotal/`) is used, so a local `cotal up`
|
|
12
|
-
* is byte-for-byte unchanged. It hands the two creds + the account id to the core feed engine
|
|
13
|
-
* ({@link startMembershipFeed}, which owns the two connections + the poll loop + the rw 75% renewal).
|
|
14
|
-
*
|
|
15
|
-
* Deliberately ISOLATED from Plane-3: a separate module, separate connections, and a fail-soft contract —
|
|
16
|
-
* if the creds aren't provisioned (a pre-feature space) or the feed can't start, it logs and returns
|
|
17
|
-
* `undefined`; the graph degrades to traffic-only and delivery is untouched.
|
|
18
|
-
*
|
|
19
|
-
* Residual (deferred, non-blocking): the observer cred + `membership.json` (the account id) are still
|
|
20
|
-
* read from `.cotal/` files, so a fully hosted feed on an injected store also needs those two threaded.
|
|
21
|
-
* They are a static $SYS cred and non-secret config, respectively — the renewable rw kind is what 3b
|
|
22
|
-
* migrates; the manager re-signs only it.
|
|
23
|
-
*/
|
|
24
5
|
export async function startMembership(opts, store) {
|
|
25
6
|
const root = findCotalRoot();
|
|
26
7
|
const dir = join(root, ".cotal");
|
|
@@ -28,14 +9,63 @@ export async function startMembership(opts, store) {
|
|
|
28
9
|
const cfgPath = join(dir, "membership.json");
|
|
29
10
|
const secrets = store ?? workspaceSecretStore(root);
|
|
30
11
|
const rw = await secrets.get(MEMBERSHIP_RW_CREDS_KEY);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
12
|
+
const missing = [
|
|
13
|
+
rw === undefined ? MEMBERSHIP_RW_CREDS_KEY : undefined,
|
|
14
|
+
existsSync(obsPath) ? undefined : "membership-observer.creds",
|
|
15
|
+
existsSync(cfgPath) ? undefined : "membership.json",
|
|
16
|
+
].filter((f) => f !== undefined);
|
|
17
|
+
if (missing.length) {
|
|
18
|
+
// Name the missing piece AND a repair that reaches it. `cotal up --rotate-sys` re-mints the $SYS
|
|
19
|
+
// pair, since it holds a system-account signing seed for exactly that moment, but it writes neither
|
|
20
|
+
// the DATA-account rw cred nor the account-id config: those are written once, when a space is
|
|
21
|
+
// first provisioned. So a rotation repairs a missing OBSERVER and nothing else, and pointing a
|
|
22
|
+
// pre-feature space at one would send an operator through a stop/start that cannot help it.
|
|
23
|
+
const down = missing.length === 1 && missing[0] === "membership-observer.creds"
|
|
24
|
+
? "the $SYS observer cred is missing - re-mint it with `cotal down` then `cotal up --rotate-sys`"
|
|
25
|
+
: `the membership bundle is incomplete here (missing ${missing.join(", ")}) - a space created before broker-sourced membership gains the feed only when its auth is regenerated; \`cotal up --rotate-sys\` re-mints the $SYS pair but writes neither the rw cred nor the account id`;
|
|
26
|
+
console.error(`• membership: ${down}. The graph falls back to traffic-only; delivery is unaffected.`);
|
|
27
|
+
return { down };
|
|
34
28
|
}
|
|
35
29
|
const accountId = JSON.parse(readFileSync(cfgPath, "utf8")).accountId;
|
|
36
30
|
if (!accountId) {
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
const down = ".cotal/membership.json has no accountId";
|
|
32
|
+
console.error(`• membership: ${down}; membership disabled (delivery unaffected)`);
|
|
33
|
+
return { down };
|
|
34
|
+
}
|
|
35
|
+
// Check the OBSERVER's own expiry before connecting. It is `rotation-renewed`, so unlike every
|
|
36
|
+
// renewable cred here nothing re-signs it: at its 30-day horizon the broker simply answers
|
|
37
|
+
// "Authorization Violation", which names neither the credential nor the repair. Reading the JWT
|
|
38
|
+
// costs nothing and turns the daemon's one loud line into the actual diagnosis: the difference
|
|
39
|
+
// between an operator finding this in minutes and finding it in a support thread.
|
|
40
|
+
const obsCreds = readFileSync(obsPath, "utf8");
|
|
41
|
+
// A TORN rotation is the other way this cred goes broker-dead without a byte of its own changing:
|
|
42
|
+
// `rotateSystemCreds` commits the trust record, then writes both $SYS creds, so a crash between the
|
|
43
|
+
// two leaves one file on the retired system account. The broker answers the same bare
|
|
44
|
+
// "Authorization Violation" either way. The daemon cannot ask the trust record which account is
|
|
45
|
+
// current (it deliberately never loads the signer), but it does not need to: the pair is written
|
|
46
|
+
// by one rotation, so two DIFFERENT issuers prove one of them is stale, with no signer read at all.
|
|
47
|
+
// (`doctor auth`, which legitimately holds the record, checks each file against it directly.)
|
|
48
|
+
const evPath = join(dir, "connection-evictor.creds");
|
|
49
|
+
if (existsSync(evPath)) {
|
|
50
|
+
let obsIss, evIss;
|
|
51
|
+
try {
|
|
52
|
+
obsIss = credsClaims(obsCreds).iss;
|
|
53
|
+
evIss = credsClaims(readFileSync(evPath, "utf8")).iss;
|
|
54
|
+
}
|
|
55
|
+
catch { /* an unreadable file is the health check's case just below */ }
|
|
56
|
+
if (obsIss !== undefined && evIss !== undefined && obsIss !== evIss) {
|
|
57
|
+
const down = `the two $SYS creds are signed by DIFFERENT system accounts (observer ${obsIss.slice(0, 12)}…, evictor ${evIss.slice(0, 12)}…) - a system-account rotation did not finish, so one of them is broker-dead: re-run \`cotal down\` then \`cotal up --rotate-sys\` to land a complete generation`;
|
|
58
|
+
console.error(`! membership: ${down}; graph membership degraded, delivery unaffected`);
|
|
59
|
+
return { down };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const obs = inspectCredHealth(obsCreds);
|
|
63
|
+
if (obs.state === "expired" || obs.state === "unreadable") {
|
|
64
|
+
const down = obs.state === "expired"
|
|
65
|
+
? `the $SYS observer cred (${obsPath}) EXPIRED ${new Date((obs.exp ?? 0) * 1000).toISOString()} and the broker denies it - it is rotation-renewed, so nothing re-signs it: run \`cotal down\` then \`cotal up --rotate-sys\` (agents, creds and data are untouched)`
|
|
66
|
+
: `the $SYS observer cred (${obsPath}) is unreadable (${obs.error})`;
|
|
67
|
+
console.error(`! membership: ${down}; graph membership degraded, delivery unaffected`);
|
|
68
|
+
return { down };
|
|
39
69
|
}
|
|
40
70
|
const intervalMs = Number(process.env.COTAL_MEMBERSHIP_INTERVAL_MS) || undefined; // test/ops override
|
|
41
71
|
const handle = await startMembershipFeed({
|
|
@@ -43,7 +73,7 @@ export async function startMembership(opts, store) {
|
|
|
43
73
|
space: opts.space,
|
|
44
74
|
accountId,
|
|
45
75
|
// Observer is rotation-renewed ($SYS): a static read — its renewal is a system rotation + restart.
|
|
46
|
-
observerCreds:
|
|
76
|
+
observerCreds: obsCreds,
|
|
47
77
|
// rw is class-2 standing-renewable: read through the store seam and renewed on a 75% timer that
|
|
48
78
|
// preflight-proves each candidate the manager re-signs into the store (D5 slice 5). The daemon
|
|
49
79
|
// never sees the signer; the manager owns the re-sign.
|
|
@@ -56,6 +86,6 @@ export async function startMembership(opts, store) {
|
|
|
56
86
|
intervalMs,
|
|
57
87
|
});
|
|
58
88
|
console.log(`✓ membership feed up (broker-sourced channel membership) — space ${opts.space}`);
|
|
59
|
-
return handle;
|
|
89
|
+
return { handle };
|
|
60
90
|
}
|
|
61
91
|
//# sourceMappingURL=membership.js.map
|
package/dist/membership.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"membership.js","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAA+C,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"membership.js","sourceRoot":"","sources":["../src/membership.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotal-ai/delivery",
|
|
3
3
|
"description": "Cotal delivery daemon: the server-side Plane-3 durable backstop (fan-out writer + trusted reader + membership/ACL authority), a scoped least-privilege NATS client co-located with the broker.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@cotal-ai/core": "0.
|
|
22
|
-
"@cotal-ai/workspace": "0.
|
|
21
|
+
"@cotal-ai/core": "0.18.0",
|
|
22
|
+
"@cotal-ai/workspace": "0.18.0"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist"
|