@cotal-ai/core 0.68.0 → 0.70.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 (112) hide show
  1. package/dist/agent-file.d.ts.map +1 -1
  2. package/dist/agent-file.js +8 -6
  3. package/dist/agent-file.js.map +1 -1
  4. package/dist/auth-provider.d.ts +0 -1
  5. package/dist/auth-provider.d.ts.map +1 -1
  6. package/dist/connector-config.d.ts +4 -0
  7. package/dist/connector-config.d.ts.map +1 -1
  8. package/dist/connector-config.js +49 -0
  9. package/dist/connector-config.js.map +1 -1
  10. package/dist/contract-manifest.d.ts +20 -0
  11. package/dist/contract-manifest.d.ts.map +1 -0
  12. package/dist/contract-manifest.js +28 -0
  13. package/dist/contract-manifest.js.map +1 -0
  14. package/dist/endpoint-binding.d.ts.map +1 -1
  15. package/dist/endpoint-binding.js +5 -0
  16. package/dist/endpoint-binding.js.map +1 -1
  17. package/dist/endpoint-cluster.d.ts.map +1 -1
  18. package/dist/endpoint-cluster.js +9 -4
  19. package/dist/endpoint-cluster.js.map +1 -1
  20. package/dist/endpoint-contract-store.d.ts +2 -18
  21. package/dist/endpoint-contract-store.d.ts.map +1 -1
  22. package/dist/endpoint-contract-store.js +3 -19
  23. package/dist/endpoint-contract-store.js.map +1 -1
  24. package/dist/endpoint-envelope.d.ts +6 -2
  25. package/dist/endpoint-envelope.d.ts.map +1 -1
  26. package/dist/endpoint-envelope.js +19 -2
  27. package/dist/endpoint-envelope.js.map +1 -1
  28. package/dist/endpoint-error.d.ts +13 -0
  29. package/dist/endpoint-error.d.ts.map +1 -1
  30. package/dist/endpoint-error.js +15 -0
  31. package/dist/endpoint-error.js.map +1 -1
  32. package/dist/endpoint-invoke.d.ts +26 -0
  33. package/dist/endpoint-invoke.d.ts.map +1 -1
  34. package/dist/endpoint-invoke.js +60 -4
  35. package/dist/endpoint-invoke.js.map +1 -1
  36. package/dist/endpoint-serve-kv.d.ts +0 -1
  37. package/dist/endpoint-serve-kv.d.ts.map +1 -1
  38. package/dist/endpoint-serve-kv.js +0 -1
  39. package/dist/endpoint-serve-kv.js.map +1 -1
  40. package/dist/endpoint-serve.d.ts.map +1 -1
  41. package/dist/endpoint-serve.js +4 -3
  42. package/dist/endpoint-serve.js.map +1 -1
  43. package/dist/endpoint-service.d.ts +12 -6
  44. package/dist/endpoint-service.d.ts.map +1 -1
  45. package/dist/endpoint-service.js +29 -23
  46. package/dist/endpoint-service.js.map +1 -1
  47. package/dist/endpoint-verbs.d.ts.map +1 -1
  48. package/dist/endpoint-verbs.js +2 -1
  49. package/dist/endpoint-verbs.js.map +1 -1
  50. package/dist/endpoint.d.ts +31 -22
  51. package/dist/endpoint.d.ts.map +1 -1
  52. package/dist/endpoint.js +117 -90
  53. package/dist/endpoint.js.map +1 -1
  54. package/dist/evict.d.ts.map +1 -1
  55. package/dist/evict.js +2 -1
  56. package/dist/evict.js.map +1 -1
  57. package/dist/identity.d.ts +3 -0
  58. package/dist/identity.d.ts.map +1 -1
  59. package/dist/identity.js +5 -0
  60. package/dist/identity.js.map +1 -1
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +2 -0
  64. package/dist/index.js.map +1 -1
  65. package/dist/kv-scan.d.ts +1 -3
  66. package/dist/kv-scan.d.ts.map +1 -1
  67. package/dist/kv-scan.js.map +1 -1
  68. package/dist/loopback.d.ts +8 -0
  69. package/dist/loopback.d.ts.map +1 -0
  70. package/dist/loopback.js +17 -0
  71. package/dist/loopback.js.map +1 -0
  72. package/dist/managed-handoff.d.ts.map +1 -1
  73. package/dist/managed-handoff.js +1 -18
  74. package/dist/managed-handoff.js.map +1 -1
  75. package/dist/presence-ages.d.ts +22 -0
  76. package/dist/presence-ages.d.ts.map +1 -0
  77. package/dist/presence-ages.js +33 -0
  78. package/dist/presence-ages.js.map +1 -0
  79. package/dist/provision.d.ts +23 -28
  80. package/dist/provision.d.ts.map +1 -1
  81. package/dist/provision.js +112 -126
  82. package/dist/provision.js.map +1 -1
  83. package/dist/remote-manager-authority.d.ts +30 -7
  84. package/dist/remote-manager-authority.d.ts.map +1 -1
  85. package/dist/remote-manager-authority.js +23 -15
  86. package/dist/remote-manager-authority.js.map +1 -1
  87. package/dist/resolve.d.ts +15 -2
  88. package/dist/resolve.d.ts.map +1 -1
  89. package/dist/resolve.js +15 -2
  90. package/dist/resolve.js.map +1 -1
  91. package/dist/run-journal.d.ts.map +1 -1
  92. package/dist/run-journal.js +17 -2
  93. package/dist/run-journal.js.map +1 -1
  94. package/dist/run-record.d.ts +6 -8
  95. package/dist/run-record.d.ts.map +1 -1
  96. package/dist/run-record.js.map +1 -1
  97. package/dist/schema-profile.d.ts +11 -0
  98. package/dist/schema-profile.d.ts.map +1 -1
  99. package/dist/schema-profile.js +13 -2
  100. package/dist/schema-profile.js.map +1 -1
  101. package/dist/secret-store.d.ts +6 -2
  102. package/dist/secret-store.d.ts.map +1 -1
  103. package/dist/secret-store.js +9 -6
  104. package/dist/secret-store.js.map +1 -1
  105. package/dist/streams.d.ts.map +1 -1
  106. package/dist/streams.js +0 -1
  107. package/dist/streams.js.map +1 -1
  108. package/dist/subjects.d.ts +5 -0
  109. package/dist/subjects.d.ts.map +1 -1
  110. package/dist/subjects.js +11 -0
  111. package/dist/subjects.js.map +1 -1
  112. package/package.json +1 -1
package/dist/provision.js CHANGED
@@ -6,19 +6,19 @@
6
6
  * `@nats-io/jwt`, so there is no dependency on the external `nsc` CLI and the signing
7
7
  * key stays in one place (whoever holds {@link SpaceAuth.account.signingSeed}).
8
8
  *
9
- * Demo-1 stage: out-of-band mint. `cotal up` creates the space's trust material
9
+ * The static mint is out of band: `cotal up` creates the space's trust material
10
10
  * once and writes a `nats-server` config (operator + system account + MEMORY resolver);
11
- * `cotal mint` and the manager load that material and mint per-agent creds files. There
12
- * is no connect-time token exchange yet (that's the later auth-callout stage).
11
+ * `cotal mint` and the manager load that material and mint per-agent creds files. The auth
12
+ * callout mints at connect time through the same {@link permissionsFor}.
13
13
  *
14
- * D5 adds the first credential-death primitive: profile-classified user-JWT lifetimes. Full revocation,
15
- * live eviction, standing-host renewal, and issuance audit still land in later D5 slices.
14
+ * Every credential kind is classified in {@link CREDENTIAL_LIFETIMES}: its lifetime class, default
15
+ * expiry and renewal owner.
16
16
  */
17
17
  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, livenessSubject, livenessServeFilter, livenessReplyGrant, isTransferStream, } from "./subjects.js";
21
+ import { token, spacePrefix, chatSubject, assertValidChannel, resolveReadAcl, 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, isTransferStream, } from "./subjects.js";
22
22
  import { LIVENESS_PLANES } from "./liveness.js";
23
23
  import { epCallerGrantRows, epServeGrantRows, epBaselineGrantRows, baselineCallerCapabilities, spawnCallerCapabilities, runCallerCapabilities, epRequestGrantRows, operatorInstrumentCapabilities, instanceOnlyManagerCapabilities, epDescribeAllGrantRow, BASELINE_LIFECYCLE_ENDPOINT, } from "./endpoint-grants.js";
24
24
  import { assertServeGrantMintable, finalizeServeIssuance } from "./endpoint-service.js";
@@ -31,7 +31,7 @@ import { transferReaderGrants, transferWriterGrants } from "./transfer.js";
31
31
  import { recordsBucket, recordSpecKey, recordStatusKey, recordAtomicKey, RECORD_KINDS, GOVERN_HEAD } from "./endpoint-records.js";
32
32
  import { lifecycleHeadKey, uidReservationKey, issuanceGateKey, staticSlotKey, STATIC_SLOT_PREFIX, epgateKey, epcredFamilyPrefix, eprepairKey } from "./lifecycle-state.js";
33
33
  import { rawDigest } from "./canonical.js";
34
- import { credsClaims } from "./identity.js";
34
+ import { credsClaims, isUserNkey } from "./identity.js";
35
35
  import { backupProfilePermissions, restoreProfilePermissions, } from "./backup.js";
36
36
  const FIVE_MINUTES = 5 * 60;
37
37
  /** The broker's pre-auth control-line cap, in bytes (`max_control_line` in the generated config —
@@ -57,7 +57,7 @@ export const CONNECT_ENVELOPE_OVERHEAD_BYTES = 512;
57
57
  * (issue #375's review finding: six distinct 4096-char channels, each individually accepted,
58
58
  * minted a JWT the broker would silently drop). */
