@cotal-ai/cli 0.48.2 → 0.50.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 (82) hide show
  1. package/dist/command.d.ts.map +1 -1
  2. package/dist/command.js +8 -0
  3. package/dist/command.js.map +1 -1
  4. package/dist/commands/agents.d.ts +167 -6
  5. package/dist/commands/agents.d.ts.map +1 -1
  6. package/dist/commands/agents.js +151 -61
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/doctor.d.ts.map +1 -1
  9. package/dist/commands/doctor.js +175 -11
  10. package/dist/commands/doctor.js.map +1 -1
  11. package/dist/commands/down.d.ts +18 -1
  12. package/dist/commands/down.d.ts.map +1 -1
  13. package/dist/commands/down.js +99 -3
  14. package/dist/commands/down.js.map +1 -1
  15. package/dist/commands/join.d.ts.map +1 -1
  16. package/dist/commands/join.js +9 -1
  17. package/dist/commands/join.js.map +1 -1
  18. package/dist/commands/meshes.js +5 -5
  19. package/dist/commands/meshes.js.map +1 -1
  20. package/dist/commands/send.d.ts.map +1 -1
  21. package/dist/commands/send.js +2 -8
  22. package/dist/commands/send.js.map +1 -1
  23. package/dist/commands/setup.d.ts +1 -1
  24. package/dist/commands/setup.d.ts.map +1 -1
  25. package/dist/commands/setup.js +1 -1
  26. package/dist/commands/spawn-manifest.d.ts.map +1 -1
  27. package/dist/commands/spawn-manifest.js +3 -2
  28. package/dist/commands/spawn-manifest.js.map +1 -1
  29. package/dist/commands/status.d.ts +11 -3
  30. package/dist/commands/status.d.ts.map +1 -1
  31. package/dist/commands/status.js +176 -13
  32. package/dist/commands/status.js.map +1 -1
  33. package/dist/commands/up.d.ts +13 -0
  34. package/dist/commands/up.d.ts.map +1 -1
  35. package/dist/commands/up.js +65 -5
  36. package/dist/commands/up.js.map +1 -1
  37. package/dist/commands/update.d.ts.map +1 -1
  38. package/dist/commands/update.js +19 -7
  39. package/dist/commands/update.js.map +1 -1
  40. package/dist/commands/use.js +2 -2
  41. package/dist/commands/use.js.map +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +4 -3
  44. package/dist/index.js.map +1 -1
  45. package/dist/lib/auth-proc.d.ts.map +1 -1
  46. package/dist/lib/auth-proc.js +2 -1
  47. package/dist/lib/auth-proc.js.map +1 -1
  48. package/dist/lib/control.d.ts +28 -1
  49. package/dist/lib/control.d.ts.map +1 -1
  50. package/dist/lib/control.js +58 -11
  51. package/dist/lib/control.js.map +1 -1
  52. package/dist/lib/delivery-proc.d.ts +22 -5
  53. package/dist/lib/delivery-proc.d.ts.map +1 -1
  54. package/dist/lib/delivery-proc.js +82 -18
  55. package/dist/lib/delivery-proc.js.map +1 -1
  56. package/dist/lib/delivery-responder.d.ts +92 -0
  57. package/dist/lib/delivery-responder.d.ts.map +1 -0
  58. package/dist/lib/delivery-responder.js +78 -0
  59. package/dist/lib/delivery-responder.js.map +1 -0
  60. package/dist/lib/manager-proc.d.ts +15 -13
  61. package/dist/lib/manager-proc.d.ts.map +1 -1
  62. package/dist/lib/manager-proc.js +38 -7
  63. package/dist/lib/manager-proc.js.map +1 -1
  64. package/dist/lib/restore.d.ts.map +1 -1
  65. package/dist/lib/restore.js +6 -4
  66. package/dist/lib/restore.js.map +1 -1
  67. package/dist/lib/self-exec.d.ts +15 -1
  68. package/dist/lib/self-exec.d.ts.map +1 -1
  69. package/dist/lib/self-exec.js +30 -3
  70. package/dist/lib/self-exec.js.map +1 -1
  71. package/dist/lib/systemd-supervision.d.ts +15 -0
  72. package/dist/lib/systemd-supervision.d.ts.map +1 -0
  73. package/dist/lib/systemd-supervision.js +64 -0
  74. package/dist/lib/systemd-supervision.js.map +1 -0
  75. package/dist/lib/transient.d.ts +1 -10
  76. package/dist/lib/transient.d.ts.map +1 -1
  77. package/dist/lib/transient.js +4 -24
  78. package/dist/lib/transient.js.map +1 -1
  79. package/dist/seed/reconcile.d.ts.map +1 -1
  80. package/dist/seed/reconcile.js +27 -1
  81. package/dist/seed/reconcile.js.map +1 -1
  82. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"up.d.ts","sourceRoot":"","sources":["../../src/commands/up.ts"],"names":[],"mappings":"AAkBA,OAAO,EAOL,KAAK,eAAe,EAgBpB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EA6BL,KAAK,SAAS,EAoBd,KAAK,eAAe,EASrB,MAAM,qBAAqB,CAAC;AAmD7B,yGAAyG;AACzG,eAAO,MAAM,OAAO,EAAE,QAAQ,EAwB7B,CAAC;AAEF;;;qGAGqG;AACrG,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAS3D;AAED;2FAC2F;AAC3F,wBAAsB,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAWzF;AAswDD,MAAM,WAAW,UAAU;IACzB;;;;;;;;;OASG;IACH,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,8FAA8F;IAC9F,QAAQ,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B;;4FAEwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;qEACiE;IACjE,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,sFAAsF;IACtF,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC;wDACoD;IACpD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;yGACqG;IACrG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;+EAC2E;IAC3E,aAAa,CAAC,EAAE;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9C,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;KACzB,CAAC;IACF,gGAAgG;IAChG,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAGrC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAkI5I;AA2DD;;;;;oGAKoG;AACpG,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElF;AAED;;;+DAG+D;AAC/D;;;;;;;;;;GAUG;AACH,KAAK,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;AAExC;;mCAEmC;AACnC,eAAO,MAAM,oBAAoB,GAAI,GAAG,SAAS,EAAE,YAAY,UAAU,KAAG,IAAoC,CAAC"}
