@cotal-ai/manager 0.16.0 → 0.17.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 (53) hide show
  1. package/dist/attach-endpoint.d.ts +53 -59
  2. package/dist/attach-endpoint.d.ts.map +1 -1
  3. package/dist/attach-endpoint.js +120 -244
  4. package/dist/attach-endpoint.js.map +1 -1
  5. package/dist/commands.js +9 -4
  6. package/dist/commands.js.map +1 -1
  7. package/dist/console/app.js +68 -30
  8. package/dist/console/index.html +3 -2
  9. package/dist/console/session-bundle.js +9736 -0
  10. package/dist/console-crypto-shim.d.ts +16 -0
  11. package/dist/console-crypto-shim.d.ts.map +1 -0
  12. package/dist/console-crypto-shim.js +20 -0
  13. package/dist/console-crypto-shim.js.map +1 -0
  14. package/dist/console-session-entry.d.ts +2 -0
  15. package/dist/console-session-entry.d.ts.map +1 -0
  16. package/dist/console-session-entry.js +26 -0
  17. package/dist/console-session-entry.js.map +1 -0
  18. package/dist/endpoint-evict.d.ts +29 -0
  19. package/dist/endpoint-evict.d.ts.map +1 -0
  20. package/dist/endpoint-evict.js +80 -0
  21. package/dist/endpoint-evict.js.map +1 -0
  22. package/dist/manager-service-contract.d.ts +132 -0
  23. package/dist/manager-service-contract.d.ts.map +1 -0
  24. package/dist/manager-service-contract.js +332 -0
  25. package/dist/manager-service-contract.js.map +1 -0
  26. package/dist/manager.d.ts +504 -22
  27. package/dist/manager.d.ts.map +1 -1
  28. package/dist/manager.js +2193 -257
  29. package/dist/manager.js.map +1 -1
  30. package/dist/runtime/pty.d.ts.map +1 -1
  31. package/dist/runtime/pty.js +15 -2
  32. package/dist/runtime/pty.js.map +1 -1
  33. package/dist/session/bridge.d.ts +63 -0
  34. package/dist/session/bridge.d.ts.map +1 -0
  35. package/dist/session/bridge.js +157 -0
  36. package/dist/session/bridge.js.map +1 -0
  37. package/dist/session/establish.d.ts +212 -0
  38. package/dist/session/establish.d.ts.map +1 -0
  39. package/dist/session/establish.js +174 -0
  40. package/dist/session/establish.js.map +1 -0
  41. package/dist/session/index.d.ts +12 -0
  42. package/dist/session/index.d.ts.map +1 -0
  43. package/dist/session/index.js +15 -0
  44. package/dist/session/index.js.map +1 -0
  45. package/dist/session/plane.d.ts +120 -0
  46. package/dist/session/plane.d.ts.map +1 -0
  47. package/dist/session/plane.js +327 -0
  48. package/dist/session/plane.js.map +1 -0
  49. package/dist/static-lifecycle.d.ts +97 -0
  50. package/dist/static-lifecycle.d.ts.map +1 -0
  51. package/dist/static-lifecycle.js +262 -0
  52. package/dist/static-lifecycle.js.map +1 -0
  53. package/package.json +9 -5
