@steve02081504/fount-p2p 0.0.0 → 0.0.2

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/README.md CHANGED
@@ -44,7 +44,8 @@ Root contains only the facade and package metadata; all modules live in layered
44
44
  ## Tests
45
45
 
46
46
  ```bash
47
- npm test # pure + integration (--test-force-exit: node-datachannel leaves native handles)
47
+ npm test # package pure + integration (Node)
48
+ npm run test:fount # cross-repo bridge (Deno; requires fount on PATH)
48
49
  npm run test:live # RTC / link smoke (requires node-datachannel)
49
50
  npm run test:sim # tunables co-evolution sim (dev only, not published; --social-tunables to write back)
50
51
  ```
@@ -34,18 +34,18 @@ export function hashFromPubKeyHex(pubKeyHex) {
34
34
  /**
35
35
  * @param {string} nodeHash 成员所属节点 hash
36
36
  * @param {string} recoveryPubKeyHex 32 字节 recovery 公钥 hex(稳定身份锚)
37
- * @returns {string} 用户 entityHash
37
+ * @returns {string} entityHash
38
38
  */
39
- export function userEntityHashFromRecoveryPubKeyHex(nodeHash, recoveryPubKeyHex) {
39
+ export function entityHashFromRecoveryPubKeyHex(nodeHash, recoveryPubKeyHex) {
40
40
  return encodeEntityHash(nodeHash, hashFromPubKeyHex(recoveryPubKeyHex))
41
41
  }
42
42
 
43
43
  /**
44
44
  * @param {string} nodeHash 成员所属节点 hash
45
45
  * @param {string} subjectHash 成员签名 pubKeyHash(DAG sender)
46
- * @returns {string} 用户 entityHash
46
+ * @returns {string} entityHash
47
47
  */
48
- export function userEntityHashFromSubjectHash(nodeHash, subjectHash) {
48
+ export function entityHashFromSubjectHash(nodeHash, subjectHash) {
49
49
  const node = normalizeHex64(nodeHash)
50
50
  const subject = normalizeHex64(subjectHash)
51
51
  if (!isHex64(node) || !isHex64(subject)) throw new Error('invalid subject hash')
package/deno.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "nodeModulesDir": "none",
3
+ "lock": false
4
+ }
package/discovery/bt.mjs CHANGED
@@ -38,6 +38,35 @@ async function loadBleno() {
38
38
  return mod.default ?? mod
39
39
  }
40
40
 
41
+ /**
42
+ * 等待 BLE 运行时进入 poweredOn(兼容 noble v1/v2 与 bleno)。
43
+ * @param {*} runtime noble 或 bleno 实例
44
+ * @param {number} [timeout] 超时毫秒
45
+ * @returns {Promise<void>}
46
+ */
47
+ export async function waitPoweredOn(runtime, timeout) {
48
+ const wait = runtime.waitForPoweredOnAsync ?? runtime.waitForPoweredOn
49
+ if (typeof wait !== 'function')
50
+ throw new Error('p2p: bluetooth runtime missing waitForPoweredOn(Async)')
51
+ return wait.call(runtime, timeout)
52
+ }
53
+
54
+ /**
55
+ * 探测本机 noble 运行时是否具备 BT 扫描所需 API。
56
+ * @returns {Promise<boolean>} noble 可加载且具备 startScanningAsync 与 waitForPoweredOn(Async) 时为 true
57
+ */
58
+ export async function canUseBluetoothDiscovery() {
59
+ try {
60
+ const noble = await loadNoble()
61
+ if (typeof noble.startScanningAsync !== 'function') return false
62
+ const wait = noble.waitForPoweredOnAsync ?? noble.waitForPoweredOn
63
+ return typeof wait === 'function'
64
+ }
65
+ catch {
66
+ return false
67
+ }
68
+ }
69
+
41
70
  /**
42
71
  * 将 advert 映射序列化为可读 characteristic blob。
43
72
  * @param {Map<string, Uint8Array>} adverts topic → payload 映射
@@ -144,7 +173,7 @@ export function createBluetoothDiscoveryProvider() {
144
173
  }
145
174
  },
146
175
  })
147
- await bleno.waitForPoweredOnAsync(5_000)
176
+ await waitPoweredOn(bleno, 5_000)
148
177
  await bleno.setServicesAsync([
149
178
  new bleno.PrimaryService({
150
179
  uuid: BT_SERVICE_UUID,
@@ -218,7 +247,7 @@ export function createBluetoothDiscoveryProvider() {
218
247
  async function ensureScanRuntime() {
219
248
  if (scanningStarted) return
220
249
  const noble = await loadNoble()
221
- await noble.waitForPoweredOnAsync()
250
+ await waitPoweredOn(noble, 5_000)
222
251
  noble.on('discover', peripheral => {
223
252
  void inspectPeripheral(peripheral).catch(() => { })
224
253
  })
@@ -113,7 +113,7 @@ export async function getProfile(entityHash, replicaUsername = null, options = {
113
113
 
114
114
  if (options.skipPresentation) return merged
115
115
 
116
- let infoDefaults = options.infoDefaults
116
+ let { infoDefaults } = options
117
117
  if (!infoDefaults && replicaUsername)
118
118
  infoDefaults = await resolveInfoDefaultsForEntity(replicaUsername, parsed.entityHash, locales)
119
119
  if (!infoDefaults)
@@ -1,12 +1,12 @@
1
1
  import { parseEntityHash } from '../core/entity_id.mjs'
2
- import { operatorEntityHashFromKeys, getNodeHash } from '../node/identity.mjs'
2
+ import { entityHashFromKeys, getNodeHash } from '../node/identity.mjs'
3
3
 
4
4
  /**
5
5
  * @param {string} recoveryPubKeyHex 64 位十六进制 recovery 公钥
6
- * @returns {string | null} 本节点操作者 entityHash
6
+ * @returns {string | null} 本节点 entityHash
7
7
  */
8
- export function resolveLocalOperatorEntityHash(recoveryPubKeyHex) {
9
- return operatorEntityHashFromKeys(getNodeHash(), recoveryPubKeyHex)
8
+ export function resolveLocalEntityHashFromRecoveryPubKeyHex(recoveryPubKeyHex) {
9
+ return entityHashFromKeys(getNodeHash(), recoveryPubKeyHex)
10
10
  }
11
11
 
12
12
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Operator 密钥历史链:entityHash 锚定 recovery 公钥,活跃钥可轮换。
2
+ * Entity 密钥历史链:entityHash 锚定 recovery 公钥,活跃钥可轮换。
3
3
  */
4
4
  import { Buffer } from 'node:buffer'
5
5
 
@@ -10,7 +10,7 @@ import { isHex64, normalizeHex64 } from '../core/hexIds.mjs'
10
10
  /**
11
11
  *
12
12
  */
13
- export const OPERATOR_KEY_REVOKE_DOMAIN = 'fount-operator-key-revoke'
13
+ export const ENTITY_KEY_REVOKE_DOMAIN = 'fount-entity-key-revoke'
14
14
 
15
15
  /**
16
16
  * @typedef {{
@@ -19,7 +19,7 @@ export const OPERATOR_KEY_REVOKE_DOMAIN = 'fount-operator-key-revoke'
19
19
  * attestedBy: 'recovery' | 'active' | 'revoked',
20
20
  * validFrom?: number,
21
21
  * revokedGenerations?: number[],
22
- * }} OperatorKeyHistoryEntry
22
+ * }} EntityKeyHistoryEntry
23
23
  */
24
24
 
25
25
  /**
@@ -42,7 +42,7 @@ export function activeSenderHashFromPubKeyHex(activePubKeyHex) {
42
42
  * @param {string} recoveryPubKeyHex recovery 公钥
43
43
  * @param {string} activePubKeyHex 初始活跃公钥
44
44
  * @param {number} [validFrom] 生效时间
45
- * @returns {OperatorKeyHistoryEntry[]} 创世链
45
+ * @returns {EntityKeyHistoryEntry[]} 创世链
46
46
  */
47
47
  export function createGenesisKeyHistory(recoveryPubKeyHex, activePubKeyHex, validFrom = Date.now()) {
48
48
  return [{
@@ -54,7 +54,7 @@ export function createGenesisKeyHistory(recoveryPubKeyHex, activePubKeyHex, vali
54
54
  }
55
55
 
56
56
  /**
57
- * @param {OperatorKeyHistoryEntry[]} keyHistory 密钥历史
57
+ * @param {EntityKeyHistoryEntry[]} keyHistory 密钥历史
58
58
  * @param {number} generation 代际
59
59
  * @returns {string | null} 活跃公钥 hex
60
60
  */
@@ -64,7 +64,7 @@ export function resolveActiveKeyAtGeneration(keyHistory, generation) {
64
64
  }
65
65
 
66
66
  /**
67
- * @param {OperatorKeyHistoryEntry[]} keyHistory 密钥历史
67
+ * @param {EntityKeyHistoryEntry[]} keyHistory 密钥历史
68
68
  * @returns {number} 最新代际,无则 -1
69
69
  */
70
70
  export function resolveLatestActiveGeneration(keyHistory) {
@@ -73,7 +73,7 @@ export function resolveLatestActiveGeneration(keyHistory) {
73
73
  }
74
74
 
75
75
  /**
76
- * @param {OperatorKeyHistoryEntry[]} keyHistory 密钥历史
76
+ * @param {EntityKeyHistoryEntry[]} keyHistory 密钥历史
77
77
  * @param {number} generation 代际
78
78
  * @returns {boolean} 是否已吊销
79
79
  */
@@ -84,7 +84,7 @@ export function isActiveGenerationRevoked(keyHistory, generation) {
84
84
  }
85
85
 
86
86
  /**
87
- * @param {OperatorKeyHistoryEntry[]} keyHistory 密钥历史
87
+ * @param {EntityKeyHistoryEntry[]} keyHistory 密钥历史
88
88
  * @param {string} recoveryPubKeyHex recovery 公钥
89
89
  * @param {string} senderPubKeyHash 事件 sender(64 hex pubKeyHash)
90
90
  * @returns {boolean} sender 是否为未吊销的活跃钥
@@ -112,18 +112,18 @@ export function isRecoverySender(recoveryPubKeyHex, senderPubKeyHash) {
112
112
 
113
113
  /**
114
114
  * @param {object} state 物化状态
115
- * @param {object} event operator_key_rotate 事件
115
+ * @param {object} event entity_key_rotate 事件
116
116
  * @returns {object} 更新后状态
117
117
  */
118
- export function reduceOperatorKeyRotate(state, event) {
118
+ export function reduceEntityKeyRotate(state, event) {
119
119
  const generation = Number(event.content?.generation)
120
120
  const activePubKeyHex = normalizeHex64(event.content?.activePubKeyHex || '')
121
121
  if (!Number.isFinite(generation) || generation < 0 || !isHex64(activePubKeyHex))
122
122
  return state
123
- state.operatorKeyHistory = state.operatorKeyHistory || []
124
- if (state.operatorKeyHistory.some(row => row.generation === generation))
123
+ state.entityKeyHistory = state.entityKeyHistory || []
124
+ if (state.entityKeyHistory.some(row => row.generation === generation))
125
125
  return state
126
- state.operatorKeyHistory.push({
126
+ state.entityKeyHistory.push({
127
127
  generation,
128
128
  activePubKeyHex,
129
129
  attestedBy: generation === 0 ? 'recovery' : 'active',
@@ -134,10 +134,10 @@ export function reduceOperatorKeyRotate(state, event) {
134
134
 
135
135
  /**
136
136
  * @param {object} state 物化状态
137
- * @param {object} event operator_key_revoke 事件
137
+ * @param {object} event entity_key_revoke 事件
138
138
  * @returns {object} 更新后状态
139
139
  */
140
- export function reduceOperatorKeyRevoke(state, event) {
140
+ export function reduceEntityKeyRevoke(state, event) {
141
141
  const newGeneration = Number(event.content?.newGeneration)
142
142
  const activePubKeyHex = normalizeHex64(event.content?.activePubKeyHex || '')
143
143
  const revokeGenerations = Array.isArray(event.content?.revokeGenerations)
@@ -145,17 +145,17 @@ export function reduceOperatorKeyRevoke(state, event) {
145
145
  : []
146
146
  if (!Number.isFinite(newGeneration) || newGeneration < 0 || !isHex64(activePubKeyHex))
147
147
  return state
148
- state.operatorKeyHistory = state.operatorKeyHistory || []
148
+ state.entityKeyHistory = state.entityKeyHistory || []
149
149
  for (const gen of revokeGenerations) {
150
- const entry = state.operatorKeyHistory.find(row => row.generation === gen)
150
+ const entry = state.entityKeyHistory.find(row => row.generation === gen)
151
151
  if (entry) {
152
152
  entry.revokedGenerations = entry.revokedGenerations || []
153
153
  if (!entry.revokedGenerations.includes(gen))
154
154
  entry.revokedGenerations.push(gen)
155
155
  }
156
156
  }
157
- if (!state.operatorKeyHistory.some(row => row.generation === newGeneration))
158
- state.operatorKeyHistory.push({
157
+ if (!state.entityKeyHistory.some(row => row.generation === newGeneration))
158
+ state.entityKeyHistory.push({
159
159
  generation: newGeneration,
160
160
  activePubKeyHex,
161
161
  attestedBy: 'recovery',
@@ -166,35 +166,34 @@ export function reduceOperatorKeyRevoke(state, event) {
166
166
  }
167
167
  /**
168
168
  * @param {object[]} events 时间线事件(拓扑序)
169
- * @returns {{ recoveryPubKeyHex: string | null, operatorKeyHistory: OperatorKeyHistoryEntry[] }} 折叠密钥链
169
+ * @returns {{ recoveryPubKeyHex: string | null, entityKeyHistory: EntityKeyHistoryEntry[] }} 折叠密钥链
170
170
  */
171
- export function foldOperatorKeyHistoryFromEvents(events) {
172
- /** @type {OperatorKeyHistoryEntry[]} */
173
- let operatorKeyHistory = []
171
+ export function foldEntityKeyHistoryFromEvents(events) {
172
+ /** @type {EntityKeyHistoryEntry[]} */
173
+ let entityKeyHistory = []
174
174
  for (const event of events || []) {
175
- if (event.type === 'operator_key_rotate') {
176
- const state = reduceOperatorKeyRotate({ operatorKeyHistory }, event)
177
- operatorKeyHistory = state.operatorKeyHistory
175
+ if (event.type === 'entity_key_rotate') {
176
+ const state = reduceEntityKeyRotate({ entityKeyHistory }, event)
177
+ entityKeyHistory = state.entityKeyHistory
178
178
  }
179
- if (event.type === 'operator_key_revoke') {
180
- const state = reduceOperatorKeyRevoke({ operatorKeyHistory }, event)
181
- operatorKeyHistory = state.operatorKeyHistory
179
+ if (event.type === 'entity_key_revoke') {
180
+ const state = reduceEntityKeyRevoke({ entityKeyHistory }, event)
181
+ entityKeyHistory = state.entityKeyHistory
182
182
  }
183
183
  }
184
- return { recoveryPubKeyHex: null, operatorKeyHistory }
184
+ return { recoveryPubKeyHex: null, entityKeyHistory }
185
185
  }
186
186
 
187
187
  /**
188
188
  * @param {object} revokeBody 吊销正文
189
189
  * @returns {Buffer} 固定域签名消息
190
190
  */
191
- export function operatorKeyRevokeSignBytes(revokeBody) {
191
+ export function entityKeyRevokeSignBytes(revokeBody) {
192
192
  const body = {
193
193
  revokeGenerations: (revokeBody.revokeGenerations || []).map(g => Number(g)),
194
194
  newGeneration: Number(revokeBody.newGeneration),
195
195
  activePubKeyHex: normalizeHex64(revokeBody.activePubKeyHex || ''),
196
196
  entityHash: String(revokeBody.entityHash || '').trim().toLowerCase(),
197
197
  }
198
- return Buffer.from(`${OPERATOR_KEY_REVOKE_DOMAIN}\0${canonicalStringify(body)}`, 'utf8')
198
+ return Buffer.from(`${ENTITY_KEY_REVOKE_DOMAIN}\0${canonicalStringify(body)}`, 'utf8')
199
199
  }
200
-
@@ -46,7 +46,7 @@ function chunkFetchPeerTargets() {
46
46
  */
47
47
  export async function fetchChunk(context) {
48
48
  const hash = String(context.ciphertextHash || '').trim().toLowerCase()
49
- const username = context.username
49
+ const { username } = context
50
50
  if (!hash || !username) return null
51
51
 
52
52
  if (await hasChunk(hash))
@@ -24,8 +24,7 @@ export async function resolveContentKey(descriptor, manifest, deps = {}) {
24
24
  return null
25
25
 
26
26
  if (type === 'file-master-key-wrap') {
27
- const groupId = descriptor.groupId
28
- const fileId = descriptor.fileId
27
+ const { groupId, fileId } = descriptor
29
28
  if (!groupId || !fileId || !descriptor.wrappedKey || !deps.getGroupFileMasterKey) return null
30
29
  const groupKey = await deps.getGroupFileMasterKey(String(groupId), descriptor.keyGeneration)
31
30
  if (!groupKey) return null
@@ -33,8 +32,7 @@ export async function resolveContentKey(descriptor, manifest, deps = {}) {
33
32
  }
34
33
 
35
34
  if (type === 'vault-wrap') {
36
- const entityHash = descriptor.entityHash
37
- const fileId = descriptor.fileId
35
+ const { entityHash, fileId } = descriptor
38
36
  if (!entityHash || !fileId || !descriptor.wrappedKey || !deps.getVaultMasterKey) return null
39
37
  const vaultKey = await deps.getVaultMasterKey(String(entityHash))
40
38
  if (!vaultKey) return null
@@ -101,7 +101,7 @@ export function verifyJoinPow(powSolution, opts) {
101
101
  if (!powSolution || typeof powSolution !== 'object') return { ok: false, achievedBits: 0 }
102
102
 
103
103
  const anchorRef = String(powSolution.anchorRef ?? '').trim()
104
- const nonce = powSolution.nonce
104
+ const { nonce } = powSolution
105
105
  const epoch = Number(powSolution.epoch)
106
106
  const joinerNodeHash = String(powSolution.joinerNodeHash ?? opts.senderNodeHash ?? '').trim().toLowerCase()
107
107
  const senderNodeHash = String(opts.senderNodeHash ?? '').trim().toLowerCase()
@@ -131,7 +131,7 @@ export function onBufferedAmountLow(channel, cb) {
131
131
  * @returns {{ enqueue: (action: string, bytes: Uint8Array, preferredChannel?: 'control' | 'bulk') => void, flush: (channelName?: 'control' | 'bulk') => void, pending: () => { control: number, bulk: number }, clear: () => void }} 发送队列 API
132
132
  */
133
133
  export function createChannelSendQueues(opts) {
134
- const getChannel = opts.getChannel
134
+ const { getChannel } = opts
135
135
  const highWatermarkBytes = Math.max(CHANNEL_LOW_THRESHOLD_BYTES, Number(opts.highWatermarkBytes) || CHANNEL_HIGH_WATERMARK_BYTES)
136
136
  /** @type {{ control: Array<{ priority: number, seq: number, bytes: Uint8Array }>, bulk: Array<{ priority: number, seq: number, bytes: Uint8Array }> }} */
137
137
  const queues = { control: [], bulk: [] }
@@ -10,7 +10,7 @@ import { normalizeDtlsFingerprint } from './sdp_fingerprint.mjs'
10
10
  /**
11
11
  * Link 握手签名域标识符。
12
12
  */
13
- export const LINK_HANDSHAKE_DOMAIN = 'fount-link-v1'
13
+ export const LINK_HANDSHAKE_DOMAIN = 'fount-link'
14
14
 
15
15
  /**
16
16
  * 构造 link auth 待签名字节串。
@@ -126,7 +126,7 @@ export async function verifyAuth(hello, auth, expectedNonce, remoteFingerprintFr
126
126
  * @returns {Uint8Array} 待签名消息字节
127
127
  */
128
128
  export function buildAdvertMessage(topic, ts, nodeHash) {
129
- return Buffer.from(`fount-advert-v1\0${String(topic)}\0${String(ts)}\0${normalizeHex64(nodeHash)}`, 'utf8')
129
+ return Buffer.from(`fount-advert\0${String(topic)}\0${String(ts)}\0${normalizeHex64(nodeHash)}`, 'utf8')
130
130
  }
131
131
 
132
132
  /**
package/link/link.mjs CHANGED
@@ -326,7 +326,7 @@ export async function createLink(opts) {
326
326
  * @returns {boolean} 应重新入队则 true
327
327
  */
328
328
  function shouldRetryQueuedIce(error) {
329
- return /without ICE transport/i.test(String(error?.message ?? error ?? ''))
329
+ return /without ice transport/i.test(String(error?.message ?? error ?? ''))
330
330
  }
331
331
 
332
332
  /**
@@ -20,6 +20,6 @@ export function normalizeDtlsFingerprint(value) {
20
20
  * @returns {string | null} 规范化 fingerprint,未找到时返回 null
21
21
  */
22
22
  export function extractDtlsFingerprint(sdp) {
23
- const line = String(sdp || '').match(/^a=fingerprint:sha-256\s+([0-9A-Fa-f:]+)$/mu)?.[1]
23
+ const line = String(sdp || '').match(/^a=fingerprint:sha-256\s+([\d:A-Fa-f]+)$/mu)?.[1]
24
24
  return normalizeDtlsFingerprint(line)
25
25
  }
package/mailbox/store.mjs CHANGED
@@ -162,7 +162,7 @@ export async function storeMailboxRecord(record) {
162
162
  const toPubKeyHash = normalizeHex64(record.toPubKeyHash)
163
163
  if (!isHex64(toPubKeyHash)) return false
164
164
  const hop = normalizeMailboxHop(record.hop)
165
- const tier = record.tier
165
+ const { tier } = record
166
166
  if (!['trusted', 'normal', 'quarantine'].includes(tier)) return false
167
167
  const app = String(record.app || '').trim()
168
168
  if (!app) return false
package/node/denylist.mjs CHANGED
@@ -69,7 +69,7 @@ export function invalidateDenylistIndex() {
69
69
  function getDenylistIndex() {
70
70
  if (cachedIndex) return cachedIndex
71
71
  const raw = readNodeJsonSync(DATA_NAME)
72
- const blocked = normalizeDenylist(raw).blocked
72
+ const { blocked } = normalizeDenylist(raw)
73
73
  cachedIndex = buildDenylistIndex(blocked)
74
74
  return cachedIndex
75
75
  }
@@ -111,7 +111,7 @@ export function loadDenylist() {
111
111
  * @returns {void}
112
112
  */
113
113
  export function saveDenylist(list) {
114
- const blocked = normalizeDenylist(list).blocked
114
+ const { blocked } = normalizeDenylist(list)
115
115
  writeNodeJsonSync(DATA_NAME, { blocked })
116
116
  cachedIndex = buildDenylistIndex(blocked)
117
117
  }
package/node/identity.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { randomBytes } from 'node:crypto'
2
2
 
3
- import { userEntityHashFromRecoveryPubKeyHex } from '../core/entity_id.mjs'
3
+ import { entityHashFromRecoveryPubKeyHex } from '../core/entity_id.mjs'
4
4
  import { isHex64 } from '../core/hexIds.mjs'
5
5
  import { nodeHashFromSeed } from '../entity/node_hash.mjs'
6
6
  import { normalizeMailboxSettings } from '../mailbox/settings.mjs'
@@ -8,7 +8,7 @@ import { normalizeMailboxSettings } from '../mailbox/settings.mjs'
8
8
  import { emitNodeChange } from './instance.mjs'
9
9
  import { readNodeJsonSync, writeNodeJsonSync } from './storage.mjs'
10
10
 
11
- const NODE_SEED_HEX_RE = /^[0-9a-f]{64}$/iu
11
+ const NODE_SEED_HEX_RE = /^[\da-f]{64}$/iu
12
12
  const NODE_JSON = 'node'
13
13
 
14
14
  /**
@@ -90,10 +90,10 @@ export function ensureNodeDefaults() {
90
90
  /**
91
91
  * @param {string} nodeHash 64 位十六进制
92
92
  * @param {string} recoveryPubKeyHex 64 位十六进制 recovery 公钥(稳定身份锚)
93
- * @returns {string | null} 操作者 entityHash
93
+ * @returns {string | null} entityHash(非法 hex 时 null)
94
94
  */
95
- export function operatorEntityHashFromKeys(nodeHash, recoveryPubKeyHex) {
95
+ export function entityHashFromKeys(nodeHash, recoveryPubKeyHex) {
96
96
  const pub = String(recoveryPubKeyHex || '').trim().toLowerCase().replace(/^0x/iu, '')
97
97
  if (!isHex64(nodeHash) || !isHex64(pub)) return null
98
- return userEntityHashFromRecoveryPubKeyHex(nodeHash, pub)
98
+ return entityHashFromRecoveryPubKeyHex(nodeHash, pub)
99
99
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 按实体的个人列表(Chat 与 Social 共享)。
2
+ * 按实体的个人列表。
3
3
  * **block**:对外联邦公开拉黑(timeline block 事件 + personal_block 索引);
4
4
  * **hide**:纯本地隐藏(personal_hide.json,永不联邦)。
5
5
  */
package/overlay/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { pubKeyHash, sign, verify } from '../crypto/crypto.mjs'
4
4
  import { randomMsgIdHex } from '../link/frame.mjs'
5
5
  import { createLruMap } from '../utils/lru.mjs'
6
6
 
7
- const ROUTE_DOMAIN = 'fount-route-v1'
7
+ const ROUTE_DOMAIN = 'fount-route'
8
8
 
9
9
  /**
10
10
  * 构造 overlay 路由签名用的字节序列。
@@ -25,7 +25,7 @@ function routeSignBytes(reqId, path) {
25
25
  export function createOverlayRouter(registry, ttl = 3) {
26
26
  const selfNodeHash = registry.localIdentity.nodeHash
27
27
  const selfPubKey = registry.localIdentity.nodePubKey
28
- const secretKey = registry.localIdentity.secretKey
28
+ const { secretKey } = registry.localIdentity
29
29
  const seenReqs = createLruMap(4096)
30
30
  /** @type {Map<string, { resolve: (path: string[]) => void, reject: (err: Error) => void, timer: number }>} */
31
31
  const pendingRoutes = new Map()
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@steve02081504/fount-p2p",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "fount federation P2P layer — link, trust graph, mailbox, DAG, EVFS.",
5
- "categories": [
5
+ "keywords": [
6
6
  "network",
7
7
  "p2p",
8
8
  "fount",
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "scripts": {
21
21
  "test": "node --test --test-concurrency=1 --test-force-exit test/pure/*.test.mjs test/integration/*.test.mjs",
22
+ "test:fount": "deno test -A --config deno.json test/fount/",
22
23
  "test:live": "node --test --test-concurrency=1 --test-force-exit test/live/*.test.mjs",
23
24
  "test:sim": "node --test --test-concurrency=1 --test-force-exit sim/test/*.test.mjs"
24
25
  },
@@ -60,14 +61,14 @@
60
61
  "./utils/*": "./utils/*.mjs"
61
62
  },
62
63
  "dependencies": {
63
- "@noble/curves": "^1.9.0",
64
- "node-datachannel": "^0.11.0"
64
+ "@noble/curves": "latest",
65
+ "node-datachannel": "latest"
65
66
  },
66
67
  "devDependencies": {
67
- "@steve02081504/exec": "file:../../exec"
68
+ "@steve02081504/exec": "latest"
68
69
  },
69
70
  "optionalDependencies": {
70
- "@stoprocent/bleno": "^0.11.0",
71
- "@stoprocent/noble": "^1.9.0"
71
+ "@stoprocent/bleno": "latest",
72
+ "@stoprocent/noble": "latest"
72
73
  }
73
74
  }
@@ -15,7 +15,7 @@ import { createLruMap } from '../utils/lru.mjs'
15
15
 
16
16
  import { DEFAULT_ICE_SERVERS } from './ice_servers.mjs'
17
17
 
18
- const SIGNAL_DOMAIN = 'fount-signal-v1'
18
+ const SIGNAL_DOMAIN = 'fount-signal'
19
19
  const NODE_TOPIC_DOMAIN = 'fount-rdv-node:'
20
20
  const GROUP_TOPIC_DOMAIN = 'fount-rdv-group:'
21
21
 
@@ -235,11 +235,11 @@ export function createLinkRegistry(opts = {}) {
235
235
  const providerIds = new Set(listDiscoveryProviders().map(provider => provider.id))
236
236
  if (!providerIds.has('mdns'))
237
237
  registerDiscoveryProvider(createMdnsDiscoveryProvider())
238
- if (!providerIds.has('bt'))
239
- await import('@stoprocent/noble')
240
- .then(() => import('../discovery/bt.mjs'))
241
- .then(bt => registerDiscoveryProvider(bt.createBluetoothDiscoveryProvider()))
242
- .catch(() => { })
238
+ if (!providerIds.has('bt')) {
239
+ const bt = await import('../discovery/bt.mjs').catch(() => null)
240
+ if (await bt?.canUseBluetoothDiscovery?.())
241
+ registerDiscoveryProvider(bt.createBluetoothDiscoveryProvider())
242
+ }
243
243
  if (!providerIds.has('nostr'))
244
244
  // 测试通过 initNode({ signaling: { relayOverride } }) 注入共享 loopback relay;生产则回落到用户 relay + 默认公网 relay。
245
245
  // 新 discovery 栈也必须尊重这层 runtime override,否则 live 双节点测试会各打各的公网 relay。
@@ -3,9 +3,7 @@
3
3
  *
4
4
  * 无回退行为——仅在发生处记录异常「身份映射滞后于活跃连接」
5
5
  * (群/房间 + peerId + nodeHash),以便追踪 onPeerLeave 遗漏。
6
- * 计数通过 catchup stats 暴露给测试;磁盘记录可在 debug_logs/ 下 grep。
7
6
  */
8
- import { debugLog } from '../utils/debug_log.mjs'
9
7
 
10
8
  /** @type {Map<string, number>} scopeId → 累计修剪次数 */
11
9
  const pruneCounts = new Map()
@@ -31,7 +29,6 @@ export function recordStalePeerPrune(scope, staleEntries, meta = {}) {
31
29
  lines.push(JSON.stringify(record))
32
30
  }
33
31
  while (recent.length > RECENT_CAP) recent.shift()
34
- void debugLog('federation_stale_peer', `${lines.join('\n')}\n`).catch(() => { /* best-effort observability */ })
35
32
  }
36
33
 
37
34
  /**
@@ -76,6 +76,6 @@ export function normalizePartpath(value) {
76
76
  */
77
77
  export function isPartInvoke(value) {
78
78
  if (!isPlainObject(value)) return false
79
- const kind = value.kind
79
+ const { kind } = value
80
80
  return typeof kind === 'string' && kind.length > 0
81
81
  }
@@ -18,7 +18,7 @@ const buckets = createLruMap(BUCKET_MAP_MAX)
18
18
  export function consumeWireRateBucket(bucketKey, limits) {
19
19
  const byteCount = Math.max(0, Number(limits.byteCount) || 0)
20
20
  const maxBytes = Number(limits.maxBytesPerWindow) || 0
21
- const maxCount = limits.maxCount
21
+ const { maxCount } = limits
22
22
  const now = Date.now()
23
23
  let bucket = buckets.get(bucketKey)
24
24
  if (!bucket) {
@@ -1,16 +0,0 @@
1
- import fs from 'node:fs'
2
- import path from 'node:path'
3
-
4
- const dir = path.resolve(process.cwd(), 'debug_logs')
5
-
6
- /**
7
- * P2P 观测落盘:写入进程 cwd 下 `debug_logs/`(与 monorepo `debugLog` 行为对齐)。
8
- * @param {string} name 日志文件名(不含扩展名)
9
- * @param {unknown} data 调试数据
10
- * @returns {Promise<void>}
11
- */
12
- export async function debugLog(name, data) {
13
- const text = Object(data) instanceof String ? data : JSON.stringify(data)
14
- await fs.promises.mkdir(dir, { recursive: true })
15
- await fs.promises.appendFile(path.join(dir, `${name}.log`), text)
16
- }