1
+ {"version":3,"file":"up.d.ts","sourceRoot":"","sources":["../../src/commands/up.ts"],"names":[],"mappings":"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"}
@@ -6,15 +6,18 @@ import { mkdirSync, writeFileSync, readFileSync, existsSync, openSync, statSync,
6
6
  import { dirname, join, resolve } from "node:path";
7
7
  import { isReachable, DEFAULT_SERVER, createSpaceAuth, serverConfig, openServerConfig, validateTlsMaterial, mintCreds, mintLifecycleUid, DEV_OWNER, mintConnectionEvictorCreds, mintMembershipObserverCreds, newIdentity, setupSpaceStreams, reconcileSpaceTtls, standaloneConnectOpts, seedChannelRegistry, ensureDefaultDeliveryClass, mkSecretDir, writeSecretFile, } from "@cotal-ai/core";
8
8
  import { connect } from "@nats-io/transport-node";
9
- import { assertSingleSpaceBroker, assertUserAuthInfo, authDir, getSoleSpaceAuth, getSpaceAuth, hasUserAuthState, listSpaceAccounts, preloadSpaceAccounts, putSpaceAuth, clearCurrent, findMesh, getCurrent, loadMeshes, connectionEvictorCredsKey, membershipConfigPath, membershipObserverCredsKey, membershipRwCredsKey, MEMBERSHIP_CONFIG_KIND, MEMBERSHIP_RW_CREDS_KIND, recordMesh, meshesForRoot, removeMesh, rotateSystemCreds, setCurrent, staleSystemCreds, SYSTEM_CREDS_FILES, userAuthStateDir, workspaceSecretStore, acquireMaintenanceLock, assertStoreIdentity, assessRestoreClaim, beginOrdinaryResume, bindOrdinaryResumeListener, consumeRetiredMaintenance, markOrdinaryResumeActive, markOrdinaryResumeDegraded, replaceDeadOrdinaryResumeListener, localProcessOwnerStatus, readMaintenanceJournal, readMaintenanceResumeDocument, readStoreIdentity, recordOrdinaryResumeManagerCommit, releaseMaintenanceLock, retireOrdinaryResume, sameStoreIdentity, readBrokerPolicy, writeBrokerPolicy, removeIdentityPin, writeIdentityPin, } from "@cotal-ai/workspace";
9
+ import { assertSingleSpaceBroker, assertUserAuthInfo, authDir, getSoleSpaceAuth, getSpaceAuth, hasUserAuthState, listSpaceAccounts, preloadSpaceAccounts, putSpaceAuth, clearCurrent, findMesh, getCurrent, loadMeshes, connectionEvictorCredsKey, membershipConfigPath, membershipObserverCredsKey, membershipRwCredsKey, MEMBERSHIP_CONFIG_KIND, MEMBERSHIP_RW_CREDS_KIND, parsePositiveIntegerFlag, recordMesh, meshesForRoot, removeMesh, rotateSystemCreds, setCurrent, staleSystemCreds, SYSTEM_CREDS_FILES, userAuthStateDir, workspaceSecretStore, acquireMaintenanceLock, assertStoreIdentity, assessRestoreClaim, beginOrdinaryResume, bindOrdinaryResumeListener, consumeRetiredMaintenance, markOrdinaryResumeActive, markOrdinaryResumeDegraded, replaceDeadOrdinaryResumeListener, localProcessOwnerStatus, readMaintenanceJournal, readMaintenanceResumeDocument, readStoreIdentity, recordOrdinaryResumeManagerCommit, releaseMaintenanceLock, retireOrdinaryResume, sameStoreIdentity, readBrokerPolicy, writeBrokerPolicy, removeIdentityPin, writeIdentityPin, } from "@cotal-ai/workspace";
10
10
  import { ensureAuthService, resolveAuthProvider, stopAuthService } from "../lib/auth-proc.js";
11
11
  import { resolveSpace } from "../lib/status.js";
12
12
  import { c } from "../ui.js";
13
13
  import { resolveNatsServer } from "../lib/nats-bin.js";
14
14
  import { cotalPath, cotalRoot } from "../lib/paths.js";
15
15
  import { renderDetachedSummary } from "../lib/up-report.js";
16
+ import { detachedSystemdSupervisionWarning } from "../lib/systemd-supervision.js";
16
17
  import { deliveryUp, ensureControlPlane, stopDelivery } from "../lib/delivery-proc.js";
17
- import { managerHasDeliveryMarker, managerLogDisplayPath, managerUp, stopManager } from "../lib/manager-proc.js";
18
+ import { RESPONDER_UNBOUND_CONSEQUENCE } from "../lib/delivery-responder.js";
19
+ import { displayCmd } from "../lib/self-exec.js";
20
+ import { liveManagerWouldApplyMaxSessions, managerHasDeliveryMarker, managerLogDisplayPath, managerRecordState, managerUp, stopManager } from "../lib/manager-proc.js";
18
21
  import { loadManifest } from "../lib/manifest/index.js";
19
22
  import { buildLaunchSpec, genRunId, manifestToChannels, preflightConnectors, writeLaunchSpec } from "../lib/manifest/apply.js";
20
23
  import { renderUpPlan, renderInherited, renderWarnings } from "../lib/manifest/render.js";
@@ -48,6 +51,7 @@ export const upFlags = [
48
51
  { name: "rotate-sys", type: "boolean", description: "renew the expired/expiring $SYS creds by rotating the system account (agents, data and creds survive; needs a stopped mesh)" },
49
52
  { name: "detach", type: "boolean", description: "run in the background (stop with `cotal down`)" },
50
53
  { name: "runtime", type: "string", value: "<name>", description: "agent runtime for the mesh manager (default pty; extension runtimes are explicit-only, see `cotal runtimes`); with -f overrides the manifest's runtime" },
54
+ { name: "max-sessions", type: "string", value: "<n>", description: "live-session ceiling for the mesh manager (default 64; one session per console pane, so size for agents × panes). Recorded and reused by later manager launches" },
51
55
  { name: "file", type: "string", short: "f", value: "<cotal.yaml>", description: "launch a whole mesh from a manifest" },
52
56
  { name: "dry-run", type: "boolean", description: "with -f: print the plan, mutate nothing" },
53
57
  ];
@@ -367,6 +371,7 @@ async function runUp(args, inheritedLock, onAdopt) {
367
371
  throw new Error('--idp is for user-auth spaces; pair it with --user-auth, or set broker.auth: "user" in a manifest');
368
372
  }
369
373
  const publicExchange = publicExchangeArgs(values, wantUser);
374
+ const maxSessions = parsePositiveIntegerFlag("--max-sessions", values["max-sessions"]);
370
375
  // An open mesh has no operator, no system account, and no $SYS creds, so there is nothing to
371
376
  // rotate; the request is a misunderstanding to name, never a silent no-op that reports success.
372
377
  if (values["rotate-sys"] && values.open) {
@@ -455,6 +460,7 @@ async function runUp(args, inheritedLock, onAdopt) {
455
460
  userAuth: wantUser,
456
461
  idp: values.idp,
457
462
  rotateSys: values["rotate-sys"],
463
+ maxSessions,
458
464
  });
459
465
  return;
460
466
  }
