@steve02081504/fount-p2p 0.0.43 → 0.0.44
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/AGENTS.md +1 -0
- package/docs/runtime.md +18 -0
- package/index.mjs +2 -0
- package/node/identity.mjs +6 -0
- package/node/instance.mjs +37 -5
- package/package.json +2 -2
package/AGENTS.md
CHANGED
|
@@ -66,6 +66,7 @@ Deno / native / BT: [runtime.md](docs/runtime.md).
|
|
|
66
66
|
### Node / network
|
|
67
67
|
|
|
68
68
|
- **Node data:** `initNode({ nodeDir, entityStore? })` — `node.json`, `network.json`, `denylist.json`, `reputation.json`, `mailbox/`, `chunks/`. Default EntityStore: `{nodeDir}/entities/`. No `FOUNT_*` env knobs — subprocess IPC uses argv.
|
|
69
|
+
- **Storage vs runtime:** local identity (`ensureNodeSeed` / `getNodeHash` / `resolveLocalEntityHashFromRecoveryPubKeyHex`, all `node/storage` JSON helpers) must not require `initNode`. Configure the dir with `configureNodeStorage({ nodeDir })` (idempotent; cannot switch while running — `closeNode()` first) and only call `initNode` when the network should run. Hosts configure storage unconditionally so entities have a local identity with P2P disabled. [runtime.md](docs/runtime.md)
|
|
69
70
|
- **fount network:** shells use `startNode` / `ensureLinkToNode` / `sendToNodeLink` / rooms — never import `link/` internals or pick a transport. Providers: `registerLinkProvider` from `./link` or facade.
|
|
70
71
|
- **Link `level` vs discovery `priority`:** descending `level` picks data transport (`nostr` = −∞ last resort); ascending `priority` orders handshake/presence media only. [transports.md](docs/transports.md)
|
|
71
72
|
- **Mesh first / no versioning:** ≥N links (K acquaintances + N−K explore); discovery API is `listVisibleNodeHashes` + `connectToNode` only; no topic on the fount-network surface; no version/compat fields. [mesh.md](docs/mesh.md)
|
package/docs/runtime.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
`ensureRuntime`, startup/shutdown budgets, and Bluetooth hardware probe. Day-to-day shell rules: [AGENTS.md](../AGENTS.md). Providers: [transports.md](transports.md). Mesh N/K: [mesh.md](mesh.md).
|
|
4
4
|
|
|
5
|
+
## Offline local identity (storage without a running node)
|
|
6
|
+
|
|
7
|
+
Three independent stages — do not couple them:
|
|
8
|
+
|
|
9
|
+
1. **Storage** — `configureNodeStorage({ nodeDir })` records where `node.json` / identity seed live. No runtime, no disk I/O, no discovery.
|
|
10
|
+
2. **Local identity** — `ensureNodeSeed` / `getNodeHash` / `resolveLocalEntityHashFromRecoveryPubKeyHex` (and `node/storage` JSON helpers) only need stage 1, so a local `entityHash` can be created while federation is off.
|
|
11
|
+
3. **Network** — `initNode({ nodeDir, entityStore })` builds the runtime and link graph. Same `nodeDir` as stage 1 reuses the persisted seed and hashes.
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { configureNodeStorage } from '@steve02081504/fount-p2p/node/instance'
|
|
15
|
+
import { resolveLocalEntityHashFromRecoveryPubKeyHex } from '@steve02081504/fount-p2p/node/identity'
|
|
16
|
+
|
|
17
|
+
configureNodeStorage({ nodeDir: '/data/p2p/node' })
|
|
18
|
+
const entityHash = resolveLocalEntityHashFromRecoveryPubKeyHex(recoveryPubKeyHex) // works with P2P disabled
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`configureNodeStorage` is idempotent for the same dir; switching dirs while a node is running is rejected (call `closeNode()` first). `closeNode()` clears the storage config, so re-configure after closing. Consumer hosts (fount, subfount) call `configureNodeStorage` unconditionally at startup and only call `initNode` when P2P is enabled.
|
|
22
|
+
|
|
5
23
|
## `ensureRuntime` contract
|
|
6
24
|
|
|
7
25
|
Returns after registering lan / nostr discovery providers (subject to the signaling `channels` whitelist), registering all enabled link providers (including `ble_gatt`), and scheduling background warm — does **not** await lan_tcp listen, Nostr relays, or BT availability.
|
package/index.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { registerLinkProvider } from './link/providers/index.mjs'
|
|
|
13
13
|
import { ensureNodeDefaults, getNodeHash } from './node/identity.mjs'
|
|
14
14
|
import {
|
|
15
15
|
closeNode,
|
|
16
|
+
configureNodeStorage,
|
|
16
17
|
getNodeDir,
|
|
17
18
|
getP2PFeatures,
|
|
18
19
|
initNode,
|
|
@@ -65,6 +66,7 @@ export {
|
|
|
65
66
|
attachNodeScopeDefaultFeatures,
|
|
66
67
|
closeNode,
|
|
67
68
|
configureLinkRegistry,
|
|
69
|
+
configureNodeStorage,
|
|
68
70
|
createGroupLinkSet,
|
|
69
71
|
createScopedLinkRoom,
|
|
70
72
|
ensureChannelAvailable,
|
package/node/identity.mjs
CHANGED
|
@@ -42,6 +42,8 @@ function saveNodeFile(patch) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
+
* 确保本地节点 seed 已持久化并返回。
|
|
46
|
+
* 只依赖本地存储目录(`configureNodeStorage` 或 `initNode` 提供),不要求运行中的节点。
|
|
45
47
|
* @returns {string} 64 位十六进制 节点种子
|
|
46
48
|
*/
|
|
47
49
|
export function ensureNodeSeed() {
|
|
@@ -54,6 +56,8 @@ export function ensureNodeSeed() {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
59
|
+
* 由本地 seed 派生本节点 nodeHash。
|
|
60
|
+
* 只依赖本地存储目录,不要求运行中的节点。
|
|
57
61
|
* @returns {string} 本节点 64 hex nodeHash
|
|
58
62
|
*/
|
|
59
63
|
export function getNodeHash() {
|
|
@@ -111,6 +115,8 @@ export function entityHashFromKeys(nodeHash, recoveryPubKeyHex) {
|
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
/**
|
|
118
|
+
* 由本地 nodeHash 与 recovery 公钥派生本节点 entityHash。
|
|
119
|
+
* 只依赖本地存储目录,不要求运行中的节点(联邦未启动时也可创建本地身份)。
|
|
114
120
|
* @param {string} recoveryPubKeyHex 64 位十六进制 recovery 公钥
|
|
115
121
|
* @returns {string | null} 本节点 entityHash
|
|
116
122
|
*/
|
package/node/instance.mjs
CHANGED
|
@@ -22,6 +22,13 @@ import { defaultSignalingRuntimeConfig, resolveSignalingRuntimeConfig } from './
|
|
|
22
22
|
/** @type {NodeRuntime | null} */
|
|
23
23
|
let runtime = null
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* 本地节点数据目录。与 `runtime` 解耦:身份 seed 等纯本地持久化只依赖存储目录,
|
|
27
|
+
* 不要求 `initNode()`(联邦未启动时也要能派生本地身份)。
|
|
28
|
+
* @type {string | null}
|
|
29
|
+
*/
|
|
30
|
+
let nodeStorageDir = null
|
|
31
|
+
|
|
25
32
|
/** @type {Set<(event: string, payload?: unknown) => void>} */
|
|
26
33
|
const changeListeners = new Set()
|
|
27
34
|
|
|
@@ -35,6 +42,28 @@ export function getRtcPolyfillCacheEpoch() {
|
|
|
35
42
|
return rtcPolyfillCacheEpoch
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
/**
|
|
46
|
+
* 配置本地节点数据目录(`node.json` / 身份 seed 等纯本地持久化位置),
|
|
47
|
+
* 不初始化节点运行时、不创建 disk 资源、不启动发现或连接。
|
|
48
|
+
*
|
|
49
|
+
* 配置后 `getNodeDir` / `ensureNodeSeed` / `getNodeHash` /
|
|
50
|
+
* `resolveLocalEntityHashFromRecoveryPubKeyHex` 可在无运行节点时使用;
|
|
51
|
+
* 之后以同一目录调用 `initNode` 会复用已持久化的 seed 与身份。
|
|
52
|
+
*
|
|
53
|
+
* 同目录重复配置为幂等;运行中的节点不允许切换到不同目录,须先 `closeNode()`。
|
|
54
|
+
* @param {{ nodeDir: string }} options - 本地节点数据目录
|
|
55
|
+
* @returns {string} 规范化为绝对路径后的目录
|
|
56
|
+
*/
|
|
57
|
+
export function configureNodeStorage(options) {
|
|
58
|
+
const rawDir = options?.nodeDir
|
|
59
|
+
if (typeof rawDir !== 'string' || !rawDir) throw new Error('p2p: configureNodeStorage requires nodeDir')
|
|
60
|
+
const nodeDir = path.resolve(rawDir)
|
|
61
|
+
if (runtime && runtime.nodeDir !== nodeDir)
|
|
62
|
+
throw new Error('p2p: running node uses another nodeDir — call closeNode() before reconfiguring storage')
|
|
63
|
+
nodeStorageDir = nodeDir
|
|
64
|
+
return nodeDir
|
|
65
|
+
}
|
|
66
|
+
|
|
38
67
|
/**
|
|
39
68
|
* @param {{ nodeDir: string, entityStore?: import('./entity_store.mjs').EntityStore }} options - 节点目录与可选 entity store
|
|
40
69
|
* @returns {NodeRuntime} 初始化后的运行时
|
|
@@ -43,7 +72,7 @@ export function initNode(options) {
|
|
|
43
72
|
if (runtime) throw new Error('p2p: initNode already called — use setNodeLogger / setSignalingRuntimeConfig or closeNode')
|
|
44
73
|
if (options?.logger !== undefined || options?.signaling !== undefined)
|
|
45
74
|
throw new Error('p2p: initNode only accepts nodeDir/entityStore — use setNodeLogger / setSignalingRuntimeConfig')
|
|
46
|
-
const nodeDir =
|
|
75
|
+
const nodeDir = options?.nodeDir == null ? nodeStorageDir : configureNodeStorage({ nodeDir: options.nodeDir })
|
|
47
76
|
if (!nodeDir) throw new Error('p2p: initNode requires nodeDir')
|
|
48
77
|
const entityStore = options.entityStore ?? createFsEntityStore(path.join(nodeDir, 'entities'))
|
|
49
78
|
runtime = {
|
|
@@ -123,10 +152,12 @@ export function getP2PFeatures() {
|
|
|
123
152
|
}
|
|
124
153
|
|
|
125
154
|
/**
|
|
126
|
-
* @returns {string}
|
|
155
|
+
* @returns {string} 节点数据目录绝对路径;未配置且节点未初始化时抛错
|
|
127
156
|
*/
|
|
128
157
|
export function getNodeDir() {
|
|
129
|
-
|
|
158
|
+
if (runtime) return runtime.nodeDir
|
|
159
|
+
if (nodeStorageDir) return nodeStorageDir
|
|
160
|
+
throw new Error('p2p: node storage not configured — call configureNodeStorage() or initNode() first')
|
|
130
161
|
}
|
|
131
162
|
|
|
132
163
|
/**
|
|
@@ -164,12 +195,13 @@ export function onNodeChange(listener) {
|
|
|
164
195
|
}
|
|
165
196
|
|
|
166
197
|
/**
|
|
167
|
-
* 关闭节点:释放全部文件句柄(chunk
|
|
168
|
-
* 之后可用 initNode 重新引导。
|
|
198
|
+
* 关闭节点:释放全部文件句柄(chunk 读/写流等)并清空运行时、存储目录配置与监听器。
|
|
199
|
+
* 之后可用 initNode / configureNodeStorage 重新引导。
|
|
169
200
|
* @returns {Promise<void>}
|
|
170
201
|
*/
|
|
171
202
|
export async function closeNode() {
|
|
172
203
|
runtime = null
|
|
204
|
+
nodeStorageDir = null
|
|
173
205
|
changeListeners.clear()
|
|
174
206
|
rtcPolyfillCacheEpoch++
|
|
175
207
|
await closeAllFileStreams()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steve02081504/fount-p2p",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.44",
|
|
4
4
|
"description": "fount federation P2P layer — link, trust graph, mailbox, DAG, EVFS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"network",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
17
|
"url": "git+https://github.com/steve02081504/fount-p2p.git",
|
|
18
|
-
"directory": "
|
|
18
|
+
"directory": "js"
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=20"
|