@cotal-ai/core 0.58.0 → 0.60.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/acls.d.ts +7 -0
- package/dist/acls.d.ts.map +1 -1
- package/dist/acls.js +10 -0
- package/dist/acls.js.map +1 -1
- package/dist/auth-provider.d.ts +13 -1
- package/dist/auth-provider.d.ts.map +1 -1
- package/dist/auth-provider.js.map +1 -1
- package/dist/backup-config.d.ts +10 -2
- package/dist/backup-config.d.ts.map +1 -1
- package/dist/backup-config.js +12 -3
- package/dist/backup-config.js.map +1 -1
- package/dist/broker-tls.d.ts +15 -0
- package/dist/broker-tls.d.ts.map +1 -1
- package/dist/broker-tls.js +6 -6
- package/dist/broker-tls.js.map +1 -1
- package/dist/checkpoint-answer.d.ts +24 -0
- package/dist/checkpoint-answer.d.ts.map +1 -1
- package/dist/checkpoint-answer.js +45 -4
- package/dist/checkpoint-answer.js.map +1 -1
- package/dist/command.d.ts +4 -3
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +20 -4
- package/dist/command.js.map +1 -1
- package/dist/connector-setup.d.ts +27 -0
- package/dist/connector-setup.d.ts.map +1 -1
- package/dist/connector.d.ts +25 -3
- package/dist/connector.d.ts.map +1 -1
- package/dist/connector.js.map +1 -1
- package/dist/endpoint-reconcile.d.ts +5 -1
- package/dist/endpoint-reconcile.d.ts.map +1 -1
- package/dist/endpoint-reconcile.js +43 -21
- package/dist/endpoint-reconcile.js.map +1 -1
- package/dist/endpoint-service.d.ts +25 -0
- package/dist/endpoint-service.d.ts.map +1 -1
- package/dist/endpoint-service.js +25 -25
- package/dist/endpoint-service.js.map +1 -1
- package/dist/endpoint.d.ts +105 -8
- package/dist/endpoint.d.ts.map +1 -1
- package/dist/endpoint.js +349 -43
- package/dist/endpoint.js.map +1 -1
- package/dist/evict.d.ts +27 -0
- package/dist/evict.d.ts.map +1 -1
- package/dist/evict.js +99 -42
- package/dist/evict.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/issued-authority.d.ts +0 -2
- package/dist/issued-authority.d.ts.map +1 -1
- package/dist/issued-authority.js +0 -2
- package/dist/issued-authority.js.map +1 -1
- package/dist/launch-artifacts.d.ts +42 -0
- package/dist/launch-artifacts.d.ts.map +1 -0
- package/dist/launch-artifacts.js +155 -0
- package/dist/launch-artifacts.js.map +1 -0
- package/dist/launch.d.ts +5 -1
- package/dist/launch.d.ts.map +1 -1
- package/dist/lease.d.ts +5 -0
- package/dist/lease.d.ts.map +1 -1
- package/dist/lease.js.map +1 -1
- package/dist/lifecycle-state.d.ts +1 -1
- package/dist/lifecycle-state.js +3 -3
- package/dist/lifecycle-state.js.map +1 -1
- package/dist/liveness.d.ts +180 -0
- package/dist/liveness.d.ts.map +1 -0
- package/dist/liveness.js +114 -0
- package/dist/liveness.js.map +1 -0
- package/dist/provision.d.ts +24 -11
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +127 -42
- package/dist/provision.js.map +1 -1
- package/dist/remote-manager-authority.d.ts +109 -2
- package/dist/remote-manager-authority.d.ts.map +1 -1
- package/dist/remote-manager-authority.js +104 -0
- package/dist/remote-manager-authority.js.map +1 -1
- package/dist/run-driver-grants.d.ts +1 -0
- package/dist/run-driver-grants.d.ts.map +1 -1
- package/dist/run-driver-grants.js +3 -2
- package/dist/run-driver-grants.js.map +1 -1
- package/dist/run-host.d.ts +66 -2
- package/dist/run-host.d.ts.map +1 -1
- package/dist/run-journal.d.ts +14 -6
- package/dist/run-journal.d.ts.map +1 -1
- package/dist/run-journal.js +14 -14
- package/dist/run-journal.js.map +1 -1
- package/dist/run-record.d.ts +5 -5
- package/dist/runtime.d.ts +6 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/schema-profile.d.ts.map +1 -1
- package/dist/schema-profile.js +4 -3
- package/dist/schema-profile.js.map +1 -1
- package/dist/streams.d.ts +22 -16
- package/dist/streams.d.ts.map +1 -1
- package/dist/streams.js +26 -6
- package/dist/streams.js.map +1 -1
- package/dist/subjects.d.ts +65 -8
- package/dist/subjects.d.ts.map +1 -1
- package/dist/subjects.js +76 -9
- package/dist/subjects.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/liveness.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/** The two planes a peer may ask about. Deliberately a CLOSED set rather than a free string: the
|
|
2
|
+
* whole complaint is that liveness is unaskable, and the fix must not become a general-purpose
|
|
3
|
+
* probe rail that grows a third meaning later. `manager` is the lifecycle/control plane (its
|
|
4
|
+
* control moved to the v0.4 `service` endpoint at 1d); `delivery` is the server-side delivery
|
|
5
|
+
* daemon that serves `ctl.delivery`. */
|
|
6
|
+
export const LIVENESS_PLANES = Object.freeze(["manager", "delivery"]);
|
|
7
|
+
/** Is `plane` one this surface answers for? A closed-set guard, so an unrecognised plane is refused
|
|
8
|
+
* rather than answered with a default — a probe that answers every input is the "sweep that gives
|
|
9
|
+
* the same answer at every input" failure, and it would report health for planes that do not
|
|
10
|
+
* exist. */
|
|
11
|
+
export function isLivenessPlane(plane) {
|
|
12
|
+
return LIVENESS_PLANES.includes(plane);
|
|
13
|
+
}
|
|
14
|
+
/** Classify a lease record into responder state — #1594's pure classifier, moved here so the CLI
|
|
15
|
+
* status surfaces and this liveness surface grade by the SAME rule.
|
|
16
|
+
*
|
|
17
|
+
* `undefined` means the lease key is absent, which on a mesh that HAS a daemon recorded is an
|
|
18
|
+
* unbound responder rather than a missing opinion: the daemon creates its lease before it binds, so
|
|
19
|
+
* no lease at all means nothing has even claimed the slot.
|
|
20
|
+
*
|
|
21
|
+
* `expectedHolder` is not optional information when it is knowable. A daemon that is SIGKILLed
|
|
22
|
+
* leaves its `ready:true` record in the bucket for the rest of the bucket TTL, so `ready` alone
|
|
23
|
+
* answers "did SOME daemon bind recently enough that its record has not expired", which is not the
|
|
24
|
+
* question being asked. When the caller can name the daemon it means, a foreign or dead holder's
|
|
25
|
+
* record classifies as `stale` rather than as health. When it genuinely cannot know, pass
|
|
26
|
+
* `undefined` and the holder is not checked. */
|
|
27
|
+
export function responderFromLease(lease, expectedHolder) {
|
|
28
|
+
if (lease === undefined)
|
|
29
|
+
return "unbound";
|
|
30
|
+
if (lease.ready !== true)
|
|
31
|
+
return "unbound";
|
|
32
|
+
if (expectedHolder !== undefined && lease.holder !== expectedHolder)
|
|
33
|
+
return "stale";
|
|
34
|
+
return "bound";
|
|
35
|
+
}
|
|
36
|
+
/** Read the axis through a caller-supplied lease reader, mapping a FAILED read to `unknown` rather
|
|
37
|
+
* than to either health state. The reader is injected so offline surfaces (which must not open a
|
|
38
|
+
* connection) and connected ones share one classification without sharing a transport. */
|
|
39
|
+
export async function responderStateFromReader(readLease, expectedHolder) {
|
|
40
|
+
try {
|
|
41
|
+
return responderFromLease(await readLease(), expectedHolder);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return "unknown";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** THE ONE RULE that turns a probe outcome into a responder verdict, and the heart of #1577.
|
|
48
|
+
*
|
|
49
|
+
* ONLY `noResponders` may become `unbound`, because only it is the broker's own positive report
|
|
50
|
+
* that no responder is subscribed. `timeout`, `refused` and `malformed` ALL become `unknown`: each
|
|
51
|
+
* is a failure to find out, and this issue exists because failures to find out were being rendered
|
|
52
|
+
* as findings. A peer that cannot reach the broker learns that it cannot reach the broker.
|
|
53
|
+
*
|
|
54
|
+
* `replied` defers to the responder's OWN verdict, which it derived from its lease through
|
|
55
|
+
* {@link responderFromLease} — so a daemon that has claimed its slot but not bound its loops
|
|
56
|
+
* answers `unbound` about itself rather than being counted as alive merely because it answered. A
|
|
57
|
+
* reply carrying no readable verdict is `malformed`, hence `unknown`; it is not silently promoted
|
|
58
|
+
* to `bound` on the strength of having replied at all. That promotion is precisely the `pgrep`
|
|
59
|
+
* error — treating evidence that a process exists as evidence that it works. */
|
|
60
|
+
export function responderFromProbe(outcome, answered) {
|
|
61
|
+
if (outcome === "replied")
|
|
62
|
+
return answered ?? "unknown";
|
|
63
|
+
if (outcome === "noResponders")
|
|
64
|
+
return "unbound";
|
|
65
|
+
return "unknown";
|
|
66
|
+
}
|
|
67
|
+
/** Read a wire reply into an answer, or `undefined` if it is not one. Strict on purpose: an
|
|
68
|
+
* unrecognised `responder` value, a plane that does not match the one asked about, or a body of the
|
|
69
|
+
* wrong shape all return `undefined`, which the caller grades as `malformed` and therefore
|
|
70
|
+
* `unknown`. A lenient parse here would let a garbled or foreign reply be read as health, and this
|
|
71
|
+
* is the exact seam where "it said something" becomes "it said it was fine".
|
|
72
|
+
*
|
|
73
|
+
* `instance` IS STRICT TOO, FOR A DIFFERENT REASON THAN THE OTHER FIELDS. It is not the verdict, so
|
|
74
|
+
* a bad one cannot say "healthy" — but a caller compares tokens to tell two responders apart, and
|
|
75
|
+
* a value it cannot compare (a number, an object, an empty string) would silently read as "the same
|
|
76
|
+
* responder as the other answer that also had no usable token". That is a split rendered as
|
|
77
|
+
* agreement. A reply naming a responder in a form this surface cannot read is therefore `malformed`
|
|
78
|
+
* rather than an answer with the field dropped. A reply with NO `instance` at all is still
|
|
79
|
+
* readable: it is an answer from a responder that predates the field. */
|
|
80
|
+
export function parseLivenessAnswer(body, expectPlane) {
|
|
81
|
+
if (typeof body !== "object" || body === null)
|
|
82
|
+
return undefined;
|
|
83
|
+
const { plane, responder, instance } = body;
|
|
84
|
+
if (typeof plane !== "string" || !isLivenessPlane(plane) || plane !== expectPlane)
|
|
85
|
+
return undefined;
|
|
86
|
+
if (responder !== "bound" && responder !== "unbound" && responder !== "stale" && responder !== "unknown")
|
|
87
|
+
return undefined;
|
|
88
|
+
if (instance !== undefined && (typeof instance !== "string" || instance === ""))
|
|
89
|
+
return undefined;
|
|
90
|
+
return instance === undefined ? { plane, responder } : { plane, responder, instance };
|
|
91
|
+
}
|
|
92
|
+
/** The consequence of an unbound responder, in the operator's terms rather than the daemon's — per
|
|
93
|
+
* plane, because they break different things and the reporter's cost was diagnosis time, not
|
|
94
|
+
* ignorance that something was wrong. Purely presentational: no state, no identity. */
|
|
95
|
+
export function livenessConsequence(plane) {
|
|
96
|
+
return plane === "delivery"
|
|
97
|
+
? "no spawn, no retirement, no join until it binds"
|
|
98
|
+
: "no spawn, stop, attach or despawn until it binds";
|
|
99
|
+
}
|
|
100
|
+
/** Render one axis for a human, naming UNKNOWN as unchecked rather than as either health state.
|
|
101
|
+
* The `unknown` arm is the line this whole issue is about: it must never read as reassurance. */
|
|
102
|
+
export function livenessLine(answer) {
|
|
103
|
+
switch (answer.responder) {
|
|
104
|
+
case "bound":
|
|
105
|
+
return `${answer.plane}: responder bound`;
|
|
106
|
+
case "unbound":
|
|
107
|
+
return `${answer.plane}: RESPONDER NOT BOUND - ${livenessConsequence(answer.plane)}`;
|
|
108
|
+
case "stale":
|
|
109
|
+
return `${answer.plane}: RESPONDER NOT BOUND - a DEAD holder's ready record is still in the lease (it expires on its own) - ${livenessConsequence(answer.plane)}`;
|
|
110
|
+
case "unknown":
|
|
111
|
+
return `${answer.plane}: responder state UNCHECKED - the probe did not complete, so this is not a health report`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=liveness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveness.js","sourceRoot":"","sources":["../src/liveness.ts"],"names":[],"mappings":"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"}
|
package/dist/provision.d.ts
CHANGED
|
@@ -460,6 +460,19 @@ export declare function mintPublicUserJwt(auth: SpaceAuth, publicId: string, pro
|
|
|
460
460
|
jwt: string;
|
|
461
461
|
exp: number;
|
|
462
462
|
}>;
|
|
463
|
+
/** Build the NATS user permission object for a profile: a default-deny allow-list scoped to
|
|
464
|
+
* exactly what each profile does. Every profile is now enumerated least-privilege — the former
|
|
465
|
+
* allow-all `manager` is gone (its roles split across supervisor/provisioner/operator/purger and the
|
|
466
|
+
* PR 1.5 CLI-surface profiles). Subject/stream/durable names come from the shared builders so the ACLs
|
|
467
|
+
* can't drift from the wire layout.
|
|
468
|
+
*
|
|
469
|
+
* PRINCIPAL-PARAMETERIZED + MODE-AGNOSTIC (owner+actor grammar): `pr` carries the owner+actor wire
|
|
470
|
+
* principal (chat/inst/svc/ctl subjects, per-agent durables, the presence key all scope to it) PLUS the
|
|
471
|
+
* connection nkey (`pr.connId`, which scopes the private reply inbox `_INBOX_<connId>`). Core does NOT
|
|
472
|
+
* fork on dev-vs-user — the composition root supplies the principal: the auth callout passes the derived
|
|
473
|
+
* owner + ledger actor + the per-connection ephemeral nkey; the static/dev mint passes
|
|
474
|
+
* `{owner:"local", actor:<id>, connId:<id>}` via {@link principalOf}. EXPORTED so the callout's injected
|
|
475
|
+
* `permissionsFor` hook can feed a validated principal straight into the same builder. */
|
|
463
476
|
export declare function permissionsFor(profile: Profile, space: string, pr: MintPrincipal, opts: MintOpts): Record<string, unknown>;
|
|
464
477
|
/** Mint the scoped `membership-observer` creds — a SYSTEM-account user (conn A of the graph feed),
|
|
465
478
|
* signed with the in-memory `auth.sys.signingSeed` from a fresh {@link createSpaceAuth}. THROWS if that
|
|
@@ -472,17 +485,6 @@ export declare function mintMembershipObserverCreds(auth: SpaceAuth, identity: I
|
|
|
472
485
|
* eviction holds. Same mint-only-at-provision property as the observer (the $SYS seed is in-memory
|
|
473
486
|
* only), same fail-loud when it's absent. Paired with the observer at `up`. */
|
|
474
487
|
export declare function mintConnectionEvictorCreds(auth: SpaceAuth, identity: Identity, opts?: MintOpts): Promise<string>;
|
|
475
|
-
/** Render the `nats-server` config that trusts ONE broker operator and serves N spaces' accounts via
|
|
476
|
-
* the in-config MEMORY resolver.
|
|
477
|
-
*
|
|
478
|
-
* Broker trust (operator + system account) comes from `broker` and has exactly one owner; the
|
|
479
|
-
* per-space data accounts are listed in `spaces`. Every space account is asserted to be signed by
|
|
480
|
-
* THIS broker's operator before it is preloaded: rendering a foreign-signed account would either
|
|
481
|
-
* refuse broker boot or, worse, advertise a tenant the broker cannot actually authenticate.
|
|
482
|
-
*
|
|
483
|
-
* NOTE (W4): the MEMORY resolver is one static whole-broker map, so every mutation rewrites all of
|
|
484
|
-
* it. Concurrent add/remove of spaces needs a broker-authoritative inventory with generation/CAS
|
|
485
|
-
* and atomic promotion above this function; this renderer is deliberately pure. */
|
|
486
488
|
/**
|
|
487
489
|
* Render the config for an OPEN (no-auth) broker.
|
|
488
490
|
*
|
|
@@ -513,6 +515,17 @@ export declare function openServerConfig(opts: {
|
|
|
513
515
|
maxFileStore?: number;
|
|
514
516
|
transport: BrokerTransport;
|
|
515
517
|
}): string;
|
|
518
|
+
/** Render the `nats-server` config that trusts ONE broker operator and serves N spaces' accounts via
|
|
519
|
+
* the in-config MEMORY resolver.
|
|
520
|
+
*
|
|
521
|
+
* Broker trust (operator + system account) comes from `broker` and has exactly one owner; the
|
|
522
|
+
* per-space data accounts are listed in `spaces`. Every space account is asserted to be signed by
|
|
523
|
+
* THIS broker's operator before it is preloaded: rendering a foreign-signed account would either
|
|
524
|
+
* refuse broker boot or, worse, advertise a tenant the broker cannot actually authenticate.
|
|
525
|
+
*
|
|
526
|
+
* NOTE (W4): the MEMORY resolver is one static whole-broker map, so every mutation rewrites all of
|
|
527
|
+
* it. Concurrent add/remove of spaces needs a broker-authoritative inventory with generation/CAS
|
|
528
|
+
* and atomic promotion above this function; this renderer is deliberately pure. */
|
|
516
529
|
export declare function serverConfig(broker: BrokerAuth, spaces: readonly SpaceAccountAuth[], opts: {
|
|
517
530
|
port?: number;
|
|
518
531
|
host?: string;
|
package/dist/provision.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAmCL,KAAK,iBAAiB,
|
|
1
|
+
{"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../src/provision.ts"],"names":[],"mappings":"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"}
|
package/dist/provision.js
CHANGED
|
@@ -18,7 +18,8 @@ import { TTL_RECONCILE_CANARY_KEY, ttlBuckets } from "./streams.js";
|
|
|
18
18
|
import { join } from "node:path";
|
|
19
19
|
import { decode, encodeOperator, encodeAccount, encodeUser, fmtCreds, } from "@nats-io/jwt";
|
|
20
20
|
import { createOperator, createAccount, fromPublic, fromSeed } from "@nats-io/nkeys";
|
|
21
|
-
import { token, spacePrefix, chatSubject, assertValidChannel, channelInAllow, principalKey, parsePrincipalKey, deprovisionTargetPrincipal, principalTags, assertInboxConnId, DEV_OWNER, unicastSubject, anycastSubject, controlServiceSubject, CONTROL_DELIVERY, CONTROL_DELIVERY_ADMIN, artifactBucket, objectStoreStream, chatStream, dmStream, taskStream, dlvStream, inboxStream, chatHistDurable, dmDurable, taskDurable, dlvDurable, presenceBucket, channelBucket, membersBucket, aclBucket, aclKey, memberKey, assertLifecycleToken, membershipBucket, deliveryBucket, managerBucket, MANAGER_LEASE_KEY, MANAGER_RENEWAL_LEASE_KEY, connzRequestSubject, accountConnectSubject, accountDisconnectSubject, MEMBERSHIP_INBOX_PREFIX, FANOUT_DURABLE, INBOX_READER_DURABLE, } from "./subjects.js";
|
|
21
|
+
import { token, spacePrefix, chatSubject, assertValidChannel, channelInAllow, principalKey, parsePrincipalKey, deprovisionTargetPrincipal, principalTags, assertInboxConnId, DEV_OWNER, unicastSubject, anycastSubject, controlServiceSubject, CONTROL_DELIVERY, CONTROL_DELIVERY_ADMIN, artifactBucket, objectStoreStream, chatStream, dmStream, taskStream, dlvStream, inboxStream, chatHistDurable, dmDurable, taskDurable, dlvDurable, presenceBucket, channelBucket, membersBucket, aclBucket, aclKey, memberKey, assertLifecycleToken, membershipBucket, deliveryBucket, managerBucket, MANAGER_LEASE_KEY, MANAGER_RENEWAL_LEASE_KEY, connzRequestSubject, accountConnectSubject, accountDisconnectSubject, MEMBERSHIP_INBOX_PREFIX, FANOUT_DURABLE, INBOX_READER_DURABLE, livenessSubject, livenessServeFilter, livenessReplyGrant, } from "./subjects.js";
|
|
22
|
+
import { LIVENESS_PLANES } from "./liveness.js";
|
|
22
23
|
import { epCallerGrantRows, epServeGrantRows, epBaselineGrantRows, baselineCallerCapabilities, spawnCallerCapabilities, runCallerCapabilities, epRequestGrantRows, operatorInstrumentCapabilities, instanceOnlyManagerCapabilities, epDescribeAllGrantRow, BASELINE_LIFECYCLE_ENDPOINT, } from "./endpoint-grants.js";
|
|
23
24
|
import { assertServeGrantMintable, finalizeServeIssuance } from "./endpoint-service.js";
|
|
24
25
|
import { effectsBindGrants, poolOwnerBindGrants, goalWriterGrants, sessionLedgerGrants, epAuthBucket, sessionsBucket, epcStreamName, endpointPlaneStreamNames, eptReqStreamName, eptStreamName, hardenedAuthorityStreamNames, timerWriterDurable, timerWriterGrants } from "./endpoint-binding.js";
|
|
@@ -532,6 +533,9 @@ export async function mintPublicUserJwt(auth, publicId, profile, opts) {
|
|
|
532
533
|
}
|
|
533
534
|
return { jwt, exp: validDates.exp };
|
|
534
535
|
}
|
|
536
|
+
/** The profiles whose credential carries caller rails, and can therefore be minted as an
|
|
537
|
+
* issuance (SPEC 13.15). Everything else is infrastructure or a one-shot with no rail to bind. */
|
|
538
|
+
const ISSUABLE_PROFILES = new Set(["agent", "control-caller-privileged", "control-caller-admin", "deployer"]);
|
|
535
539
|
/** Build the NATS user permission object for a profile: a default-deny allow-list scoped to
|
|
536
540
|
* exactly what each profile does. Every profile is now enumerated least-privilege — the former
|
|
537
541
|
* allow-all `manager` is gone (its roles split across supervisor/provisioner/operator/purger and the
|
|
@@ -545,9 +549,6 @@ export async function mintPublicUserJwt(auth, publicId, profile, opts) {
|
|
|
545
549
|
* owner + ledger actor + the per-connection ephemeral nkey; the static/dev mint passes
|
|
546
550
|
* `{owner:"local", actor:<id>, connId:<id>}` via {@link principalOf}. EXPORTED so the callout's injected
|
|
547
551
|
* `permissionsFor` hook can feed a validated principal straight into the same builder. */
|
|
548
|
-
/** The profiles whose credential carries caller rails, and can therefore be minted as an
|
|
549
|
-
* issuance (SPEC 13.15). Everything else is infrastructure or a one-shot with no rail to bind. */
|
|
550
|
-
const ISSUABLE_PROFILES = new Set(["agent", "control-caller-privileged", "control-caller-admin", "deployer"]);
|
|
551
552
|
export function permissionsFor(profile, space, pr, opts) {
|
|
552
553
|
// Guard the connId BEFORE any profile builds `_INBOX_<connId>.>`: in user mode connId is a client-
|
|
553
554
|
// chosen nonce (untrusted), so a metacharacter here would escalate the inbox grant to every inbox.
|
|
@@ -843,6 +844,20 @@ export function permissionsFor(profile, space, pr, opts) {
|
|
|
843
844
|
// daemon (NOT the manager). The reply rides this same subtree (`ctl.delivery.<o>.<a>.reply.<n>`, in
|
|
844
845
|
// sub.allow below) so the daemon can answer without broad inbox-publish — see CONTROL_DELIVERY.
|
|
845
846
|
controlServiceSubject(space, CONTROL_DELIVERY, pr.owner, pr.actor),
|
|
847
|
+
// THE PEER-READABLE LIVENESS PROBE (#1577) — one publish row per plane, each pinned to THIS
|
|
848
|
+
// agent's own principal slots, so a peer asks as itself and can never forge a probe from
|
|
849
|
+
// another. This is the row that makes liveness askable by a NON-OWNER: without it, the only
|
|
850
|
+
// hypothesis a peer could form for any failure was "my credentials are wrong", because the
|
|
851
|
+
// subjects that answer "is the manager alive" were owner-only.
|
|
852
|
+
//
|
|
853
|
+
// WHAT THIS DOES NOT GRANT, which is the half that keeps it from being a back door. It is
|
|
854
|
+
// PUBLISH ON A REQUEST SUBJECT, not a read of anything. The agent gains NO grant on the manager
|
|
855
|
+
// bucket (still absent by deliberate omission, below) and NO new read of the delivery bucket.
|
|
856
|
+
// The answer it can obtain is one enum per plane, because the responder reduces the lease row
|
|
857
|
+
// before replying and the row never crosses the wire. It cannot subscribe the SERVE filter
|
|
858
|
+
// either: `live.<plane>.*.*` is not in `sub.allow`, so an agent cannot impersonate a responder
|
|
859
|
+
// and answer a peer's probe with a comforting lie.
|
|
860
|
+
...LIVENESS_PLANES.map((plane) => livenessSubject(space, plane, pr.owner, pr.actor)),
|
|
846
861
|
// JetStream control plane — scoped to this agent's own streams/durables.
|
|
847
862
|
"$JS.API.INFO",
|
|
848
863
|
// STREAM.INFO: CHAT (join watermark, recall drop-marker, channel-list counts — a documented
|
|
@@ -1017,9 +1032,15 @@ export function permissionsFor(profile, space, pr, opts) {
|
|
|
1017
1032
|
// Replies to this agent's durable join/leave/list requests ride `ctl.delivery.<o>.<a>.>` (NOT the
|
|
1018
1033
|
// per-id _INBOX), so the scoped delivery daemon can answer without broad inbox-publish.
|
|
1019
1034
|
const deliveryReplies = `${controlServiceSubject(space, CONTROL_DELIVERY, pr.owner, pr.actor)}.>`;
|
|
1035
|
+
// Liveness answers (#1577) ride the probe's OWN request subtree, exactly like the delivery
|
|
1036
|
+
// control replies above and for the same reason: the responder answers without needing broad
|
|
1037
|
+
// inbox-publish. One row per plane, pinned to this agent's principal, so it can hear answers to
|
|
1038
|
+
// ITS OWN probes and nothing else — it cannot subscribe a peer's reply lane and harvest answers
|
|
1039
|
+
// addressed elsewhere.
|
|
1040
|
+
const livenessReplies = LIVENESS_PLANES.map((plane) => `${livenessSubject(space, plane, pr.owner, pr.actor)}.>`);
|
|
1020
1041
|
// Manager control replies ride the v0.4 ep reply rail (in `epSub`, keyed on the caller triple) —
|
|
1021
1042
|
// the `ctl.<tier>.<id>.reply.>` subtrees are gone with the ctl rail (1d).
|
|
1022
|
-
return { pub: { allow: pubAllow, deny: pubDeny }, sub: { allow: [inbox, deliveryReplies, ...subChat, ...epSub] } };
|
|
1043
|
+
return { pub: { allow: pubAllow, deny: pubDeny }, sub: { allow: [inbox, deliveryReplies, ...livenessReplies, ...subChat, ...epSub] } };
|
|
1023
1044
|
}
|
|
1024
1045
|
/** One seat's manager control view. It carries no agent messaging or delivery surface, and every
|
|
1025
1046
|
* manager request, including describe, is pinned to one exact registered instance. */
|
|
@@ -1098,6 +1119,15 @@ function supervisorPermissions(space, pr) {
|
|
|
1098
1119
|
// files it requests `reloadCreds` here so adoption is an explicit, auditable event. Self-scoped
|
|
1099
1120
|
// request subject (its own owner+actor slots), bounded reply subtree in sub.allow below.
|
|
1100
1121
|
controlServiceSubject(space, CONTROL_DELIVERY_ADMIN, pr.owner, pr.actor),
|
|
1122
|
+
// LIVENESS REPLIES for the MANAGER plane (#1577), bounded to the `.reply.` leaf under a
|
|
1123
|
+
// caller's own request subject. The supervisor is the natural responder for this plane: it
|
|
1124
|
+
// is the process that HOLDS the manager lease, so it is the one that can answer the
|
|
1125
|
+
// question honestly without anyone else being granted a read of the lease row.
|
|
1126
|
+
//
|
|
1127
|
+
// REPLIES ONLY, exactly like the delivery daemon's `ctl.delivery.*.*.reply.>`. The row stops
|
|
1128
|
+
// at the leaf, so the supervisor cannot publish to the liveness REQUEST subjects themselves
|
|
1129
|
+
// and therefore cannot forge a probe from a peer.
|
|
1130
|
+
livenessReplyGrant(space, "manager"),
|
|
1101
1131
|
// #1694: a POINT READ of the delivery lease row, so the store-identity challenge can verify
|
|
1102
1132
|
// that the process which answered the queue-grouped admin rail is the holder that actually
|
|
1103
1133
|
// reloads the standing credentials. Without it the challenge can only report what some
|
|
@@ -1116,7 +1146,16 @@ function supervisorPermissions(space, pr) {
|
|
|
1116
1146
|
// Own reply inbox + the delivery-admin reply subtree for its OWN requests. NO chat/inst/dlv
|
|
1117
1147
|
// native sub (the supervisor reads no feed), NO manager control-tier serve (1d: that moved to
|
|
1118
1148
|
// the endpoint-serve credential), NO broad `$JS.>`/`$KV.>` (the residual-2 read/admin path is gone).
|
|
1119
|
-
|
|
1149
|
+
//
|
|
1150
|
+
// PLUS the manager-plane liveness SERVE filter (#1577): `live.manager.*.*`, queue-grouped, so
|
|
1151
|
+
// any credentialed peer's presence probe reaches the lease holder. This is a serve
|
|
1152
|
+
// subscription on a presence-only rail, not a widening of what the supervisor may read: the
|
|
1153
|
+
// request body is empty and the subject carries only the caller's own principal.
|
|
1154
|
+
allow: [
|
|
1155
|
+
`_INBOX_${pr.connId}.>`,
|
|
1156
|
+
`${controlServiceSubject(space, CONTROL_DELIVERY_ADMIN, pr.owner, pr.actor)}.>`,
|
|
1157
|
+
livenessServeFilter(space, "manager"),
|
|
1158
|
+
],
|
|
1120
1159
|
},
|
|
1121
1160
|
};
|
|
1122
1161
|
}
|
|
@@ -1143,6 +1182,12 @@ function remoteManagerPermissions(space, pr, pin) {
|
|
|
1143
1182
|
const gateKey = epgateKey("manager", iid);
|
|
1144
1183
|
const credPrefix = epcredFamilyPrefix("manager", iid);
|
|
1145
1184
|
const repairKey = eprepairKey("manager", iid);
|
|
1185
|
+
// The SUPERVISOR actor of the pair, which is the one that runs `Manager.start()` and therefore
|
|
1186
|
+
// the only one that binds the manager plane's liveness responder (`service.ts` hands
|
|
1187
|
+
// `credentials.supervisor` to the manager; the executor gets the instance-scoped registration
|
|
1188
|
+
// surface). The liveness rows below are pinned to it so the executor does not carry authority to
|
|
1189
|
+
// answer a probe it never serves.
|
|
1190
|
+
const isSupervisorActor = pin.actor === `manager_${iid}`;
|
|
1146
1191
|
const recordKeys = [
|
|
1147
1192
|
recordSpecKey(RECORD_KINDS.svc, ["manager", iid]),
|
|
1148
1193
|
recordStatusKey(RECORD_KINDS.svc, ["manager", iid]),
|
|
@@ -1177,9 +1222,35 @@ function remoteManagerPermissions(space, pr, pin) {
|
|
|
1177
1222
|
`$JS.API.CONSUMER.DELETE.KV_${AUTH}.>`,
|
|
1178
1223
|
...recordKeys.map((key) => `$JS.API.DIRECT.GET.KV_${REC}.$KV.${REC}.${key}`),
|
|
1179
1224
|
`$JS.API.STREAM.MSG.GET.KV_${REC}`,
|
|
1225
|
+
// LIVENESS REPLIES for the MANAGER plane (#1577), bounded to the `.reply.` leaf under a
|
|
1226
|
+
// caller's own request subject — the same row and the same bound as the local supervisor
|
|
1227
|
+
// profile holds.
|
|
1228
|
+
//
|
|
1229
|
+
// WHY THIS PROFILE NEEDS IT AT ALL. `Manager.start()` binds the manager plane's liveness
|
|
1230
|
+
// responder unconditionally, and a remote manager runs the same `start()` under this
|
|
1231
|
+
// credential. Without the serve subscription below the bind is denied, and a peer probing
|
|
1232
|
+
// the manager plane then gets the broker's own no-responders answer, which this surface
|
|
1233
|
+
// grades `unbound` — a definite verdict about the plane, produced by a gap in a credential,
|
|
1234
|
+
// while the remote manager is bound and serving. A surface that exists to stop a failure to
|
|
1235
|
+
// find out being rendered as a finding must not render its own missing grant as one.
|
|
1236
|
+
//
|
|
1237
|
+
// REPLIES ONLY. The row stops at the leaf, so a remote manager cannot publish to the
|
|
1238
|
+
// liveness REQUEST subjects and therefore cannot forge a probe that appears to come from a
|
|
1239
|
+
// peer, exactly as the supervisor row cannot.
|
|
1240
|
+
...(isSupervisorActor ? [livenessReplyGrant(space, "manager")] : []),
|
|
1241
|
+
],
|
|
1242
|
+
},
|
|
1243
|
+
sub: {
|
|
1244
|
+
allow: [
|
|
1245
|
+
`_INBOX_${pr.connId}.>`,
|
|
1246
|
+
// The manager-plane liveness SERVE filter (#1577): `live.manager.*.*`, queue-grouped, so a
|
|
1247
|
+
// credentialed peer's presence probe reaches this instance. A serve subscription on a
|
|
1248
|
+
// presence-only rail and not a widening of what a remote manager may read: the request body
|
|
1249
|
+
// is empty and the subject carries only the caller's own principal. The MANAGER plane only —
|
|
1250
|
+
// this credential cannot serve `delivery`, so each plane still answers for itself.
|
|
1251
|
+
...(isSupervisorActor ? [livenessServeFilter(space, "manager")] : []),
|
|
1180
1252
|
],
|
|
1181
1253
|
},
|
|
1182
|
-
sub: { allow: [`_INBOX_${pr.connId}.>`] },
|
|
1183
1254
|
};
|
|
1184
1255
|
}
|
|
1185
1256
|
/** The human-CLI OPERATOR permission set (closure (ii), residual 2) — the ephemeral key the headless
|
|
@@ -1718,17 +1789,6 @@ function provisionerPermissions(space, pr) {
|
|
|
1718
1789
|
sub: { allow: [`_INBOX_${pr.connId}.>`] },
|
|
1719
1790
|
};
|
|
1720
1791
|
}
|
|
1721
|
-
/** The ephemeral, LIFECYCLE-PINNED §13.1 state-write permission set for the STATIC manager's
|
|
1722
|
-
* lifecycle executor (Unit B). One credential per lifecycle OPERATION (activation, terminal,
|
|
1723
|
-
* renewal ledger append): every grant names exactly ONE incarnation's keys — the alias head,
|
|
1724
|
-
* the uid reservation, the manager slot row, the issuance gate, and the `cred.<uid>.>` ledger
|
|
1725
|
-
* family — so a leaked executor cred can move one incarnation's state machine and nothing else.
|
|
1726
|
-
*
|
|
1727
|
-
* Reads: records reads ride the keyed Direct Get form (the key is ON the subject, so the read
|
|
1728
|
-
* grant stays key-pinned); the auth store is leader-served (`allow_direct=false`), so its reads
|
|
1729
|
-
* are body-selected `STREAM.MSG.GET` — stream-scoped, NOT key-scoped (the requested key rides
|
|
1730
|
-
* the PAYLOAD, which a subject grant cannot see). NAMED RESIDUAL: for its one-shot lifetime the
|
|
1731
|
-
* executor can READ (never write) other rows in the auth store. */
|
|
1732
1792
|
/** The ISSUER permission set (SPEC 13.15): value-writes on the evidence store (evidence, attempt,
|
|
1733
1793
|
* source index: create-only and CAS rows, keyed by generation) and on the accepted-row store
|
|
1734
1794
|
* (create-only, keyed by the client's token); the leader-served point read on the evidence
|
|
@@ -1775,6 +1835,17 @@ function runAdmitterPermissions(space, pr, pin) {
|
|
|
1775
1835
|
sub: { allow: [`_INBOX_${pr.connId}.>`] },
|
|
1776
1836
|
};
|
|
1777
1837
|
}
|
|
1838
|
+
/** The ephemeral, LIFECYCLE-PINNED §13.1 state-write permission set for the STATIC manager's
|
|
1839
|
+
* lifecycle executor (Unit B). One credential per lifecycle OPERATION (activation, terminal,
|
|
1840
|
+
* renewal ledger append): every grant names exactly ONE incarnation's keys — the alias head,
|
|
1841
|
+
* the uid reservation, the manager slot row, the issuance gate, and the `cred.<uid>.>` ledger
|
|
1842
|
+
* family — so a leaked executor cred can move one incarnation's state machine and nothing else.
|
|
1843
|
+
*
|
|
1844
|
+
* Reads: records reads ride the keyed Direct Get form (the key is ON the subject, so the read
|
|
1845
|
+
* grant stays key-pinned); the auth store is leader-served (`allow_direct=false`), so its reads
|
|
1846
|
+
* are body-selected `STREAM.MSG.GET` — stream-scoped, NOT key-scoped (the requested key rides
|
|
1847
|
+
* the PAYLOAD, which a subject grant cannot see). NAMED RESIDUAL: for its one-shot lifetime the
|
|
1848
|
+
* executor can READ (never write) other rows in the auth store. */
|
|
1778
1849
|
function lifecycleExecutorPermissions(space, pr, pin) {
|
|
1779
1850
|
const REC = recordsBucket(space), AUTH = epAuthBucket(space);
|
|
1780
1851
|
// ALL keys DERIVED here from the pin coordinates — the slot key is `staticSlotKey(owner, alias)`,
|
|
@@ -1810,19 +1881,6 @@ function lifecycleExecutorPermissions(space, pr, pin) {
|
|
|
1810
1881
|
sub: { allow: [`_INBOX_${pr.connId}.>`] },
|
|
1811
1882
|
};
|
|
1812
1883
|
}
|
|
1813
|
-
/** The ephemeral, ENDPOINT-INSTANCE-PINNED endpoint-serve executor permission set (P2 item 1,
|
|
1814
|
-
* 1a-serve): the manager mints this per registration/serve-mint op and drives the endpoint
|
|
1815
|
-
* registration barrier's `epgate` CAS + the mint fence's `epcred` stage/revoke THROUGH it — never
|
|
1816
|
-
* its standing seed/supervisor connection (critic #1's manager-specific "no seed shortcut"). Every
|
|
1817
|
-
* WRITE is key-pinned to exactly ONE (endpoint, instanceId): the gate `epgate.<ep>.<iid>`, its
|
|
1818
|
-
* serving ledger family `epcred.<ep>.<iid>.>`, and the registration's two records keys (the
|
|
1819
|
-
* instance's `svc` spec + the endpoint's governance head — `registerServiceInstance` drives the
|
|
1820
|
-
* slot-take/promote over this same connection). A leaked/mis-constructed executor can move exactly
|
|
1821
|
-
* one endpoint instance's serve state and nothing else. The auth store is `allow_direct=false`, so
|
|
1822
|
-
* reads are leader-served `STREAM.MSG.GET` (stream-scoped, NOT key-scoped — the key rides the
|
|
1823
|
-
* payload); enumeration of the epcred family rides an ordered `keys()` consumer. NAMED RESIDUAL:
|
|
1824
|
-
* for its one-shot lifetime the executor can READ (never write) other auth rows — endpoint/
|
|
1825
|
-
* credential metadata, no bearer bytes; every WRITE stays key-pinned. */
|
|
1826
1884
|
/** The SELF-MEDIATED goal-writer profile (P2 item 2, spawn-as-action): exactly
|
|
1827
1885
|
* {@link goalWriterGrants} for ITS endpoint — the goal bind + terminal facts, the goal-record KV
|
|
1828
1886
|
* writes, and the leader-served fencing reads — plus the connection-scoped reply inbox. Disjoint
|
|
@@ -1880,6 +1938,19 @@ function sessionLedgerPermissions(space, pr) {
|
|
|
1880
1938
|
const g = sessionLedgerGrants(space, pr.connId);
|
|
1881
1939
|
return { pub: { allow: g.publish }, sub: { allow: g.subscribe } };
|
|
1882
1940
|
}
|
|
1941
|
+
/** The ephemeral, ENDPOINT-INSTANCE-PINNED endpoint-serve executor permission set (P2 item 1,
|
|
1942
|
+
* 1a-serve): the manager mints this per registration/serve-mint op and drives the endpoint
|
|
1943
|
+
* registration barrier's `epgate` CAS + the mint fence's `epcred` stage/revoke THROUGH it — never
|
|
1944
|
+
* its standing seed/supervisor connection (critic #1's manager-specific "no seed shortcut"). Every
|
|
1945
|
+
* WRITE is key-pinned to exactly ONE (endpoint, instanceId): the gate `epgate.<ep>.<iid>`, its
|
|
1946
|
+
* serving ledger family `epcred.<ep>.<iid>.>`, and the registration's two records keys (the
|
|
1947
|
+
* instance's `svc` spec + the endpoint's governance head — `registerServiceInstance` drives the
|
|
1948
|
+
* slot-take/promote over this same connection). A leaked/mis-constructed executor can move exactly
|
|
1949
|
+
* one endpoint instance's serve state and nothing else. The auth store is `allow_direct=false`, so
|
|
1950
|
+
* reads are leader-served `STREAM.MSG.GET` (stream-scoped, NOT key-scoped — the key rides the
|
|
1951
|
+
* payload); enumeration of the epcred family rides an ordered `keys()` consumer. NAMED RESIDUAL:
|
|
1952
|
+
* for its one-shot lifetime the executor can READ (never write) other auth rows — endpoint/
|
|
1953
|
+
* credential metadata, no bearer bytes; every WRITE stays key-pinned. */
|
|
1883
1954
|
function endpointServeExecutorPermissions(space, pr, pin) {
|
|
1884
1955
|
const AUTH = epAuthBucket(space), REC = recordsBucket(space);
|
|
1885
1956
|
// DERIVED from (endpoint, instanceId) via the core builders — never a caller literal.
|
|
@@ -2053,6 +2124,10 @@ function deliveryPermissions(space, pr) {
|
|
|
2053
2124
|
`$JS.API.CONSUMER.MSG.NEXT.${INBOX}.${INBOX_READER_DURABLE}`,
|
|
2054
2125
|
`$JS.API.CONSUMER.DELETE.${INBOX}.${INBOX_READER_DURABLE}`,
|
|
2055
2126
|
`$JS.ACK.${INBOX}.${INBOX_READER_DURABLE}.>`,
|
|
2127
|
+
// The reader removes an entry addressed to a retired lifecycle (its ACL row is tombstoned), so the
|
|
2128
|
+
// store does not keep what no reader can ever deliver. Nothing new in substance: the reader can
|
|
2129
|
+
// already ack any INBOX entry away.
|
|
2130
|
+
`$JS.API.STREAM.MSG.DELETE.${INBOX}`,
|
|
2056
2131
|
"$JS.FC.>", // ordered-consumer flow control
|
|
2057
2132
|
// Reads: presence (@mention resolve) + channel registry (delivery class) + members + ACL (re-auth).
|
|
2058
2133
|
...kvRead(PKV), ...kvRead(CHKV), ...kvRead(MKV), ...kvRead(AKV),
|
|
@@ -2098,11 +2173,21 @@ function deliveryPermissions(space, pr) {
|
|
|
2098
2173
|
// The privileged delivery-admin rail (D5 slice 5/6): same replies-only shape. Requests reach the
|
|
2099
2174
|
// daemon on the sub below; only the supervisor cred can PUBLISH them (nats-server is the boundary).
|
|
2100
2175
|
`${p}.ctl.delivery-admin.*.*.reply.>`,
|
|
2176
|
+
// LIVENESS REPLIES for the DELIVERY plane (#1577) — same replies-only shape as the two control
|
|
2177
|
+
// rails above. The daemon is the only process that can grade its own responder honestly: it
|
|
2178
|
+
// knows its own incarnation, so a predecessor's `ready:true` corpse in the lease classifies as
|
|
2179
|
+
// stale rather than as its own health. Bounded to the `.reply.` leaf, so it cannot publish to
|
|
2180
|
+
// the request subjects and forge a probe from a peer.
|
|
2181
|
+
livenessReplyGrant(space, "delivery"),
|
|
2101
2182
|
];
|
|
2102
2183
|
const sub = [
|
|
2103
2184
|
`_INBOX_${pr.connId}.>`,
|
|
2104
2185
|
`${p}.ctl.delivery.*.*`, // serve the delivery control service (queue-grouped; owner+actor caller slots)
|
|
2105
2186
|
`${p}.ctl.delivery-admin.*.*`, // serve the privileged admin rail (reloadCreds; eviction executor next)
|
|
2187
|
+
// Serve the delivery plane's liveness probe (#1577), queue-grouped. Presence-only: the handler
|
|
2188
|
+
// reduces the lease row to one enum before replying, so this subscription lets the daemon ANSWER
|
|
2189
|
+
// a peer without any peer gaining a read of the lease.
|
|
2190
|
+
livenessServeFilter(space, "delivery"),
|
|
2106
2191
|
];
|
|
2107
2192
|
return { pub: { allow: pub }, sub: { allow: sub } };
|
|
2108
2193
|
}
|
|
@@ -2204,17 +2289,6 @@ export async function mintConnectionEvictorCreds(auth, identity, opts = {}) {
|
|
|
2204
2289
|
const creds = fmtCreds(userJwt, fromSeed(new TextEncoder().encode(identity.seed)));
|
|
2205
2290
|
return new TextDecoder().decode(creds);
|
|
2206
2291
|
}
|
|
2207
|
-
/** Render the `nats-server` config that trusts ONE broker operator and serves N spaces' accounts via
|
|
2208
|
-
* the in-config MEMORY resolver.
|
|
2209
|
-
*
|
|
2210
|
-
* Broker trust (operator + system account) comes from `broker` and has exactly one owner; the
|
|
2211
|
-
* per-space data accounts are listed in `spaces`. Every space account is asserted to be signed by
|
|
2212
|
-
* THIS broker's operator before it is preloaded: rendering a foreign-signed account would either
|
|
2213
|
-
* refuse broker boot or, worse, advertise a tenant the broker cannot actually authenticate.
|
|
2214
|
-
*
|
|
2215
|
-
* NOTE (W4): the MEMORY resolver is one static whole-broker map, so every mutation rewrites all of
|
|
2216
|
-
* it. Concurrent add/remove of spaces needs a broker-authoritative inventory with generation/CAS
|
|
2217
|
-
* and atomic promotion above this function; this renderer is deliberately pure. */
|
|
2218
2292
|
/**
|
|
2219
2293
|
* Render the config for an OPEN (no-auth) broker.
|
|
2220
2294
|
*
|
|
@@ -2271,6 +2345,17 @@ function renderTlsBlock(transport) {
|
|
|
2271
2345
|
}
|
|
2272
2346
|
`;
|
|
2273
2347
|
}
|
|
2348
|
+
/** Render the `nats-server` config that trusts ONE broker operator and serves N spaces' accounts via
|
|
2349
|
+
* the in-config MEMORY resolver.
|
|
2350
|
+
*
|
|
2351
|
+
* Broker trust (operator + system account) comes from `broker` and has exactly one owner; the
|
|
2352
|
+
* per-space data accounts are listed in `spaces`. Every space account is asserted to be signed by
|
|
2353
|
+
* THIS broker's operator before it is preloaded: rendering a foreign-signed account would either
|
|
2354
|
+
* refuse broker boot or, worse, advertise a tenant the broker cannot actually authenticate.
|
|
2355
|
+
*
|
|
2356
|
+
* NOTE (W4): the MEMORY resolver is one static whole-broker map, so every mutation rewrites all of
|
|
2357
|
+
* it. Concurrent add/remove of spaces needs a broker-authoritative inventory with generation/CAS
|
|
2358
|
+
* and atomic promotion above this function; this renderer is deliberately pure. */
|
|
2274
2359
|
export function serverConfig(broker, spaces, opts) {
|
|
2275
2360
|
if (!spaces.length)
|
|
2276
2361
|
throw new Error("serverConfig: at least one space account is required");
|