@@ -472,6 +478,7 @@ async function runUp(args, inheritedLock, onAdopt) {
472
478
  userAuth: wantUser,
473
479
  idp: values.idp,
474
480
  rotateSys: values["rotate-sys"],
481
+ maxSessions,
475
482
  });
476
483
  }
477
484
  finally {
@@ -609,6 +616,18 @@ async function runUp(args, inheritedLock, onAdopt) {
609
616
  console.error(c.red(`✗ mesh "${held.space}" is already running at ${server} - a running broker can't change its transport; \`cotal down\` it first, then \`cotal up --tls-cert <cert> --tls-key <key>\``));
610
617
  process.exit(1);
611
618
  }
619
+ // `--max-sessions` is fixed when the manager starts. A refresh reuses a live manager as-is
620
+ // (`ensureManager` returns immediately), then used to persist the requested ceiling anyway.
621
+ // That printed `✓ already running` over an unchanged plane and left MeshEntry lying about it.
622
+ // Refuse rather than warn: the flag either applies or the command fails. Matching the live
623
+ // argv is a no-op and may proceed. An unreadable command line cannot prove a match.
624
+ if (maxSessions !== undefined) {
625
+ const live = managerRecordState(undefined, undefined, held.space);
626
+ if (live.state === "alive" && !liveManagerWouldApplyMaxSessions(live.command, maxSessions)) {
627
+ console.error(c.red(`✗ mesh "${held.space}" is already running at ${server} - a running manager can't change --max-sessions (it is fixed at start); \`cotal down\` it first, then \`cotal up --max-sessions ${maxSessions}\``));
628
+ process.exit(1);
629
+ }
630
+ }
612
631
  // Live INFO must agree with the recorded/requested transport. A bare refresh that greets
613
632
  // "already running" over a plaintext listener while policy claims TLS is S5's second half —
614
633
  // the refuse left a durable lie and this path used to reprint TLS success over cleartext.
@@ -719,18 +738,20 @@ async function runUp(args, inheritedLock, onAdopt) {
719
738
  const controlPlane = await startDeliveryWithBroker(held.space, server, wantTls, {
720
739
  runtime: values.runtime,
721
740
  attachHost: attachHostFor(held.space, values.host),
741
+ maxSessions: maxSessionsFor(held.space, maxSessions),
722
742
  });
723
743
  if (!controlPlane)
724
744
  process.exitCode = 1;
725
745
  }
726
746
  const heldAttachHost = attachHostFor(held.space, values.host);
747
+ const heldMaxSessions = maxSessionsFor(held.space, maxSessions);
727
748
  // A broker was already answering here — this branch starts nothing, so it must not claim the
728
749
  // record as ours (see `Provenance`).
729
750
  // `tlsRequired` is CARRIED FORWARD, not re-derived. This branch starts no listener, so it has
730
751
  // no transport decision to record — and `recordOurMesh` writes the entry whole, so omitting
731
752
  // the field here would erase the requirement on every bare refresh, exactly the way dropping
732
753
  // `attachHost` would silently demote the mesh to loopback.
733
- recordOurMesh({ space: held.space, server, root, mode: held.mode, ...(held.tlsRequired !== undefined ? { tlsRequired: held.tlsRequired } : {}), ...(userAuth ? { userAuth } : {}), ...(heldAttachHost ? { attachHost: heldAttachHost } : {}), ts: new Date().toISOString() }, "refresh");
754
+ recordOurMesh({ space: held.space, server, root, mode: held.mode, ...(held.tlsRequired !== undefined ? { tlsRequired: held.tlsRequired } : {}), ...(userAuth ? { userAuth } : {}), ...(heldAttachHost ? { attachHost: heldAttachHost } : {}), ...(heldMaxSessions !== undefined ? { maxSessions: heldMaxSessions } : {}), ts: new Date().toISOString() }, "refresh");
734
755
  return;
735
756
  }
