@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.
- package/dist/acls.d.ts +45 -0
- package/dist/acls.d.ts.map +1 -0
- package/dist/acls.js +86 -0
- package/dist/acls.js.map +1 -0
- package/dist/agent-file.d.ts +7 -0
- package/dist/agent-file.d.ts.map +1 -1
- package/dist/agent-file.js +29 -2
- package/dist/agent-file.js.map +1 -1
- package/dist/channels.d.ts +13 -2
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +24 -1
- package/dist/channels.js.map +1 -1
- package/dist/command.d.ts +3 -0
- package/dist/command.d.ts.map +1 -1
- package/dist/endpoint.d.ts +341 -61
- package/dist/endpoint.d.ts.map +1 -1
- package/dist/endpoint.js +1178 -205
- package/dist/endpoint.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lease.d.ts +40 -0
- package/dist/lease.d.ts.map +1 -0
- package/dist/lease.js +64 -0
- package/dist/lease.js.map +1 -0
- package/dist/members.d.ts +93 -0
- package/dist/members.d.ts.map +1 -0
- package/dist/members.js +193 -0
- package/dist/members.js.map +1 -0
- package/dist/provision.d.ts +38 -13
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +121 -17
- package/dist/provision.js.map +1 -1
- package/dist/streams.d.ts +48 -23
- package/dist/streams.d.ts.map +1 -1
- package/dist/streams.js +101 -32
- package/dist/streams.js.map +1 -1
- package/dist/subjects.d.ts +85 -4
- package/dist/subjects.d.ts.map +1 -1
- package/dist/subjects.js +134 -4
- package/dist/subjects.js.map +1 -1
- package/dist/types.d.ts +128 -5
- package/dist/types.d.ts.map +1 -1
- 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: {
|
package/dist/streams.d.ts.map
CHANGED
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"streams.d.ts","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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"}
|
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,
|
|
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();
|
package/dist/streams.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,eAAe,EACf,aAAa,EACb,WAAW,GAGZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,
|
|
1
|
+
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../src/streams.ts"],"names":[],"mappings":"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"}
|
package/dist/subjects.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
|
127
|
-
* delete grants
|
|
128
|
-
*
|
|
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;
|
package/dist/subjects.d.ts.map
CHANGED
|
@@ -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
|
-
|
|
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
|
|
242
|
-
* delete grants
|
|
243
|
-
*
|
|
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
|
}
|