@cotal-ai/core 0.4.0 → 0.6.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 (45) hide show
  1. package/dist/acls.d.ts +45 -0
  2. package/dist/acls.d.ts.map +1 -0
  3. package/dist/acls.js +86 -0
  4. package/dist/acls.js.map +1 -0
  5. package/dist/agent-file.d.ts +7 -0
  6. package/dist/agent-file.d.ts.map +1 -1
  7. package/dist/agent-file.js +29 -2
  8. package/dist/agent-file.js.map +1 -1
  9. package/dist/channels.d.ts +13 -2
  10. package/dist/channels.d.ts.map +1 -1
  11. package/dist/channels.js +24 -1
  12. package/dist/channels.js.map +1 -1
  13. package/dist/command.d.ts +3 -0
  14. package/dist/command.d.ts.map +1 -1
  15. package/dist/endpoint.d.ts +341 -61
  16. package/dist/endpoint.d.ts.map +1 -1
  17. package/dist/endpoint.js +1178 -205
  18. package/dist/endpoint.js.map +1 -1
  19. package/dist/index.d.ts +3 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/lease.d.ts +40 -0
  24. package/dist/lease.d.ts.map +1 -0
  25. package/dist/lease.js +64 -0
  26. package/dist/lease.js.map +1 -0
  27. package/dist/members.d.ts +93 -0
  28. package/dist/members.d.ts.map +1 -0
  29. package/dist/members.js +193 -0
  30. package/dist/members.js.map +1 -0
  31. package/dist/provision.d.ts +38 -13
  32. package/dist/provision.d.ts.map +1 -1
  33. package/dist/provision.js +121 -17
  34. package/dist/provision.js.map +1 -1
  35. package/dist/streams.d.ts +48 -23
  36. package/dist/streams.d.ts.map +1 -1
  37. package/dist/streams.js +101 -32
  38. package/dist/streams.js.map +1 -1
  39. package/dist/subjects.d.ts +85 -4
  40. package/dist/subjects.d.ts.map +1 -1
  41. package/dist/subjects.js +134 -4
  42. package/dist/subjects.js.map +1 -1
  43. package/dist/types.d.ts +128 -5
  44. package/dist/types.d.ts.map +1 -1
  45. package/package.json +2 -2
package/dist/streams.d.ts CHANGED
@@ -3,6 +3,27 @@ import { type ConsumerConfig, type JetStreamManager } from "@nats-io/jetstream";
3
3
  * oldest message on a subject is discarded (`DiscardPolicy.Old`). Also the horizon of focus
4
4
  * recall: only the last {@link MAX_MSGS_PER_SUBJECT} per sender-subject are recallable. */
5
5
  export declare const MAX_MSGS_PER_SUBJECT = 1000;