59
59
  export const MAX_MINTED_JWT_BYTES = MAX_CONTROL_LINE_BYTES - CONNECT_ENVELOPE_OVERHEAD_BYTES;
60
- /** Bounded lifetime for `standing-renewable` credentials whose renewal owner is ONLINE (D5 slice 5):
60
+ /** Bounded lifetime for `standing-renewable` credentials whose renewal owner is ONLINE:
61
61
  * the holder (or its launcher) re-mints at 75% of the lifetime via the endpoint's creds-source seam,
62
62
  * so a copied cred is broker-dead within a day while renewal never involves an operator. 24h keeps
63
63
  * the remaining-25% loud-failure window at ~6h — wide enough to notice and repair before expiry. */
@@ -66,19 +66,19 @@ export const STANDING_RENEWABLE_TTL_SEC = 24 * 60 * 60;
66
66
  * evictor). They are NOT online-renewable (the $SYS seed dies at end of `up`), so this exp is the
67
67
  * credential-death horizon: a copied observer/evictor cred becomes broker-dead after it, and the
68
68
  * operator is expected to have run a coordinated system-account rotation + broker restart within it
69
- * (the doctor surface warns ahead — slice 6). 30 days balances "copied cred eventually dies" against
69
+ * (the doctor surface warns ahead). 30 days balances "copied cred eventually dies" against
70
70
  * a comfortable monthly rotation cadence; tune here as one named knob. */
71
71
  export const ROTATION_RENEWED_TTL_SEC = 30 * 24 * 60 * 60;
72
- /** D5 profile matrix. This is intentionally centralized so every new mint profile must classify its
72
+ /** The credential lifetime matrix. This is intentionally centralized so every new mint profile must classify its
73
73
  * credential-death behavior instead of silently inheriting non-expiring static creds. */
74
74
  export const CREDENTIAL_LIFETIMES = {
75
75
  agent: { class: "mixed", note: "manager children, foreground spawn/join, and cotal mint static outputs all use this profile; split or repair flow required before default exp" },
76
76
  "manager-caller": { class: "mixed", note: "short-lived user-auth view bound to the bearer expiry and one manager instance" },
77
77
  observer: { class: "static-operator-managed", note: "out-of-band dashboard/audit credential from cotal mint" },
78
78
  admin: { class: "static-operator-managed", note: "out-of-band elevated dashboard/audit credential from cotal mint" },
79
- supervisor: { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "manager's always-on endpoint; the manager holds the DATA seed and self-remints via the endpoint creds source (D5 slice 5 class 1)" },
80
- delivery: { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "server-side Plane-3 daemon; seed-less - the manager re-signs .cotal/delivery.creds for the SAME nkey, requests delivery-admin reloadCreds for explicit adoption, and the endpoint source re-read is only a backstop (D5 slice 5 class 2)" },
81
- "membership-rw": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "membership feed writer; seed-less - the manager re-signs the membership-rw.creds store key for the SAME nkey, the feed adopts it on a 75% preflight-proven renewal timer (its active self-heal), and delivery-admin reloadCreds is the explicit adoption on top (D5 slice 5 class 2)" },
79
+ supervisor: { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "manager's always-on endpoint; the manager holds the DATA seed and self-remints via the endpoint creds source" },
80
+ delivery: { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "server-side Plane-3 daemon; seed-less - the manager re-signs .cotal/delivery.creds for the SAME nkey, requests delivery-admin reloadCreds for explicit adoption, and the endpoint source re-read is only a backstop" },
81
+ "membership-rw": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "membership feed writer; seed-less - the manager re-signs the membership-rw.creds store key for the SAME nkey, the feed adopts it on a 75% preflight-proven renewal timer (its active self-heal), and delivery-admin reloadCreds is the explicit adoption on top" },
82
82
  provisioner: { class: "one-shot", defaultTtlSeconds: FIVE_MINUTES, note: "setup/spawn provisioning window only" },
83
83
  deprovisioner: { class: "one-shot", defaultTtlSeconds: FIVE_MINUTES, note: "target-pinned teardown window only" },
84
84
  "retirement-requester": { class: "one-shot", defaultTtlSeconds: FIVE_MINUTES, note: "one despawn's retirement request window; request+reply only" },
@@ -96,10 +96,10 @@ export const CREDENTIAL_LIFETIMES = {
96
96
  "control-caller-admin": { class: "one-shot", defaultTtlSeconds: FIVE_MINUTES, note: "stop/attach admin control call" },
97
97
  deployer: { class: "one-shot", note: "manifest deploy spans planning/launch/ledger; needs near-expiry guard or remint before default exp" },
98
98
  "endpoint-serve": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "per-instance endpoint serve credential (SPEC 13.9); the managing authority re-mints on renewal and on takeover (new epoch), and the 13.1 barrier revokes the superseded one" },
99
- "goal-writer": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "self-mediated goal-writer for spawn-as-action (P2 item 2); the manager re-mints for the SAME nkey on renewal, disjoint from the serve credential (Q2)" },
100
- "session-caller": { class: "one-shot", defaultTtlSeconds: 24 * 60 * 60, note: "per-session console/CLI caller cred (P2 item 6): rails-only for ONE §13.6 session; TTL-BOUND to the session (the face mints with expiresAt = the session exp; the 24h default is the SESSION_GRANT_MAX_TTL ceiling, never a standing lifetime); NEVER renewed - a new session mints a new cred" },
101
- "session-serving": { class: "one-shot", defaultTtlSeconds: 24 * 60 * 60, note: "per-session SERVING cred (P2 item 6): rails-only for ONE §13.6 session, the mirror of session-caller with the directions swapped; minted at redemption and TTL-BOUND to the session (the 24h default is the SESSION_GRANT_MAX_TTL ceiling, never a standing lifetime); NEVER renewed - a new session mints a new cred, and the session's terminal revokes this one by name" },
102
- "session-ledger": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "manager's session LEDGER (P2 item 6): the dedicated sessions-bucket `session.<id>` rows and NOTHING else - no session rail of any shape. Standing because SPEC 13.6 makes it the durable revocation authority that must survive the serving endpoint; the manager re-mints for the SAME nkey on the half-TTL loop (the goal-writer precedent)" },
99
+ "goal-writer": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "self-mediated goal-writer for spawn-as-action; the manager re-mints for the SAME nkey on renewal, disjoint from the serve credential" },
100
+ "session-caller": { class: "one-shot", defaultTtlSeconds: 24 * 60 * 60, note: "per-session console/CLI caller cred: rails-only for ONE §13.6 session; TTL-BOUND to the session (the face mints with expiresAt = the session exp; the 24h default is the SESSION_GRANT_MAX_TTL ceiling, never a standing lifetime); NEVER renewed - a new session mints a new cred" },
101
+ "session-serving": { class: "one-shot", defaultTtlSeconds: 24 * 60 * 60, note: "per-session SERVING cred: rails-only for ONE §13.6 session, the mirror of session-caller with the directions swapped; minted at redemption and TTL-BOUND to the session (the 24h default is the SESSION_GRANT_MAX_TTL ceiling, never a standing lifetime); NEVER renewed - a new session mints a new cred, and the session's terminal revokes this one by name" },
102
+ "session-ledger": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "manager's session LEDGER: the dedicated sessions-bucket `session.<id>` rows and NOTHING else - no session rail of any shape. Standing because SPEC 13.6 makes it the durable revocation authority that must survive the serving endpoint; the manager re-mints for the SAME nkey on the half-TTL loop (the goal-writer precedent)" },
103
103
  "run-driver": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "one workflow run's driver, per takeover attempt (SPEC 14.6): the hosting manager mints it when it takes the run over and re-mints for the SAME nkey on renewal; a new takeover mints a new one" },
104
104
  "run-mediator": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "manager", note: "trusted workflow host operations, bound to one run and attempt; kept on the hosting process's connection" },
105
105
  "run-operator": { class: "one-shot", defaultTtlSeconds: 60, note: "one served run-status / run-ps / run-answer call (SPEC 14.3): the hosting manager mints it per call on its own connection, never the serve rails; 60s bounds a copied cred to a minute" },
@@ -107,10 +107,10 @@ export const CREDENTIAL_LIFETIMES = {
107
107
  "run-admitter": { class: "one-shot", defaultTtlSeconds: 60, note: "one hosted run's admission record create, or its revocation marker (SPEC 14.8); 60s bounds a copied cred to a minute" },
108
108
  "transfer-writer": { class: "one-shot", defaultTtlSeconds: FIVE_MINUTES, note: "one carried resume per CLI call: one object's chunk and meta subjects in one instance's transfer bucket" },
109
109
  "transfer-reader": { class: "one-shot", defaultTtlSeconds: FIVE_MINUTES, note: "one transcript-receive or sweep over the minting instance's own transfer bucket" },
110
- "endpoint-evictor": { class: "one-shot", defaultTtlSeconds: 60, note: "one re-registration's verify-evict window (P2 item 3): a scoped delivery-admin caller that kicks+verifies the SUPERSEDED serve family before the epoch advances; 60s bounds a copied cred to a minute" },
110
+ "endpoint-evictor": { class: "one-shot", defaultTtlSeconds: 60, note: "one re-registration's verify-evict window: a scoped delivery-admin caller that kicks+verifies the SUPERSEDED serve family before the epoch advances; 60s bounds a copied cred to a minute" },
111
111
  "remote-manager": { class: "standing-renewable", defaultTtlSeconds: STANDING_RENEWABLE_TTL_SEC, renewalOwner: "auth-service", note: "the scoped remote manager lifecycle: own lease/presence plus same-owner agent provisioning; issued only by the typed supervise protocol, never by cotal mint or a raw view/profile string" },
112
112
  "membership-observer": { class: "rotation-renewed", defaultTtlSeconds: ROTATION_RENEWED_TTL_SEC, renewalOwner: "system-account rotation", note: "$SYS-account CONNZ observer; NOT online-renewable ($SYS seed dies at `up`) - bounded exp, renewed only by rotateSystemAccount + broker restart; doctor warns near expiry" },
113
- "connection-evictor": { class: "rotation-renewed", defaultTtlSeconds: ROTATION_RENEWED_TTL_SEC, renewalOwner: "system-account rotation", note: "$SYS-account KICK-only live-eviction cred (D5 slice 4); same rotation-renewed posture as the observer" },
113
+ "connection-evictor": { class: "rotation-renewed", defaultTtlSeconds: ROTATION_RENEWED_TTL_SEC, renewalOwner: "system-account rotation", note: "$SYS-account KICK-only live-eviction cred; same rotation-renewed posture as the observer" },
114
114
  };