@@ -0,0 +1,174 @@
1
+ import { EpEnvelopeError, mintSessionGrant, redeemSession, retrieveServingCredential, verifySessionGrant, } from "@cotal-ai/core";
2
+ import { MANAGER_ENDPOINT } from "../manager-service-contract.js";
3
+ /** Mint an attach offer: a §13.6 session grant on the `manager` endpoint bound to the holder triple
4
+ * + serving instance/epoch, paired with the manager-side target binding. Pure — signs + returns;
5
+ * the manager records the offer in its {@link ManagerSessionRegistry}. */
6
+ export function mintAttachOffer(args) {
7
+ const grant = mintSessionGrant({
8
+ space: args.space,
9
+ endpoint: MANAGER_ENDPOINT,
10
+ holder: args.holder,
11
+ serving: args.serving,
12
+ ttlMs: args.ttlMs,
13
+ ...(args.window !== undefined ? { window: args.window } : {}),
14
+ issuerKeyId: args.signer.keyId,
15
+ ...(args.now !== undefined ? { now: args.now } : {}),
16
+ }, args.signer.keyPair);
17
+ return { grant, target: { name: args.target.name, lifecycleUid: args.target.lifecycleUid } };
18
+ }
19
+ /**
20
+ * The STATIC redeem seam (item 6's wired path): the manager runs the §13.6 redemption — verify the
21
+ * signed grant, then `redeemSession` (the one-use `issuing` create-CAS + presenter-equality + the
22
+ * gate-pinned stage + the finalize's fresh epoch re-checks) — and, for the SERVING half, mints,
23
+ * stages, releases and revokes a real per-session credential through {@link SessionServing}.
24
+ *
25
+ * WHAT EACH HALF ACTUALLY GETS, stated exactly, because the two halves are NOT symmetric here and a
26
+ * comment claiming they are would be worse than the asymmetry:
27
+ *
28
+ * - SERVING half (this manager's own): a real `session-serving` credential, exact-subject
29
+ * (`eps.<endpoint>.<sessionId>.<epoch>.{in,out}`), TTL-bound to the session, staged into this
30
+ * instance's §13.1 `epcred.<endpoint>.<instanceId>` family under the open-and-commit fence, and
31
+ * revoked BY NAME at the session's terminal. It replaces a STANDING wildcard credential that
32
+ * reached every live session's bytes at its epoch (SPEC 13.9:2526).
33
+ * - CALLER half: NOT minted, staged, or revoked here, and `releaseCredential` returns a MARKER with
34
+ * no usable bytes for it. The caller's `session-caller` JWT is minted OUT OF BAND from the local
35
+ * space seed — by the console establisher and by CLI attach — after redemption.
36
+ *
37
+ * SPEC 13.6 PAIR-REVOKE IS NOT IMPLEMENTED FOR THE CALLER HALF. Stated exactly, because the
38
+ * difference between this and pair-revoke is the whole residual: that JWT is not written into the
39
+ * session lineage, is not named by the session row's credential material, and is untouched by
40
+ * session close and by takeover reconcile. It dies by TTL alone, at a 24h ceiling
41
+ * (`SESSION_GRANT_MAX_TTL_MS`).
42
+ *
43
+ * Why the window is tolerable for THIS static slice, as reviewed: the caller grant is
44
+ * exact-pinned to one `(endpoint, sessionId, epoch)`, so it can reach neither a sibling session
45
+ * nor a successor epoch; and once the serving half is revoked and its connection closed there is
46
+ * no counterparty on those two subjects. What survives is a broker-valid connection, not reach
47
+ * into another session. Closing it properly means minting the caller half inside redemption,
48
+ * which needs the user-mode (#29) callout exchange. Until that lands, do not describe any part of
49
+ * this as pair teardown.
50
+ * - `revokeCredential` is therefore ONE-SIDED by construction: it revokes an id this seam minted
51
+ * and is an explicit no-op for the caller id, rather than silently appearing to revoke both.
52
+ */
53
+ export function staticRedemptionSeam(deps) {
54
+ // The minted-but-unreleased serving credential, held between `allocateCredentialIds` and
55
+ // `releaseCredential`. NAMED RESIDUAL: the JWT exists before the one-use create-CAS is won,
56
+ // because the credential id the `issuing` row must name IS its digest. If the create loses, the
57
+ // bytes are dropped here having never been staged, released, or connected with — they reach no
58
+ // other party and no ledger row was written for them.
59
+ const minted = new Map();
60
+ const callerId = (grant) => `${grant.sessionId}.c`;
61
+ const marker = async (sessionId, credentialId) => {
62
+ const row = await deps.ledger.read(sessionId);
63
+ if (!row)
64
+ throw new EpEnvelopeError("failed-precondition", `no session row for ${sessionId}; nothing releases without its authority row (SPEC 13.6)`);
65
+ return { id: credentialId, creds: "", exp: row.exp };
66
+ };
67
+ // The serving pin core observed for THIS redemption, kept so the stage fences on the WHOLE
68
+ // observation rather than the bare {key, revision} core's hook type carries. One seam is built per
69
+ // establishment, so this holds exactly one redemption's pin and cannot be crossed with another's.
70
+ let servingPin;
71
+ const hooks = {
72
+ ledger: deps.ledger,
73
+ holderProcessEpoch: (h) => deps.holderProcessEpoch(h),
74
+ servingEpoch: (e, i) => deps.servingEpoch(e, i),
75
+ observeHolderGate: (h) => deps.observeHolderGate(h),
76
+ observeServingGate: async (e, i) => {
77
+ servingPin = await deps.observeServingGate(e, i);
78
+ return servingPin;
79
+ },
80
+ allocateCredentialIds: async (grant) => {
81
+ const cred = await deps.serving.mint(grant);
82
+ minted.set(grant.sessionId, cred);
83
+ return { credCaller: callerId(grant), credServing: cred.id };
84
+ },
85
+ // The §13.1 fence for the half this manager owns: stage the serving credential's ledger row
86
+ // REVISION-PINNED to the serving gate observed moments ago. A barrier that moved the gate makes
87
+ // this LOSE and throw, and redeemSession turns that into a refusal that releases nothing.
88
+ stagePair: async (grant, ids, pins) => {
89
+ const cred = minted.get(grant.sessionId);
90
+ if (!cred || cred.id !== ids.credServing)
91
+ throw new EpEnvelopeError("internal", `no minted serving credential for session ${grant.sessionId}; the stage cannot pin a credential that was never minted (SPEC 13.6)`);
92
+ // The pin handed to the stage MUST be the one this seam observed. Core passes the observation
93
+ // through, so this is an identity re-check, not a conversion: if it ever failed we would be
94
+ // fencing against a gate nobody in this redemption read, which is worse than not staging.
95
+ if (!servingPin || servingPin.revision !== pins.serving.revision || servingPin.key !== pins.serving.key)
96
+ throw new EpEnvelopeError("internal", `the serving gate pin staged against is not the one this redemption observed for session ${grant.sessionId} (SPEC 13.1)`);
97
+ await deps.serving.stage(grant, cred, servingPin);
98
+ },
99
+ releaseCredential: async (sessionId, credentialId) => {
100
+ const cred = minted.get(sessionId);
101
+ // Idempotent for the row's life (§13.6 lost-response retry): the SAME bytes, never a re-mint.
102
+ if (cred && cred.id === credentialId)
103
+ return cred;
104
+ return marker(sessionId, credentialId); // the caller half: no bytes to hand back (see doc)
105
+ },
106
+ revokeCredential: async (id) => {
107
+ for (const [sessionId, cred] of minted) {
108
+ if (cred.id !== id)
109
+ continue;
110
+ await deps.serving.revoke(id);
111
+ minted.delete(sessionId);
112
+ return;
113
+ }
114
+ // Not ours: the caller id, which this seam never minted and therefore cannot revoke.
115
+ },
116
+ ...(deps.now ? { now: deps.now } : {}),
117
+ };
118
+ return {
119
+ async redeem(grant, presenter) {
120
+ const now = deps.now?.() ?? Date.now();
121
+ const verified = await verifySessionGrant(grant, { space: deps.space, resolveAnchor: deps.resolveAnchor, now });
122
+ try {
123
+ await redeemSession(verified, presenter, hooks); // one-use CAS + presenter-equality (throws on refuse)
124
+ }
125
+ catch (e) {
126
+ minted.delete(verified.sessionId); // a refused redemption keeps no minted bytes
127
+ throw e;
128
+ }
129
+ return { sessionId: verified.sessionId };
130
+ },
131
+ serving: (sessionId, presenter) => retrieveServingCredential(sessionId, presenter, hooks),
132
+ };
133
+ }
134
+ /** The USER-MODE redeem seam: UNWIRED (the #29 auth-trigger slice owns the callout-minted
135
+ * per-session credential path). It REFUSES LOUD, naming the path — a user-mode attach fails rather
136
+ * than degrading to the static seam (item-6 binding no-fallback rule). */
137
+ export function userModeRedemptionSeam() {
138
+ const refuse = () => Promise.reject(new EpEnvelopeError("unimplemented", "user-mode session redemption (callout-minted per-session credentials) is not wired in this build; " +
139
+ "it is the #29 auth-trigger slice. A user-mode attach fails loud rather than degrading (SPEC 13.6, item-6 no-fallback)."));
140
+ return { redeem: refuse, serving: refuse };
141
+ }
142
+ /** The manager's in-memory session registry: the sessionId → (target lifecycle, offer) binding the
143
+ * §13.6 grant cannot carry. The serve path looks the target up here and re-checks its liveness
144
+ * before bridging, so a despawned/replaced target ends the session (`target-despawn`). */
145
+ export class ManagerSessionRegistry {
146
+ #byId = new Map();
147
+ /** Record a freshly minted offer. Throws on a sessionId collision (a fresh unguessable id never
148
+ * repeats — a repeat is a mint bug, never silently overwritten). */
149
+ record(offer) {
150
+ if (this.#byId.has(offer.grant.sessionId))
151
+ throw new EpEnvelopeError("already-exists", `session ${offer.grant.sessionId} is already recorded; a fresh sessionId never repeats (SPEC 13.6)`);
152
+ this.#byId.set(offer.grant.sessionId, { target: offer.target, grant: offer.grant });
153
+ }
154
+ /** The target bound to a sessionId, or undefined if unknown/removed. */
155
+ target(sessionId) {
156
+ return this.#byId.get(sessionId)?.target;
157
+ }
158
+ /** Drop a session (redeem-refused, closed, expired, or target gone). Idempotent. */
159
+ remove(sessionId) {
160
+ this.#byId.delete(sessionId);
161
+ }
162
+ /** Every sessionId whose target is a given agent incarnation — the set a despawn/restart must end. */
163
+ forTarget(name, lifecycleUid) {
164
+ const out = [];
165
+ for (const [id, e] of this.#byId)
166
+ if (e.target.name === name && e.target.lifecycleUid === lifecycleUid)
167
+ out.push(id);
168
+ return out;
169
+ }
170
+ get size() {
171
+ return this.#byId.size;
172
+ }
173
+ }
174
+ //# sourceMappingURL=establish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"establish.js","sourceRoot":"","sources":["../../src/session/establish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * P2 item 6 — the manager's mesh-attach session plane: the offer mint + redeem enforcement, the
3
+ * PTY ↔ rail bridge, and the application framing over the core §13.6 session composite. The
4
+ * generic composite (grant/redeem/rails/ledger) lives in `@cotal-ai/core` +
5
+ * `implementations/auth`; this module is the manager-side CONSUMER that replaces the loopback
6
+ * `ws://127.0.0.1` attach face.
7
+ */
8
+ export { mintAttachOffer, staticRedemptionSeam, userModeRedemptionSeam, ManagerSessionRegistry, type AttachOffer, type MintAttachOfferArgs, type SessionTarget, type RedemptionSeam, type SessionServing, type StaticRedemptionDeps, } from "./establish.js";
9
+ export { serveSessionBridge, type ServeSessionBridgeOpts, type SessionBridge, type AttachEndReason } from "./bridge.js";
10
+ export { ManagerSessionPlane, kvManagerSessionLedger, openSessionLedgerKv, MAX_LIVE_SESSIONS_DEFAULT, type ManagerSessionPlaneDeps, } from "./plane.js";
11
+ export { encodeTerminalData, terminalFrameBytes, decodeTerminalFrame, type TerminalFrame } from "@cotal-ai/core";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACxH,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,EACzB,KAAK,uBAAuB,GAC7B,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * P2 item 6 — the manager's mesh-attach session plane: the offer mint + redeem enforcement, the
3
+ * PTY ↔ rail bridge, and the application framing over the core §13.6 session composite. The
4
+ * generic composite (grant/redeem/rails/ledger) lives in `@cotal-ai/core` +
5
+ * `implementations/auth`; this module is the manager-side CONSUMER that replaces the loopback
6
+ * `ws://127.0.0.1` attach face.
7
+ */
8
+ export { mintAttachOffer, staticRedemptionSeam, userModeRedemptionSeam, ManagerSessionRegistry, } from "./establish.js";
9
+ export { serveSessionBridge } from "./bridge.js";
10
+ export { ManagerSessionPlane, kvManagerSessionLedger, openSessionLedgerKv, MAX_LIVE_SESSIONS_DEFAULT, } from "./plane.js";
11
+ // The terminal-session frame codec is NORMATIVE WIRE and lives in core (the §13.6 terminal-session
12
+ // profile); re-exported here for the manager's own consumers/smokes. The CLI + console import it
13
+ // straight from @cotal-ai/core (implementations cannot import each other).
14
+ export { encodeTerminalData, terminalFrameBytes, decodeTerminalFrame } from "@cotal-ai/core";
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,GAOvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAyE,MAAM,aAAa,CAAC;AACxH,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,yBAAyB,GAE1B,MAAM,YAAY,CAAC;AACpB,mGAAmG;AACnG,iGAAiG;AACjG,2EAA2E;AAC3E,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAsB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,120 @@
1
+ import type { KV } from "@nats-io/kv";
2
+ import type { NatsConnection } from "@nats-io/transport-node";
3
+ import { type AnchorResolver, type AttachSession, type SessionGrant, type SessionLedger } from "@cotal-ai/core";
4
+ import { type SessionServing, type SessionTarget } from "./establish.js";
5
+ import type { AttachEndReason } from "./bridge.js";
6
+ /** A manager-local {@link SessionLedger} over `session.<id>` rows in the DEDICATED sessions bucket
7
+ * KV (never the auth bucket — §13.9 subject-blindness confinement). Drives the SAME core row/key
8
+ * types as implementations/auth's ledger with the manager's own KV CAS: the
9
+ * one-use is `createIssuing`'s create-only CAS; finalize/terminal/revoke are revision-pinned
10
+ * updates (a lost CAS returns false / retries on the next sweep, never a silent overwrite). */
11
+ export declare function kvManagerSessionLedger(kv: KV): SessionLedger;
12
+ /** The default global live-session ceiling. Configurable per manager
13
+ * ({@link ManagerSessionPlaneDeps.maxSessions}) because the right number is deployment-shaped: the
14
+ * browser console opens a session PER PANE, so a dashboard over a large mesh legitimately holds
15
+ * many at once, and a ceiling low enough to feel like a bound would break it. */
16
+ export declare const MAX_LIVE_SESSIONS_DEFAULT = 64;
17
+ export interface ManagerSessionPlaneDeps {
18
+ space: string;
19
+ /** The serving manager incarnation — instanceId = the managerLifecycleUid, epoch = the serve grant epoch. */
20
+ serving: {
21
+ instanceId: string;
22
+ epoch: number;
23
+ };
24
+ /** The manager's `sessions`-role signer + the resolver that returns its matching anchor (the
25
+ * manager self-signs and self-verifies its own offers). */
26
+ signer: {
27
+ keyId: string;
28
+ keyPair: {
29
+ sign(input: Uint8Array): Uint8Array;
30
+ };
31
+ };
32
+ resolveAnchor: AnchorResolver;
33
+ /** The DEDICATED sessions-bucket KV holding the `session.<id>` ledger rows (never the auth bucket). */
34
+ ledgerKv: KV;
35
+ /** The per-session SERVING credential seam: mint, gate-checked stage, connection open, revoke.
36
+ * Injected because minting needs the space auth + the §13.1 gate, which live on the Manager. */
37
+ servingCredential: SessionServing;
38
+ /** Session lifetime (§13.6 live-class) + the bounded flow window. */
39
+ ttlMs: number;
40
+ window?: number;
41
+ /**
42
+ * Global ceiling on concurrently live sessions; defaults to {@link MAX_LIVE_SESSIONS_DEFAULT}.
43
+ *
44
+ * Redemption is caller-triggered and each session now mints a credential and opens a connection,
45
+ * so without a ceiling an authorized caller could drive both without bound. This is deliberately
46
+ * the plane's OWN bound rather than an inherited one: whatever route authz sits in front of
47
+ * establishment, the process-level resource limit is a number here that can be executed against.
48
+ *
49
+ * RESIDUAL, NAMED: the ceiling is GLOBAL, not per caller. It protects the process, which is what
50
+ * it is for, but it does not stop one authorized operator from exhausting it and denying sessions
51
+ * to another. That was accepted on the grounds that only authorized operators reach redemption at
52
+ * all — the attach face's console-token gate refuses before the establisher runs. If that gate is
53
+ * ever relaxed, weakened, or made optional, this scoping assumption goes with it and the ceiling
54
+ * needs a per-caller dimension.
55
+ */
56
+ maxSessions?: number;
57
+ now?(): number;
58
+ }
59
+ export declare class ManagerSessionPlane {
60
+ #private;
61
+ constructor(deps: ManagerSessionPlaneDeps);
62
+ /** Mint an offer for the authenticated attach caller, enforce its one-use redemption, and stand
63
+ * up the PTY bridge — all atomically. Returns the HOLDER-BOUND grant (the attach reply): no URL,
64
+ * and it must never be logged. Throws on a redeem refusal (a foreign presenter, an already-live
65
+ * session, a stale serving epoch). */
66
+ establishAttach(caller: {
67
+ owner: string;
68
+ actor: string;
69
+ uid: string;
70
+ }, target: SessionTarget, session: AttachSession,
71
+ /** A slot already claimed by an establisher in front of this one ({@link claimSlot}), so the
72
+ * reservation covers ITS side effects too (the PTY attach above all). Omitted = claim here. */
73
+ claimed?: {
74
+ release(): void;
75
+ }): Promise<{
76
+ grant: SessionGrant;
77
+ }>;
78
+ /** End every session bound to a target incarnation, with a distinct reason — the despawn/restart
79
+ * hook (the successor incarnation's differing epoch already refuses old-epoch redemptions; this
80
+ * proactively tears the live bridges down and surfaces the reason to each client). The bridge's
81
+ * own `onEnd` drives {@link #teardown}, so the connection and credential go with it. */
82
+ endForTarget(name: string, lifecycleUid: string, reason: AttachEndReason): void;
83
+ /** End all live sessions (manager stop / drain). */
84
+ endAll(reason: AttachEndReason): void;
85
+ /** Await every in-flight teardown (manager stop): the bridges' `onEnd` teardown is async, so a
86
+ * synchronous `endAll` alone would let the process exit with connections still draining. */
87
+ drain(reason: AttachEndReason): Promise<void>;
88
+ get liveSessions(): number;
89
+ /** The configured global ceiling (surfaced so an operator-facing status can report it). */
90
+ get maxSessions(): number;
91
+ /**
92
+ * CLAIM a session slot, or refuse loudly. ONE implementation, called by {@link establishAttach}
93
+ * and by every establisher in front of it, so the two cannot drift into disagreeing about
94
+ * capacity. It must run before ANY of: minting the offer, redeeming it, minting a per-session
95
+ * credential on either side, opening a connection, or attaching the target's PTY.
96
+ *
97
+ * THIS RESERVES, IT DOES NOT MERELY CHECK. A bare read of the live count would be check-then-act:
98
+ * establishment awaits a mint, a redemption and a connection before the session becomes live, so
99
+ * two concurrent establishes at `max - 1` would both observe room, both mint credentials, and
100
+ * both land — overshooting the ceiling by exactly the concurrency the HTTP face already has, and
101
+ * over-minting the seed-signed credentials the ceiling exists to bound. Occupancy is therefore
102
+ * live sessions PLUS in-flight reservations, and the increment happens with no `await` between
103
+ * the read and the write, so on a single-threaded runtime no second establishment can interleave.
104
+ *
105
+ * The returned release is idempotent: the caller releases on every failure path, and
106
+ * {@link establishAttach} releases at the same instant it records the live session, so a slot is
107
+ * counted once and never twice.
108
+ *
109
+ * The message names the ceiling and its current value: an operator who hits it should not have to
110
+ * read source to learn which knob to raise.
111
+ */
112
+ claimSlot(): {
113
+ release(): void;
114
+ };
115
+ }
116
+ /** Open the DEDICATED sessions-bucket KV a plane's ledger needs (the `session.<id>` rows live in
117
+ * the §13.6 session store — {@link sessionsBucket}, NOT the auth bucket). Split out so the manager
118
+ * can build the plane from its own bucket handle. */
119
+ export declare function openSessionLedgerKv(nc: NatsConnection, bucket: string): Promise<KV>;
120
+ //# sourceMappingURL=plane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plane.d.ts","sourceRoot":"","sources":["../../src/session/plane.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,aAAa,EAElB,KAAK,YAAY,EACjB,KAAK,aAAa,EAGnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAiE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAExI,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD;;;;gGAIgG;AAChG,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,EAAE,GAAG,aAAa,CA4C5D;AAED;;;kFAGkF;AAClF,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,6GAA6G;IAC7G,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C;gEAC4D;IAC5D,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE;YAAE,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAAA;SAAE,CAAA;KAAE,CAAC;IAC5E,aAAa,EAAE,cAAc,CAAC;IAC9B,uGAAuG;IACvG,QAAQ,EAAE,EAAE,CAAC;IACb;qGACiG;IACjG,iBAAiB,EAAE,cAAc,CAAC;IAClC,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,IAAI,MAAM,CAAC;CAChB;AAUD,qBAAa,mBAAmB;;gBASlB,IAAI,EAAE,uBAAuB;IAQzC;;;2CAGuC;IACjC,eAAe,CACnB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EACrD,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,aAAa;IACtB;oGACgG;IAChG,OAAO,CAAC,EAAE;QAAE,OAAO,IAAI,IAAI,CAAA;KAAE,GAC5B,OAAO,CAAC;QAAE,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC;IA0HnC;;;6FAGyF;IACzF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAI/E,oDAAoD;IACpD,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAIrC;iGAC6F;IACvF,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,2FAA2F;IAC3F,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,IAAI;QAAE,OAAO,IAAI,IAAI,CAAA;KAAE;CAUjC;AAED;;sDAEsD;AACtD,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAEzF"}