@cello-protocol/daemon 0.0.228 → 0.0.230
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/channel-collect-tick.d.ts +36 -0
- package/dist/channel-collect-tick.d.ts.map +1 -0
- package/dist/channel-collect-tick.js +133 -0
- package/dist/channel-collect-tick.js.map +1 -0
- package/dist/channel-collector.d.ts +99 -0
- package/dist/channel-collector.d.ts.map +1 -0
- package/dist/channel-collector.js +249 -0
- package/dist/channel-collector.js.map +1 -0
- package/dist/channel-config-store.d.ts +33 -0
- package/dist/channel-config-store.d.ts.map +1 -0
- package/dist/channel-config-store.js +61 -0
- package/dist/channel-config-store.js.map +1 -0
- package/dist/channel-inbox-store.d.ts +46 -0
- package/dist/channel-inbox-store.d.ts.map +1 -0
- package/dist/channel-inbox-store.js +88 -0
- package/dist/channel-inbox-store.js.map +1 -0
- package/dist/channel-log-store.d.ts +14 -0
- package/dist/channel-log-store.d.ts.map +1 -1
- package/dist/channel-log-store.js +58 -0
- package/dist/channel-log-store.js.map +1 -1
- package/dist/channel-publish-handlers.d.ts +42 -0
- package/dist/channel-publish-handlers.d.ts.map +1 -0
- package/dist/channel-publish-handlers.js +212 -0
- package/dist/channel-publish-handlers.js.map +1 -0
- package/dist/channel-publish-wiring.d.ts +41 -0
- package/dist/channel-publish-wiring.d.ts.map +1 -0
- package/dist/channel-publish-wiring.js +124 -0
- package/dist/channel-publish-wiring.js.map +1 -0
- package/dist/channel-publisher.d.ts +189 -0
- package/dist/channel-publisher.d.ts.map +1 -0
- package/dist/channel-publisher.js +394 -0
- package/dist/channel-publisher.js.map +1 -0
- package/dist/channel-relay-client.d.ts +88 -0
- package/dist/channel-relay-client.d.ts.map +1 -0
- package/dist/channel-relay-client.js +180 -0
- package/dist/channel-relay-client.js.map +1 -0
- package/dist/channel-subscription-store.d.ts +66 -0
- package/dist/channel-subscription-store.d.ts.map +1 -0
- package/dist/channel-subscription-store.js +109 -0
- package/dist/channel-subscription-store.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +14 -0
- package/dist/daemon.js.map +1 -1
- package/dist/gateway-config-handlers.d.ts +2 -2
- package/dist/gateway-config-handlers.d.ts.map +1 -1
- package/dist/gateway-config-handlers.js +37 -12
- package/dist/gateway-config-handlers.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { decodeRelayPostReceipt, encodeBroadcastArtifact, encodeChannelInfo, signBroadcastArtifact, signChannelInfo, verifyRelayPostReceipt, buildChannelPruneTbs, } from "@cello-protocol/protocol-types";
|
|
2
|
+
/**
|
|
3
|
+
* Gap between deposits during a refill, so a long backlog stays under the relay's per-publisher
|
|
4
|
+
* rate limit. Only `resendMissing` paces — an ordinary publish is two deposits and pacing it would
|
|
5
|
+
* add latency to every post to protect against a burst that cannot happen.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_RESEND_PACE_MS = 50;
|
|
8
|
+
export class ChannelPublisher {
|
|
9
|
+
#opts;
|
|
10
|
+
#now;
|
|
11
|
+
constructor(opts) {
|
|
12
|
+
this.#opts = opts;
|
|
13
|
+
this.#now = opts.now ?? (() => Date.now());
|
|
14
|
+
}
|
|
15
|
+
async publish(agentName, channelHex, title, body, correlationId) {
|
|
16
|
+
const { logger, log } = this.#opts;
|
|
17
|
+
const info = this.#opts.channelInfo(channelHex);
|
|
18
|
+
if (!info)
|
|
19
|
+
return { ok: false, reason: "channel_unknown", detail: "no channel info for that pubkey" };
|
|
20
|
+
const channelKey = this.#opts.getChannelKey(channelHex);
|
|
21
|
+
const agentKey = this.#opts.getAgentKey(agentName);
|
|
22
|
+
if (!channelKey || !agentKey) {
|
|
23
|
+
return { ok: false, reason: "key_unavailable", detail: "the channel key or the agent key is not loaded" };
|
|
24
|
+
}
|
|
25
|
+
// 1. Screen BEFORE anything is signed or stored. The subscriber's inbound screen is the
|
|
26
|
+
// enforcement; this is the early check that spares an honest publisher the friction of
|
|
27
|
+
// publishing something every reader will refuse.
|
|
28
|
+
const verdict = await this.#opts.screenOutbound(new TextEncoder().encode(`${title}\n${body}`), { agentName, ...(correlationId !== undefined ? { correlationId } : {}) });
|
|
29
|
+
if (verdict.disposition !== "allow") {
|
|
30
|
+
logger.info("channel.publish.refused", {
|
|
31
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
32
|
+
channel_pubkey: channelHex, disposition: verdict.disposition, reason: verdict.reason ?? "blocked",
|
|
33
|
+
});
|
|
34
|
+
return { ok: false, reason: "blocked_by_screen", detail: verdict.reason ?? verdict.disposition };
|
|
35
|
+
}
|
|
36
|
+
// 2. A public channel is readable by anyone, so encrypting it would be theatre — and would lock
|
|
37
|
+
// out the subscribers it exists for, who hold no key.
|
|
38
|
+
const plaintext = new TextEncoder().encode(body);
|
|
39
|
+
const wire = info.access === "public" ? plaintext : await this.#opts.encryptBody(plaintext, channelHex);
|
|
40
|
+
// 3. Position and signatures.
|
|
41
|
+
log.ensureChannel(channelHex);
|
|
42
|
+
const { seq } = log.nextPosition(channelHex);
|
|
43
|
+
let post;
|
|
44
|
+
try {
|
|
45
|
+
post = await signBroadcastArtifact(channelKey, agentKey, {
|
|
46
|
+
seq, published_at: this.#now(), title, body: wire, supersedes: null, ext: null,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return { ok: false, reason: "post_invalid", detail: extract(err) };
|
|
51
|
+
}
|
|
52
|
+
// 4. THE LOG, BEFORE THE NETWORK.
|
|
53
|
+
log.append(channelHex, post, correlationId);
|
|
54
|
+
// 5. Both relays, in parallel — a deposit is independent of the other, and making the second
|
|
55
|
+
// wait on the first would double the latency of the ordinary case for no gain.
|
|
56
|
+
let deposited = await Promise.all(info.relays.map((relay) => this.#depositOnce(relay, post, channelHex, correlationId)));
|
|
57
|
+
let ok = deposited.filter((d) => d.ok);
|
|
58
|
+
/**
|
|
59
|
+
* ⚠️ **THE CLOCK-SKEW CORRECTION IS ONE POST FOR ALL RELAYS, AND ONLY WHILE NONE HAS TAKEN IT.**
|
|
60
|
+
*
|
|
61
|
+
* `published_at` is inside both signatures, so a corrected post is different BYTES at the same
|
|
62
|
+
* number. Correcting per relay would let relay A hold one body at seq N and relay B another —
|
|
63
|
+
* and a subscriber taking the union of the two sees exactly what a fork looks like, produced by
|
|
64
|
+
* an honest publisher with a wrong clock. So the correction happens here, once, and only when
|
|
65
|
+
* NO relay accepted the original: a relay that already answered holds a receipt bound to those
|
|
66
|
+
* bytes by hash, and re-signing under it would strand the proof.
|
|
67
|
+
*/
|
|
68
|
+
if (ok.length === 0 && deposited.some((d) => d.reason === "clock_skew")) {
|
|
69
|
+
const corrected = await this.#resignForSkew(agentName, channelHex, post, deposited, correlationId);
|
|
70
|
+
if (corrected) {
|
|
71
|
+
post = corrected;
|
|
72
|
+
deposited = await Promise.all(info.relays.map((relay) => this.#depositOnce(relay, post, channelHex, correlationId)));
|
|
73
|
+
ok = deposited.filter((d) => d.ok);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
logger.info("channel.post.published", {
|
|
77
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
78
|
+
channel_pubkey: channelHex,
|
|
79
|
+
seq,
|
|
80
|
+
relays_ok: ok.map((d) => d.relay),
|
|
81
|
+
relays_failed: deposited.filter((d) => !d.ok).map((d) => d.relay),
|
|
82
|
+
});
|
|
83
|
+
if (ok.length === 0) {
|
|
84
|
+
// The post STAYS in the log. A retry must send these bytes, not a new post at a new number:
|
|
85
|
+
// the number and the time are inside both signatures.
|
|
86
|
+
return { ok: false, reason: "no_relay_accepted", seq, deposited };
|
|
87
|
+
}
|
|
88
|
+
return { ok: true, seq, deposited };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* One deposit on one relay. No retry lives here — see the skew note in `publish`, which is where
|
|
92
|
+
* a correction has to happen if it is to produce the same bytes for every relay.
|
|
93
|
+
*
|
|
94
|
+
* A relay that TOOK the post is `ok: true` even when its receipt could not be filed. The two facts
|
|
95
|
+
* are reported separately because folding them together turned a post that is safely on two relays
|
|
96
|
+
* into `no_relay_accepted`, and sent the operator to resend something already there.
|
|
97
|
+
*/
|
|
98
|
+
async #depositOnce(relay, post, channelHex, correlationId) {
|
|
99
|
+
const { logger } = this.#opts;
|
|
100
|
+
let answer;
|
|
101
|
+
try {
|
|
102
|
+
answer = await this.#opts.deposit(relay, { post_cbor: encodeBroadcastArtifact(post) });
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
logger.warn("channel.post.deposit_failed", {
|
|
106
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
107
|
+
channel_pubkey: channelHex, seq: post.seq, relay, reason: extract(err),
|
|
108
|
+
});
|
|
109
|
+
return { relay, ok: false, reason: extract(err) };
|
|
110
|
+
}
|
|
111
|
+
if (answer.ok) {
|
|
112
|
+
const filed = this.#storeReceipt(channelHex, post, answer.receipt_cbor, relay, correlationId);
|
|
113
|
+
return filed.ok ? { relay, ok: true } : { relay, ok: true, receipt_unfiled: filed.reason };
|
|
114
|
+
}
|
|
115
|
+
logger.warn("channel.post.deposit_failed", {
|
|
116
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
117
|
+
channel_pubkey: channelHex, seq: post.seq, relay, reason: answer.reason,
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
relay, ok: false, reason: answer.reason,
|
|
121
|
+
...(answer.skew_ms !== undefined ? { skew_ms: answer.skew_ms } : {}),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Re-sign a post ONCE at a clock the relays will accept, and put the corrected bytes in the log.
|
|
126
|
+
*
|
|
127
|
+
* The correction is the LARGEST skew any relay reported: a post the fastest relay will take is one
|
|
128
|
+
* the others will too, and correcting to the smallest would leave the strictest relay refusing
|
|
129
|
+
* again with no retry left.
|
|
130
|
+
*
|
|
131
|
+
* Returns `null` when the correction cannot be made — the keys are gone, or the log refuses to
|
|
132
|
+
* replace bytes it has already receipted. `null` leaves the ORIGINAL post standing, which is the
|
|
133
|
+
* safe direction: the post is in the log at its number and `resendMissing` can carry it later.
|
|
134
|
+
*/
|
|
135
|
+
async #resignForSkew(agentName, channelHex, post, deposited, correlationId) {
|
|
136
|
+
const { logger } = this.#opts;
|
|
137
|
+
const channelKey = this.#opts.getChannelKey(channelHex);
|
|
138
|
+
// BY NAME, from the caller. This read the empty string once, which is no agent, so the lookup
|
|
139
|
+
// was always null and the correction never ran in production — the test harness ignored the
|
|
140
|
+
// argument, so nothing said so.
|
|
141
|
+
const agentKey = this.#opts.getAgentKey(agentName);
|
|
142
|
+
if (!channelKey || !agentKey) {
|
|
143
|
+
logger.warn("channel.post.resign_failed", {
|
|
144
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
145
|
+
channel_pubkey: channelHex, seq: post.seq, reason: "key_unavailable",
|
|
146
|
+
});
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const skews = deposited.map((d) => d.skew_ms ?? 0);
|
|
150
|
+
const corrected = this.#now() - Math.max(...skews);
|
|
151
|
+
let resigned;
|
|
152
|
+
try {
|
|
153
|
+
resigned = await signBroadcastArtifact(channelKey, agentKey, {
|
|
154
|
+
seq: post.seq, published_at: corrected, title: post.title,
|
|
155
|
+
body: post.body, supersedes: post.supersedes, ext: null,
|
|
156
|
+
});
|
|
157
|
+
this.#opts.log.replaceUnreceipted(channelHex, resigned, correlationId);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
logger.warn("channel.post.resign_failed", {
|
|
161
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
162
|
+
channel_pubkey: channelHex, seq: post.seq, reason: extract(err),
|
|
163
|
+
});
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return resigned;
|
|
167
|
+
}
|
|
168
|
+
/** Verify a receipt against the post it names before storing it; an unverified one is worthless. */
|
|
169
|
+
#storeReceipt(channelHex, post, receiptCbor, relay, correlationId) {
|
|
170
|
+
const decoded = decodeRelayPostReceipt(receiptCbor);
|
|
171
|
+
if (!decoded.ok || !verifyRelayPostReceipt(decoded.receipt, post)) {
|
|
172
|
+
this.#opts.logger.warn("channel.post.deposit_failed", {
|
|
173
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
174
|
+
channel_pubkey: channelHex, seq: post.seq, relay, reason: "receipt_invalid",
|
|
175
|
+
});
|
|
176
|
+
return { ok: false, reason: "receipt_invalid" };
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
this.#opts.log.recordReceipt(channelHex, decoded.receipt, correlationId);
|
|
180
|
+
return { ok: true };
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
// A receipt naming bytes this log does not hold. The relay DID take the post — the caller
|
|
184
|
+
// reports that separately, because calling the deposit failed would send the operator to
|
|
185
|
+
// resend a post the relay already has.
|
|
186
|
+
this.#opts.logger.warn("channel.post.receipt_unfiled", {
|
|
187
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
188
|
+
channel_pubkey: channelHex, seq: post.seq, relay, reason: extract(err),
|
|
189
|
+
});
|
|
190
|
+
return { ok: false, reason: extract(err) };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Re-deposit everything a relay has not receipted, oldest first.
|
|
195
|
+
*
|
|
196
|
+
* This is what refills a relay that lost content and what fills a newly added one — the same path,
|
|
197
|
+
* which is why it is exercised by ordinary operation rather than only by a crash.
|
|
198
|
+
*/
|
|
199
|
+
async resendMissing(agentName, channelHex, relay, correlationId) {
|
|
200
|
+
const { log, logger } = this.#opts;
|
|
201
|
+
// A channel that has been set up but never published has no log row yet, and `head` THROWS on
|
|
202
|
+
// one. Nothing to resend is an ANSWER — `deposited: 0` — not an error: a freshly created channel
|
|
203
|
+
// is the most likely thing an operator runs this against, and it came back looking broken.
|
|
204
|
+
log.ensureChannel(channelHex);
|
|
205
|
+
const head = log.head(channelHex);
|
|
206
|
+
if (head.first_seq === null || head.last_seq === null)
|
|
207
|
+
return { deposited: 0 };
|
|
208
|
+
/**
|
|
209
|
+
* ⚠️ WHAT THE RELAY HOLDS NOW, NOT WHAT IT ONCE RECEIPTED — and this is a deviation from the
|
|
210
|
+
* order, raised there.
|
|
211
|
+
*
|
|
212
|
+
* The order says to deposit "every logged post that relay has not receipted". But a receipt
|
|
213
|
+
* proves the relay TOOK the post once, not that it still has it — and the case the same sentence
|
|
214
|
+
* names, "refills a relay that lost content", is precisely a relay whose receipts are all in the
|
|
215
|
+
* log and whose queue is empty. Skipping on receipts refills nothing, which the enforcer caught:
|
|
216
|
+
* a restarted relay got 3 posts back out of 6.
|
|
217
|
+
*
|
|
218
|
+
* So the relay is asked where its queue begins and ends, and anything outside that is sent. When
|
|
219
|
+
* it cannot be asked, every logged post is sent — a repeat is a no-op the relay answers with the
|
|
220
|
+
* receipt it already signed, so the cost of over-sending is bandwidth and the cost of
|
|
221
|
+
* under-sending is a relay permanently missing posts.
|
|
222
|
+
*/
|
|
223
|
+
let holds = null;
|
|
224
|
+
if (this.#opts.relayHead) {
|
|
225
|
+
try {
|
|
226
|
+
const reported = await this.#opts.relayHead(relay, channelHex);
|
|
227
|
+
if (reported.first_held_seq !== null && reported.last_seq !== null) {
|
|
228
|
+
holds = { first: reported.first_held_seq, last: reported.last_seq };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
logger.warn("channel.resend.head_unavailable", {
|
|
233
|
+
channel_pubkey: channelHex, relay, reason: extract(err),
|
|
234
|
+
impact: "every logged post is re-sent; a repeat is a no-op at the relay",
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
let deposited = 0;
|
|
239
|
+
// Oldest first: a relay's queue only accepts the next number, so any other order stalls at the
|
|
240
|
+
// first gap and refills nothing after it.
|
|
241
|
+
for (const post of log.readRange(channelHex, head.first_seq, head.last_seq)) {
|
|
242
|
+
if (holds !== null && post.seq >= holds.first && post.seq <= holds.last)
|
|
243
|
+
continue;
|
|
244
|
+
// PACED. A refill of a long backbone is the one path that deposits hundreds of posts in a
|
|
245
|
+
// row, and the relay rate-limits per publisher — so an unpaced refill trips the limiter part
|
|
246
|
+
// way through and the rest of the backlog is refused, which looks exactly like a relay that
|
|
247
|
+
// will not take the channel's posts at all.
|
|
248
|
+
if (deposited > 0)
|
|
249
|
+
await this.#pause(this.#opts.resendPaceMs ?? DEFAULT_RESEND_PACE_MS);
|
|
250
|
+
const outcome = await this.#depositOnce(relay, post, channelHex, correlationId);
|
|
251
|
+
if (outcome.ok)
|
|
252
|
+
deposited += 1;
|
|
253
|
+
}
|
|
254
|
+
logger.info("channel.resend.completed", {
|
|
255
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
256
|
+
// WHO ran it. A refill and a prune are the two verbs that change what the world can read, and
|
|
257
|
+
// an operator asking "why did the backbone change" needs the acting agent in the line.
|
|
258
|
+
agent: agentName,
|
|
259
|
+
channel_pubkey: channelHex, relay, deposited,
|
|
260
|
+
});
|
|
261
|
+
return { deposited };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Prune the log, then tell both relays to drop the same range.
|
|
265
|
+
*
|
|
266
|
+
* ⚠️ **EACH RELAY'S OUTCOME IS THE ONE IT GAVE.** This used to report `ok: true` for every relay
|
|
267
|
+
* without contacting any of them, so an operator pruning 500 posts was shown two successful
|
|
268
|
+
* relays while both still held and served every post. A relay that is down is `ok: false` with its
|
|
269
|
+
* reason, and its copy survives until its own retention sweeps it — which is what retention is for.
|
|
270
|
+
*/
|
|
271
|
+
async pruneChannel(agentName, channelHex, throughSeq, correlationId) {
|
|
272
|
+
const { logger } = this.#opts;
|
|
273
|
+
// Same as `resendMissing`: pruning a channel with nothing in it yet is "pruned 0", not an error.
|
|
274
|
+
this.#opts.log.ensureChannel(channelHex);
|
|
275
|
+
const { pruned } = this.#opts.log.pruneThrough(channelHex, throughSeq);
|
|
276
|
+
const info = this.#opts.channelInfo(channelHex);
|
|
277
|
+
const relays = info?.relays ?? [];
|
|
278
|
+
/**
|
|
279
|
+
* ⚠️ THE PRUNE IS SIGNED BY THE CHANNEL KEY, and it has to be: the frame is not a post, so
|
|
280
|
+
* nothing else proves the caller owns the channel. An unsigned prune would let anyone who knows
|
|
281
|
+
* a channel's public key delete its backbone from both relays.
|
|
282
|
+
*/
|
|
283
|
+
const channelKey = this.#opts.getChannelKey(channelHex);
|
|
284
|
+
const timeMs = this.#now();
|
|
285
|
+
const signature = channelKey
|
|
286
|
+
? await channelKey.sign(buildChannelPruneTbs(Buffer.from(channelHex, "hex"), throughSeq, timeMs))
|
|
287
|
+
: null;
|
|
288
|
+
const outcomes = await Promise.all(relays.map(async (relay) => {
|
|
289
|
+
if (!this.#opts.prune || signature === null) {
|
|
290
|
+
// No seam wired, or no key to sign with, is not a successful prune. Say which it was.
|
|
291
|
+
return { relay, ok: false, reason: signature === null ? "key_unavailable" : "relay_prune_unavailable" };
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const answer = await this.#opts.prune(relay, { channelHex, throughSeq, timeMs, signature });
|
|
295
|
+
if (!answer.ok) {
|
|
296
|
+
logger.warn("channel.prune.relay_refused", {
|
|
297
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
298
|
+
channel_pubkey: channelHex, relay, through_seq: throughSeq, reason: answer.reason,
|
|
299
|
+
});
|
|
300
|
+
return { relay, ok: false, reason: answer.reason };
|
|
301
|
+
}
|
|
302
|
+
return { relay, ok: true };
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
logger.warn("channel.prune.relay_unreachable", {
|
|
306
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
307
|
+
channel_pubkey: channelHex, relay, through_seq: throughSeq, reason: extract(err),
|
|
308
|
+
});
|
|
309
|
+
return { relay, ok: false, reason: extract(err) };
|
|
310
|
+
}
|
|
311
|
+
}));
|
|
312
|
+
logger.info("channel.log.pruned", {
|
|
313
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
314
|
+
agent: agentName,
|
|
315
|
+
channel_pubkey: channelHex, through_seq: throughSeq, pruned,
|
|
316
|
+
relays_pruned: outcomes.filter((o) => o.ok).map((o) => o.relay),
|
|
317
|
+
relays_still_holding: outcomes.filter((o) => !o.ok).map((o) => o.relay),
|
|
318
|
+
});
|
|
319
|
+
return { pruned, relays: outcomes };
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Sign and deposit the channel's info record. Only the CHANNEL key signs it.
|
|
323
|
+
*
|
|
324
|
+
* ⚠️ **THE DEPOSIT IS THE POINT, NOT THE SIGNATURE.** The record is the only way a subscriber
|
|
325
|
+
* learns a channel's relays, access and admin key. This used to sign it, log
|
|
326
|
+
* `channel.info.published` and return the bytes to the caller without contacting a relay — so an
|
|
327
|
+
* operator was told their channel was published while no one could find it. The event now names
|
|
328
|
+
* only the relays that actually took it.
|
|
329
|
+
*/
|
|
330
|
+
async publishInfo(agentName, channelHex, correlationId) {
|
|
331
|
+
const { logger } = this.#opts;
|
|
332
|
+
const info = this.#opts.channelInfo(channelHex);
|
|
333
|
+
const channelKey = this.#opts.getChannelKey(channelHex);
|
|
334
|
+
const agentKey = this.#opts.getAgentKey(agentName);
|
|
335
|
+
if (!info)
|
|
336
|
+
return { ok: false, reason: "channel_unknown" };
|
|
337
|
+
if (!channelKey || !agentKey)
|
|
338
|
+
return { ok: false, reason: "key_unavailable" };
|
|
339
|
+
const record = await signChannelInfo(channelKey, {
|
|
340
|
+
access: info.access,
|
|
341
|
+
admin_pubkey: await agentKey.getPublicKey(),
|
|
342
|
+
relays: info.relays,
|
|
343
|
+
guidance: info.guidance,
|
|
344
|
+
retention_seconds: info.retention_seconds,
|
|
345
|
+
updated_at: this.#now(),
|
|
346
|
+
ext: null,
|
|
347
|
+
});
|
|
348
|
+
const info_cbor = encodeChannelInfo(record);
|
|
349
|
+
const outcomes = await Promise.all(info.relays.map(async (relay) => {
|
|
350
|
+
try {
|
|
351
|
+
const answer = await this.#opts.depositInfo(relay, { info_cbor });
|
|
352
|
+
if (!answer.ok)
|
|
353
|
+
return { relay, ok: false, reason: answer.reason };
|
|
354
|
+
return { relay, ok: true };
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
return { relay, ok: false, reason: extract(err) };
|
|
358
|
+
}
|
|
359
|
+
}));
|
|
360
|
+
const took = outcomes.filter((o) => o.ok);
|
|
361
|
+
if (took.length === 0) {
|
|
362
|
+
logger.warn("channel.info.deposit_failed", {
|
|
363
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
364
|
+
channel_pubkey: channelHex,
|
|
365
|
+
reasons: outcomes.map((o) => o.reason ?? "unknown"),
|
|
366
|
+
});
|
|
367
|
+
return { ok: false, reason: "no_relay_accepted", detail: outcomes.map((o) => `${o.relay}: ${o.reason ?? "unknown"}`).join("; ") };
|
|
368
|
+
}
|
|
369
|
+
logger.info("channel.info.published", {
|
|
370
|
+
...(correlationId !== undefined ? { correlationId } : {}),
|
|
371
|
+
channel_pubkey: channelHex, access: info.access,
|
|
372
|
+
relays_ok: took.map((o) => o.relay),
|
|
373
|
+
relays_failed: outcomes.filter((o) => !o.ok).map((o) => o.relay),
|
|
374
|
+
});
|
|
375
|
+
return { ok: true, info_cbor, relays: outcomes };
|
|
376
|
+
}
|
|
377
|
+
/** The relays this channel publishes to, so a caller can refill all of them without naming one. */
|
|
378
|
+
relaysFor(channelHex) {
|
|
379
|
+
return this.#opts.channelInfo(channelHex)?.relays ?? [];
|
|
380
|
+
}
|
|
381
|
+
/** The pacing gap between refill deposits. Its own method so a test can drive it to zero. */
|
|
382
|
+
#pause(ms) {
|
|
383
|
+
if (ms <= 0)
|
|
384
|
+
return Promise.resolve();
|
|
385
|
+
return new Promise((resolve) => { setTimeout(resolve, ms); });
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function extract(err) {
|
|
389
|
+
if (err instanceof Error)
|
|
390
|
+
return err.message;
|
|
391
|
+
const m = err?.message;
|
|
392
|
+
return typeof m === "string" ? m : JSON.stringify(err);
|
|
393
|
+
}
|
|
394
|
+
//# sourceMappingURL=channel-publisher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-publisher.js","sourceRoot":"","sources":["../src/channel-publisher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { CelloNode } from "@cello-protocol/transport";
|
|
2
|
+
import type { Logger } from "./types.js";
|
|
3
|
+
export declare const CHANNEL_PROTOCOL_ID = "/cello/channel/1.0.0";
|
|
4
|
+
export interface ChannelRelayClientOptions {
|
|
5
|
+
getNode: () => CelloNode | null;
|
|
6
|
+
logger: Logger;
|
|
7
|
+
}
|
|
8
|
+
export declare class ChannelRelayClient {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(opts: ChannelRelayClientOptions);
|
|
11
|
+
/**
|
|
12
|
+
* One request, one stream. THROWS when the relay could not be reached at all, and RESOLVES with
|
|
13
|
+
* whatever the relay said otherwise — including a refusal, which is an answer.
|
|
14
|
+
*/
|
|
15
|
+
request(relayAddr: string, frame: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
16
|
+
/** Deposit one post. A refusal comes back as `{ ok: false, reason }`, never as a throw. */
|
|
17
|
+
deposit(relayAddr: string, req: {
|
|
18
|
+
post_cbor: Uint8Array;
|
|
19
|
+
fetch_key?: {
|
|
20
|
+
pubkey: Uint8Array;
|
|
21
|
+
time_ms: number;
|
|
22
|
+
signature: Uint8Array;
|
|
23
|
+
};
|
|
24
|
+
}): Promise<{
|
|
25
|
+
ok: true;
|
|
26
|
+
receipt_cbor: Uint8Array;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
reason: string;
|
|
30
|
+
skew_ms?: number;
|
|
31
|
+
}>;
|
|
32
|
+
/** Fetch from a post number. The caller verifies everything that comes back. */
|
|
33
|
+
fetch(relayAddr: string, req: {
|
|
34
|
+
channel_pubkey: Uint8Array;
|
|
35
|
+
since_seq: number;
|
|
36
|
+
max_bytes: number;
|
|
37
|
+
auth?: {
|
|
38
|
+
signature: Uint8Array;
|
|
39
|
+
time_ms: number;
|
|
40
|
+
};
|
|
41
|
+
agent_pubkeys?: Uint8Array[];
|
|
42
|
+
}): Promise<{
|
|
43
|
+
ok: true;
|
|
44
|
+
posts: Array<{
|
|
45
|
+
seq: number;
|
|
46
|
+
post_cbor: Uint8Array;
|
|
47
|
+
receipt_cbor?: Uint8Array;
|
|
48
|
+
}>;
|
|
49
|
+
first_held_seq: number | null;
|
|
50
|
+
last_seq: number | null;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
reason: string;
|
|
54
|
+
}>;
|
|
55
|
+
/** Where a relay's queue begins and ends, without pulling any posts. */
|
|
56
|
+
head(relayAddr: string, channelPubkey: Uint8Array): Promise<{
|
|
57
|
+
first_held_seq: number | null;
|
|
58
|
+
last_seq: number | null;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Deposit the channel's info record. No receipt: the record carries no post number, so there is
|
|
62
|
+
* nothing for a relay to order or countersign — it either holds the latest one or it does not.
|
|
63
|
+
*/
|
|
64
|
+
depositInfo(relayAddr: string, req: {
|
|
65
|
+
info_cbor: Uint8Array;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
ok: true;
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
reason: string;
|
|
71
|
+
}>;
|
|
72
|
+
/** Ask a relay to drop everything through a post number. Oldest end only — the relay enforces that. */
|
|
73
|
+
prune(relayAddr: string, req: {
|
|
74
|
+
channel_pubkey: Uint8Array;
|
|
75
|
+
through_seq: number;
|
|
76
|
+
time_ms: number;
|
|
77
|
+
signature: Uint8Array;
|
|
78
|
+
}): Promise<{
|
|
79
|
+
ok: true;
|
|
80
|
+
dropped?: number;
|
|
81
|
+
} | {
|
|
82
|
+
ok: false;
|
|
83
|
+
reason: string;
|
|
84
|
+
}>;
|
|
85
|
+
/** The channel's info record as this relay holds it, or null. */
|
|
86
|
+
info(relayAddr: string, channelPubkey: Uint8Array): Promise<Uint8Array | null>;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=channel-relay-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-relay-client.d.ts","sourceRoot":"","sources":["../src/channel-relay-client.ts"],"names":[],"mappings":"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"}
|