@cello-protocol/daemon 0.0.229 → 0.0.231
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-admin-lookup.d.ts +54 -0
- package/dist/channel-admin-lookup.d.ts.map +1 -0
- package/dist/channel-admin-lookup.js +122 -0
- package/dist/channel-admin-lookup.js.map +1 -0
- 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 +36 -0
- package/dist/channel-config-store.d.ts.map +1 -0
- package/dist/channel-config-store.js +84 -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-join-exchange.d.ts +87 -0
- package/dist/channel-join-exchange.d.ts.map +1 -0
- package/dist/channel-join-exchange.js +289 -0
- package/dist/channel-join-exchange.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-membership-store.d.ts +118 -0
- package/dist/channel-membership-store.d.ts.map +1 -0
- package/dist/channel-membership-store.js +226 -0
- package/dist/channel-membership-store.js.map +1 -0
- package/dist/channel-membership-wiring.d.ts +90 -0
- package/dist/channel-membership-wiring.d.ts.map +1 -0
- package/dist/channel-membership-wiring.js +387 -0
- package/dist/channel-membership-wiring.js.map +1 -0
- 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 +52 -0
- package/dist/channel-publish-wiring.d.ts.map +1 -0
- package/dist/channel-publish-wiring.js +143 -0
- package/dist/channel-publish-wiring.js.map +1 -0
- package/dist/channel-publisher.d.ts +221 -0
- package/dist/channel-publisher.d.ts.map +1 -0
- package/dist/channel-publisher.js +430 -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 +137 -0
- package/dist/channel-subscription-store.d.ts.map +1 -0
- package/dist/channel-subscription-store.js +250 -0
- package/dist/channel-subscription-store.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +54 -0
- package/dist/daemon.js.map +1 -1
- package/dist/inbound-sessions.d.ts +6 -0
- package/dist/inbound-sessions.d.ts.map +1 -1
- package/dist/inbound-sessions.js +27 -1
- package/dist/inbound-sessions.js.map +1 -1
- package/dist/refusal-reasons.d.ts +7 -0
- package/dist/refusal-reasons.d.ts.map +1 -1
- package/dist/refusal-reasons.js +15 -0
- package/dist/refusal-reasons.js.map +1 -1
- package/dist/session-content-context.d.ts +8 -0
- package/dist/session-content-context.d.ts.map +1 -1
- package/dist/session-content-ingest.d.ts.map +1 -1
- package/dist/session-content-ingest.js +28 -0
- package/dist/session-content-ingest.js.map +1 -1
- package/dist/session-node-manager.d.ts +4 -0
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +11 -0
- package/dist/session-node-manager.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { DEFAULT_RETENTION_SECONDS } from "./channel-config-store.js";
|
|
2
|
+
function needAgent(deps, params, connectionId) {
|
|
3
|
+
const agentName = deps.resolveCurrentAgent(connectionId, params?.["agent"]);
|
|
4
|
+
if (agentName === null) {
|
|
5
|
+
return {
|
|
6
|
+
ok: false,
|
|
7
|
+
answer: {
|
|
8
|
+
ok: false, reason: "no_current_agent",
|
|
9
|
+
guidance: "Name the agent, or select one for this connection with cello_use_agent.",
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return { ok: true, agentName };
|
|
14
|
+
}
|
|
15
|
+
function needChannel(params) {
|
|
16
|
+
const raw = params?.["channel"];
|
|
17
|
+
// 64 hex characters, checked here rather than deeper: a malformed pubkey that reaches the log
|
|
18
|
+
// creates a channel row under a key nothing will ever publish to again.
|
|
19
|
+
if (typeof raw !== "string" || !/^[0-9a-fA-F]{64}$/.test(raw)) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
answer: {
|
|
23
|
+
ok: false, reason: "bad_channel",
|
|
24
|
+
guidance: "Pass the channel's 64-character hex public key as `channel`.",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return { ok: true, channelHex: raw.toLowerCase() };
|
|
29
|
+
}
|
|
30
|
+
export function registerChannelPublishHandlers(deps) {
|
|
31
|
+
const { handlers, logger } = deps;
|
|
32
|
+
handlers.set("cello_channel_publish", async (params, connectionId) => {
|
|
33
|
+
const agent = needAgent(deps, params, connectionId);
|
|
34
|
+
if (!agent.ok)
|
|
35
|
+
return agent.answer;
|
|
36
|
+
const channel = needChannel(params);
|
|
37
|
+
if (!channel.ok)
|
|
38
|
+
return channel.answer;
|
|
39
|
+
const title = params?.["title"];
|
|
40
|
+
const body = params?.["body"];
|
|
41
|
+
if (typeof title !== "string" || typeof body !== "string") {
|
|
42
|
+
return { ok: false, reason: "bad_post", guidance: "Pass a `title` and a `body`, both strings." };
|
|
43
|
+
}
|
|
44
|
+
const publisher = deps.getPublisher(agent.agentName);
|
|
45
|
+
if (!publisher) {
|
|
46
|
+
return { ok: false, reason: "channel_unknown", guidance: `${agent.agentName} is not a channel this daemon publishes for.` };
|
|
47
|
+
}
|
|
48
|
+
const result = await publisher.publish(agent.agentName, channel.channelHex, title, body);
|
|
49
|
+
if (result.ok) {
|
|
50
|
+
return {
|
|
51
|
+
ok: true, seq: result.seq,
|
|
52
|
+
relays_ok: result.deposited.filter((d) => d.ok).map((d) => d.relay),
|
|
53
|
+
relays_failed: result.deposited.filter((d) => !d.ok).map((d) => d.relay),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
logger.info("channel.publish.refused", { channel_pubkey: channel.channelHex, reason: result.reason });
|
|
57
|
+
return {
|
|
58
|
+
ok: false, reason: result.reason, detail: result.detail,
|
|
59
|
+
// `no_relay_accepted` is the one refusal where the post SURVIVES, and a caller that did not
|
|
60
|
+
// know that would publish it again and burn a second post number on the same content.
|
|
61
|
+
guidance: result.reason === "no_relay_accepted"
|
|
62
|
+
// `cello channel resend`, NOT the handler's own name: these verbs are terminal-only, so a
|
|
63
|
+
// `cello_*` token here would hand the operator a command that does not exist on any surface
|
|
64
|
+
// they can reach.
|
|
65
|
+
? "No relay took the post. It is in your log — retry with 'cello channel resend' rather than publishing it again."
|
|
66
|
+
: undefined,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
70
|
+
* Record what this publisher has decided about its own channel: which relays it publishes to,
|
|
71
|
+
* whether it is public, what it is for, how long posts are kept.
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ **NOTHING ELSE WRITES THIS, AND WITHOUT IT NO CHANNEL CAN PUBLISH AT ALL.** The publisher
|
|
74
|
+
* reads the relay pair from here; with no row, every verb answers `channel_unknown` for ever. The
|
|
75
|
+
* first version of this unit shipped the four verbs with no way to create the row they all read.
|
|
76
|
+
*
|
|
77
|
+
* It does NOT deposit anything. `cello_channel_info_set` signs and publishes the description to
|
|
78
|
+
* the relays; this is the local decision it is signed from. Keeping them apart means a publisher
|
|
79
|
+
* can change its mind and re-publish, rather than the relays' copy being the only record.
|
|
80
|
+
*/
|
|
81
|
+
handlers.set("cello_channel_config", async (params, connectionId) => {
|
|
82
|
+
const agent = needAgent(deps, params, connectionId);
|
|
83
|
+
if (!agent.ok)
|
|
84
|
+
return agent.answer;
|
|
85
|
+
const channel = needChannel(params);
|
|
86
|
+
if (!channel.ok)
|
|
87
|
+
return channel.answer;
|
|
88
|
+
const rawRelays = params?.["relays"];
|
|
89
|
+
const relays = Array.isArray(rawRelays) ? rawRelays.filter((r) => typeof r === "string") : [];
|
|
90
|
+
if (relays.length === 0 || relays.length !== rawRelays.length) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false, reason: "bad_relays",
|
|
93
|
+
guidance: "Pass `relays`: the multiaddrs this channel publishes to. Two is the design — one is a single point of failure, and the subscriber takes the union of both.",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const access = params?.["access"];
|
|
97
|
+
if (access !== "public" && access !== "open" && access !== "invite_only") {
|
|
98
|
+
return {
|
|
99
|
+
ok: false, reason: "bad_access",
|
|
100
|
+
guidance: "Pass `access`: public (anyone can read), open (anyone may ask to join) or invite_only.",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const guidance = typeof params?.["guidance"] === "string" ? params["guidance"] : "";
|
|
104
|
+
const retention = params?.["retention_seconds"];
|
|
105
|
+
const retention_seconds = typeof retention === "number" && Number.isSafeInteger(retention) && retention > 0
|
|
106
|
+
? retention
|
|
107
|
+
: DEFAULT_RETENTION_SECONDS;
|
|
108
|
+
const saved = deps.setChannelConfig(agent.agentName, channel.channelHex, {
|
|
109
|
+
access, relays, guidance, retention_seconds,
|
|
110
|
+
});
|
|
111
|
+
if (!saved.ok)
|
|
112
|
+
return { ok: false, reason: saved.reason, guidance: saved.guidance };
|
|
113
|
+
logger.info("channel.config.recorded", {
|
|
114
|
+
channel_pubkey: channel.channelHex, access, relays: relays.length,
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
ok: true, channel: channel.channelHex, access, relays, retention_seconds,
|
|
118
|
+
guidance: "Recorded locally. Run 'cello channel info-set' to publish the description so subscribers can find the channel.",
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
handlers.set("cello_channel_info_set", async (params, connectionId) => {
|
|
122
|
+
const agent = needAgent(deps, params, connectionId);
|
|
123
|
+
if (!agent.ok)
|
|
124
|
+
return agent.answer;
|
|
125
|
+
const channel = needChannel(params);
|
|
126
|
+
if (!channel.ok)
|
|
127
|
+
return channel.answer;
|
|
128
|
+
const publisher = deps.getPublisher(agent.agentName);
|
|
129
|
+
if (!publisher)
|
|
130
|
+
return { ok: false, reason: "channel_unknown" };
|
|
131
|
+
const result = await publisher.publishInfo(agent.agentName, channel.channelHex);
|
|
132
|
+
if (!result.ok) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false, reason: result.reason, detail: result.detail,
|
|
135
|
+
guidance: result.reason === "no_relay_accepted"
|
|
136
|
+
? "No relay took the channel's description, so nobody can discover this channel yet. Check the relays are reachable and run it again."
|
|
137
|
+
: undefined,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
bytes: result.info_cbor.length,
|
|
143
|
+
// WHICH relays hold it, not just that it was signed. A subscriber can only find this channel
|
|
144
|
+
// through a relay that actually took the record.
|
|
145
|
+
relays_ok: result.relays.filter((r) => r.ok).map((r) => r.relay),
|
|
146
|
+
relays_failed: result.relays.filter((r) => !r.ok).map((r) => ({ relay: r.relay, reason: r.reason })),
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
handlers.set("cello_channel_prune", async (params, connectionId) => {
|
|
150
|
+
const agent = needAgent(deps, params, connectionId);
|
|
151
|
+
if (!agent.ok)
|
|
152
|
+
return agent.answer;
|
|
153
|
+
const channel = needChannel(params);
|
|
154
|
+
if (!channel.ok)
|
|
155
|
+
return channel.answer;
|
|
156
|
+
const through = params?.["through_seq"];
|
|
157
|
+
if (typeof through !== "number" || !Number.isSafeInteger(through) || through < 1) {
|
|
158
|
+
return { ok: false, reason: "bad_seq", guidance: "Pass `through_seq`: the last post number to drop." };
|
|
159
|
+
}
|
|
160
|
+
const publisher = deps.getPublisher(agent.agentName);
|
|
161
|
+
if (!publisher)
|
|
162
|
+
return { ok: false, reason: "channel_unknown" };
|
|
163
|
+
const result = await publisher.pruneChannel(agent.agentName, channel.channelHex, through);
|
|
164
|
+
const stillHolding = result.relays.filter((r) => !r.ok);
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
pruned: result.pruned,
|
|
168
|
+
relays: result.relays,
|
|
169
|
+
// The log is pruned either way — that part is local and cannot fail halfway. A relay that did
|
|
170
|
+
// not drop its copy KEEPS SERVING those posts, and saying so is the difference between an
|
|
171
|
+
// operator who knows their content is still out there and one who believes it is gone.
|
|
172
|
+
guidance: stillHolding.length > 0
|
|
173
|
+
? `Your own copy is pruned. ${String(stillHolding.length)} relay(s) did not drop theirs and will keep serving those posts until retention expires.`
|
|
174
|
+
: undefined,
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
handlers.set("cello_channel_resend", async (params, connectionId) => {
|
|
178
|
+
const agent = needAgent(deps, params, connectionId);
|
|
179
|
+
if (!agent.ok)
|
|
180
|
+
return agent.answer;
|
|
181
|
+
const channel = needChannel(params);
|
|
182
|
+
if (!channel.ok)
|
|
183
|
+
return channel.answer;
|
|
184
|
+
const publisher = deps.getPublisher(agent.agentName);
|
|
185
|
+
if (!publisher)
|
|
186
|
+
return { ok: false, reason: "channel_unknown" };
|
|
187
|
+
/**
|
|
188
|
+
* ⚠️ **NO RELAY NAMED MEANS ALL OF THEM**, and that is the ordinary case. Requiring the operator
|
|
189
|
+
* to type a multiaddr made this verb impossible to run from the terminal, which left a relay
|
|
190
|
+
* that lost content with no repair path at all — the one job this verb has. Naming a relay
|
|
191
|
+
* stays available for the case where only one needs refilling.
|
|
192
|
+
*/
|
|
193
|
+
const raw = params?.["relay"];
|
|
194
|
+
if (raw !== undefined && (typeof raw !== "string" || raw.length === 0)) {
|
|
195
|
+
return { ok: false, reason: "bad_relay", guidance: "Pass `relay` as a multiaddr, or leave it out to refill every relay." };
|
|
196
|
+
}
|
|
197
|
+
const targets = typeof raw === "string" ? [raw] : publisher.relaysFor(channel.channelHex);
|
|
198
|
+
if (targets.length === 0) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false, reason: "channel_unknown",
|
|
201
|
+
guidance: "This daemon has no relays recorded for that channel, so there is nothing to refill.",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const per = [];
|
|
205
|
+
for (const target of targets) {
|
|
206
|
+
const result = await publisher.resendMissing(agent.agentName, channel.channelHex, target);
|
|
207
|
+
per.push({ relay: target, deposited: result.deposited });
|
|
208
|
+
}
|
|
209
|
+
return { ok: true, deposited: per.reduce((n, r) => n + r.deposited, 0), relays: per };
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=channel-publish-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-publish-handlers.js","sourceRoot":"","sources":["../src/channel-publish-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M16 018-PUBCOLLECT — everything the channel publishing verbs need, assembled in one place.
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ The publisher is built PER CHANNEL AGENT and only when its key is loaded — a channel is an
|
|
5
|
+
* agent whose key this daemon holds, and a publisher without one could sign nothing. `null` makes
|
|
6
|
+
* the verb answer `channel_unknown`, which is the truth: this daemon does not publish for it.
|
|
7
|
+
*
|
|
8
|
+
* Order 017's own wiring gap is why this module is registered in the same commit as the handlers it
|
|
9
|
+
* serves: a handler nothing calls is a feature that does not exist, and `startup-ordering.test.ts`
|
|
10
|
+
* is what caught exactly that here.
|
|
11
|
+
*/
|
|
12
|
+
import type { DaemonDatabase } from "./sqlcipher-db.js";
|
|
13
|
+
import type { Logger } from "./types.js";
|
|
14
|
+
import type { CelloNode } from "@cello-protocol/transport";
|
|
15
|
+
import type { KeyProvider } from "@cello-protocol/crypto";
|
|
16
|
+
import type { ScreenContext, ScreenVerdict } from "@cello-protocol/gateway";
|
|
17
|
+
type Handler = (params: Record<string, unknown> | undefined, connectionId: string) => Promise<unknown>;
|
|
18
|
+
export interface ChannelPublishWiringDeps {
|
|
19
|
+
handlers: Map<string, Handler>;
|
|
20
|
+
logger: Logger;
|
|
21
|
+
getDb: () => DaemonDatabase;
|
|
22
|
+
getNode: () => CelloNode | null;
|
|
23
|
+
screenOutbound: (content: Uint8Array, ctx: ScreenContext) => Promise<ScreenVerdict>;
|
|
24
|
+
/** Every agent this daemon loaded, read per lookup so one added after boot is publishable. */
|
|
25
|
+
loadedAgents: ReadonlyArray<{
|
|
26
|
+
name: string;
|
|
27
|
+
pubkey: string;
|
|
28
|
+
keyProvider: KeyProvider;
|
|
29
|
+
}>;
|
|
30
|
+
keyProviders: Map<string, KeyProvider>;
|
|
31
|
+
resolveCurrentAgent: (connectionId: string, explicitAgent?: string) => string | null;
|
|
32
|
+
/**
|
|
33
|
+
* M16 019: the channel's current fetch key, signed, or undefined when this daemon holds no group
|
|
34
|
+
* key for it. Injected rather than derived here so the membership half owns the group key and
|
|
35
|
+
* this half never has to hold one.
|
|
36
|
+
*/
|
|
37
|
+
currentFetchKey?: (channelHex: string) => Promise<{
|
|
38
|
+
pubkey: Uint8Array;
|
|
39
|
+
time_ms: number;
|
|
40
|
+
signature: Uint8Array;
|
|
41
|
+
} | undefined>;
|
|
42
|
+
/**
|
|
43
|
+
* Online AND not explicitly switched off — the same pair every other background loop here reads.
|
|
44
|
+
* Collecting for an agent the operator switched off is the kill switch failing to switch off.
|
|
45
|
+
*/
|
|
46
|
+
isAgentOnline: (agentId: string) => boolean;
|
|
47
|
+
}
|
|
48
|
+
export declare function wireChannelPublishing(deps: ChannelPublishWiringDeps): {
|
|
49
|
+
stop: () => void;
|
|
50
|
+
};
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=channel-publish-wiring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-publish-wiring.d.ts","sourceRoot":"","sources":["../src/channel-publish-wiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAW5E,KAAK,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvG,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,OAAO,EAAE,MAAM,SAAS,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACpF,8FAA8F;IAC9F,YAAY,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;IACxF,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACvC,mBAAmB,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACrF;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAC9H;;;OAGG;IACH,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;CAC7C;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,wBAAwB,GAAG;IAAE,IAAI,EAAE,MAAM,IAAI,CAAA;CAAE,CA2I1F"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { registerChannelPublishHandlers } from "./channel-publish-handlers.js";
|
|
2
|
+
import { ChannelPublisher } from "./channel-publisher.js";
|
|
3
|
+
import { ChannelLogStore } from "./channel-log-store.js";
|
|
4
|
+
import { ChannelConfigStore } from "./channel-config-store.js";
|
|
5
|
+
import { ChannelRelayClient } from "./channel-relay-client.js";
|
|
6
|
+
import { ChannelCollector } from "./channel-collector.js";
|
|
7
|
+
import { ChannelSubscriptionStore } from "./channel-subscription-store.js";
|
|
8
|
+
import { ChannelInboxStore } from "./channel-inbox-store.js";
|
|
9
|
+
import { createChannelCollectTicker } from "./channel-collect-tick.js";
|
|
10
|
+
export function wireChannelPublishing(deps) {
|
|
11
|
+
const { logger, keyProviders } = deps;
|
|
12
|
+
const log = new ChannelLogStore(deps.getDb(), logger);
|
|
13
|
+
const config = new ChannelConfigStore(deps.getDb(), logger);
|
|
14
|
+
const relay = new ChannelRelayClient({ getNode: deps.getNode, logger });
|
|
15
|
+
/**
|
|
16
|
+
* The channel key is looked up BY PUBKEY, because that is what a post is signed with and what a
|
|
17
|
+
* subscriber verifies against — the agent NAME is a display label and may be reused.
|
|
18
|
+
*/
|
|
19
|
+
const channelKeyByPubkey = (channelHex) => {
|
|
20
|
+
const want = channelHex.toLowerCase();
|
|
21
|
+
const match = deps.loadedAgents.find((a) => a.pubkey.toLowerCase() === want);
|
|
22
|
+
return match ? match.keyProvider : null;
|
|
23
|
+
};
|
|
24
|
+
const buildPublisher = (agentName) => {
|
|
25
|
+
if (!keyProviders.has(agentName))
|
|
26
|
+
return null;
|
|
27
|
+
return new ChannelPublisher({
|
|
28
|
+
logger,
|
|
29
|
+
log,
|
|
30
|
+
deposit: (addr, req) => relay.deposit(addr, {
|
|
31
|
+
post_cbor: req.post_cbor,
|
|
32
|
+
...(req.fetch_key ? { fetch_key: req.fetch_key } : {}),
|
|
33
|
+
}),
|
|
34
|
+
/**
|
|
35
|
+
* ⚠️ **THE KEY THAT MAKES AN EJECTION BITE AT THE RELAY.** Derived from the channel's current
|
|
36
|
+
* group key, signed with the CHANNEL key because the post's signature does not cover it, and
|
|
37
|
+
* sent with each deposit so a re-key reaches the relays on the very next post. `undefined`
|
|
38
|
+
* means this daemon holds no group key for that channel — a public one, or one it does not
|
|
39
|
+
* administer — and the relay then serves it to anyone, which for a public channel is correct.
|
|
40
|
+
*/
|
|
41
|
+
currentFetchKey: deps.currentFetchKey,
|
|
42
|
+
depositInfo: (addr, req) => relay.depositInfo(addr, { info_cbor: req.info_cbor }),
|
|
43
|
+
prune: (addr, req) => relay.prune(addr, {
|
|
44
|
+
channel_pubkey: Buffer.from(req.channelHex, "hex"),
|
|
45
|
+
through_seq: req.throughSeq,
|
|
46
|
+
time_ms: req.timeMs,
|
|
47
|
+
signature: req.signature,
|
|
48
|
+
}),
|
|
49
|
+
relayHead: (addr, channelHex) => relay.head(addr, Buffer.from(channelHex, "hex")),
|
|
50
|
+
// The screen has no session to name, so it names what it IS screening. Borrowing a session id
|
|
51
|
+
// would file channel posts in another conversation's governance history.
|
|
52
|
+
screenOutbound: (bytes, ctx) => deps.screenOutbound(bytes, {
|
|
53
|
+
direction: "outbound",
|
|
54
|
+
agentName: ctx.agentName,
|
|
55
|
+
sessionId: `channel:${agentName}`,
|
|
56
|
+
...(ctx.correlationId !== undefined ? { correlationId: ctx.correlationId } : {}),
|
|
57
|
+
}),
|
|
58
|
+
getChannelKey: channelKeyByPubkey,
|
|
59
|
+
getAgentKey: (name) => keyProviders.get(name) ?? null,
|
|
60
|
+
/**
|
|
61
|
+
* ⚠️ **THERE IS NO GROUP KEY UNTIL 019**, so a private channel REFUSES to publish rather than
|
|
62
|
+
* depositing a body that looks encrypted and is not. Publishing plaintext under an `access`
|
|
63
|
+
* that promises members-only would put the operator's content on two relays under a claim
|
|
64
|
+
* this code cannot keep — the one failure the whole encrypt step exists to prevent.
|
|
65
|
+
*/
|
|
66
|
+
encryptBody: () => Promise.reject(new Error("channel_group_key_unavailable")),
|
|
67
|
+
channelInfo: (channelHex) => config.get(channelHex),
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
registerChannelPublishHandlers({
|
|
71
|
+
handlers: deps.handlers,
|
|
72
|
+
logger,
|
|
73
|
+
resolveCurrentAgent: deps.resolveCurrentAgent,
|
|
74
|
+
getPublisher: buildPublisher,
|
|
75
|
+
setChannelConfig: (agentName, channelHex, cfg) => {
|
|
76
|
+
// The channel key must be one this daemon HOLDS. Recording relays for a channel we cannot
|
|
77
|
+
// sign for would leave every later verb failing on a key lookup, which describes neither the
|
|
78
|
+
// mistake nor how to correct it.
|
|
79
|
+
if (channelKeyByPubkey(channelHex) === null) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false, reason: "channel_key_not_held",
|
|
82
|
+
guidance: "This daemon does not hold that channel's key. A channel is an agent — create it with 'cello create-agent' and use its public key here.",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (!keyProviders.has(agentName)) {
|
|
86
|
+
return { ok: false, reason: "no_such_agent", guidance: `${agentName} is not an agent this daemon holds.` };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* ⚠️ **THE ADMIN AGENT IS RECORDED HERE, and it is what makes the channel joinable at all.**
|
|
90
|
+
* `agentName` is the agent running the setup, and in this release the publisher and the admin
|
|
91
|
+
* are the same operator — so that agent's key is the one a subscriber will check the answering
|
|
92
|
+
* party against. Leaving it empty was how 019's first cut produced a channel that refused
|
|
93
|
+
* every join with `not_admin_of_channel` on a channel it demonstrably administered.
|
|
94
|
+
*/
|
|
95
|
+
const adminAgent = deps.loadedAgents.find((a) => a.name === agentName);
|
|
96
|
+
config.set(channelHex, { ...cfg, admin_pubkey: adminAgent?.pubkey ?? "" }, Date.now());
|
|
97
|
+
return { ok: true };
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* ⚠️ THE SUBSCRIBER HALF, AND IT NEEDS A SCHEDULE TO EXIST AT ALL. There is no inbound event for a
|
|
102
|
+
* channel post — the relay holds a queue and waits to be asked — so a collector nobody calls is a
|
|
103
|
+
* subscriber who receives nothing while every component reports healthy.
|
|
104
|
+
*/
|
|
105
|
+
const subscriptions = new ChannelSubscriptionStore(deps.getDb(), logger);
|
|
106
|
+
const inbox = new ChannelInboxStore(deps.getDb(), logger);
|
|
107
|
+
const collector = new ChannelCollector({
|
|
108
|
+
logger,
|
|
109
|
+
subscriptions,
|
|
110
|
+
inbox,
|
|
111
|
+
fetch: (addr, req) => relay.fetch(addr, req),
|
|
112
|
+
/**
|
|
113
|
+
* ⚠️ **PUBLIC CHANNELS ONLY, UNTIL 019.** `undefined` means "send no auth", which is correct for
|
|
114
|
+
* a public channel and correct nowhere else: 019 owns the fetch key. A non-public channel is
|
|
115
|
+
* refused by the RELAY rather than silently fetched — the failure is visible and belongs to the
|
|
116
|
+
* side that can check it.
|
|
117
|
+
*/
|
|
118
|
+
fetchAuth: () => Promise.resolve(undefined),
|
|
119
|
+
// The reader count is best-effort and never affects delivery, so declaring nothing costs only
|
|
120
|
+
// the publisher's view of how many read a post. 019 records the membership this reads from.
|
|
121
|
+
localAgentKeys: () => [],
|
|
122
|
+
/**
|
|
123
|
+
* ⚠️ NOT IMPLEMENTED, AND IT SAYS SO. Asking the publisher to re-deposit needs a session to the
|
|
124
|
+
* publishing agent, which is 019's join path. Until then the OTHER RELAY is the whole repair —
|
|
125
|
+
* `repairGaps` tries that first and usually closes the gap there. A silent no-op would let a
|
|
126
|
+
* permanent gap look like one still being worked on.
|
|
127
|
+
*/
|
|
128
|
+
requestRepair: (_agentId, channelHex, from, to) => {
|
|
129
|
+
logger.warn("channel.repair.unavailable", {
|
|
130
|
+
channel_pubkey: channelHex, from, to,
|
|
131
|
+
impact: "neither relay holds these posts; asking the publisher directly needs the join path (019)",
|
|
132
|
+
});
|
|
133
|
+
return Promise.resolve();
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
const ticker = createChannelCollectTicker({
|
|
137
|
+
logger, collector, subscriptions,
|
|
138
|
+
isAgentOnline: deps.isAgentOnline,
|
|
139
|
+
});
|
|
140
|
+
ticker.start();
|
|
141
|
+
return { stop: () => { ticker.stop(); } };
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=channel-publish-wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-publish-wiring.js","sourceRoot":"","sources":["../src/channel-publish-wiring.ts"],"names":[],"mappings":"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"}
|