@steve02081504/fount-p2p 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +62 -0
  2. package/core/bytes_codec.mjs +51 -0
  3. package/core/canonical_json.mjs +21 -0
  4. package/core/composite_key.mjs +91 -0
  5. package/core/constants.mjs +53 -0
  6. package/core/entity_id.mjs +53 -0
  7. package/core/entity_id_parse.mjs +40 -0
  8. package/core/evfs_logical_path.mjs +15 -0
  9. package/core/hexIds.mjs +73 -0
  10. package/core/hlc.mjs +114 -0
  11. package/core/random_id.mjs +9 -0
  12. package/crypto/channel.mjs +101 -0
  13. package/crypto/checkpoint_sign.mjs +67 -0
  14. package/crypto/crypto.mjs +158 -0
  15. package/crypto/key.mjs +431 -0
  16. package/dag/canonicalize_row.mjs +65 -0
  17. package/dag/event_query.mjs +15 -0
  18. package/dag/index.mjs +335 -0
  19. package/dag/storage.mjs +333 -0
  20. package/dag/strip_extensions.mjs +18 -0
  21. package/dag/tunables.json +6 -0
  22. package/discovery/bt.mjs +260 -0
  23. package/discovery/index.mjs +134 -0
  24. package/discovery/mdns.mjs +138 -0
  25. package/discovery/nostr.mjs +273 -0
  26. package/entity/files/acl.mjs +38 -0
  27. package/entity/files/evfs.mjs +189 -0
  28. package/entity/files/evfs_ref.mjs +37 -0
  29. package/entity/files/manifest_acl_registry.mjs +93 -0
  30. package/entity/files/replica_host_cache.mjs +46 -0
  31. package/entity/files/url.mjs +19 -0
  32. package/entity/hosting_registry.mjs +47 -0
  33. package/entity/localized_core.mjs +141 -0
  34. package/entity/logical_entity.mjs +25 -0
  35. package/entity/logical_entity_id_registry.mjs +40 -0
  36. package/entity/node_hash.mjs +15 -0
  37. package/entity/presentation_registry.mjs +44 -0
  38. package/entity/profile.mjs +256 -0
  39. package/entity/replica.mjs +20 -0
  40. package/entity/session_snapshot_registry.mjs +38 -0
  41. package/federation/chunk_fetch_pending.mjs +128 -0
  42. package/federation/chunk_fetch_scheduler.mjs +97 -0
  43. package/federation/dag_order_cache.mjs +124 -0
  44. package/federation/dedupe_slot.mjs +33 -0
  45. package/federation/message_rate_limit.mjs +23 -0
  46. package/federation/operator_key_chain.mjs +200 -0
  47. package/federation/topo_order_memo.mjs +44 -0
  48. package/federation/volatile_streams.mjs +52 -0
  49. package/federation/want_ids.mjs +158 -0
  50. package/files/assemble.mjs +220 -0
  51. package/files/assemble_stream.mjs +129 -0
  52. package/files/chunk_fetch.mjs +113 -0
  53. package/files/chunk_fetch_verify.mjs +23 -0
  54. package/files/chunk_provider_registry.mjs +70 -0
  55. package/files/chunk_responder.mjs +79 -0
  56. package/files/chunk_store.mjs +87 -0
  57. package/files/manifest.mjs +99 -0
  58. package/files/transfer_key.mjs +87 -0
  59. package/files/transfer_key_registry.mjs +108 -0
  60. package/governance/branch.mjs +230 -0
  61. package/governance/join_pow.mjs +165 -0
  62. package/governance/owner_succession_ballot.mjs +57 -0
  63. package/governance/tunables.json +7 -0
  64. package/index.mjs +39 -0
  65. package/link/channel_mux.mjs +222 -0
  66. package/link/frame.mjs +246 -0
  67. package/link/handshake.mjs +175 -0
  68. package/link/link.mjs +617 -0
  69. package/link/rtc.mjs +109 -0
  70. package/link/sdp_fingerprint.mjs +25 -0
  71. package/mailbox/consumer_registry.mjs +71 -0
  72. package/mailbox/deliver_or_store.mjs +190 -0
  73. package/mailbox/importance.mjs +72 -0
  74. package/mailbox/parse.mjs +73 -0
  75. package/mailbox/prune.mjs +106 -0
  76. package/mailbox/rate.mjs +86 -0
  77. package/mailbox/settings.mjs +53 -0
  78. package/mailbox/store.mjs +256 -0
  79. package/mailbox/tunables.json +12 -0
  80. package/mailbox/wire.mjs +40 -0
  81. package/node/denylist.mjs +309 -0
  82. package/node/entity_store.mjs +214 -0
  83. package/node/identity.mjs +99 -0
  84. package/node/instance.mjs +115 -0
  85. package/node/network.mjs +306 -0
  86. package/node/personal_block.mjs +286 -0
  87. package/node/reputation_store.mjs +326 -0
  88. package/node/retention_policy.mjs +82 -0
  89. package/node/signaling_config.mjs +32 -0
  90. package/node/storage.mjs +30 -0
  91. package/node/storage_plugins.mjs +71 -0
  92. package/node/user_paths.mjs +10 -0
  93. package/overlay/index.mjs +191 -0
  94. package/package.json +73 -0
  95. package/permissions/bitmask.mjs +35 -0
  96. package/permissions/evaluator.mjs +90 -0
  97. package/permissions/index.mjs +12 -0
  98. package/permissions/layered.mjs +39 -0
  99. package/registries/action.mjs +94 -0
  100. package/registries/event_type.mjs +75 -0
  101. package/registries/inbound.mjs +90 -0
  102. package/registries/p2p_viewer.mjs +35 -0
  103. package/registries/part_path.mjs +35 -0
  104. package/registries/room_provider.mjs +47 -0
  105. package/reputation/engine.mjs +458 -0
  106. package/reputation/math.mjs +73 -0
  107. package/reputation/pick_score.mjs +11 -0
  108. package/reputation/relay_dedupe.mjs +22 -0
  109. package/reputation/tunables.json +36 -0
  110. package/rooms/scoped_link.mjs +185 -0
  111. package/schemas/discovery.mjs +110 -0
  112. package/schemas/federation_pull.mjs +102 -0
  113. package/schemas/mailbox.mjs +37 -0
  114. package/schemas/remote_event.mjs +17 -0
  115. package/timeline/append_core.mjs +54 -0
  116. package/timeline/materialize_runner.mjs +26 -0
  117. package/timeline/prune.mjs +39 -0
  118. package/timeline/retention.mjs +54 -0
  119. package/timeline/retention_runner.mjs +35 -0
  120. package/timeline/verify_remote.mjs +25 -0
  121. package/transport/group_link_set.mjs +324 -0
  122. package/transport/ice_servers.mjs +72 -0
  123. package/transport/link_registry.mjs +749 -0
  124. package/transport/peer_identity_maps.mjs +94 -0
  125. package/transport/peer_pool.mjs +355 -0
  126. package/transport/remote_user_room.mjs +109 -0
  127. package/transport/room_scopes.mjs +2 -0
  128. package/transport/room_wire_action.mjs +19 -0
  129. package/transport/rtc_connection_budget.mjs +180 -0
  130. package/transport/rtc_mdns_filter.mjs +118 -0
  131. package/transport/stale_peer_log.mjs +50 -0
  132. package/transport/user_room.mjs +263 -0
  133. package/trust_graph/build.mjs +99 -0
  134. package/trust_graph/cache.mjs +44 -0
  135. package/trust_graph/engine.mjs +184 -0
  136. package/trust_graph/registry.mjs +38 -0
  137. package/trust_graph/resolve.mjs +105 -0
  138. package/trust_graph/send.mjs +88 -0
  139. package/trust_graph/tunables.json +13 -0
  140. package/utils/async_mutex.mjs +69 -0
  141. package/utils/debug_log.mjs +16 -0
  142. package/utils/duration.mjs +28 -0
  143. package/utils/json_io.mjs +57 -0
  144. package/utils/lru.mjs +22 -0
  145. package/utils/map_pool.mjs +27 -0
  146. package/wire/group_part.mjs +43 -0
  147. package/wire/ingress.mjs +58 -0
  148. package/wire/part_common.mjs +55 -0
  149. package/wire/part_fanout.mjs +71 -0
  150. package/wire/part_ingress.mjs +172 -0
  151. package/wire/part_invoke.mjs +81 -0
  152. package/wire/rate_bucket.mjs +49 -0
  153. package/wire/volatile_signature.mjs +76 -0
  154. package/wire/wait.mjs +127 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Mailbox 入站 put 限速(按来源节点,节点级单例)。