6
+ /** JetStream message-dedup window on the Plane-3 streams: a `Nats-Msg-Id`
7
+ * (`<msgId>:<owner>:<generation>`) repeated within this window is collapsed. Sized generous (2h) so
8
+ * an activation-catch-up copy and a racing fan-out copy of the same message dedup even for a slow/
9
+ * backlogged owner. **This window IS the cross-path exactly-once correctness horizon** — two writes
10
+ * of the same logical copy separated by more than it (e.g. a manager crash after a DLV publish, the
11
+ * dinbox ack lost, the window expiring, then a re-transfer after restart) are NOT collapsed at the
12
+ * stream. The connector's commit-aware id-cache (`MeshAgent.ingest`) coalesces live↔durable and
13
+ * redelivery duplicates within a SESSION, but it is in-memory and reset on agent restart, so it is
14
+ * NOT a cross-restart guarantee. A persistent per-owner delivery ledger would lift the bound; not
15
+ * built (the 2h horizon covers the realistic crash/redelivery lag). Keep the window ≥ worst-case lag. */
16
+ export declare const PLANE3_DEDUP_WINDOW_MS: number;
17
+ /** Bound on the trusted reader's in-flight (un-acked) entries per owner — an offline owner with a large
18
+ * backlog can't stall the reader's own redelivery by pinning unbounded pending. */
19
+ export declare const DINBOX_MAX_ACK_PENDING = 1000;
20
+ /** Delivery-daemon single-flight lease TTL (ms) — the bucket-level `max_age` on `cotal_delivery_<space>`.
21
+ * A live holder renews at ~half this; a crashed holder stops renewing and the bucket TTL expires its
22
+ * lease key, freeing it for a fresh daemon's CAS create. Sized well above the renew interval so a brief
23
+ * GC/scheduling pause never self-evicts a healthy holder, yet short enough that a crash frees the shard
24
+ * promptly. (The bucket holds ONLY lease keys, so a bucket TTL is exact here; per-key TTL is also
25
+ * available on this stack — a deliberate simplicity choice, not a capability gap. See {@link deliveryBucket}.) */
26
+ export declare const LEASE_TTL_MS = 30000;
6
27
  export interface ClearSpaceHistoryResult {
7
28
  chat: number;
8
29
  dm?: number;
@@ -35,29 +56,6 @@ export declare function dmDurableConfig(space: string, id: string, opts?: {
35
56
  ackWaitMs?: number;
36
57
  inactiveThresholdMs?: number;
37
58
  }): Partial<ConsumerConfig>;
38
- /**
39
- * The chat live-tail durable for an instance — ONE definition, used both by the privileged
40
- * pre-create (manager/provisioner, auth mode) and the endpoint's open-mode self-create, so an
41
- * idempotent re-add can't error on a config delta. `filter_subjects` binds it to the instance's
42
- * subscribe set (`chat.*.<ch>` per channel); only the privileged creator sets it under auth, which
43
- * is the whole point — the agent BINDS-only (denied CONSUMER.CREATE/UPDATE on CHAT) and so can
44
- * never widen its own read past `allowSubscribe`. `DeliverPolicy.New` (a tail): history is the
45
- * explicit per-channel backfill on join, the only shape that can honor per-channel replay policy
46
- * given `deliver_policy` is consumer-wide.
47
- *
48
- * Multi-channel ⇒ plural `filter_subjects`, which the client sends on the filter-less create
49
- * subject (`CONSUMER.CREATE.<stream>.<name>`) — so this durable's filter is NOT ACL-pinnable by
50
- * subject; bind-only + a trusted creator is the enforcement, exactly as for DM/TASK.
51
- *
52
- * `inactive_threshold` is set ONLY when the caller passes one — the open-mode self-create, where
53
- * the agent owns the durable. The privileged auth pre-create OMITS it (same bind-only reasoning as
54
- * {@link dmDurableConfig}): a threshold would retire the durable before a late/relaunched agent
55
- * binds it, and bind would then fail permanently.
56
- */
57
- export declare function chatDurableConfig(space: string, id: string, channels: string[], opts?: {
58
- ackWaitMs?: number;
59
- inactiveThresholdMs?: number;
60
- }): Partial<ConsumerConfig>;
61
59
  /**
62
60
  * The TASK work-queue durable for a role — ONE definition, shared by the privileged
63
61
  * pre-create (auth mode) and the endpoint's open-mode self-create. The durable is shared
@@ -68,6 +66,33 @@ export declare function chatDurableConfig(space: string, id: string, channels: s
68
66
  export declare function taskDurableConfig(space: string, role: string, opts?: {
69
67
  ackWaitMs?: number;
70
68
  }): Partial<ConsumerConfig>;
69
+ /** The single privileged trusted-reader consumer over the WHOLE INBOX (mixed pre-auth) store
70
+ * (`dinbox.>`, all owners) — created + bound only by the manager. Explicit ack: the reader holds an
71
+ * entry un-acked until it has transferred the re-authorized copy to DLV (a crash before transfer
72
+ * redelivers). `max_ack_pending` bounds the reader's in-flight set. The per-message owner is
73
+ * recovered from the subject (`parseDinboxOwner`). */
74
+ export declare function inboxReaderConfig(space: string, opts?: {
75
+ ackWaitMs?: number;
76
+ shard?: number;
77
+ shards?: number;
78
+ }): Partial<ConsumerConfig>;
79
+ /** An agent's bind-only per-member DELIVER consumer (mirrors {@link dmDurableConfig}): the provisioner
80
+ * pre-creates it filtered to `dlv.<owner>`; the agent BINDS it (denied CREATE on DLV) and acks via
81
+ * native JetStream — the §8 "equivalent per-member at-least-once mechanism with the same ack
82
+ * semantics". `inactive_threshold` only for an open-mode self-create (none today; Plane-3 is
83
+ * auth-only). */
84
+ export declare function dlvDurableConfig(space: string, owner: string, opts?: {
85
+ ackWaitMs?: number;
86
+ inactiveThresholdMs?: number;
87
+ }): Partial<ConsumerConfig>;
88
+ /** The single privileged fan-out consumer on CHAT (manager-pumped; routing, not auth).
89
+ * `DeliverPolicy.New` at creation (pre-existing backlog is pre-membership); a DURABLE, so on a
90
+ * manager restart it resumes from its ack cursor and fans out the gap, idempotent via `Nats-Msg-Id`. */
91
+ export declare function fanoutDurableConfig(space: string, opts?: {
92
+ ackWaitMs?: number;
93
+ shard?: number;
94
+ shards?: number;
95
+ }): Partial<ConsumerConfig>;
71
96
  /** Connect with the given (privileged) creds, create the space's streams, and disconnect.
72
97
  * Used by `cotal up` to pre-create streams once at setup. */
73
98
  export declare function setupSpaceStreams(opts: {
@@ -1 +1 @@
1
- {"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAuB5B;;4FAE4F;AAC5F,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,cAAc,CAAC,CAUzB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,cAAc,CAAC,CAUzB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,cAAc,CAAC,CAOzB;AAED;8DAC8D;AAC9D,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhB;AAED;kDACkD;AAClD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAcnC;AAED;;;;;oFAKoF;AACpF,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB/C"}
1
+ {"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AA+B5B;;4FAE4F;AAC5F,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC;;;;;;;;;0GAS0G;AAC1G,eAAO,MAAM,sBAAsB,QAAqB,CAAC;AAEzD;oFACoF;AACpF,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C;;;;;mHAKmH;AACnH,eAAO,MAAM,YAAY,QAAS,CAAC;AAEnC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,cAAc,CAAC,CAUzB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,cAAc,CAAC,CAOzB;AAID;;;;uDAIuD;AACvD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACjE,OAAO,CAAC,cAAc,CAAC,CASzB;AAED;;;;kBAIkB;AAClB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9D,OAAO,CAAC,cAAc,CAAC,CAUzB;AAED;;yGAEyG;AACzG,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACjE,OAAO,CAAC,cAAc,CAAC,CAQzB;AAED;8DAC8D;AAC9D,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BhB;AAED;kDACkD;AAClD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAcnC;AAED;;;;;oFAKoF;AACpF,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB/C"}
package/dist/streams.js CHANGED
@@ -1,13 +1,34 @@
1
1
  import { jetstreamManager, AckPolicy, DeliverPolicy, RetentionPolicy, DiscardPolicy, StorageType, } from "@nats-io/jetstream";
2
2
  import { connect, credsAuthenticator, nanos } from "@nats-io/transport-node";
3
3
  import { Kvm } from "@nats-io/kv";
4
- import { spacePrefix, chatStream, chatSubject, chatDurable, collapseFilterSubjects, isConcreteChannel, dmStream, dmDurable, unicastSubject, taskStream, taskDurable, anycastSubject, presenceBucket, channelBucket, } from "./subjects.js";
4
+ import { spacePrefix, chatStream, chatSubject, chatWildcard, isConcreteChannel, dmStream, dmDurable, unicastSubject, taskStream, taskDurable, anycastSubject, presenceBucket, channelBucket, membersBucket, aclBucket, deliveryBucket, inboxStream, dlvStream, dlvSubject, dlvDurable, fanoutDurable, readerDurable, } from "./subjects.js";
5
5
  /** Default presence-bucket entry TTL (ms) — matches the endpoint's default liveness window. */
6
6
  const PRESENCE_TTL_MS = 6_000;
7
7
  /** Per-(sender,channel)-subject retention cap on the chat stream — the bound past which the
8
8
  * oldest message on a subject is discarded (`DiscardPolicy.Old`). Also the horizon of focus
9
9
  * recall: only the last {@link MAX_MSGS_PER_SUBJECT} per sender-subject are recallable. */
10
10
  export const MAX_MSGS_PER_SUBJECT = 1000;
11
+ /** JetStream message-dedup window on the Plane-3 streams: a `Nats-Msg-Id`
12
+ * (`<msgId>:<owner>:<generation>`) repeated within this window is collapsed. Sized generous (2h) so
13
+ * an activation-catch-up copy and a racing fan-out copy of the same message dedup even for a slow/
14
+ * backlogged owner. **This window IS the cross-path exactly-once correctness horizon** — two writes
15
+ * of the same logical copy separated by more than it (e.g. a manager crash after a DLV publish, the
16
+ * dinbox ack lost, the window expiring, then a re-transfer after restart) are NOT collapsed at the
17
+ * stream. The connector's commit-aware id-cache (`MeshAgent.ingest`) coalesces live↔durable and
18
+ * redelivery duplicates within a SESSION, but it is in-memory and reset on agent restart, so it is
19
+ * NOT a cross-restart guarantee. A persistent per-owner delivery ledger would lift the bound; not
20
+ * built (the 2h horizon covers the realistic crash/redelivery lag). Keep the window ≥ worst-case lag. */
21
+ export const PLANE3_DEDUP_WINDOW_MS = 2 * 60 * 60 * 1000;
22
+ /** Bound on the trusted reader's in-flight (un-acked) entries per owner — an offline owner with a large
23
+ * backlog can't stall the reader's own redelivery by pinning unbounded pending. */
24
+ export const DINBOX_MAX_ACK_PENDING = 1000;
25
+ /** Delivery-daemon single-flight lease TTL (ms) — the bucket-level `max_age` on `cotal_delivery_<space>`.
26
+ * A live holder renews at ~half this; a crashed holder stops renewing and the bucket TTL expires its
27
+ * lease key, freeing it for a fresh daemon's CAS create. Sized well above the renew interval so a brief
28
+ * GC/scheduling pause never self-evicts a healthy holder, yet short enough that a crash frees the shard
29
+ * promptly. (The bucket holds ONLY lease keys, so a bucket TTL is exact here; per-key TTL is also
30
+ * available on this stack — a deliberate simplicity choice, not a capability gap. See {@link deliveryBucket}.) */
31
+ export const LEASE_TTL_MS = 30_000;
11
32
  /**
12
33
  * Create (idempotently) the three backing streams for a space — CHAT (multicast backlog +
13
34
  * history), DM (per-instance inboxes), TASK (anycast work queue).
@@ -44,6 +65,29 @@ export async function createSpaceStreams(jsm, space) {
44
65
  retention: RetentionPolicy.Workqueue,
45
66
  storage: StorageType.File,
46
67
  });
68
+ // Plane-3 (SPEC §8). INBOX = the mixed pre-auth store (fan-out target; agents hold no grant — see
69
+ // permissionsFor). DLV = the per-member post-auth handoff the agent binds + acks. Both per-owner
70
+ // (one subject per owner), capped per-owner backlog (DiscardPolicy.Old; an evicted entry is a
71
+ // delivery miss, surfaced, never a satisfied durable guarantee — SPEC §7). `duplicate_window`
72
+ // collapses a catch-up/fan-out double of the same Nats-Msg-Id. No Direct Get on either.
73
+ await jsm.streams.add({
74
+ name: inboxStream(space),
75
+ subjects: [`${p}.dinbox.>`],
76
+ retention: RetentionPolicy.Limits,
77
+ storage: StorageType.File,
78
+ max_msgs_per_subject: MAX_MSGS_PER_SUBJECT,
79
+ discard: DiscardPolicy.Old,
80
+ duplicate_window: nanos(PLANE3_DEDUP_WINDOW_MS),
81
+ });
82
+ await jsm.streams.add({
83
+ name: dlvStream(space),
84
+ subjects: [`${p}.dlv.>`],
85
+ retention: RetentionPolicy.Limits,
86
+ storage: StorageType.File,
87
+ max_msgs_per_subject: MAX_MSGS_PER_SUBJECT,
88
+ discard: DiscardPolicy.Old,
89
+ duplicate_window: nanos(PLANE3_DEDUP_WINDOW_MS),
90
+ });
47
91
  }
48
92
  /**
49
93
  * The DM inbox durable for an instance — ONE definition, used both by the privileged
@@ -71,37 +115,6 @@ export function dmDurableConfig(space, id, opts = {}) {
71
115
  cfg.inactive_threshold = nanos(opts.inactiveThresholdMs);
72
116
  return cfg;
73
117
  }
74
- /**
75
- * The chat live-tail durable for an instance — ONE definition, used both by the privileged
76
- * pre-create (manager/provisioner, auth mode) and the endpoint's open-mode self-create, so an
77
- * idempotent re-add can't error on a config delta. `filter_subjects` binds it to the instance's
78
- * subscribe set (`chat.*.<ch>` per channel); only the privileged creator sets it under auth, which
79
- * is the whole point — the agent BINDS-only (denied CONSUMER.CREATE/UPDATE on CHAT) and so can
80
- * never widen its own read past `allowSubscribe`. `DeliverPolicy.New` (a tail): history is the
81
- * explicit per-channel backfill on join, the only shape that can honor per-channel replay policy
82
- * given `deliver_policy` is consumer-wide.
83
- *
84
- * Multi-channel ⇒ plural `filter_subjects`, which the client sends on the filter-less create
85
- * subject (`CONSUMER.CREATE.<stream>.<name>`) — so this durable's filter is NOT ACL-pinnable by
86
- * subject; bind-only + a trusted creator is the enforcement, exactly as for DM/TASK.
87
- *
88
- * `inactive_threshold` is set ONLY when the caller passes one — the open-mode self-create, where
89
- * the agent owns the durable. The privileged auth pre-create OMITS it (same bind-only reasoning as
90
- * {@link dmDurableConfig}): a threshold would retire the durable before a late/relaunched agent
91
- * binds it, and bind would then fail permanently.
92
- */
93
- export function chatDurableConfig(space, id, channels, opts = {}) {
94
- const cfg = {
95
- durable_name: chatDurable(id),
96
- filter_subjects: collapseFilterSubjects(channels.map((ch) => chatSubject(space, "*", ch))),
97
- ack_policy: AckPolicy.Explicit,
98
- ack_wait: nanos(opts.ackWaitMs ?? 60_000),
99
- deliver_policy: DeliverPolicy.New,
100
- };
101
- if (opts.inactiveThresholdMs)
102
- cfg.inactive_threshold = nanos(opts.inactiveThresholdMs);
103
- return cfg;
104
- }
105
118
  /**
106
119
  * The TASK work-queue durable for a role — ONE definition, shared by the privileged
107
120
  * pre-create (auth mode) and the endpoint's open-mode self-create. The durable is shared
@@ -117,6 +130,51 @@ export function taskDurableConfig(space, role, opts = {}) {
117
130
  ack_wait: nanos(opts.ackWaitMs ?? 60_000),
118
131
  };
119
132
  }
133
+ // ---- Plane-3 consumers (SPEC §8) ----
134
+ /** The single privileged trusted-reader consumer over the WHOLE INBOX (mixed pre-auth) store
135
+ * (`dinbox.>`, all owners) — created + bound only by the manager. Explicit ack: the reader holds an
136
+ * entry un-acked until it has transferred the re-authorized copy to DLV (a crash before transfer
137
+ * redelivers). `max_ack_pending` bounds the reader's in-flight set. The per-message owner is
138
+ * recovered from the subject (`parseDinboxOwner`). */
139
+ export function inboxReaderConfig(space, opts = {}) {
140
+ return {
141
+ durable_name: readerDurable(opts.shard, opts.shards),
142
+ filter_subject: `${spacePrefix(space)}.dinbox.>`,
143
+ ack_policy: AckPolicy.Explicit,
144
+ ack_wait: nanos(opts.ackWaitMs ?? 60_000),
145
+ deliver_policy: DeliverPolicy.All,
146
+ max_ack_pending: DINBOX_MAX_ACK_PENDING,
147
+ };
148
+ }
149
+ /** An agent's bind-only per-member DELIVER consumer (mirrors {@link dmDurableConfig}): the provisioner
150
+ * pre-creates it filtered to `dlv.<owner>`; the agent BINDS it (denied CREATE on DLV) and acks via
151
+ * native JetStream — the §8 "equivalent per-member at-least-once mechanism with the same ack
152
+ * semantics". `inactive_threshold` only for an open-mode self-create (none today; Plane-3 is
153
+ * auth-only). */
154
+ export function dlvDurableConfig(space, owner, opts = {}) {
155
+ const cfg = {
156
+ durable_name: dlvDurable(owner),
157
+ filter_subject: dlvSubject(space, owner),
158
+ ack_policy: AckPolicy.Explicit,
159
+ ack_wait: nanos(opts.ackWaitMs ?? 60_000),
160
+ deliver_policy: DeliverPolicy.All,
161
+ };
162
+ if (opts.inactiveThresholdMs)
163
+ cfg.inactive_threshold = nanos(opts.inactiveThresholdMs);
164
+ return cfg;
165
+ }
166
+ /** The single privileged fan-out consumer on CHAT (manager-pumped; routing, not auth).
167
+ * `DeliverPolicy.New` at creation (pre-existing backlog is pre-membership); a DURABLE, so on a
168
+ * manager restart it resumes from its ack cursor and fans out the gap, idempotent via `Nats-Msg-Id`. */
169
+ export function fanoutDurableConfig(space, opts = {}) {
170
+ return {
171
+ durable_name: fanoutDurable(opts.shard, opts.shards),
172
+ filter_subject: chatWildcard(space),
173
+ ack_policy: AckPolicy.Explicit,
174
+ ack_wait: nanos(opts.ackWaitMs ?? 60_000),
175
+ deliver_policy: DeliverPolicy.New,
176
+ };
177
+ }
120
178
  /** Connect with the given (privileged) creds, create the space's streams, and disconnect.
121
179
  * Used by `cotal up` to pre-create streams once at setup. */
122
180
  export async function setupSpaceStreams(opts) {
@@ -132,6 +190,17 @@ export async function setupSpaceStreams(opts) {
132
190
  const kvm = new Kvm(nc);
133
191
  await kvm.create(presenceBucket(opts.space), { ttl: PRESENCE_TTL_MS });
134
192
  await kvm.create(channelBucket(opts.space));
193
+ // Durable-membership registry (Plane-3): privileged-write, no TTL (durable config, like the
194
+ // channel registry). Pre-created so the delivery daemon (and open-mode self) can OPEN it; agents
195
+ // hold no grant. Idempotent.
196
+ await kvm.create(membersBucket(opts.space));
197
+ // Durable read-ACL registry (Plane-3 keystone): privileged-write, no TTL. The manager records an
198
+ // agent's read ACL here at mint; the delivery daemon re-auths every durable entry against it.
199
+ await kvm.create(aclBucket(opts.space));
200
+ // Delivery-daemon single-flight lease + readiness bucket: bucket-level TTL (`max_age`) so a crashed
201
+ // holder's lease auto-expires and a fresh daemon can re-acquire. Holds ONLY lease keys, writable
202
+ // only by the `delivery` cred, world-readable (the non-gating delivery-health surface). Idempotent.
203
+ await kvm.create(deliveryBucket(opts.space), { ttl: LEASE_TTL_MS });
135
204
  }
136
205
  finally {
137
206
  await nc.drain();
@@ -1 +1 @@
1
- {"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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"}
@@ -69,6 +69,15 @@ export declare function controlServiceSubject(space: string, service: string, se
69
69
  export declare const CONTROL_PRIVILEGED: "manager";
70
70
  export declare const CONTROL_SELF_SERVICE: "self";
71
71
  export declare const CONTROL_ADMIN: "admin";
72
+ /** The delivery service — a control service served by the server-side **delivery daemon** (NOT the
73
+ * manager), carrying the runtime durable `join` / `leave` / `listMemberships` ops agents call. Agents
74
+ * publish a request to `ctl.delivery.<agentId>` and receive the reply on `ctl.delivery.<agentId>.…`,
75
+ * a subtree both sides scope tightly: the agent gets pub on `ctl.delivery.<id>` + sub on
76
+ * `ctl.delivery.<id>.>`, and the daemon gets sub on `ctl.delivery.*` (queue) + pub on `ctl.delivery.>`
77
+ * (replies). This keeps the daemon least-privilege — it never needs broad inbox-publish to answer an
78
+ * agent (only the allow-all manager could reply into the per-id `_INBOX_<id>` prefix). Lifecycle ops
79
+ * (spawn/stop/despawn) stay on the manager's tiers; durable membership is the daemon's. */
80
+ export declare const CONTROL_DELIVERY: "delivery";
72
81
  /** The three control-plane tiers the manager serves — values tie to the `CONTROL_*` service
73
82
  * names so handler routing can't drift from the subject names. */
74
83
  export type ControlTier = typeof CONTROL_PRIVILEGED | typeof CONTROL_SELF_SERVICE | typeof CONTROL_ADMIN;
@@ -114,18 +123,90 @@ export declare function channelBucket(space: string): string;
114
123
  * character (`/^[-/=.\w]+$/`) but one `token()` can never produce (it maps every char
115
124
  * outside `[A-Za-z0-9_-]` to `_`), so this key can never collide with a real channel. */
116
125
  export declare const CHANNEL_DEFAULTS_KEY = "=defaults";
126
+ /** Name of the KV bucket holding the durable-membership registry (Plane-3) for a space — a
127
+ * privileged-write sibling of the channels/presence buckets. One record per (concrete channel,
128
+ * owner) under {@link memberKey}; the source of truth for `channelMembers()` and the fan-out's
129
+ * member list, moved off JetStream consumer topology (which core-sub joins don't create). */
130
+ export declare function membersBucket(space: string): string;
131
+ /** KV key for one membership record: `<channel>/<owner>`. The channel is concrete (no `*`/`>`,
132
+ * validated at the write path) so it is dotted-but-`/`-free, and an owner id is an nkey
133
+ * (`[A-Z0-9]`, also `/`-free), so the single `/` separates them unambiguously — both halves
134
+ * recover via {@link parseMemberKey}. `/`, `.`, and `[A-Za-z0-9_-]` are all legal KV-key chars
135
+ * (`/^[-/=.\w]+$/`), so no encoding is needed. */
136
+ export declare function memberKey(channel: string, owner: string): string;
137
+ /** Inverse of {@link memberKey}: split a member key back into `{ channel, owner }`, or `null` if
138
+ * it isn't one (no `/`). Splits on the single separator — channels and owner ids are both `/`-free. */
139
+ export declare function parseMemberKey(key: string): {
140
+ channel: string;
141
+ owner: string;
142
+ } | null;
143
+ /** Name of the KV bucket holding the durable read-ACL registry (Plane-3) for a space — a
144
+ * privileged-write sibling of the members/channels buckets. One record per OWNER (key = owner id),
145
+ * holding that owner's current read ACL (`allowSubscribe`). The delivery daemon's trusted reader
146
+ * re-authorizes every durable entry against this — moved off the manager's in-memory ledger so a
147
+ * stateless, server-side daemon re-reads it on boot (fixes the restart-fragility nak-loop). It is
148
+ * ALSO what the daemon validates a runtime durable-join against (channel ∈ the owner's ACL). */
149
+ export declare function aclBucket(space: string): string;
150
+ /** KV key for one owner's read-ACL record: the owner id (an nkey — `[A-Z0-9]`, `/`-free, a `token()`
151
+ * no-op; keyed like presence, which uses the bare id). */
152
+ export declare function aclKey(owner: string): string;
153
+ /** Name of the KV bucket holding the delivery daemon's single-flight lease + readiness signal for a
154
+ * space. One key per shard ({@link leaseKey}); writable only by the `delivery` cred, world-readable
155
+ * (an agent reads it for the non-gating delivery-health surface). The bucket holds ONLY lease keys,
156
+ * so a bucket-level TTL (`max_age`) cleanly expires a crashed holder's lease. (Per-key KV TTL via
157
+ * `Nats-TTL`/marker TTL is also available on this stack — `@nats-io/kv` 3.4 + server 2.14 — so the
158
+ * bucket-level TTL is a deliberate simplicity choice for a one-purpose bucket, not a capability gap.) */
159
+ export declare function deliveryBucket(space: string): string;
160
+ /** KV key for one shard's delivery lease/readiness (N=1 → `lease.0`). */
161
+ export declare function leaseKey(shardIndex: number): string;
162
+ /** Deterministic FNV-1a (32-bit) hash of `key` into `[0, n)` — stable across processes/restarts, so a
163
+ * shard assignment never moves under a running daemon. The Plane-3 partition seam (sharding):
164
+ * **N=1 is the only operating mode shipped** (`shards > 1` is hard-rejected at the daemon entrypoint)
165
+ * because a hash partition is not expressible as a NATS `sub.allow`/durable filter under the flat chat
166
+ * grammar — see core-sub-fabric.md. Present so the N>1 follow-up (with a channel-prefix grammar) is a
167
+ * small diff. */
168
+ export declare function partition(n: number, key: string): number;
117
169
  /** Stream capturing `chat.>` — multicast backlog + history. */
118
170
  export declare function chatStream(space: string): string;
119
171
  /** Stream capturing `inst.>` — per-instance direct-message inboxes. */
120
172
  export declare function dmStream(space: string): string;
121
173
  /** Stream capturing `svc.>` — anycast work queue. */
122
174
  export declare function taskStream(space: string): string;
123
- /** Durable consumer name for an instance's view of the chat stream its live tail. */
175
+ /** Stream capturing `dinbox.>` the per-owner mixed durable inbox (fan-out target; agent unreadable). */
176
+ export declare function inboxStream(space: string): string;
177
+ /** Stream capturing `dlv.>` — the per-member post-auth delivery store (agent binds + acks). */
178
+ export declare function dlvStream(space: string): string;
179
+ /** Subject of an owner's mixed durable inbox: `cotal.<space>.dinbox.<owner>` (one per owner). */
180
+ export declare function dinboxSubject(space: string, owner: string): string;
181
+ /** Subject of an owner's post-auth delivery: `cotal.<space>.dlv.<owner>` (one per owner). */
182
+ export declare function dlvSubject(space: string, owner: string): string;
183
+ /** Parse the owner id out of an owner's mixed-inbox subject `cotal.<space>.dinbox.<owner>`, or null.
184
+ * The trusted reader is a SINGLE consumer over `dinbox.>` (all owners), so it recovers the per-message
185
+ * owner from the subject (the routing token is `routeToken(owner)` — an nkey, a `token()` no-op). */
186
+ export declare function parseDinboxOwner(subject: string): string | null;
187
+ /** An agent's bind-only per-owner consumer on {@link dlvStream} (filter `dlv.<owner>`). */
188
+ export declare function dlvDurable(owner: string): string;
189
+ /** The single privileged fan-out consumer on the CHAT stream (delivery-daemon-pumped; routing, not
190
+ * auth). N=1 keeps this exact name (see {@link fanoutDurable}). */
191
+ export declare const FANOUT_DURABLE: "fanout";
192
+ /** The single privileged trusted-reader consumer on {@link inboxStream} (filter `dinbox.>`,
193
+ * delivery-daemon-pumped). It re-authorizes each entry and transfers the authorized copy to
194
+ * `dlv.<owner>`. N=1 keeps this exact name (see {@link readerDurable}). */
195
+ export declare const INBOX_READER_DURABLE: "reader";
196
+ /** Per-shard fan-out durable name (the sharding seam). N=1 (`shards <= 1`) keeps the exact legacy
197
+ * name `fanout` so a running space's existing durable + ack cursor carry over; N>1 (deferred until
198
+ * the channel-prefix grammar) → `fanout_<i>`. */
199
+ export declare function fanoutDurable(shard?: number, shards?: number): string;
200
+ /** Per-shard trusted-reader durable name (the sharding seam). N=1 keeps `reader`; N>1 → `reader_<i>`. */
201
+ export declare function readerDurable(shard?: number, shards?: number): string;
202
+ /** Name of the REMOVED per-instance chat live-tail durable. Retained only as the canonical name the
203
+ * read-ACL conformance test asserts an agent can NOT create — it has no live callers, the live read is
204
+ * now a native core subscription. */
124
205
  export declare function chatDurable(instance: string): string;
125
206
  /** Consumer name for an instance's short-lived chat **history** reads (join-backfill, focus-recall,
126
- * drop-marker). A single per-instance name (not the live `chat_<id>`) so its create/info/fetch/
127
- * delete grants are name-scoped to the agent's own id — a peer can never bind it — while the
128
- * per-read single `filter_subject` is what the create-time ACL pins to `allowSubscribe`. */
207
+ * drop-marker). A single per-instance name, scoped to the agent's own id so its create/info/fetch/
208
+ * delete grants name-scope to that id — a peer can never bind it — while the per-read single
209
+ * `filter_subject` is what the create-time ACL pins to `allowSubscribe`. */
129
210
  export declare function chatHistDurable(instance: string): string;
130
211
  /** Durable consumer name for an instance's private DM inbox. */
131
212
  export declare function dmDurable(instance: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"subjects.d.ts","sourceRoot":"","sources":["../src/subjects.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subjects.d.ts","sourceRoot":"","sources":["../src/subjects.ts"],"names":[],"mappings":"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"}
package/dist/subjects.js CHANGED
@@ -155,6 +155,15 @@ export function controlServiceSubject(space, service, sender) {
155
155
  export const CONTROL_PRIVILEGED = "manager";
156
156
  export const CONTROL_SELF_SERVICE = "self";
157
157
  export const CONTROL_ADMIN = "admin";
158
+ /** The delivery service — a control service served by the server-side **delivery daemon** (NOT the
159
+ * manager), carrying the runtime durable `join` / `leave` / `listMemberships` ops agents call. Agents
160
+ * publish a request to `ctl.delivery.<agentId>` and receive the reply on `ctl.delivery.<agentId>.…`,
161
+ * a subtree both sides scope tightly: the agent gets pub on `ctl.delivery.<id>` + sub on
162
+ * `ctl.delivery.<id>.>`, and the daemon gets sub on `ctl.delivery.*` (queue) + pub on `ctl.delivery.>`
163
+ * (replies). This keeps the daemon least-privilege — it never needs broad inbox-publish to answer an
164
+ * agent (only the allow-all manager could reply into the per-id `_INBOX_<id>` prefix). Lifecycle ops
165
+ * (spawn/stop/despawn) stay on the manager's tiers; durable membership is the daemon's. */
166
+ export const CONTROL_DELIVERY = "delivery";
158
167
  export function traceSubject(space, agentId) {
159
168
  return `${spacePrefix(space)}.trace.${token(agentId)}`;
160
169
  }
@@ -220,6 +229,72 @@ export function channelBucket(space) {
220
229
  * character (`/^[-/=.\w]+$/`) but one `token()` can never produce (it maps every char
221
230
  * outside `[A-Za-z0-9_-]` to `_`), so this key can never collide with a real channel. */
222
231
  export const CHANNEL_DEFAULTS_KEY = "=defaults";
232
+ /** Name of the KV bucket holding the durable-membership registry (Plane-3) for a space — a
233
+ * privileged-write sibling of the channels/presence buckets. One record per (concrete channel,
234
+ * owner) under {@link memberKey}; the source of truth for `channelMembers()` and the fan-out's
235
+ * member list, moved off JetStream consumer topology (which core-sub joins don't create). */
236
+ export function membersBucket(space) {
237
+ return `cotal_members_${token(space)}`;
238
+ }
239
+ /** KV key for one membership record: `<channel>/<owner>`. The channel is concrete (no `*`/`>`,
240
+ * validated at the write path) so it is dotted-but-`/`-free, and an owner id is an nkey
241
+ * (`[A-Z0-9]`, also `/`-free), so the single `/` separates them unambiguously — both halves
242
+ * recover via {@link parseMemberKey}. `/`, `.`, and `[A-Za-z0-9_-]` are all legal KV-key chars
243
+ * (`/^[-/=.\w]+$/`), so no encoding is needed. */
244
+ export function memberKey(channel, owner) {
245
+ return `${channel}/${owner}`;
246
+ }
247
+ /** Inverse of {@link memberKey}: split a member key back into `{ channel, owner }`, or `null` if
248
+ * it isn't one (no `/`). Splits on the single separator — channels and owner ids are both `/`-free. */
249
+ export function parseMemberKey(key) {
250
+ const i = key.indexOf("/");
251
+ if (i <= 0 || i >= key.length - 1)
252
+ return null;
253
+ return { channel: key.slice(0, i), owner: key.slice(i + 1) };
254
+ }
255
+ /** Name of the KV bucket holding the durable read-ACL registry (Plane-3) for a space — a
256
+ * privileged-write sibling of the members/channels buckets. One record per OWNER (key = owner id),
257
+ * holding that owner's current read ACL (`allowSubscribe`). The delivery daemon's trusted reader
258
+ * re-authorizes every durable entry against this — moved off the manager's in-memory ledger so a
259
+ * stateless, server-side daemon re-reads it on boot (fixes the restart-fragility nak-loop). It is
260
+ * ALSO what the daemon validates a runtime durable-join against (channel ∈ the owner's ACL). */
261
+ export function aclBucket(space) {
262
+ return `cotal_acl_${token(space)}`;
263
+ }
264
+ /** KV key for one owner's read-ACL record: the owner id (an nkey — `[A-Z0-9]`, `/`-free, a `token()`
265
+ * no-op; keyed like presence, which uses the bare id). */
266
+ export function aclKey(owner) {
267
+ return token(owner);
268
+ }
269
+ /** Name of the KV bucket holding the delivery daemon's single-flight lease + readiness signal for a
270
+ * space. One key per shard ({@link leaseKey}); writable only by the `delivery` cred, world-readable
271
+ * (an agent reads it for the non-gating delivery-health surface). The bucket holds ONLY lease keys,
272
+ * so a bucket-level TTL (`max_age`) cleanly expires a crashed holder's lease. (Per-key KV TTL via
273
+ * `Nats-TTL`/marker TTL is also available on this stack — `@nats-io/kv` 3.4 + server 2.14 — so the
274
+ * bucket-level TTL is a deliberate simplicity choice for a one-purpose bucket, not a capability gap.) */
275
+ export function deliveryBucket(space) {
276
+ return `cotal_delivery_${token(space)}`;
277
+ }
278
+ /** KV key for one shard's delivery lease/readiness (N=1 → `lease.0`). */
279
+ export function leaseKey(shardIndex) {
280
+ return `lease.${shardIndex}`;
281
+ }
282
+ /** Deterministic FNV-1a (32-bit) hash of `key` into `[0, n)` — stable across processes/restarts, so a
283
+ * shard assignment never moves under a running daemon. The Plane-3 partition seam (sharding):
284
+ * **N=1 is the only operating mode shipped** (`shards > 1` is hard-rejected at the daemon entrypoint)
285
+ * because a hash partition is not expressible as a NATS `sub.allow`/durable filter under the flat chat
286
+ * grammar — see core-sub-fabric.md. Present so the N>1 follow-up (with a channel-prefix grammar) is a
287
+ * small diff. */
288
+ export function partition(n, key) {
289
+ if (n <= 1)
290
+ return 0;
291
+ let h = 0x811c9dc5;
292
+ for (let i = 0; i < key.length; i++) {
293
+ h ^= key.charCodeAt(i);
294
+ h = Math.imul(h, 0x01000193);
295
+ }
296
+ return (h >>> 0) % n;
297
+ }
223
298
  // ---- JetStream streams (the durable backing for the three delivery modes) ----
224
299
  /** Stream capturing `chat.>` — multicast backlog + history. */
225
300
  export function chatStream(space) {
@@ -233,14 +308,69 @@ export function dmStream(space) {
233
308
  export function taskStream(space) {
234
309
  return `TASK_${token(space)}`;
235
310
  }
236
- /** Durable consumer name for an instance's view of the chat stream its live tail. */
311
+ // ---- Plane-3 (durable backstop, SPEC §8)two per-space streams ----
312
+ //
313
+ // `dinbox.<owner>` is the MIXED pre-auth store (fan-out target): the agent holds NO grant on
314
+ // {@link inboxStream} and the trusted reader (the delivery daemon) is its only consumer. `dlv.<owner>` is the
315
+ // per-member POST-auth handoff: the reader transfers each re-authorized copy here and the agent binds
316
+ // {@link dlvDurable} bind-only and acks it via native JetStream (§8 "an equivalent per-member
317
+ // at-least-once mechanism with the same ack semantics"). `dlv` carries channel messages only, so the
318
+ // receiver derives `kind=channel` from the delivery path — no payload/header kind (SPEC §4).
319
+ /** Stream capturing `dinbox.>` — the per-owner mixed durable inbox (fan-out target; agent unreadable). */
320
+ export function inboxStream(space) {
321
+ return `INBOX_${token(space)}`;
322
+ }
323
+ /** Stream capturing `dlv.>` — the per-member post-auth delivery store (agent binds + acks). */
324
+ export function dlvStream(space) {
325
+ return `DLV_${token(space)}`;
326
+ }
327
+ /** Subject of an owner's mixed durable inbox: `cotal.<space>.dinbox.<owner>` (one per owner). */
328
+ export function dinboxSubject(space, owner) {
329
+ return `${spacePrefix(space)}.dinbox.${routeToken(owner)}`;
330
+ }
331
+ /** Subject of an owner's post-auth delivery: `cotal.<space>.dlv.<owner>` (one per owner). */
332
+ export function dlvSubject(space, owner) {
333
+ return `${spacePrefix(space)}.dlv.${routeToken(owner)}`;
334
+ }
335
+ /** Parse the owner id out of an owner's mixed-inbox subject `cotal.<space>.dinbox.<owner>`, or null.
336
+ * The trusted reader is a SINGLE consumer over `dinbox.>` (all owners), so it recovers the per-message
337
+ * owner from the subject (the routing token is `routeToken(owner)` — an nkey, a `token()` no-op). */
338
+ export function parseDinboxOwner(subject) {
339
+ const parts = subject.split(".");
340
+ // cotal.<space>.dinbox.<owner>
341
+ return parts.length === 4 && parts[0] === ROOT && parts[2] === "dinbox" ? parts[3] : null;
342
+ }
343
+ /** An agent's bind-only per-owner consumer on {@link dlvStream} (filter `dlv.<owner>`). */
344
+ export function dlvDurable(owner) {
345
+ return `dlv_${token(owner)}`;
346
+ }
347
+ /** The single privileged fan-out consumer on the CHAT stream (delivery-daemon-pumped; routing, not
348
+ * auth). N=1 keeps this exact name (see {@link fanoutDurable}). */
349
+ export const FANOUT_DURABLE = "fanout";
350
+ /** The single privileged trusted-reader consumer on {@link inboxStream} (filter `dinbox.>`,
351
+ * delivery-daemon-pumped). It re-authorizes each entry and transfers the authorized copy to
352
+ * `dlv.<owner>`. N=1 keeps this exact name (see {@link readerDurable}). */
353
+ export const INBOX_READER_DURABLE = "reader";
354
+ /** Per-shard fan-out durable name (the sharding seam). N=1 (`shards <= 1`) keeps the exact legacy
355
+ * name `fanout` so a running space's existing durable + ack cursor carry over; N>1 (deferred until
356
+ * the channel-prefix grammar) → `fanout_<i>`. */
357
+ export function fanoutDurable(shard = 0, shards = 1) {
358
+ return shards <= 1 ? FANOUT_DURABLE : `${FANOUT_DURABLE}_${shard}`;
359
+ }
360
+ /** Per-shard trusted-reader durable name (the sharding seam). N=1 keeps `reader`; N>1 → `reader_<i>`. */
361
+ export function readerDurable(shard = 0, shards = 1) {
362
+ return shards <= 1 ? INBOX_READER_DURABLE : `${INBOX_READER_DURABLE}_${shard}`;
363
+ }
364
+ /** Name of the REMOVED per-instance chat live-tail durable. Retained only as the canonical name the
365
+ * read-ACL conformance test asserts an agent can NOT create — it has no live callers, the live read is
366
+ * now a native core subscription. */
237
367
  export function chatDurable(instance) {
238
368
  return `chat_${token(instance)}`;
239
369
  }
240
370
  /** Consumer name for an instance's short-lived chat **history** reads (join-backfill, focus-recall,
241
- * drop-marker). A single per-instance name (not the live `chat_<id>`) so its create/info/fetch/
242
- * delete grants are name-scoped to the agent's own id — a peer can never bind it — while the
243
- * per-read single `filter_subject` is what the create-time ACL pins to `allowSubscribe`. */
371
+ * drop-marker). A single per-instance name, scoped to the agent's own id so its create/info/fetch/
372
+ * delete grants name-scope to that id — a peer can never bind it — while the per-read single
373
+ * `filter_subject` is what the create-time ACL pins to `allowSubscribe`. */
244
374
  export function chatHistDurable(instance) {
245
375
  return `chathist_${token(instance)}`;
246
376
  }