115
115
  // RUNTIME integrity (the afa715b class): the matrix and every policy freeze at module load
116
116
  // (a post-import `defaultTtlSeconds = undefined` would otherwise mint a NON-EXPIRING credential
@@ -198,8 +198,8 @@ export function stripSpaceAuth(auth) {
198
198
  };
199
199
  }
200
200
  /** Rotate the DATA-account signing key and re-issue the data-account JWT so the old data signer is no
201
- * longer trusted by the broker once it loads the returned auth. This does NOT rotate the system account:
202
- * persisted `membership-observer` creds remain valid until the system-account renewal/rotation slice. */
201
+ * longer trusted by the broker once it loads the returned auth. This does NOT rotate the system account
202
+ * (that is {@link rotateSystemAccount}): persisted `membership-observer` creds stay valid. */
203
203
  export async function rotateDataAccountSigningKey(auth) {
204
204
  if (!auth.operator.seed || !auth.account.seed)
205
205
  throw new Error("rotateDataAccountSigningKey: full operator/account seed material is required (a stripped signer cannot rotate trust)");
@@ -291,7 +291,7 @@ export async function createSpaceAccountAuth(broker, space) {
291
291
  }
292
292
  /** Generate a fresh operator → account(+signing key) → system-account chain for a space.
293
293
  * The single-space composition of {@link createBrokerAuth} + {@link createSpaceAccountAuth}: one
294
- * broker whose only tenant is this space, which is exactly the pre-W4 shape. */
294
+ * broker whose only tenant is this space. */
295
295
  export async function createSpaceAuth(space) {
296
296
  const broker = await createBrokerAuth(space);
297
297
  const spaceAccount = await createSpaceAccountAuth(broker, space);
@@ -343,16 +343,6 @@ export async function provisionAgent(provisioner, auth, identity, opts = {}) {
343
343
  const allowSubscribe = await provisionAgentDurables(provisioner, { owner: pr.owner, actor: pr.actor, lifecycleUid: opts.lifecycleUid }, opts);
344
344
  return mintCreds(auth, identity, "agent", { ...opts, allowSubscribe });
345
345
  }
346
- /** The read ACL an agent's lists resolve to: an omitted or empty `allowSubscribe` reads exactly
347
- * `subscribe`. Throws when a subscription lies outside it. A delegated intent's admission resolves
348
- * here too, so its dry walk checks the list this writer provisions. */
349
- export function resolveReadAcl(subscribe, allowSubscribe) {
350
- const allow = allowSubscribe?.length ? allowSubscribe : subscribe;
351
- for (const ch of subscribe)
352
- if (!channelInAllow(allow, ch))
353
- throw new Error(`subscribe "${ch}" is not within allowSubscribe [${allow.join(", ")}]`);
354
- return allow;
355
- }
356
346
  /** The DURABLE half of agent onboarding, principal-keyed and credential-agnostic: pre-create the
357
347
  * bind-only DM + DELIVER durables, record the read ACL, ensure the role TASK queue. The static
358
348
  * path ({@link provisionAgent}) follows it with a mint; the USER-MODE spawn path runs it alone —
@@ -382,7 +372,7 @@ export async function provisionAgentDurables(provisioner, pr, opts = {}) {
382
372
  // reissueAcl — rides THIS mint: allowSubscribe was just baked into the user JWT above. Separate
383
373
  // from commitAcl so an ordinary registry writer cannot raise the ceiling by passing a flag
384
374
  // (SPEC §9.6). Not crypto-bound to the JWT bytes — process discipline that this call stays next
385
- // to the mint (ACL-authority panel residual).
375
+ // to the mint.
386
376
  if (opts.durableMembership !== false) {
387
377
  await provisioner.reissueAcl(principalKey(pr.owner, pr.actor).key, uid, allowSubscribe);
388
378
  }
@@ -502,7 +492,7 @@ async function releaseIssuance(space, profile, pr, opts, perms, exp, credentialI
502
492
  * lifetime builders. It signs with the space account's signing key alone, so a host that holds
503
493
  * only that key passes no placeholder broker trust. */
504
494
  export async function mintPublicUserJwt(auth, publicId, profile, opts) {
505
- if (!/^U[A-Z2-7]{55}$/.test(publicId))
495
+ if (!isUserNkey(publicId))
506
496
  throw new Error("mintPublicUserJwt: publicId must be a user nkey");
507
497
  if (!["remote-manager", "endpoint-serve", "goal-writer", "session-ledger", "session-serving", "retirement-requester", "run-driver", "run-mediator", "run-operator", "transfer-reader"].includes(profile))
508
498
  throw new Error(`mintPublicUserJwt: profile "${profile}" is not part of the closed remote manager protocol`);
@@ -550,7 +540,7 @@ const ISSUABLE_PROFILES = new Set(["agent", "control-caller-privileged", "contro
550
540
  /** Build the NATS user permission object for a profile: a default-deny allow-list scoped to
551
541
  * exactly what each profile does. Every profile is now enumerated least-privilege — the former
552
542
  * allow-all `manager` is gone (its roles split across supervisor/provisioner/operator/purger and the
553
- * PR 1.5 CLI-surface profiles). Subject/stream/durable names come from the shared builders so the ACLs
543
+ * CLI-surface profiles). Subject/stream/durable names come from the shared builders so the ACLs
554
544
  * can't drift from the wire layout.
555
545
  *
556
546
  * PRINCIPAL-PARAMETERIZED + MODE-AGNOSTIC (owner+actor grammar): `pr` carries the owner+actor wire
@@ -582,11 +572,11 @@ export function permissionsFor(profile, space, pr, opts) {
582
572
  if (profile === "membership-rw")
583
573
  return membershipRwPermissions(space, pr); // scoped graph-feed reader/writer
584
574
  if (profile === "supervisor")
585
- return supervisorPermissions(space, pr); // always-on daemon (closure (ii) gate)
575
+ return supervisorPermissions(space, pr); // always-on daemon
586
576
  if (profile === "provisioner")
587
- return provisionerPermissions(space, pr); // ephemeral onboarding authority (closure (ii))
577
+ return provisionerPermissions(space, pr); // ephemeral onboarding authority
588
578
  if (profile === "deprovisioner") {
589
- // Ephemeral, TARGET-PINNED teardown (#159 B) — the counterpart to `provisioner`. The target is a
579
+ // Ephemeral, TARGET-PINNED teardown — the counterpart to `provisioner`. The target is a
590
580
  // full principal dot-form for user-mode agents, or a bare static/dev actor id (keyed under
591
581
  // DEV_OWNER) — see {@link deprovisionTargetPrincipal}.
592
582
  if (!opts.deprovisionTarget)
@@ -594,7 +584,7 @@ export function permissionsFor(profile, space, pr, opts) {
594
584
  return deprovisionerPermissions(space, pr, opts.deprovisionTarget);
595
585
  }
596
586
  if (profile === "retirement-requester") {
597
- // Ephemeral request+reply on the AUTH ENDPOINT rail (#29 piece 3; moved off `ctl` by #350):
587
+ // Ephemeral request+reply on the AUTH ENDPOINT rail:
598
588
  // publish EXACTLY this caller triple's own request subject for the ONE target it names +
599
589
  // subscribe its own reply-plane filter and inbox. No store reads, no barrier/scanner/plane
600
590
  // authority - the requester only asks; the auth plane holds every executing right and
@@ -664,7 +654,7 @@ export function permissionsFor(profile, space, pr, opts) {
664
654
  return { pub: { allow: g.publish }, sub: { allow: g.subscribe } };
665
655
  }
666
656
  if (profile === "endpoint-evictor") {
667
- // P2 item 3 (slice 3a): a SCOPED delivery-admin caller for ONE re-registration's verify-evict.
657
+ // A SCOPED delivery-admin caller for ONE re-registration's verify-evict.
668
658
  // Publish EXACTLY this credential's OWN delivery-admin control subject + $JS.API.INFO; subscribe
669
659
  // its own reply subtree + inbox. NO lease, presence, store read, consumer, KV, or executing
670
660
  // right — the daemon does the $SYS scan/KICK (subject-gated authority, like the supervisor evictor
@@ -689,7 +679,7 @@ export function permissionsFor(profile, space, pr, opts) {
689
679
  return goalWriterPermissions(space, pr, opts.goalWriter);
690
680
  }
691
681
  if (profile === "purger")
692
- return purgerPermissions(space, pr); // ephemeral history-purge (closure (ii))
682
+ return purgerPermissions(space, pr); // ephemeral history-purge
693
683
  if (profile === "backup") {
694
684
  if (!opts.backup)
695
685
  throw new Error("permissionsFor: backup requires opts.backup");
@@ -701,27 +691,27 @@ export function permissionsFor(profile, space, pr, opts) {
701
691
  return restoreProfilePermissions(space, pr.connId, opts.restore);
702
692
  }
703
693
  if (profile === "operator")
704
- return operatorPermissions(space, pr); // human-CLI client (send/dm/ask) (closure (ii))
694
+ return operatorPermissions(space, pr); // human-CLI client (send/dm/ask)
705
695
  if (profile === "probe")
706
- return probePermissions(pr); // connect-only liveness/auth preflight (PR 1.5)
696
+ return probePermissions(pr); // connect-only liveness/auth preflight
707
697
  if (profile === "channel-writer")
708
- return channelWriterPermissions(space, pr); // channel-registry writes (PR 1.5)
698
+ return channelWriterPermissions(space, pr); // channel-registry writes
709
699
  if (profile === "channel-purger")
710
- return channelPurgerPermissions(space, pr); // channel-writer + CHAT purge (PR 1.5)
700
+ return channelPurgerPermissions(space, pr); // channel-writer + CHAT purge
711
701
  if (profile === "teardown")
712
- return teardownPermissions(space, pr, opts.transferStreams ?? []); // sole STREAM.DELETE holder (PR 1.5)
702
+ return teardownPermissions(space, pr, opts.transferStreams ?? []); // sole STREAM.DELETE holder
713
703
  if (profile === "control-caller-privileged")
714
- return controlCallerPermissions(space, pr, "privileged", opts); // ps/start reads (PR 1.5)
704
+ return controlCallerPermissions(space, pr, "privileged", opts); // ps/start reads
715
705
  if (profile === "control-caller-admin")
716
- return controlCallerPermissions(space, pr, "admin", opts); // any-mode stop/attach (PR 1.5)
706
+ return controlCallerPermissions(space, pr, "admin", opts); // any-mode stop/attach
717
707
  if (profile === "deployer")
718
- return deployerPermissions(space, pr, opts.controlTier ?? "admin", opts); // spawn -f deploy authority (PR 1.5; user-mode view rides privileged)
708
+ return deployerPermissions(space, pr, opts.controlTier ?? "admin", opts); // spawn -f deploy authority (user-mode view rides privileged)
719
709
  if (profile === "session-caller")
720
- return sessionCallerPermissions(space, pr, opts.sessionCaller); // one §13.6 session's caller rails (P2 item 6)
710
+ return sessionCallerPermissions(space, pr, opts.sessionCaller); // one §13.6 session's caller rails
721
711
  if (profile === "session-serving")
722
- return sessionServingPermissions(space, pr, opts.sessionServing); // one §13.6 session's SERVING rails (P2 item 6)
712
+ return sessionServingPermissions(space, pr, opts.sessionServing); // one §13.6 session's SERVING rails
723
713
  if (profile === "session-ledger")
724
- return sessionLedgerPermissions(space, pr); // the dedicated session ledger, no rails (P2 item 6)
714
+ return sessionLedgerPermissions(space, pr); // the dedicated session ledger, no rails
725
715
  if (profile === "run-driver") {
726
716
  if (!opts.runDriver)
727
717
  throw new Error("permissionsFor: run-driver requires opts.runDriver ({endpoint, runId, takeoverId, instanceId, epoch} of the ONE run and attempt it drives)");
@@ -849,7 +839,7 @@ export function permissionsFor(profile, space, pr, opts) {
849
839
  unicastSubject(space, "*", "*", pr.owner, pr.actor), // inst.*.*.<o>.<a> — DM any instance, as me
850
840
  anycastSubject(space, "*", pr.owner, pr.actor), // svc.*.<o>.<a> — anycast any role, as me
851
841
  // Self stop/despawn rides the v0.4 ep baseline (`stop` self-mode) — the manager `ctl` rail is
852
- // deleted (1d). The delivery-daemon rail below is a separate service (Plane-3), kept.
842
+ // deleted. The delivery-daemon rail below is a separate service (Plane-3), kept.
853
843
  // ctl.delivery.<o>.<a> — request a durable backstop join/leave/list from the SERVER-SIDE delivery
854
844
  // daemon (NOT the manager). The reply rides this same subtree (`ctl.delivery.<o>.<a>.reply.<n>`, in
855
845
  // sub.allow below) so the daemon can answer without broad inbox-publish — see CONTROL_DELIVERY.
@@ -937,7 +927,7 @@ export function permissionsFor(profile, space, pr, opts) {
937
927
  pubAllow.push(`$JS.API.STREAM.INFO.${TASK}`, `$JS.API.CONSUMER.INFO.${TASK}.${svcD}`, `$JS.API.CONSUMER.MSG.NEXT.${TASK}.${svcD}`, `$JS.ACK.${TASK}.${svcD}.>`);
938
928
  }
939
929
  // Spawn / admin capability control reach rides the v0.4 ep rows below (the manager `ctl` rail is
940
- // deleted, 1d) — the spawn set (owner-mode manager lifecycle) and the admin instrument set
930
+ // deleted) — the spawn set (owner-mode manager lifecycle) and the admin instrument set
941
931
  // (any-mode + manager.admin family) are added to the ep caller rows, not a ctl subject.
942
932
  // v0.4 endpoint rails (SPEC §13.9 caller rows). EVERY agent gets the Appendix-B BASELINE set
943
933
  // (wildcard describe + delivery join/leave/list + self-mode lifecycle + the reply rail), keyed
@@ -985,7 +975,7 @@ export function permissionsFor(profile, space, pr, opts) {
985
975
  epSub.push(s);
986
976
  }
987
977
  if (opts.capabilities?.includes("admin"))
988
- // The admin capability's ep mirror (the 1c grant-migration table): the v0.3 `ctl.<admin>`
978
+ // The admin capability's ep mirror: the v0.3 `ctl.<admin>`
989
979
  // subject above grants the FULL admin-tier op reach, so its holder gets the admin instrument
990
980
  // set on the ep rails — any-mode despawn/attach + the `manager.admin` family + the reads. In
991
981
  // user mode this is the ledger `admin` scope arriving via the callout, the broker-enforced
@@ -1042,7 +1032,7 @@ export function permissionsFor(profile, space, pr, opts) {
1042
1032
  // addressed elsewhere.
1043
1033
  const livenessReplies = LIVENESS_PLANES.map((plane) => `${livenessSubject(space, plane, pr.owner, pr.actor)}.>`);
1044
1034
  // Manager control replies ride the v0.4 ep reply rail (in `epSub`, keyed on the caller triple) —
1045
- // the `ctl.<tier>.<id>.reply.>` subtrees are gone with the ctl rail (1d).
1035
+ // the `ctl.<tier>.<id>.reply.>` subtrees are gone with the ctl rail.
1046
1036
  return { pub: { allow: pubAllow, deny: pubDeny }, sub: { allow: [inbox, deliveryReplies, ...livenessReplies, ...subChat, ...epSub] } };
1047
1037
  }
1048
1038
  /** One seat's manager control view. It carries no agent messaging or delivery surface, and every
@@ -1074,10 +1064,10 @@ function managerCallerPermissions(space, pr, opts) {
1074
1064
  sub: { allow: [`_INBOX_${pr.connId}.>`, ...rows.sub] },
1075
1065
  };
1076
1066
  }
1077
- /** The long-lived SUPERVISOR permission set (closure (ii), residual 2) — the always-on manager daemon
1078
- * (`manager.ts` `this.ep`), carved down from the former allow-all `manager`. THIS is the cred whose
1079
- * STANDING breadth was the residual-2 gate: tightening it removes the always-on DM/DLV body-read AND the
1080
- * stream-admin tamper from the one connection that never goes away. It does exactly three things — serve
1067
+ /** The long-lived SUPERVISOR permission set — the always-on manager daemon
1068
+ * (`manager.ts` `this.ep`), carved down from the former allow-all `manager`. Its breadth is
1069
+ * STANDING, so tightening it removes the always-on DM/DLV body-read AND the stream-admin tamper
1070
+ * from the one connection that never goes away. It does exactly three things — serve
1081
1071
  * the three lifecycle control tiers (bounded replies), hold the singleton manager lease, and publish +
1082
1072
  * watch presence (the roster) — and nothing else. Provisioning (DM/DLV/TASK consumer-create + ACL
1083
1073
  * writes) moves to the EPHEMERAL `provisioner` (opened per-spawn); destructive history-purge moves to the
@@ -1090,7 +1080,7 @@ function managerCallerPermissions(space, pr, opts) {
1090
1080
  function supervisorPermissions(space, pr) {
1091
1081
  const PKV = `KV_${presenceBucket(space)}`, MKV = `KV_${managerBucket(space)}`;
1092
1082
  const SUP_DLVKV = `KV_${deliveryBucket(space)}`;
1093
- // 1d: the supervisor no longer serves the manager control tiers — the manager's control surface
1083
+ // The supervisor no longer serves the manager control tiers — the manager's control surface
1094
1084
  // is its v0.4 `service` endpoint, served on a SEPARATE connection under its own `endpoint-serve`
1095
1085
  // credential (its rails are that credential's grant, not the supervisor's). The supervisor now
1096
1086
  // holds only the lease, presence, and the ONE delivery-admin call.
@@ -1099,18 +1089,17 @@ function supervisorPermissions(space, pr) {
1099
1089
  allow: [
1100
1090
  "$JS.API.INFO",
1101
1091
  // Per-instance manager liveness lease (managerBucket, pre-created at `cotal up`): OPEN-ONLY bind +
1102
- // CAS this instance's own `lease.<instanceId>` key (acquire/renew/release) + read the subtree. P2
1103
- // item 3 demoted the per-space singleton to per-instance keys, so the write grant spans `lease.*`
1104
- // (every instance of this space shares this one supervisor principal — the isolation between
1105
- // instances is the logical-id/CAS boundary, not a cred boundary). NO STREAM.CREATE (pre-created),
1106
- // DELETE, or PURGE.
1092
+ // CAS this instance's own `lease.<instanceId>` key (acquire/renew/release) + read the subtree. The
1093
+ // lease is per instance, so the write grant spans `lease.*` (every instance of this space
1094
+ // shares this one supervisor principal — the isolation between instances is the logical-id/CAS
1095
+ // boundary, not a cred boundary). NO STREAM.CREATE (pre-created), DELETE, or PURGE.
1107
1096
  `$JS.API.STREAM.INFO.${MKV}`,
1108
1097
  `$JS.API.STREAM.MSG.GET.${MKV}`, // readManagerLease (last_by_subj lease.*) + CAS-conflict kv.get
1109
1098
  `$KV.${managerBucket(space)}.${MANAGER_LEASE_KEY}.*`, // this instance's lease.<id> key (create/update/delete = $KV publishes)
1110
1099
  // #1634: the per-space daemon-credential renewal lease, the CAS that picks ONE renewal owner
1111
1100
  // when several managers share the daemon's store. Same bucket, its own key outside `lease.*`.
1112
1101
  `$KV.${managerBucket(space)}.${MANAGER_RENEWAL_LEASE_KEY}`,
1113
- // Presence: publish OWN key + watch the roster. Own key only (no peer-key forge — residual 3); no
1102
+ // Presence: publish OWN key + watch the roster. Own key only (no peer-key forge); no
1114
1103
  // presence-stream purge/delete (no force-offline tamper). No presence kv.get (roster is the in-memory
1115
1104
  // watch cache + sweep), so no STREAM.MSG.GET on presence.
1116
1105
  `$KV.${presenceBucket(space)}.${principalKey(pr.owner, pr.actor).key}`,
@@ -1118,8 +1107,8 @@ function supervisorPermissions(space, pr) {
1118
1107
  `$JS.API.CONSUMER.CREATE.${PKV}.>`, // kv.watch ordered consumer (roster)
1119
1108
  `$JS.API.CONSUMER.INFO.${PKV}.>`,
1120
1109
  "$JS.FC.>", // ordered-consumer flow control
1121
- // The ONE control service the supervisor CALLS (D5 slice 5): the delivery daemon's privileged
1122
- // admin rail — the manager is the class-2 renewal owner, and after re-signing the daemon creds
1110
+ // The ONE control service the supervisor CALLS: the delivery daemon's privileged
1111
+ // admin rail — the manager is the daemon's renewal owner, and after re-signing the daemon creds
1123
1112
  // files it requests `reloadCreds` here so adoption is an explicit, auditable event. Self-scoped
1124
1113
  // request subject (its own owner+actor slots), bounded reply subtree in sub.allow below.
1125
1114
  controlServiceSubject(space, CONTROL_DELIVERY_ADMIN, pr.owner, pr.actor),
@@ -1148,8 +1137,8 @@ function supervisorPermissions(space, pr) {
1148
1137
  },
1149
1138
  sub: {
1150
1139
  // Own reply inbox + the delivery-admin reply subtree for its OWN requests. NO chat/inst/dlv
1151
- // native sub (the supervisor reads no feed), NO manager control-tier serve (1d: that moved to
1152
- // the endpoint-serve credential), NO broad `$JS.>`/`$KV.>` (the residual-2 read/admin path is gone).
1140
+ // native sub (the supervisor reads no feed), NO manager control-tier serve (that is
1141
+ // the endpoint-serve credential's), NO broad `$JS.>`/`$KV.>`.
1153
1142
  //
1154
1143
  // PLUS the manager-plane liveness SERVE filter (#1577): `live.manager.*.*`, queue-grouped, so
1155
1144
  // any credentialed peer's presence probe reaches the lease holder. This is a serve
@@ -1257,7 +1246,7 @@ function remoteManagerPermissions(space, pr, pin) {
1257
1246
  },
1258
1247
  };
1259
1248
  }
1260
- /** The human-CLI OPERATOR permission set (closure (ii), residual 2) — the ephemeral key the headless
1249
+ /** The human-CLI OPERATOR permission set — the ephemeral key the headless
1261
1250
  * client commands mint (`cotal send dm|msg|ask`, `cotal dm`, `personas list --running`, via
1262
1251
  * `openTransient`). It does exactly what those do: POST as itself (chat/DM/anycast — self-scoped, can
1263
1252
  * never forge another actor), and READ the public roster (presence) + the channel registry to resolve a
@@ -1300,7 +1289,7 @@ function operatorPermissions(space, pr) {
1300
1289
  sub: { allow: [`_INBOX_${pr.connId}.>`] },
1301
1290
  };
1302
1291
  }
1303
- /** Connect-only PROBE (PR 1.5) — the liveness/auth preflight (`preflight.ts preflightTarget`, minted on
1292
+ /** Connect-only PROBE — the liveness/auth preflight (`preflight.ts preflightTarget`, minted on
1304
1293
  * ~every CLI command that resolves a mesh). `probeConnect` opens a connection to prove the broker is up
1305
1294
  * and the creds are accepted, then closes it — it performs NO pub/sub. So the tightest possible grant:
1306
1295
  * deny ALL publish, subscribe only to the own reply inbox. A leaked probe cred can open a socket and do
@@ -1308,7 +1297,7 @@ function operatorPermissions(space, pr) {
1308
1297
  function probePermissions(pr) {
1309
1298
  return { pub: { deny: [">"] }, sub: { allow: [`_INBOX_${pr.connId}.>`] } };
1310
1299
  }
1311
- /** CHANNEL-WRITER (PR 1.5) — edits the channel registry ONLY: `cotal channels set/default` and the
1300
+ /** CHANNEL-WRITER — edits the channel registry ONLY: `cotal channels set/default` and the
1312
1301
  * `spawn -f` new-channel seed (`seedChannelRegistry`). It VALUE-writes `$KV.<channelBucket>` (a channel's
1313
1302
  * config key) and read-before-writes it. NO stream data, NO other bucket, NO chat/DM — a leaked
1314
1303
  * channel-writer can only rewrite channel config, never post, read a body, or tear a stream down. */
@@ -1329,7 +1318,7 @@ function channelWriterPermissions(space, pr) {
1329
1318
  sub: { allow: [`_INBOX_${pr.connId}.>`] },
1330
1319
  };
1331
1320
  }
1332
- /** CHANNEL-PURGER (PR 1.5) — the `cotal web` dashboard's ONLY write path: delete a channel
1321
+ /** CHANNEL-PURGER — the `cotal web` dashboard's ONLY write path: delete a channel
1333
1322
  * (`clearChannel` = filtered `STREAM.PURGE.CHAT` to drop the channel's messages + a `$KV.<channelBucket>`
1334
1323
  * key delete). Pre-minted once by `web` so the account signing seed falls out of scope; the dashboard's
1335
1324
  * READ side runs on the separate read-only `admin` cred. = channel-writer + the scoped CHAT purge. */
@@ -1352,8 +1341,8 @@ function channelPurgerPermissions(space, pr) {
1352
1341
  sub: { allow: [`_INBOX_${pr.connId}.>`] },
1353
1342
  };
1354
1343
  }
1355
- /** TEARDOWN (PR 1.5) — `cotal down -f` space teardown. The SOLE cred that keeps `STREAM.DELETE` (the
1356
- * face-b tamper verb). `down -f` is multi-step: `connectProbe` (presence-watch + channel-registry read)
1344
+ /** TEARDOWN — `cotal down -f` space teardown. The SOLE cred that keeps `STREAM.DELETE` (the
1345
+ * tamper verb). `down -f` is multi-step: `connectProbe` (presence-watch + channel-registry read)
1357
1346
  * → invoke the manager's `ps` + any-mode `despawn` over the ep rails to politely stop the managed
1358
1347
  * agents → `deleteChannels`
1359
1348
  * (channel-registry key delete + CHAT purge) → `deleteSpace` (STREAM.DELETE every owned stream/bucket).
@@ -1362,7 +1351,7 @@ function channelPurgerPermissions(space, pr) {
1362
1351
  * can delete a stream; a leaked teardown can wipe a space you own + stop its agents (that IS its job),
1363
1352
  * nothing else. Minted ephemerally per teardown from the local trust material (same-checkout `down -f`). */
1364
1353
  function teardownPermissions(space, pr, transferStreams) {
1365
- // The ep-rail mirror of the admin deploy tier (1c.2c): teardown reads `ps` and stops owned agents
1354
+ // The ep-rail mirror of the admin deploy tier: teardown reads `ps` and stops owned agents
1366
1355
  // it did not spawn (any-mode despawn) - the admin instrument set. Lifecycle-keyed, so a uid is
1367
1356
  // required at mint (fail-loud).
1368
1357
  const ep = instrumentEpRows(space, pr, "admin");
@@ -1370,7 +1359,7 @@ function teardownPermissions(space, pr, transferStreams) {
1370
1359
  const PKV = `KV_${presenceBucket(space)}`, CHKV = `KV_${channelBucket(space)}`;
1371
1360
  // deleteSpace() deletes EVERY stream + KV bucket setup creates; each needs INFO (jsm existence)
1372
1361
  // + DELETE. This is the ONLY cred that
1373
- // holds STREAM.DELETE (face-b isolated here). This list and deleteSpace()'s own array must agree:
1362
+ // holds STREAM.DELETE. This list and deleteSpace()'s own array must agree:
1374
1363
  // a stream in one and not the other is either an undeletable leak or a grant for nothing.
1375
1364
  const del = [
1376
1365
  CHAT, dmStream(space), taskStream(space), inboxStream(space), dlvStream(space),
@@ -1400,7 +1389,7 @@ function teardownPermissions(space, pr, transferStreams) {
1400
1389
  `$JS.API.CONSUMER.INFO.${CHKV}.>`,
1401
1390
  "$JS.FC.>", // ordered-consumer flow control
1402
1391
  // Stop the managed agents over the v0.4 ep rails only (ps + any-mode despawn) — the admin
1403
- // instrument set. The manager `ctl` rail is deleted (1d).
1392
+ // instrument set. The manager `ctl` rail is deleted.
1404
1393
  ...ep.pub,
1405
1394
  ...del,
1406
1395
  // deleteChannels/clearChannel: purge the channel's chat messages + delete its registry key.
@@ -1409,16 +1398,16 @@ function teardownPermissions(space, pr, transferStreams) {
1409
1398
  ],
1410
1399
  },
1411
1400
  // Own inbox (connectProbe presence-watch delivery + JS API responses) + the ep reply rail (the
1412
- // ps + any-mode despawn calls reply there). The `ctl.admin.<id>.reply.>` subtree is gone (1d).
1401
+ // ps + any-mode despawn calls reply there). The `ctl.admin.<id>.reply.>` subtree is gone.
1413
1402
  sub: { allow: [`_INBOX_${pr.connId}.>`, ...ep.sub] },
1414
1403
  };
1415
1404
  }
1416
- /** CONTROL-CALLER (PR 1.5; ep-only since 1d) — the operator's lifecycle commands
1405
+ /** CONTROL-CALLER (ep rails only) — the operator's lifecycle commands
1417
1406
  * (`cotal ps/start/stop/attach`, `manager/commands.ts`). It invokes the manager's v0.4 service
1418
1407
  * endpoint and reads the bounded reply on the ep reply rail. That is ALL — no `$JS`, no `$KV`,
1419
1408
  * no chat/DM: it forges nothing, reads no body.
1420
1409
  *
1421
- * The tiers stay SPLIT because the BROKER grant is load-bearing (the 1c decision): an any-mode
1410
+ * The tiers stay SPLIT because the BROKER grant is load-bearing: an any-mode
1422
1411
  * despawn/attach row *is* cross-agent reach — the manager maps mode `any` to its admin
1423
1412
  * authorization path, so which ROWS an instrument holds is the tier boundary. Therefore:
1424
1413
  * • `control-caller-privileged` (ps/start) holds the manager reads + untargeted `spawn` +
@@ -1430,11 +1419,11 @@ function teardownPermissions(space, pr, transferStreams) {
1430
1419
  * disconnect, from the local signing seed); on a user mesh the manager's serve-time ledger
1431
1420
  * re-check sits on top. */
1432
1421
  function controlCallerPermissions(space, pr, epTier, opts = {}) {
1433
- // 1d: the manager `ctl` rail is gone — an operator instrument holds ONLY its v0.4 ep rows (the
1422
+ // The manager `ctl` rail is gone — an operator instrument holds ONLY its v0.4 ep rows (the
1434
1423
  // tier-matched request set, the reply rail, describe, the one epc fetch). The `epTier` selects
1435
1424
  // privileged (ps/start reads) vs admin (any-mode stop/attach) exactly as the ctl tier did.
1436
1425
  //
1437
- // B6 / `--on <instanceId>`: these instruments are ONE-SHOT, minted per control call, and the
1426
+ // `--on <instanceId>`: these instruments are ONE-SHOT, minted per control call, and the
1438
1427
  // resolve that pins the instance happens BEFORE the mint. So the caller can hand the exact
1439
1428
  // instance id down and get the exact `ep.inst.<endpoint>.<iid>.<command>` row for THIS invocation
1440
1429
  // and nothing else — the least-privilege issuance, with no standing wildcard anywhere. The
@@ -1444,17 +1433,17 @@ function controlCallerPermissions(space, pr, epTier, opts = {}) {
1444
1433
  const ep = instrumentEpRows(space, pr, epTier, opts.endpointCapabilities ?? [], opts);
1445
1434
  return {
1446
1435
  // The PRIVILEGED tier (the `cotal ps` instrument) also carries the SCOPED §13.9 records read the
1447
- // class scatter's freeze rides (P2 item 3): `freezeExpectedSet` enumerates `svc.<endpoint>.*.spec`
1436
+ // class scatter's freeze rides: `freezeExpectedSet` enumerates `svc.<endpoint>.*.spec`
1448
1437
  // and LEADER-reads each frozen slot's svc spec/status before it scatters `ps` on the `all` rail.
1449
1438
  // The admin tier (stop/attach) never scatters, so it gets no records read.
1450
1439
  pub: { allow: epTier === "privileged" ? [...ep.pub, ...scatterFreezeReadRows(space)] : ep.pub },
1451
1440
  sub: { allow: [`_INBOX_${pr.connId}.>`, ...ep.sub] },
1452
1441
  };
1453
1442
  }
1454
- /** The SCOPED §13.9 records-read rows the class-scatter freeze rides (P2 item 3, `cotal ps`): the
1443
+ /** The SCOPED §13.9 records-read rows the class-scatter freeze rides (`cotal ps`): the
1455
1444
  * `svc.*` enumeration consumer + the leader-served per-slot spec/status read of the endpoint's
1456
- * `svc` registry — a READ of exactly the service-registration keys, no write, no other bucket. A
1457
- * new D32 matrix row. The keyed Direct Get is subject-PINNED to `svc.>`; the enumeration consumer
1445
+ * `svc` registry — a READ of exactly the service-registration keys, no write, no other bucket.
1446
+ * The keyed Direct Get is subject-PINNED to `svc.>`; the enumeration consumer
1458
1447
  * and the leader `STREAM.MSG.GET` are STREAM-scoped because the requested key rides the PAYLOAD
1459
1448
  * (which a subject grant cannot narrow) — the SAME NAMED RESIDUAL every records reader already
1460
1449
  * accepts (the provisioner, the lifecycle/serve executors): for this EPHEMERAL one-shot instrument's
@@ -1478,12 +1467,10 @@ function scatterFreezeReadRows(space) {
1478
1467
  `$JS.API.DIRECT.GET.KV_${REC}.$KV.${REC}.svc.>`,
1479
1468
  ];
1480
1469
  }
1481
- /** The v0.4 ep-rail rows of an operator INSTRUMENT credential (the 1c grant-migration table's
1482
- * admin row): the tier-matched {@link operatorInstrumentCapabilities} request rows + the caller's
1483
- * reply rail, the wildcard `describe` form, and the ONE subject-scoped §13.7 contract-store fetch
1484
- * row (the same shape the agent baseline holds; the D32 audit's single exemption). These mirror
1485
- * the instrument's ctl tier onto the ep rails during dual-serve; at 1d the ctl row disappears and
1486
- * these ARE the instrument. The caller triple pins the instrument's own mint-time lifecycle uid
1470
+ /** The v0.4 ep-rail rows of an operator INSTRUMENT credential: the tier-matched
1471
+ * {@link operatorInstrumentCapabilities} request rows + the caller's reply rail, the wildcard
1472
+ * `describe` form, and the ONE subject-scoped §13.7 contract-store fetch row (the same shape the
1473
+ * agent baseline holds). No ctl row exists, so these ARE the instrument. The caller triple pins the instrument's own mint-time lifecycle uid
1487
1474
  * ({@link MintPrincipal.lifecycleUid}) — REQUIRED here: without it the reply rail cannot be pinned
1488
1475
  * and the mint fails loud rather than emit a triple-less (unfenced) caller surface. `extra` lets a
1489
1476
  * profile append its tier-refined additions (the user-mode deployer's owner-equality `launch`). */
@@ -1559,7 +1546,7 @@ function endpointServePermissions(space, pr, opts) {
1559
1546
  sub: { allow: [...rows.sub, `_INBOX_${pr.connId}.>`] },
1560
1547
  };
1561
1548
  }
1562
- /** DEPLOYER (PR 1.5) — the `cotal spawn -f` manifest-deploy authority. `spawn -f` drives ONE
1549
+ /** DEPLOYER — the `cotal spawn -f` manifest-deploy authority. `spawn -f` drives ONE
1563
1550
  * `connectProbe` endpoint that both READS live state (roster/presence watch, channel registry,
1564
1551
  * membership feed, manager-singleton lease) AND invokes the running manager's `launch` + `ps`
1565
1552
  * readiness over the v0.4 ep rails. Those interleave on one connection, so a strict 3-connection
@@ -1582,7 +1569,7 @@ function deployerPermissions(space, pr, epTier = "admin", opts = {}) {
1582
1569
  // set; the user-mode deployer VIEW (privileged) carries the privileged set PLUS an untargeted
1583
1570
  // `launch` row — its launch stays owner-equality-authorized (the manager's ledger-derived admin
1584
1571
  // flag is false for a spawn-scoped deployer), exactly the v0.3 user-mode privileged-tier launch.
1585
- // B6 / `--on`: the per-invocation pin APPENDS to this profile's standing set, never replaces it -
1572
+ // `--on`: the per-invocation pin APPENDS to this profile's standing set, never replaces it -
1586
1573
  // the privileged deployer view keeps its owner-equality `launch` row and additionally gets the one
1587
1574
  // exact `ep.inst.<endpoint>.<iid>.<command>` row for the instance this deploy resolved.
1588
1575
  const pinned = opts.endpointCapabilities ?? [];
@@ -1619,7 +1606,7 @@ function deployerPermissions(space, pr, epTier = "admin", opts = {}) {
1619
1606
  ...kvPointRead(MGRKV), // manager-singleton lease keyed read (waitManagerReady) — point-get, NO write, NO watch
1620
1607
  ...kvPointRead(DLVKV), // delivery-lease keyed read (preserve-state quiescence proof) — point-get, NO write, NO watch
1621
1608
  "$JS.FC.>", // ordered-consumer flow control
1622
- // 1d: launch + ps readiness ride the v0.4 ep rows only (the manager `ctl` rail is gone).
1609
+ // Launch + ps readiness ride the v0.4 ep rows only (the manager `ctl` rail is gone).
1623
1610
  // Static deploys carry the admin instrument set; the user-mode deployer VIEW carries the
1624
1611
  // privileged set + an owner-equality `launch` row (the manager's ledger-derived admin flag
1625
1612
  // is false for a spawn-scoped deployer, so its launch stays owner-equality-authorized).
@@ -1630,7 +1617,7 @@ function deployerPermissions(space, pr, epTier = "admin", opts = {}) {
1630
1617
  sub: { allow: [`_INBOX_${pr.connId}.>`, ...ep.sub] },
1631
1618
  };
1632
1619
  }
1633
- /** The ephemeral PURGER permission set (closure (ii), residual 2) — minted per-purge inside the daemon's
1620
+ /** The ephemeral PURGER permission set — minted per-purge inside the daemon's
1634
1621
  * `opPurge` and `cotal history clear`. Isolates the DESTRUCTIVE history-purge grant
1635
1622
  * (`STREAM.PURGE.CHAT` + `STREAM.PURGE.DM`) off the always-on supervisor: `--dms` purges the DM stream,
1636
1623
  * exactly the grant the supervisor must not hold. It PURGES but never READS — no DM/chat consumer, no
@@ -1650,7 +1637,7 @@ function purgerPermissions(space, pr) {
1650
1637
  sub: { allow: [`_INBOX_${pr.connId}.>`] },
1651
1638
  };
1652
1639
  }
1653
- /** The ephemeral PROVISIONER permission set (closure (ii), residual 2) — the onboarding authority,
1640
+ /** The ephemeral PROVISIONER permission set — the onboarding authority,
1654
1641
  * carved off the long-lived manager. Minted short-lived for per-spawn provisioning (pre-create each
1655
1642
  * agent's bind-only DM/DLV/TASK durables + record its read ACL via `commitAcl`) — the daemon opens it per
1656
1643
  * spawn (`manager.ts withProvisioner`). It is ALSO the cred that creates the space's streams + KV buckets
@@ -1664,8 +1651,7 @@ function purgerPermissions(space, pr) {
1664
1651
  * create a DM/DLV consumer can stream the bodies). That is exactly why it is split OFF the always-on
1665
1652
  * supervisor and made EPHEMERAL: the daemon opens a provisioner connection per spawn and drains it
1666
1653
  * immediately, so the surface exists only for the provisioning window, not as a standing target. The
1667
- * cred is MEMORY-ONLY (never written to `.cotal`) and now carries a short profile-default `exp`; signer
1668
- * rotation, live eviction, and full revocation are later D5 slices.
1654
+ * cred is MEMORY-ONLY (never written to `.cotal`) and carries a short profile-default `exp`.
1669
1655
  *
1670
1656
  * `$JS` is an ENUMERATED allow-list, never `$JS.>`: STREAM.CREATE + INFO for the space streams/buckets,
1671
1657
  * DM/DLV/TASK consumer CREATE/DURABLE.CREATE/INFO — and deliberately NO `MSG.NEXT`/`MSG.GET`/`ACK` on
@@ -1695,9 +1681,9 @@ function provisionerPermissions(space, pr) {
1695
1681
  // STREAM.CREATE + INFO for each (idempotent setup at `cotal up`; CREATE is create-if-matching, INFO covers
1696
1682
  // the client's existence checks). NO DELETE/PURGE — provisioning never tears a stream down.
1697
1683
  // The §13.7 CONTRACT store (EPC) joins the list for the static manager's start-time
1698
- // `ensureContractStore` (P2 item 1, 1c): create-or-verify only — the provisioner holds no
1684
+ // `ensureContractStore`: create-or-verify only — the provisioner holds no
1699
1685
  // artifact-publish grant on it (publication rides the scoped endpoint-serve executor).
1700
- // The seven §13.12 ENDPOINT streams join the list (P2 item 2): spawn-as-action makes the manager
1686
+ // The seven §13.12 ENDPOINT streams join the list: spawn-as-action makes the manager
1701
1687
  // the first EPF (goal facts) + EPE (progress) writer, and nothing provisioned the endpoint streams
1702
1688
  // before (no manager code wrote to them), so `createEndpointStreams` now runs at the manager's
1703
1689
  // start-time ensure over this provisioner. Create-or-verify only (idempotent, fail-loud on drift);
@@ -1774,7 +1760,7 @@ function provisionerPermissions(space, pr) {
1774
1760
  `$JS.API.DIRECT.GET.KV_${aclBucket(space)}.>`, // keyed get: `.>` (the key rides the subject)
1775
1761
  `$JS.API.STREAM.MSG.GET.KV_${channelBucket(space)}`,
1776
1762
  `$JS.API.DIRECT.GET.KV_${channelBucket(space)}.>`, // keyed get: `.>` (the key rides the subject)
1777
- // The Unit B static-manager start path: `ensureAuthorityStores` UPDATEs the records store's
1763
+ // The static-manager start path: `ensureAuthorityStores` UPDATEs the records store's
1778
1764
  // deny-flags exactly once at fresh creation (create → update → verify), and the boot
1779
1765
  // reconciliation sweep enumerates the manager's slot rows (`keys()` → an ordered consumer)
1780
1766
  // then reads each slot BODY (phase/uid/actor) to plan resume — the reads ride the
@@ -1850,7 +1836,7 @@ function runAdmitterPermissions(space, pr, pin) {
1850
1836
  };
1851
1837
  }
1852
1838
  /** The ephemeral, LIFECYCLE-PINNED §13.1 state-write permission set for the STATIC manager's
1853
- * lifecycle executor (Unit B). One credential per lifecycle OPERATION (activation, terminal,
1839
+ * lifecycle executor. One credential per lifecycle OPERATION (activation, terminal,
1854
1840
  * renewal ledger append): every grant names exactly ONE incarnation's keys — the alias head,
1855
1841
  * the uid reservation, the manager slot row, the issuance gate, and the `cred.<uid>.>` ledger
1856
1842
  * family — so a leaked executor cred can move one incarnation's state machine and nothing else.
@@ -1878,7 +1864,7 @@ function lifecycleExecutorPermissions(space, pr, pin) {
1878
1864
  // grant names exactly one of this incarnation's keys.
1879
1865
  ...recordKeys.map((k) => `$KV.${REC}.${k}`),
1880
1866
  // Auth-store CAS writes — this incarnation's gate + its cred-ledger family (renewals
1881
- // append rows here; the terminal's B1 revoke CASes them).
1867
+ // append rows here; the terminal's revoke CASes them).
1882
1868
  `$KV.${AUTH}.${issuanceGateKey(pin.lifecycleUid)}`,
1883
1869
  `$KV.${AUTH}.cred.${pin.lifecycleUid}.>`,
1884
1870
  // Keyed Direct Get reads of the same records keys (key-pinned) for direct-aware read
@@ -1895,16 +1881,16 @@ function lifecycleExecutorPermissions(space, pr, pin) {
1895
1881
  sub: { allow: [`_INBOX_${pr.connId}.>`] },
1896
1882
  };
1897
1883
  }
1898
- /** The SELF-MEDIATED goal-writer profile (P2 item 2, spawn-as-action): exactly
1884
+ /** The SELF-MEDIATED goal-writer profile (spawn-as-action): exactly
1899
1885
  * {@link goalWriterGrants} for ITS endpoint — the goal bind + terminal facts, the goal-record KV
1900
1886
  * writes, and the leader-served fencing reads — plus the connection-scoped reply inbox. Disjoint
1901
- * from the endpoint's serve credential (Q2): a serve connection carries none of these rows, so it
1887
+ * from the endpoint's serve credential: a serve connection carries none of these rows, so it
1902
1888
  * is broker-denied every goal write. */
1903
1889
  function goalWriterPermissions(space, pr, pin) {
1904
1890
  const g = goalWriterGrants(space, pin.endpoint, pr.connId);
1905
1891
  return { pub: { allow: g.publish }, sub: { allow: g.subscribe } };
1906
1892
  }
1907
- /** The console/CLI per-session CALLER rows (P2 item 6): RAILS-ONLY for ONE §13.6 session — pub the
1893
+ /** The console/CLI per-session CALLER rows: RAILS-ONLY for ONE §13.6 session — pub the
1908
1894
  * session's epoch-pinned `in` rail, sub its `out` rail plus the caller's own reply inbox, and
1909
1895
  * NOTHING else. Deliberately NO KV, NO JetStream API, NO store: the caller drives the terminal over
1910
1896
  * the two core-only eps subjects and never reads the session ledger, so there is no subject-blind
@@ -1919,7 +1905,7 @@ function sessionCallerPermissions(space, pr, pin) {
1919
1905
  sub: { allow: [epsSubject(space, pin.endpoint, pin.sessionId, pin.epoch, "out"), `_INBOX_${pr.connId}.>`] },
1920
1906
  };
1921
1907
  }
1922
- /** The manager's per-session SERVING rows (P2 item 6): the EXACT mirror of
1908
+ /** The manager's per-session SERVING rows: the EXACT mirror of
1923
1909
  * {@link sessionCallerPermissions} with the directions swapped — sub the ONE session's epoch-pinned
1924
1910
  * `in` rail (caller→serving), pub its `out` rail (serving→caller), plus the connection-scoped reply
1925
1911
  * inbox, and NOTHING else. Same asymmetry §13.6 states: "the caller publishes `in` and subscribes
@@ -1942,7 +1928,7 @@ function sessionServingPermissions(space, pr, pin) {
1942
1928
  sub: { allow: [epsSubject(space, pin.endpoint, pin.sessionId, pin.epoch, "in"), `_INBOX_${pr.connId}.>`] },
1943
1929
  };
1944
1930
  }
1945
- /** The manager's SESSION-LEDGER rows (P2 item 6): the DEDICATED sessions-bucket rows and nothing
1931
+ /** The manager's SESSION-LEDGER rows: the DEDICATED sessions-bucket rows and nothing
1946
1932
  * else — no session rail of any shape. Needs no pin: the grant carries no endpoint, epoch, or
1947
1933
  * session component, because §13.6's durable revocation authority is per-space, not per-session
1948
1934
  * (it must still be able to resolve and revoke a row after the endpoint that served it is gone).
@@ -1952,10 +1938,10 @@ function sessionLedgerPermissions(space, pr) {
1952
1938
  const g = sessionLedgerGrants(space, pr.connId);
1953
1939
  return { pub: { allow: g.publish }, sub: { allow: g.subscribe } };
1954
1940
  }
1955
- /** The ephemeral, ENDPOINT-INSTANCE-PINNED endpoint-serve executor permission set (P2 item 1,
1956
- * 1a-serve): the manager mints this per registration/serve-mint op and drives the endpoint
1941
+ /** The ephemeral, ENDPOINT-INSTANCE-PINNED endpoint-serve executor permission set:
1942
+ * the manager mints this per registration/serve-mint op and drives the endpoint
1957
1943
  * registration barrier's `epgate` CAS + the mint fence's `epcred` stage/revoke THROUGH it — never
1958
- * its standing seed/supervisor connection (critic #1's manager-specific "no seed shortcut"). Every
1944
+ * its standing seed/supervisor connection. Every
1959
1945
  * WRITE is key-pinned to exactly ONE (endpoint, instanceId): the gate `epgate.<ep>.<iid>`, its
1960
1946
  * serving ledger family `epcred.<ep>.<iid>.>`, and the registration's two records keys (the
1961
1947
  * instance's `svc` spec + the endpoint's governance head — `registerServiceInstance` drives the
@@ -1973,7 +1959,7 @@ function endpointServeExecutorPermissions(space, pr, pin) {
1973
1959
  const repairKey = eprepairKey(pin.endpoint, pin.instanceId);
1974
1960
  // The registration writes this ONE instance's spec key plus the endpoint's governance head
1975
1961
  // (`registerServiceInstance` PHASE 1/3b: the slot-take + promote ride the SAME executor), and this
1976
- // instance's own svc STATUS key (P2 item 3: the manager writes its CONVERGED `ready` status so it
1962
+ // instance's own svc STATUS key (the manager writes its CONVERGED `ready` status so it
1977
1963
  // is a §13.5 scatter member — `freezeExpectedSet` requires a status caught up to the current
1978
1964
  // registration; the write is epoch-fenced by `writeServiceStatus`).
1979
1965
  const recordKeys = [
@@ -1993,7 +1979,7 @@ function endpointServeExecutorPermissions(space, pr, pin) {
1993
1979
  `$KV.${AUTH}.${repairKey}`,
1994
1980
  `$KV.${AUTH}.${credPrefix}.>`,
1995
1981
  ...recordKeys.map((k) => `$KV.${REC}.${k}`),
1996
- // §13.7 contract-artifact publication (P2 item 1, 1c): the registration publishes the
1982
+ // §13.7 contract-artifact publication: the registration publishes the
1997
1983
  // endpoint's cluster document, closure manifests, and schema roots to the EPC store so
1998
1984
  // callers can fetch-verify-compile the registered digests. A digest subject is a SINGLE
1999
1985
  // hex token (`epc.<64hex>`), so the grant is the single-token `epc.*` form (matching
@@ -2033,7 +2019,7 @@ function endpointServeExecutorPermissions(space, pr, pin) {
2033
2019
  sub: { allow: [`_INBOX_${pr.connId}.>`] },
2034
2020
  };
2035
2021
  }
2036
- /** The ephemeral, TARGET-PINNED DEPROVISIONER permission set (#159 Part B) — the teardown counterpart
2022
+ /** The ephemeral, TARGET-PINNED DEPROVISIONER permission set — the teardown counterpart
2037
2023
  * to {@link provisionerPermissions}, minted per departed agent inside the manager's `deprovision` tail
2038
2024
  * (`withProvisioner`-style: a fresh scoped cred per teardown is cheap). It deletes exactly the
2039
2025
  * dev/static principal footprint the provisioner created for ONE agent: that agent's two bind-only
@@ -2184,7 +2170,7 @@ function deliveryPermissions(space, pr) {
2184
2170
  // the `.reply.>` leaf so the daemon can't publish to the request subjects themselves — tighter than a
2185
2171
  // blanket `ctl.delivery.>` (fact-check precision, review panel). The caller slots widened to `.*.*`.
2186
2172
  `${p}.ctl.delivery.*.*.reply.>`,
2187
- // The privileged delivery-admin rail (D5 slice 5/6): same replies-only shape. Requests reach the
2173
+ // The privileged delivery-admin rail: same replies-only shape. Requests reach the
2188
2174
  // daemon on the sub below; only the supervisor cred can PUBLISH them (nats-server is the boundary).
2189
2175
  `${p}.ctl.delivery-admin.*.*.reply.>`,
2190
2176
  // LIVENESS REPLIES for the DELIVERY plane (#1577) — same replies-only shape as the two control
@@ -2265,7 +2251,7 @@ export async function mintMembershipObserverCreds(auth, identity, opts = {}) {
2265
2251
  throw new Error("mintMembershipObserverCreds: no in-memory system-account signing seed - the observer can only be minted from a system account that is being (re)provisioned, because the $SYS seed is never persisted. Rotate the system account to mint a fresh one (`cotal down` then `cotal up --rotate-sys`); a plain re-`up` reuses the existing account and its existing creds.");
2266
2252
  const signer = fromSeed(new TextEncoder().encode(auth.sys.signingSeed));
2267
2253
  const perms = membershipObserverPermissions(auth.account.pub);
2268
- // Bounded exp (D5 slice 5): the observer is `rotation-renewed` — it carries the matrix's default
2254
+ // Bounded exp: the observer is `rotation-renewed` — it carries the matrix's default
2269
2255
  // lifetime so a copied cred becomes broker-dead, but there is NO online renewal (the $SYS seed is
2270
2256
  // gone after `up`); renewal is a coordinated system-account rotation + restart.
2271
2257
  const validDates = userValidDates("membership-observer", opts);
@@ -2273,7 +2259,7 @@ export async function mintMembershipObserverCreds(auth, identity, opts = {}) {
2273
2259
  const creds = fmtCreds(userJwt, fromSeed(new TextEncoder().encode(identity.seed)));
2274
2260
  return new TextDecoder().decode(creds);
2275
2261
  }
2276
- /** The KICK-ONLY connection-evictor permission set (D5 slice 4) — a SYSTEM-account user that can do
2262
+ /** The KICK-ONLY connection-evictor permission set — a SYSTEM-account user that can do
2277
2263
  * exactly ONE thing: `$SYS.REQ.SERVER.*.KICK` (disconnect a live client by cid). It CANNOT read
2278
2264
  * CONNZ (discovery stays on the separate observer cred — never one broad sys user that both
2279
2265
  * enumerates and kills), touch any other `$SYS` verb, or reach another account's data. A leaked
@@ -2290,14 +2276,14 @@ function connectionEvictorPermissions() {
2290
2276
  sub: { allow: ["_INBOX.>"] },
2291
2277
  };
2292
2278
  }
2293
- /** Mint the scoped `connection-evictor` creds — the kick-only SYSTEM-account user D5 slice 4's live
2294
- * eviction holds. Same mint-only-at-provision property as the observer (the $SYS seed is in-memory
2279
+ /** Mint the scoped `connection-evictor` creds — the kick-only SYSTEM-account user for live
2280
+ * eviction. Same mint-only-at-provision property as the observer (the $SYS seed is in-memory
2295
2281
  * only), same fail-loud when it's absent. Paired with the observer at `up`. */
2296
2282
  export async function mintConnectionEvictorCreds(auth, identity, opts = {}) {
2297
2283
  if (!auth.sys.signingSeed)
2298
2284
  throw new Error("mintConnectionEvictorCreds: no in-memory system-account signing seed - the evictor can only be minted from a system account that is being (re)provisioned, because the $SYS seed is never persisted. Rotate the system account to mint a fresh one (`cotal down` then `cotal up --rotate-sys`); a plain re-`up` reuses the existing account and its existing creds.");
2299
2285
  const signer = fromSeed(new TextEncoder().encode(auth.sys.signingSeed));
2300
- // Bounded exp (D5 slice 5): `rotation-renewed`, same posture as the observer above.
2286
+ // Bounded exp: `rotation-renewed`, same posture as the observer above.
2301
2287
  const validDates = userValidDates("connection-evictor", opts);
2302
2288
  const userJwt = await encodeUser("connection-evictor", fromPublic(identity.id), fromPublic(auth.sys.pub), connectionEvictorPermissions(), { signer, ...validDates });
2303
2289
  const creds = fmtCreds(userJwt, fromSeed(new TextEncoder().encode(identity.seed)));
@@ -2367,7 +2353,7 @@ function renderTlsBlock(transport) {
2367
2353
  * THIS broker's operator before it is preloaded: rendering a foreign-signed account would either
2368
2354
  * refuse broker boot or, worse, advertise a tenant the broker cannot actually authenticate.
2369
2355
  *
2370
- * NOTE (W4): the MEMORY resolver is one static whole-broker map, so every mutation rewrites all of
2356
+ * NOTE: the MEMORY resolver is one static whole-broker map, so every mutation rewrites all of
2371
2357
  * it. Concurrent add/remove of spaces needs a broker-authoritative inventory with generation/CAS
2372
2358
  * and atomic promotion above this function; this renderer is deliberately pure. */
2373
2359
  export function serverConfig(broker, spaces, opts) {
@@ -2384,7 +2370,7 @@ export function serverConfig(broker, spaces, opts) {
2384
2370
  }
2385
2371
  const port = opts.port ?? 4222;
2386
2372
  const host = opts.host ?? "127.0.0.1";
2387
- // The websocket listener (item 6): LOCALHOST by default, no_tls for the dev loopback. Emitted only
2373
+ // The websocket listener: LOCALHOST by default, no_tls for the dev loopback. Emitted only
2388
2374
  // when wsPort is set — a broker with no console session client opens no ws surface.
2389
2375
  const websocket = opts.wsPort === undefined ? "" : `websocket {
2390
2376
  host: ${opts.wsHost ?? "127.0.0.1"}