@cotal-ai/core 0.58.0 → 0.59.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.
Files changed (94) hide show
  1. package/dist/acls.d.ts +7 -0
  2. package/dist/acls.d.ts.map +1 -1
  3. package/dist/acls.js +10 -0
  4. package/dist/acls.js.map +1 -1
  5. package/dist/auth-provider.d.ts +13 -1
  6. package/dist/auth-provider.d.ts.map +1 -1
  7. package/dist/auth-provider.js.map +1 -1
  8. package/dist/backup-config.d.ts +10 -2
  9. package/dist/backup-config.d.ts.map +1 -1
  10. package/dist/backup-config.js +12 -3
  11. package/dist/backup-config.js.map +1 -1
  12. package/dist/broker-tls.d.ts +15 -0
  13. package/dist/broker-tls.d.ts.map +1 -1
  14. package/dist/broker-tls.js +6 -6
  15. package/dist/broker-tls.js.map +1 -1
  16. package/dist/checkpoint-answer.d.ts +24 -0
  17. package/dist/checkpoint-answer.d.ts.map +1 -1
  18. package/dist/checkpoint-answer.js +45 -4
  19. package/dist/checkpoint-answer.js.map +1 -1
  20. package/dist/command.d.ts +4 -3
  21. package/dist/command.d.ts.map +1 -1
  22. package/dist/command.js +20 -4
  23. package/dist/command.js.map +1 -1
  24. package/dist/connector-setup.d.ts +27 -0
  25. package/dist/connector-setup.d.ts.map +1 -1
  26. package/dist/connector.d.ts +12 -0
  27. package/dist/connector.d.ts.map +1 -1
  28. package/dist/connector.js.map +1 -1
  29. package/dist/endpoint-reconcile.d.ts +5 -1
  30. package/dist/endpoint-reconcile.d.ts.map +1 -1
  31. package/dist/endpoint-reconcile.js +43 -21
  32. package/dist/endpoint-reconcile.js.map +1 -1
  33. package/dist/endpoint-service.d.ts +25 -0
  34. package/dist/endpoint-service.d.ts.map +1 -1
  35. package/dist/endpoint-service.js +25 -25
  36. package/dist/endpoint-service.js.map +1 -1
  37. package/dist/endpoint.d.ts +105 -8
  38. package/dist/endpoint.d.ts.map +1 -1
  39. package/dist/endpoint.js +349 -43
  40. package/dist/endpoint.js.map +1 -1
  41. package/dist/evict.d.ts +27 -0
  42. package/dist/evict.d.ts.map +1 -1
  43. package/dist/evict.js +95 -38
  44. package/dist/evict.js.map +1 -1
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +2 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/issued-authority.d.ts +0 -2
  50. package/dist/issued-authority.d.ts.map +1 -1
  51. package/dist/issued-authority.js +0 -2
  52. package/dist/issued-authority.js.map +1 -1
  53. package/dist/launch-artifacts.d.ts +42 -0
  54. package/dist/launch-artifacts.d.ts.map +1 -0
  55. package/dist/launch-artifacts.js +155 -0
  56. package/dist/launch-artifacts.js.map +1 -0
  57. package/dist/lease.d.ts +5 -0
  58. package/dist/lease.d.ts.map +1 -1
  59. package/dist/lease.js.map +1 -1
  60. package/dist/liveness.d.ts +180 -0
  61. package/dist/liveness.d.ts.map +1 -0
  62. package/dist/liveness.js +114 -0
  63. package/dist/liveness.js.map +1 -0
  64. package/dist/provision.d.ts +24 -11
  65. package/dist/provision.d.ts.map +1 -1
  66. package/dist/provision.js +127 -42
  67. package/dist/provision.js.map +1 -1
  68. package/dist/remote-manager-authority.d.ts +73 -2
  69. package/dist/remote-manager-authority.d.ts.map +1 -1
  70. package/dist/remote-manager-authority.js +104 -0
  71. package/dist/remote-manager-authority.js.map +1 -1
  72. package/dist/run-host.d.ts +66 -2
  73. package/dist/run-host.d.ts.map +1 -1
  74. package/dist/run-journal.d.ts +14 -6
  75. package/dist/run-journal.d.ts.map +1 -1
  76. package/dist/run-journal.js +14 -14
  77. package/dist/run-journal.js.map +1 -1
  78. package/dist/run-record.d.ts +5 -5
  79. package/dist/runtime.d.ts +6 -1
  80. package/dist/runtime.d.ts.map +1 -1
  81. package/dist/schema-profile.d.ts.map +1 -1
  82. package/dist/schema-profile.js +4 -3
  83. package/dist/schema-profile.js.map +1 -1
  84. package/dist/streams.d.ts +22 -16
  85. package/dist/streams.d.ts.map +1 -1
  86. package/dist/streams.js +26 -6
  87. package/dist/streams.js.map +1 -1
  88. package/dist/subjects.d.ts +45 -5
  89. package/dist/subjects.d.ts.map +1 -1
  90. package/dist/subjects.js +52 -0
  91. package/dist/subjects.js.map +1 -1
  92. package/dist/types.d.ts +4 -0
  93. package/dist/types.d.ts.map +1 -1
  94. package/package.json +1 -1
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
- allow: [`_INBOX_${pr.connId}.>`, `${controlServiceSubject(space, CONTROL_DELIVERY_ADMIN, pr.owner, pr.actor)}.>`],
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");