736
757
  const who = held ? `mesh "${held.space}" (${held.root})` : "a broker not started here";
@@ -773,6 +794,7 @@ async function runUp(args, inheritedLock, onAdopt) {
773
794
  // from "nobody asked", or every mesh would persist an exposure decision it never made.
774
795
  host: values.host,
775
796
  runtime: values.runtime,
797
+ maxSessions,
776
798
  resumeAttempt,
777
799
  resumeCommitToken: restored?.managerCommit?.durableCommitToken ?? ordinaryAttempt?.managerCommit?.durableCommitToken,
778
800
  ...(restored ? {
@@ -795,6 +817,9 @@ async function runUp(args, inheritedLock, onAdopt) {
795
817
  // Transport policy is committed inside startMeshDetached before delivery launch (S5+S9).
796
818
  console.log(c.dim(`Started nats-server (${source}).`));
797
819
  console.log(c.green(renderDetachedSummary({ pid, delivery, authService: wantUser && authService, manager })));
820
+ const supervisionWarning = detachedSystemdSupervisionWarning();
821
+ if (supervisionWarning)
822
+ console.error(c.yellow(supervisionWarning));
798
823
  if (restored && process.env.COTAL_SMOKE_FAIL_AFTER_RESTORE_LISTENER_READY === "1")
799
824
  throw new Error("smoke-injected failure after restore listener readiness");
800
825
  // A user mesh whose auth service never became ready is recorded + running (a re-`cotal up`
@@ -949,6 +974,7 @@ async function runUp(args, inheritedLock, onAdopt) {
949
974
  // registry — and the record below is written whole, so reading it afterwards would find the
950
975
  // field this very call had just erased.
951
976
  const effectiveAttachHost = attachHostFor(space, values.host);
977
+ const effectiveMaxSessions = maxSessionsFor(space, maxSessions);
952
978
  recordOurMesh({
953
979
  space, server, root: cotalRoot(),
954
980
  mode: setup?.prepared ? "user" : useAuth ? "auth" : "open",
@@ -962,6 +988,7 @@ async function runUp(args, inheritedLock, onAdopt) {
962
988
  // previous launch. The bare case stays absent rather than recording the loopback default as
963
989
  // though the operator had chosen it.
964
990
  ...(effectiveAttachHost ? { attachHost: effectiveAttachHost } : {}),
991
+ ...(effectiveMaxSessions !== undefined ? { maxSessions: effectiveMaxSessions } : {}),
965
992
  ts: new Date().toISOString(),
966
993
  }, "started");
967
994
  // Bring up the delivery daemon WITH the server (auth mode only — it self-gates on `.cotal/auth`).
@@ -974,6 +1001,7 @@ async function runUp(args, inheritedLock, onAdopt) {
974
1001
  // from another machine; without it the attach face stays loopback-only, so exposing terminals
975
1002
  // is never a side effect of anything but the operator binding the mesh somewhere reachable.
976
1003
  attachHost: effectiveAttachHost,
1004
+ maxSessions: effectiveMaxSessions,
977
1005
  resumeAttempt,
978
1006
  resumeCommitToken: restored?.managerCommit?.durableCommitToken ?? ordinaryAttempt?.managerCommit?.durableCommitToken,
979
1007
  wsPort: setup?.wsPort, // P2 item 6: the console session client's broker ws port
@@ -1314,6 +1342,7 @@ async function resumeProvenOrdinaryListener(pending, heldLock) {
1314
1342
  // record built without this field would erase the operator's decision, and the adopted manager
1315
1343
  // below would then be launched loopback-only from an entry that no longer remembers otherwise.
1316
1344
  const adoptAttachHost = attachHostFor(pending.space);
1345
+ const adoptMaxSessions = maxSessionsFor(pending.space);
1317
1346
  recordOurMesh({
1318
1347
  space: pending.space,
1319
1348
  server: pending.server,
@@ -1323,11 +1352,13 @@ async function resumeProvenOrdinaryListener(pending, heldLock) {
1323
1352
  tlsRequired: adoptedTlsRequired(pending.root),
1324
1353
  ...(svc.userAuth ? { userAuth: svc.userAuth } : {}),
1325
1354
  ...(adoptAttachHost ? { attachHost: adoptAttachHost } : {}),
1355
+ ...(adoptMaxSessions !== undefined ? { maxSessions: adoptMaxSessions } : {}),
1326
1356
  ts: new Date().toISOString(),
1327
1357
  }, "started");
1328
1358
  const controlPlane = await startDeliveryWithBroker(pending.space, pending.server, adoptedTlsRequired(pending.root), {
1329
1359
  runtime: pending.runtime,
1330
1360
  attachHost: adoptAttachHost,
1361
+ maxSessions: adoptMaxSessions,
1331
1362
  resumeAttempt: pending.attemptId,
1332
1363
  resumeCommitToken: pending.managerCommit?.durableCommitToken,
1333
1364
  });
@@ -1355,6 +1386,7 @@ async function resumeProvenRestoreListener(prepared, heldLock) {
1355
1386
  // Same ordering constraint as the pending-adopt path above: capture the recorded exposure before
1356
1387
  // `recordOurMesh` rewrites the entry, or a restore silently demotes the mesh to loopback attach.
1357
1388
  const restoreAttachHost = attachHostFor(prepared.space);
1389
+ const restoreMaxSessions = maxSessionsFor(prepared.space);
1358
1390
  recordOurMesh({
1359
1391
  space: prepared.space,
1360
1392
  server: prepared.server,
@@ -1365,11 +1397,13 @@ async function resumeProvenRestoreListener(prepared, heldLock) {
1365
1397
  tlsRequired: adoptedTlsRequired(prepared.root),
1366
1398
  ...(svc.userAuth ? { userAuth: svc.userAuth } : {}),
1367
1399
  ...(restoreAttachHost ? { attachHost: restoreAttachHost } : {}),
1400
+ ...(restoreMaxSessions !== undefined ? { maxSessions: restoreMaxSessions } : {}),
1368
1401
  ts: new Date().toISOString(),
1369
1402
  }, "started");
1370
1403
  const controlPlane = await startDeliveryWithBroker(prepared.space, prepared.server, adoptedTlsRequired(prepared.root), {
1371
1404
  runtime: prepared.runtime,
1372
1405
  attachHost: restoreAttachHost,
1406
+ maxSessions: restoreMaxSessions,
1373
1407
  resumeAttempt: prepared.attemptId,
1374
1408
  resumeCommitToken: prepared.managerCommit?.durableCommitToken,
1375
1409
  });
@@ -1710,7 +1744,7 @@ async function upManifest(file, opts) {
1710
1744
  try {
1711
1745
  // `m.broker?.host`, not the defaulted `host`: same reason as the flag path — only a host the
1712
1746
  // manifest actually declared is an exposure decision worth persisting.
1713
- ({ pid, controlPlane, authService } = await startMeshDetached({ transport: opts.transport, server, space: m.space, open, userAuth, rotateSys: opts.rotateSys, host: m.broker?.host, seed: manifestToChannels(eff), runtime, launch: specPath }));
1747
+ ({ pid, controlPlane, authService } = await startMeshDetached({ transport: opts.transport, server, space: m.space, open, userAuth, rotateSys: opts.rotateSys, host: m.broker?.host, seed: manifestToChannels(eff), runtime, launch: specPath, maxSessions: opts.maxSessions }));
1714
1748
  }
1715
1749
  catch (e) {
1716
1750
  console.error(c.red(`✗ ${e.message}`));
@@ -1789,7 +1823,16 @@ async function startDeliveryWithBroker(space, server,
1789
1823
  */
1790
1824
  tlsRequired, mgr) {
1791
1825
  try {
1792
- await ensureControlPlane({ space, server, tls: tlsRequired, ...(mgr ?? {}) });
1826
+ const plane = await ensureControlPlane({ space, server, tls: tlsRequired, ...(mgr ?? {}) });
1827
+ // #1576: `up` either BINDS the responder or SAYS SO HERE. The delivery daemon is a hard
1828
+ // dependency of spawn, retirement and join, and this function used to return `true` for a boot
1829
+ // that started a daemon whose responder never bound — so `cotal up` printed its success banner
1830
+ // over a control plane that could not perform any of the three. The reporter's fleet ran 22
1831
+ // hours in that state. This is not a failure of `up` (live messaging is unaffected and the
1832
+ // responder may still bind), so it does not throw; it states the condition and its consequence.
1833
+ if (plane.responderBound === false)
1834
+ console.error(c.yellow(`! delivery responder did not bind before this boot finished - ${RESPONDER_UNBOUND_CONSEQUENCE}`) +
1835
+ c.dim(`\n The daemon process is running and the wait is open-ended; boot durable joins reconcile by themselves once it binds (agents do NOT need respawning).\n Watch it with \`${displayCmd()} status --components\`.`));
1793
1836
  return true;
1794
1837
  }
1795
1838
  catch (e) {
@@ -1919,6 +1962,7 @@ opts) {
1919
1962
  // RESUMES an already-recorded mesh, whose exposure decision exists only in the registry entry the
1920
1963
  // call below rewrites.
1921
1964
  const effectiveAttachHost = attachHostFor(space, opts.host);
1965
+ const effectiveMaxSessions = maxSessionsFor(space, opts.maxSessions);
1922
1966
  recordOurMesh({
1923
1967
  space, server, root: cotalRoot(),
1924
1968
  mode: setup?.prepared ? "user" : useAuth ? "auth" : "open",
@@ -1928,6 +1972,7 @@ opts) {
1928
1972
  ...(svc.userAuth ? { userAuth: svc.userAuth } : {}),
1929
1973
  // Persist only a real decision — declared now, or carried forward — never the loopback default.
1930
1974
  ...(effectiveAttachHost ? { attachHost: effectiveAttachHost } : {}),
1975
+ ...(effectiveMaxSessions !== undefined ? { maxSessions: effectiveMaxSessions } : {}),
1931
1976
  ts: new Date().toISOString(),
1932
1977
  }, "started");
1933
1978
  // Commit policy BEFORE delivery launch (S9). Listener is proved; refuse paths never reach here.
@@ -1939,6 +1984,7 @@ opts) {
1939
1984
  launch: opts.launch,
1940
1985
  // See the foreground path: the broker's bind address is what makes attach reachable off-box.
1941
1986
  attachHost: effectiveAttachHost,
1987
+ maxSessions: effectiveMaxSessions,
1942
1988
  resumeAttempt: opts.resumeAttempt,
1943
1989
  resumeCommitToken: opts.resumeCommitToken,
1944
1990
  wsPort: setup?.wsPort, // P2 item 6: the console session client's broker ws port
@@ -2049,6 +2095,20 @@ export async function claimSpace(space, server, root) {
2049
2095
  export function attachHostFor(space, explicit) {
2050
2096
  return explicit ?? findMesh(space)?.attachHost;
2051
2097
  }
2098
+ /**
2099
+ * The manager live-session ceiling for a launch into `space`.
2100
+ *
2101
+ * Like {@link attachHostFor}, this is an operator DECISION (`--max-sessions`), not something later
2102
+ * commands can reconstruct. Every later manager launch — same-root repair, resume, `spawn -f` —
2103
+ * has no flag of its own unless the operator typed one, so it reads the decision back. Without
2104
+ * this a replacement manager silently falls to 64 and the next console pane past that is refused.
2105
+ *
2106
+ * An explicit value on THIS invocation always wins. Absent both, undefined — the plane's own
2107
+ * default of 64, unchanged, and nothing is recorded as though chosen.
2108
+ */
2109
+ export function maxSessionsFor(space, explicit) {
2110
+ return explicit ?? findMesh(space)?.maxSessions;
2111
+ }
2052
2112
  /** {@link recordOurMesh} under a name that says it is a test seam: the origin rule it enforces is
2053
2113
  * asserted directly by the registry smoke, since reaching each branch through a full `up` would
2054
2114
  * need a live broker per case. */