3
+ */
4
+
5
+ const DEFAULT_WINDOW_MS = 60_000
6
+ const DEFAULT_MAX_PUTS = 20
7
+ const MAX_KEYS = 8000
8
+ const EXPIRE_SWEEP_BATCH = 64
9
+
10
+ /** @type {Map<string, { count: number, resetAt: number }>} */
11
+ const inboundByKey = new Map()
12
+
13
+ /**
14
+ * @param {object} [limits] 可选限额
15
+ * @returns {{ windowMs: number, maxPuts: number }} 生效限额
16
+ */
17
+ export function resolveMailboxRateLimits(limits = {}) {
18
+ return {
19
+ windowMs: Math.max(1000, Number(limits.windowMs) || DEFAULT_WINDOW_MS),
20
+ maxPuts: Math.max(1, Math.min(256, Number(limits.maxPuts) || DEFAULT_MAX_PUTS)),
21
+ }
22
+ }
23
+
24
+ /**
25
+ * @param {string} fromNodeHash 来源节点
26
+ * @returns {string} 限速键
27
+ */
28
+ export function mailboxRateKey(fromNodeHash) {
29
+ return String(fromNodeHash || '').trim()
30
+ }
31
+
32
+ /**
33
+ * @param {number} now 当前时间戳
34
+ * @returns {void}
35
+ */
36
+ function sweepExpiredEntries(now) {
37
+ let scanned = 0
38
+ for (const [rateKey, rateEntry] of inboundByKey) {
39
+ if (now > rateEntry.resetAt) inboundByKey.delete(rateKey)
40
+ if (++scanned >= EXPIRE_SWEEP_BATCH) break
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @param {string} key 限速键
46
+ * @returns {void}
47
+ */
48
+ function touchLruKey(key) {
49
+ const entry = inboundByKey.get(key)
50
+ if (!entry) return
51
+ inboundByKey.delete(key)
52
+ inboundByKey.set(key, entry)
53
+ }
54
+
55
+ /**
56
+ * @param {string} key 新插入键
57
+ * @returns {void}
58
+ */
59
+ function evictLruIfNeeded(key) {
60
+ if (inboundByKey.has(key) || inboundByKey.size < MAX_KEYS) return
61
+ const oldest = inboundByKey.keys().next().value
62
+ if (oldest != null) inboundByKey.delete(oldest)
63
+ }
64
+
65
+ /**
66
+ * @param {string} fromNodeHash 来源节点
67
+ * @param {object} [limits] 可选限额
68
+ * @returns {boolean} 允许新 put 则为 true
69
+ */
70
+ export function takeIncomingMailboxPutSlot(fromNodeHash, limits) {
71
+ const { windowMs, maxPuts } = resolveMailboxRateLimits(limits)
72
+ const key = mailboxRateKey(fromNodeHash)
73
+ const now = Date.now()
74
+ if (inboundByKey.size >= MAX_KEYS) sweepExpiredEntries(now)
75
+ evictLruIfNeeded(key)
76
+ let entry = inboundByKey.get(key)
77
+ if (!entry || now > entry.resetAt) entry = { count: 0, resetAt: now + windowMs }
78
+ if (entry.count >= maxPuts) {
79
+ touchLruKey(key)
80
+ return false
81
+ }
82
+ entry.count++
83
+ inboundByKey.set(key, entry)
84
+ touchLruKey(key)
85
+ return true
86
+ }
@@ -0,0 +1,53 @@
1
+ import { getNodeTransportSettings } from '../node/identity.mjs'
2
+ import {
3
+ resolveMailboxRelayFanout,
4
+ resolveMailboxWantFanout,
5
+ } from '../trust_graph/resolve.mjs'
6
+
7
+ import mailboxTunables from './tunables.json' with { type: 'json' }
8
+
9
+ /**
10
+ * @param {object} raw 节点 mailbox 配置片段
11
+ * @param {number} [peerCount=0] 已知在线 relay 候选数(0 时用 floor)
12
+ * @returns {{ maxHop: number, relayFanoutTrusted: number, relayFanoutNormal: number, wantFanout: number }} 规范化 mailbox 配置
13
+ */
14
+ export function normalizeMailboxSettings(raw = {}, peerCount = 0) {
15
+ const maxHop = Math.max(1, Math.min(8, Number(raw.maxHop) || mailboxTunables.maxHop))
16
+ const capTrusted = mailboxTunables.relayFanoutTrustedCap ?? 32
17
+ const capWant = mailboxTunables.wantFanoutCap ?? 32
18
+ const n = Math.max(0, Math.floor(Number(peerCount) || 0))
19
+ const relayFanoutTrusted = Number.isFinite(Number(raw.relayFanoutTrusted))
20
+ ? Math.max(1, Math.min(capTrusted, Math.floor(Number(raw.relayFanoutTrusted))))
21
+ : resolveMailboxRelayFanout(n, mailboxTunables)
22
+ const relayFanoutNormal = Math.max(1, Math.min(capTrusted, Number(raw.relayFanoutNormal) || mailboxTunables.relayFanoutNormal))
23
+ const wantFanout = Number.isFinite(Number(raw.wantFanout))
24
+ ? Math.max(1, Math.min(capWant, Math.floor(Number(raw.wantFanout))))
25
+ : resolveMailboxWantFanout(n, mailboxTunables)
26
+ return { maxHop, relayFanoutTrusted, relayFanoutNormal, wantFanout }
27
+ }
28
+
29
+ /**
30
+ * @param {number} peerCount 已知在线 relay 候选数
31
+ * @param {object} [raw] 节点 mailbox 配置片段
32
+ * @param {boolean} [batterySaver=false] 省电模式
33
+ * @returns {{ maxHop: number, relayFanoutTrusted: number, relayFanoutNormal: number, wantFanout: number, batterySaver: boolean }} 缩放后的路由
34
+ */
35
+ export function resolveMailboxRoutingForPeerCount(peerCount, raw = {}, batterySaver = false) {
36
+ const base = normalizeMailboxSettings(raw, peerCount)
37
+ if (!batterySaver) return { ...base, batterySaver: false }
38
+ return {
39
+ maxHop: base.maxHop,
40
+ relayFanoutTrusted: Math.max(1, Math.ceil(base.relayFanoutTrusted / 2)),
41
+ relayFanoutNormal: Math.max(1, Math.ceil(base.relayFanoutNormal / 2)),
42
+ wantFanout: Math.max(1, Math.ceil(base.wantFanout / 2)),
43
+ batterySaver: true,
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @returns {{ maxHop: number, relayFanoutTrusted: number, relayFanoutNormal: number, wantFanout: number, batterySaver: boolean }} mailbox 路由配置
49
+ */
50
+ export function getMailboxRoutingSettings() {
51
+ const { batterySaver, mailbox } = getNodeTransportSettings()
52
+ return resolveMailboxRoutingForPeerCount(0, mailbox, batterySaver)
53
+ }
@@ -0,0 +1,256 @@
1
+ import { mkdir, readFile } from 'node:fs/promises'
2
+ import { dirname } from 'node:path'
3
+
4
+ import { isHex64, normalizeHex64 } from '../core/hexIds.mjs'
5
+ import { jsonlMutexKey, writeJsonl } from '../dag/storage.mjs'
6
+ import { mailboxStorePath } from '../node/user_paths.mjs'
7
+ import { withAsyncMutex } from '../utils/async_mutex.mjs'
8
+
9
+ import {
10
+ defaultTtlMsForTier,
11
+ sortMailboxForRetention,
12
+ } from './importance.mjs'
13
+ import {
14
+ MAX_BUCKET_BYTES,
15
+ MAX_BUCKET_ENTRIES,
16
+ mailboxBucketKey,
17
+ mailboxRecordBytes,
18
+ pruneMailboxBuckets,
19
+ pruneMailboxGlobalFair,
20
+ } from './prune.mjs'
21
+
22
+ /**
23
+ *
24
+ */
25
+ export { MAX_BUCKET_BYTES, MAX_BUCKET_ENTRIES, mailboxBucketKey, mailboxRecordBytes }
26
+
27
+ /** 单条 mailbox envelope JSON 字节上限 */
28
+ export const MAX_ENTRY_BYTES = 256 * 1024
29
+
30
+ /**
31
+ * @typedef {'trusted' | 'normal' | 'quarantine'} MailboxTier
32
+ */
33
+
34
+ /**
35
+ * @typedef {{
36
+ * id: string,
37
+ * app: string,
38
+ * toPubKeyHash: string,
39
+ * dmSessionTag?: string,
40
+ * groupId?: string,
41
+ * channelId?: string,
42
+ * envelope: object,
43
+ * storedAt: number,
44
+ * expiresAt: number,
45
+ * fromNodeHash: string,
46
+ * hop: number,
47
+ * tier?: MailboxTier,
48
+ * importance?: number,
49
+ * }} MailboxRecord
50
+ */
51
+
52
+ /**
53
+ * @param {unknown} value 入站 hop
54
+ * @returns {number} 非负整数 hop
55
+ */
56
+ export function normalizeMailboxHop(value) {
57
+ const n = Number(value)
58
+ if (!Number.isFinite(n)) return 0
59
+ return Math.max(0, Math.floor(n))
60
+ }
61
+
62
+ /**
63
+ * @returns {string} mailbox store 进程内互斥键
64
+ */
65
+ function mailboxStoreMutexKey() {
66
+ return jsonlMutexKey(mailboxStorePath())
67
+ }
68
+
69
+ /**
70
+ * @param {number} hop 转发跳数
71
+ * @returns {MailboxTier} 信誉分层
72
+ */
73
+ export function mailboxTierFromHop(hop) {
74
+ if (hop <= 0) return 'trusted'
75
+ if (hop === 1) return 'normal'
76
+ return 'quarantine'
77
+ }
78
+
79
+ /**
80
+ * @param {object | null | undefined} row mailbox 记录
81
+ * @returns {boolean} 是否可交给 Part 消费者(排除 quarantine 与缺字段)
82
+ */
83
+ export function isDeliverableMailboxRecord(row) {
84
+ if (!row?.envelope || typeof row.envelope !== 'object') return false
85
+ if (!String(row.app || '').trim()) return false
86
+ return row.tier === 'trusted' || row.tier === 'normal'
87
+ }
88
+
89
+ /**
90
+ * @param {object | null | undefined} record mailbox 记录
91
+ * @returns {boolean} envelope 是否在存储限额内
92
+ */
93
+ export function isMailboxRecordWithinSizeLimit(record) {
94
+ if (!record?.envelope || typeof record.envelope !== 'object') return false
95
+ return JSON.stringify(record.envelope).length <= MAX_ENTRY_BYTES
96
+ }
97
+
98
+ /**
99
+ * 入站 wire 转发:不信任自报 hop,至少为 1;若本地已有同 id 则单调递增。
100
+ * @param {unknown} wireHop 线载荷 hop
101
+ * @param {unknown} [existingStoredHop] 本地已存 hop
102
+ * @returns {number} 本节点存储 hop
103
+ */
104
+ export function relayHopAfterWireIngress(wireHop, existingStoredHop) {
105
+ const fromWire = Math.max(normalizeMailboxHop(wireHop) + 1, 1)
106
+ if (existingStoredHop == null || !Number.isFinite(Number(existingStoredHop)))
107
+ return fromWire
108
+ return Math.max(fromWire, normalizeMailboxHop(existingStoredHop) + 1)
109
+ }
110
+
111
+ /**
112
+ * @returns {Promise<MailboxRecord[]>} 全部有效记录
113
+ */
114
+ async function readAll() {
115
+ try {
116
+ const text = await readFile(mailboxStorePath(), 'utf8')
117
+ /** @type {MailboxRecord[]} */
118
+ const rows = []
119
+ for (const line of text.split('\n')) {
120
+ const trimmed = line.trim()
121
+ if (!trimmed) continue
122
+ try {
123
+ rows.push(JSON.parse(trimmed))
124
+ }
125
+ catch { /* skip corrupt line */ }
126
+ }
127
+ return rows
128
+ }
129
+ catch { return [] }
130
+ }
131
+
132
+ /**
133
+ * @param {MailboxRecord[]} rows 待写入记录
134
+ * @returns {Promise<void>}
135
+ */
136
+ async function writeAll(rows) {
137
+ const filePath = mailboxStorePath()
138
+ await mkdir(dirname(filePath), { recursive: true })
139
+ const now = Date.now()
140
+ const kept = pruneMailboxGlobalFair(
141
+ pruneMailboxBuckets(sortMailboxForRetention(rows.filter(record => record.expiresAt > now))),
142
+ )
143
+ await writeJsonl(filePath, kept)
144
+ }
145
+
146
+ /**
147
+ * @param {object} envelope 载荷
148
+ * @returns {string} 稳定信封 id
149
+ */
150
+ export function mailboxEnvelopeId(envelope) {
151
+ const id = String(envelope?.id || '').trim().toLowerCase()
152
+ if (!id) throw new Error('mailbox envelope id required')
153
+ return id
154
+ }
155
+
156
+ /**
157
+ * @param {object} record mailbox 记录字段
158
+ * @returns {Promise<boolean>} 是否新写入
159
+ */
160
+ export async function storeMailboxRecord(record) {
161
+ if (!isMailboxRecordWithinSizeLimit(record)) return false
162
+ const toPubKeyHash = normalizeHex64(record.toPubKeyHash)
163
+ if (!isHex64(toPubKeyHash)) return false
164
+ const hop = normalizeMailboxHop(record.hop)
165
+ const tier = record.tier
166
+ if (!['trusted', 'normal', 'quarantine'].includes(tier)) return false
167
+ const app = String(record.app || '').trim()
168
+ if (!app) return false
169
+ let id
170
+ try {
171
+ id = mailboxEnvelopeId(record.envelope)
172
+ }
173
+ catch {
174
+ return false
175
+ }
176
+ return withAsyncMutex(mailboxStoreMutexKey(), async () => {
177
+ const rows = await readAll()
178
+ if (rows.some(row => row.id === id)) return false
179
+ const ttlMs = Number(record.ttlMs) || defaultTtlMsForTier(tier)
180
+ rows.push({
181
+ id,
182
+ app,
183
+ toPubKeyHash,
184
+ dmSessionTag: record.dmSessionTag?.trim().toLowerCase() || undefined,
185
+ groupId: record.groupId || undefined,
186
+ channelId: record.channelId || undefined,
187
+ envelope: record.envelope,
188
+ storedAt: Date.now(),
189
+ expiresAt: Date.now() + ttlMs,
190
+ fromNodeHash: String(record.fromNodeHash || '').trim(),
191
+ hop,
192
+ tier,
193
+ importance: Number.isFinite(Number(record.importance)) ? Number(record.importance) : undefined,
194
+ })
195
+ await writeAll(rows)
196
+ return true
197
+ })
198
+ }
199
+
200
+ /**
201
+ * @param {string} toPubKeyHash 收件人
202
+ * @returns {Promise<string[]>} record id 列表
203
+ */
204
+ export async function listMailboxIdsForRecipient(toPubKeyHash) {
205
+ const recipient = normalizeHex64(toPubKeyHash)
206
+ return (await readAll())
207
+ .filter(record => record.toPubKeyHash === recipient)
208
+ .map(record => record.id)
209
+ }
210
+
211
+ /**
212
+ * @param {string[]} ids record id 列表
213
+ * @returns {Promise<MailboxRecord[]>} 匹配记录
214
+ */
215
+ export async function getMailboxRecords(ids) {
216
+ const want = new Set(ids.map(id => String(id).trim().toLowerCase()))
217
+ return (await readAll()).filter(record => want.has(record.id))
218
+ }
219
+
220
+ /**
221
+ * @param {string[]} ids 待删除 id
222
+ * @returns {Promise<void>}
223
+ */
224
+ export async function deleteMailboxRecords(ids) {
225
+ const drop = new Set(ids.map(id => String(id).trim().toLowerCase()))
226
+ return withAsyncMutex(mailboxStoreMutexKey(), async () => {
227
+ await writeAll((await readAll()).filter(record => !drop.has(record.id)))
228
+ })
229
+ }
230
+
231
+ /**
232
+ * @param {string} toPubKeyHash 收件人
233
+ * @returns {Promise<MailboxRecord[]>} 待发记录(不删除)
234
+ */
235
+ export async function takeMailboxForRecipient(toPubKeyHash) {
236
+ const recipient = normalizeHex64(toPubKeyHash)
237
+ return (await readAll()).filter(record => record.toPubKeyHash === recipient)
238
+ }
239
+
240
+ /**
241
+ * @param {string} toPubKeyHash 收件人 pubKeyHash
242
+ * @returns {Promise<number>} 未过期条数
243
+ */
244
+ export async function countMailboxPendingForRecipient(toPubKeyHash) {
245
+ const recipient = normalizeHex64(toPubKeyHash)
246
+ const now = Date.now()
247
+ return (await readAll()).filter(record => record.toPubKeyHash === recipient && record.expiresAt > now).length
248
+ }
249
+
250
+ /**
251
+ * @returns {Promise<number>} 未过期条数
252
+ */
253
+ export async function countMailboxPending() {
254
+ const now = Date.now()
255
+ return (await readAll()).filter(record => record.expiresAt > now).length
256
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "maxHop": 3,
3
+ "relayFanoutTrustedFloor": 3,
4
+ "relayFanoutTrustedRatio": 0.3,
5
+ "relayFanoutTrustedCap": 32,
6
+ "relayFanoutNormal": 3,
7
+ "wantFanoutFloor": 3,
8
+ "wantFanoutRatio": 0.4,
9
+ "wantFanoutCap": 32,
10
+ "networkBudgetPerRound": 64,
11
+ "networkBudgetPerPeerRatio": 2
12
+ }
@@ -0,0 +1,40 @@
1
+ import {
2
+ ingestMailboxGive,
3
+ ingestMailboxPut,
4
+ respondMailboxWant,
5
+ } from './deliver_or_store.mjs'
6
+ import { parseMailboxGive, parseMailboxPut, parseMailboxWant } from './parse.mjs'
7
+
8
+ /**
9
+ * @typedef {{ replicaUsername?: string }} MailboxWireContext
10
+ */
11
+
12
+ /**
13
+ * @param {MailboxWireContext} ctx 入站上下文
14
+ * @param {{ on: (name: string, handler: (payload: unknown, peerId: string) => void) => void, send: (name: string, payload: unknown, peerId: string | null) => void }} wire Trystero 适配器
15
+ * @returns {void}
16
+ */
17
+ export function attachMailboxWire(ctx, wire) {
18
+ wire.on('mailbox_put', (payload, peerId) => {
19
+ const put = parseMailboxPut(payload)
20
+ if (!put.ok) return
21
+ void ingestMailboxPut(ctx, put.value, peerId).catch(err => console.error('mailbox: put ingest failed', err))
22
+ })
23
+
24
+ wire.on('mailbox_want', (payload, peerId) => {
25
+ const want = parseMailboxWant(payload)
26
+ if (!want.ok) return
27
+ void respondMailboxWant(want.value, (giveWire, targetPeerId) => {
28
+ try {
29
+ wire.send('mailbox_give', giveWire, targetPeerId)
30
+ }
31
+ catch { /* disconnected */ }
32
+ }, peerId).catch(err => console.error('mailbox: want failed', err))
33
+ })
34
+
35
+ wire.on('mailbox_give', payload => {
36
+ const give = parseMailboxGive(payload)
37
+ if (!give.ok) return
38
+ void ingestMailboxGive(ctx, give.value).catch(err => console.error('mailbox: give ingest failed', err))
39
+ })
40
+ }