@optimystic/db-p2p 0.14.0 → 0.16.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.
Files changed (275) hide show
  1. package/README.md +616 -609
  2. package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
  3. package/dist/src/cluster/block-transfer-service.js +4 -3
  4. package/dist/src/cluster/block-transfer-service.js.map +1 -1
  5. package/dist/src/cluster/block-transfer.d.ts +52 -3
  6. package/dist/src/cluster/block-transfer.d.ts.map +1 -1
  7. package/dist/src/cluster/block-transfer.js +129 -6
  8. package/dist/src/cluster/block-transfer.js.map +1 -1
  9. package/dist/src/cluster/client.d.ts.map +1 -1
  10. package/dist/src/cluster/client.js +3 -1
  11. package/dist/src/cluster/client.js.map +1 -1
  12. package/dist/src/cluster/cluster-repo.d.ts +182 -11
  13. package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
  14. package/dist/src/cluster/cluster-repo.js +429 -69
  15. package/dist/src/cluster/cluster-repo.js.map +1 -1
  16. package/dist/src/cluster/peer-key-binding.d.ts +25 -0
  17. package/dist/src/cluster/peer-key-binding.d.ts.map +1 -0
  18. package/dist/src/cluster/peer-key-binding.js +39 -0
  19. package/dist/src/cluster/peer-key-binding.js.map +1 -0
  20. package/dist/src/cluster/quorum-restore.d.ts +72 -0
  21. package/dist/src/cluster/quorum-restore.d.ts.map +1 -0
  22. package/dist/src/cluster/quorum-restore.js +0 -0
  23. package/dist/src/cluster/quorum-restore.js.map +1 -0
  24. package/dist/src/cluster/rebalance-monitor.d.ts +23 -1
  25. package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
  26. package/dist/src/cluster/rebalance-monitor.js +17 -2
  27. package/dist/src/cluster/rebalance-monitor.js.map +1 -1
  28. package/dist/src/cluster/service.d.ts.map +1 -1
  29. package/dist/src/cluster/service.js +7 -1
  30. package/dist/src/cluster/service.js.map +1 -1
  31. package/dist/src/cluster/spread-on-churn.d.ts +7 -0
  32. package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
  33. package/dist/src/cluster/spread-on-churn.js +4 -1
  34. package/dist/src/cluster/spread-on-churn.js.map +1 -1
  35. package/dist/src/cluster/supermajority-coupling.d.ts +22 -0
  36. package/dist/src/cluster/supermajority-coupling.d.ts.map +1 -0
  37. package/dist/src/cluster/supermajority-coupling.js +27 -0
  38. package/dist/src/cluster/supermajority-coupling.js.map +1 -0
  39. package/dist/src/cohort-topic/cohort-gossip-driver.d.ts +46 -4
  40. package/dist/src/cohort-topic/cohort-gossip-driver.d.ts.map +1 -1
  41. package/dist/src/cohort-topic/cohort-gossip-driver.js +62 -7
  42. package/dist/src/cohort-topic/cohort-gossip-driver.js.map +1 -1
  43. package/dist/src/cohort-topic/cohort-gossip-transport.d.ts +7 -0
  44. package/dist/src/cohort-topic/cohort-gossip-transport.d.ts.map +1 -1
  45. package/dist/src/cohort-topic/cohort-gossip-transport.js +9 -0
  46. package/dist/src/cohort-topic/cohort-gossip-transport.js.map +1 -1
  47. package/dist/src/cohort-topic/host.d.ts +197 -27
  48. package/dist/src/cohort-topic/host.d.ts.map +1 -1
  49. package/dist/src/cohort-topic/host.js +643 -87
  50. package/dist/src/cohort-topic/host.js.map +1 -1
  51. package/dist/src/dispute/arbitrator-selection.d.ts +68 -7
  52. package/dist/src/dispute/arbitrator-selection.d.ts.map +1 -1
  53. package/dist/src/dispute/arbitrator-selection.js +105 -16
  54. package/dist/src/dispute/arbitrator-selection.js.map +1 -1
  55. package/dist/src/dispute/cascade.d.ts +7 -0
  56. package/dist/src/dispute/cascade.d.ts.map +1 -1
  57. package/dist/src/dispute/cascade.js +1 -1
  58. package/dist/src/dispute/cascade.js.map +1 -1
  59. package/dist/src/dispute/client.d.ts.map +1 -1
  60. package/dist/src/dispute/client.js +3 -2
  61. package/dist/src/dispute/client.js.map +1 -1
  62. package/dist/src/dispute/dispute-service.d.ts +32 -3
  63. package/dist/src/dispute/dispute-service.d.ts.map +1 -1
  64. package/dist/src/dispute/dispute-service.js +82 -9
  65. package/dist/src/dispute/dispute-service.js.map +1 -1
  66. package/dist/src/dispute/index.d.ts +1 -1
  67. package/dist/src/dispute/index.d.ts.map +1 -1
  68. package/dist/src/dispute/index.js +1 -1
  69. package/dist/src/dispute/index.js.map +1 -1
  70. package/dist/src/dispute/invalidation.d.ts +25 -0
  71. package/dist/src/dispute/invalidation.d.ts.map +1 -1
  72. package/dist/src/dispute/invalidation.js +14 -2
  73. package/dist/src/dispute/invalidation.js.map +1 -1
  74. package/dist/src/dispute/service.d.ts.map +1 -1
  75. package/dist/src/dispute/service.js +7 -1
  76. package/dist/src/dispute/service.js.map +1 -1
  77. package/dist/src/index.d.ts +4 -1
  78. package/dist/src/index.d.ts.map +1 -1
  79. package/dist/src/index.js +4 -1
  80. package/dist/src/index.js.map +1 -1
  81. package/dist/src/it-utility.d.ts +1 -1
  82. package/dist/src/it-utility.d.ts.map +1 -1
  83. package/dist/src/it-utility.js +4 -13
  84. package/dist/src/it-utility.js.map +1 -1
  85. package/dist/src/libp2p-key-network.d.ts +98 -1
  86. package/dist/src/libp2p-key-network.d.ts.map +1 -1
  87. package/dist/src/libp2p-key-network.js +272 -17
  88. package/dist/src/libp2p-key-network.js.map +1 -1
  89. package/dist/src/libp2p-node-base.d.ts +12 -0
  90. package/dist/src/libp2p-node-base.d.ts.map +1 -1
  91. package/dist/src/libp2p-node-base.js +415 -87
  92. package/dist/src/libp2p-node-base.js.map +1 -1
  93. package/dist/src/matchmaking/query-transport.d.ts +2 -0
  94. package/dist/src/matchmaking/query-transport.d.ts.map +1 -1
  95. package/dist/src/matchmaking/query-transport.js +6 -0
  96. package/dist/src/matchmaking/query-transport.js.map +1 -1
  97. package/dist/src/matchmaking/traffic-validation.js +1 -0
  98. package/dist/src/matchmaking/traffic-validation.js.map +1 -1
  99. package/dist/src/network/network-manager-service.d.ts +2 -2
  100. package/dist/src/network/network-manager-service.d.ts.map +1 -1
  101. package/dist/src/network/network-manager-service.js +8 -4
  102. package/dist/src/network/network-manager-service.js.map +1 -1
  103. package/dist/src/owned-block-seed.d.ts +27 -0
  104. package/dist/src/owned-block-seed.d.ts.map +1 -0
  105. package/dist/src/owned-block-seed.js +37 -0
  106. package/dist/src/owned-block-seed.js.map +1 -0
  107. package/dist/src/protocol-client.d.ts +1 -0
  108. package/dist/src/protocol-client.d.ts.map +1 -1
  109. package/dist/src/protocol-client.js +9 -2
  110. package/dist/src/protocol-client.js.map +1 -1
  111. package/dist/src/protocol-limits.d.ts +32 -0
  112. package/dist/src/protocol-limits.d.ts.map +1 -0
  113. package/dist/src/protocol-limits.js +32 -0
  114. package/dist/src/protocol-limits.js.map +1 -0
  115. package/dist/src/repo/client.d.ts.map +1 -1
  116. package/dist/src/repo/client.js +3 -0
  117. package/dist/src/repo/client.js.map +1 -1
  118. package/dist/src/repo/cluster-coordinator.d.ts +41 -4
  119. package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
  120. package/dist/src/repo/cluster-coordinator.js +128 -80
  121. package/dist/src/repo/cluster-coordinator.js.map +1 -1
  122. package/dist/src/repo/coordinator-repo.d.ts +38 -1
  123. package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
  124. package/dist/src/repo/coordinator-repo.js +92 -13
  125. package/dist/src/repo/coordinator-repo.js.map +1 -1
  126. package/dist/src/repo/service.d.ts.map +1 -1
  127. package/dist/src/repo/service.js +7 -1
  128. package/dist/src/repo/service.js.map +1 -1
  129. package/dist/src/reputation/peer-reputation.js +1 -1
  130. package/dist/src/reputation/peer-reputation.js.map +1 -1
  131. package/dist/src/reputation/types.d.ts +3 -1
  132. package/dist/src/reputation/types.d.ts.map +1 -1
  133. package/dist/src/reputation/types.js +3 -0
  134. package/dist/src/reputation/types.js.map +1 -1
  135. package/dist/src/rn.d.ts +5 -1
  136. package/dist/src/rn.d.ts.map +1 -1
  137. package/dist/src/rn.js +7 -1
  138. package/dist/src/rn.js.map +1 -1
  139. package/dist/src/storage/arachnode-fret-adapter.d.ts +30 -1
  140. package/dist/src/storage/arachnode-fret-adapter.d.ts.map +1 -1
  141. package/dist/src/storage/arachnode-fret-adapter.js +15 -2
  142. package/dist/src/storage/arachnode-fret-adapter.js.map +1 -1
  143. package/dist/src/storage/arachnode-partition.d.ts +49 -0
  144. package/dist/src/storage/arachnode-partition.d.ts.map +1 -0
  145. package/dist/src/storage/arachnode-partition.js +61 -0
  146. package/dist/src/storage/arachnode-partition.js.map +1 -0
  147. package/dist/src/storage/block-storage.d.ts +40 -1
  148. package/dist/src/storage/block-storage.d.ts.map +1 -1
  149. package/dist/src/storage/block-storage.js +150 -57
  150. package/dist/src/storage/block-storage.js.map +1 -1
  151. package/dist/src/storage/i-block-storage.d.ts +8 -0
  152. package/dist/src/storage/i-block-storage.d.ts.map +1 -1
  153. package/dist/src/storage/i-raw-storage.d.ts +13 -0
  154. package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
  155. package/dist/src/storage/kv-raw-storage.d.ts +45 -0
  156. package/dist/src/storage/kv-raw-storage.d.ts.map +1 -0
  157. package/dist/src/storage/kv-raw-storage.js +113 -0
  158. package/dist/src/storage/kv-raw-storage.js.map +1 -0
  159. package/dist/src/storage/memory-storage.d.ts +20 -58
  160. package/dist/src/storage/memory-storage.d.ts.map +1 -1
  161. package/dist/src/storage/memory-storage.js +22 -148
  162. package/dist/src/storage/memory-storage.js.map +1 -1
  163. package/dist/src/storage/memory-store-driver.d.ts +39 -0
  164. package/dist/src/storage/memory-store-driver.d.ts.map +1 -0
  165. package/dist/src/storage/memory-store-driver.js +129 -0
  166. package/dist/src/storage/memory-store-driver.js.map +1 -0
  167. package/dist/src/storage/raw-store-codec.d.ts +10 -0
  168. package/dist/src/storage/raw-store-codec.d.ts.map +1 -0
  169. package/dist/src/storage/raw-store-codec.js +36 -0
  170. package/dist/src/storage/raw-store-codec.js.map +1 -0
  171. package/dist/src/storage/raw-store-driver.d.ts +69 -0
  172. package/dist/src/storage/raw-store-driver.d.ts.map +1 -0
  173. package/dist/src/storage/raw-store-driver.js +2 -0
  174. package/dist/src/storage/raw-store-driver.js.map +1 -0
  175. package/dist/src/storage/restoration-coordinator-v2.d.ts.map +1 -1
  176. package/dist/src/storage/restoration-coordinator-v2.js +1 -1
  177. package/dist/src/storage/restoration-coordinator-v2.js.map +1 -1
  178. package/dist/src/storage/restoration-coordinator.d.ts +73 -0
  179. package/dist/src/storage/restoration-coordinator.d.ts.map +1 -0
  180. package/dist/src/storage/restoration-coordinator.js +181 -0
  181. package/dist/src/storage/restoration-coordinator.js.map +1 -0
  182. package/dist/src/storage/ring-selector.d.ts +92 -5
  183. package/dist/src/storage/ring-selector.d.ts.map +1 -1
  184. package/dist/src/storage/ring-selector.js +153 -51
  185. package/dist/src/storage/ring-selector.js.map +1 -1
  186. package/dist/src/storage/ring-shift-coordinator.d.ts +128 -0
  187. package/dist/src/storage/ring-shift-coordinator.d.ts.map +1 -0
  188. package/dist/src/storage/ring-shift-coordinator.js +196 -0
  189. package/dist/src/storage/ring-shift-coordinator.js.map +1 -0
  190. package/dist/src/storage/storage-monitor.d.ts +28 -0
  191. package/dist/src/storage/storage-monitor.d.ts.map +1 -1
  192. package/dist/src/storage/storage-monitor.js +42 -2
  193. package/dist/src/storage/storage-monitor.js.map +1 -1
  194. package/dist/src/storage/storage-repo.d.ts +19 -0
  195. package/dist/src/storage/storage-repo.d.ts.map +1 -1
  196. package/dist/src/storage/storage-repo.js +172 -22
  197. package/dist/src/storage/storage-repo.js.map +1 -1
  198. package/dist/src/sync/client.d.ts.map +1 -1
  199. package/dist/src/sync/client.js +5 -1
  200. package/dist/src/sync/client.js.map +1 -1
  201. package/dist/src/sync/service.d.ts.map +1 -1
  202. package/dist/src/sync/service.js +2 -1
  203. package/dist/src/sync/service.js.map +1 -1
  204. package/dist/src/testing/cohort-topic-mesh-harness.d.ts +28 -4
  205. package/dist/src/testing/cohort-topic-mesh-harness.d.ts.map +1 -1
  206. package/dist/src/testing/cohort-topic-mesh-harness.js +51 -10
  207. package/dist/src/testing/cohort-topic-mesh-harness.js.map +1 -1
  208. package/dist/src/testing/index.d.ts +1 -0
  209. package/dist/src/testing/index.d.ts.map +1 -1
  210. package/dist/src/testing/index.js +1 -0
  211. package/dist/src/testing/index.js.map +1 -1
  212. package/dist/src/testing/mesh-harness.d.ts.map +1 -1
  213. package/dist/src/testing/mesh-harness.js +15 -7
  214. package/dist/src/testing/mesh-harness.js.map +1 -1
  215. package/dist/src/testing/raw-storage-conformance.d.ts +24 -0
  216. package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -0
  217. package/dist/src/testing/raw-storage-conformance.js +313 -0
  218. package/dist/src/testing/raw-storage-conformance.js.map +1 -0
  219. package/package.json +4 -4
  220. package/src/cluster/block-transfer-service.ts +4 -3
  221. package/src/cluster/block-transfer.ts +424 -265
  222. package/src/cluster/client.ts +3 -1
  223. package/src/cluster/cluster-repo.ts +481 -71
  224. package/src/cluster/peer-key-binding.ts +37 -0
  225. package/src/cluster/quorum-restore.ts +0 -0
  226. package/src/cluster/rebalance-monitor.ts +34 -3
  227. package/src/cluster/service.ts +7 -1
  228. package/src/cluster/spread-on-churn.ts +11 -1
  229. package/src/cluster/supermajority-coupling.ts +37 -0
  230. package/src/cohort-topic/cohort-gossip-driver.ts +96 -9
  231. package/src/cohort-topic/cohort-gossip-transport.ts +10 -0
  232. package/src/cohort-topic/host.ts +831 -105
  233. package/src/dispute/arbitrator-selection.ts +138 -28
  234. package/src/dispute/cascade.ts +8 -1
  235. package/src/dispute/client.ts +3 -2
  236. package/src/dispute/dispute-service.ts +94 -9
  237. package/src/dispute/index.ts +7 -1
  238. package/src/dispute/invalidation.ts +40 -2
  239. package/src/dispute/service.ts +7 -1
  240. package/src/index.ts +46 -43
  241. package/src/it-utility.ts +5 -13
  242. package/src/libp2p-key-network.ts +281 -18
  243. package/src/libp2p-node-base.ts +485 -79
  244. package/src/matchmaking/query-transport.ts +9 -0
  245. package/src/network/network-manager-service.ts +8 -2
  246. package/src/owned-block-seed.ts +40 -0
  247. package/src/protocol-client.ts +10 -3
  248. package/src/protocol-limits.ts +33 -0
  249. package/src/repo/client.ts +3 -0
  250. package/src/repo/cluster-coordinator.ts +145 -82
  251. package/src/repo/coordinator-repo.ts +96 -16
  252. package/src/repo/service.ts +7 -1
  253. package/src/reputation/types.ts +3 -0
  254. package/src/rn.ts +7 -1
  255. package/src/storage/arachnode-fret-adapter.ts +33 -2
  256. package/src/storage/arachnode-partition.ts +74 -0
  257. package/src/storage/block-storage.ts +169 -61
  258. package/src/storage/i-block-storage.ts +9 -0
  259. package/src/storage/i-raw-storage.ts +14 -0
  260. package/src/storage/kv-raw-storage.ts +135 -0
  261. package/src/storage/memory-storage.ts +25 -173
  262. package/src/storage/memory-store-driver.ts +157 -0
  263. package/src/storage/raw-store-codec.ts +42 -0
  264. package/src/storage/raw-store-driver.ts +80 -0
  265. package/src/storage/{restoration-coordinator-v2.ts → restoration-coordinator.ts} +43 -37
  266. package/src/storage/ring-selector.ts +317 -176
  267. package/src/storage/ring-shift-coordinator.ts +271 -0
  268. package/src/storage/storage-monitor.ts +70 -3
  269. package/src/storage/storage-repo.ts +710 -558
  270. package/src/sync/client.ts +5 -1
  271. package/src/sync/service.ts +2 -1
  272. package/src/testing/cohort-topic-mesh-harness.ts +61 -11
  273. package/src/testing/index.ts +1 -0
  274. package/src/testing/mesh-harness.ts +14 -6
  275. package/src/testing/raw-storage-conformance.ts +397 -0
package/README.md CHANGED
@@ -1,609 +1,616 @@
1
- # Optimystic DB-P2P
2
-
3
- A distributed peer-to-peer database system that provides concrete implementations of the Optimystic database abstractions using filesystem storage and libp2p networking. This package transforms the interfaces and abstractions from `@optimystic/db-core` into a fully operational distributed database system.
4
-
5
- ## Overview
6
-
7
- The `@optimystic/db-core` package provides abstractions and interfaces for database operations—immutable blocks, versioned transactions, repository interfaces, and collection data structures. However, it doesn't provide concrete implementations for storage or distribution. `db-p2p` provides concrete implementations of the core abstractions using:
8
-
9
- - **Filesystem storage** with serialization for persistent, versioned block storage
10
- - **libp2p networking** for decentralized peer-to-peer communication
11
- - **Distributed consensus** using 2-phase commit protocols for consistency
12
- - **Fault-tolerant coordination** with automatic recovery and data restoration
13
-
14
- The result is a fully operational distributed database system that implements the interfaces defined in the core abstractions to provide a complete distributed database system.
15
-
16
- ## Architecture Overview
17
-
18
- The system provides three distinct communication interfaces, each serving different roles in the distributed architecture:
19
-
20
- ```mermaid
21
- graph TD
22
- subgraph "External Client Process"
23
- A[Application Code]
24
- B[RepoClient]
25
- A --> B
26
- end
27
-
28
- subgraph "Coordinator Node Process<br/>(Orchestrates Transactions)"
29
- C[RepoService]
30
- D[CoordinatorRepo]
31
- E[ClusterClient]
32
- F[StorageRepo]
33
- subgraph "Local Storage"
34
- F1[BlockStorage]
35
- F2[FileRawStorage]
36
- F3[Local Filesystem]
37
- end
38
- C --> D
39
- D --> E
40
- D --> F
41
- F --> F1
42
- F1 --> F2
43
- F2 --> F3
44
- end
45
-
46
- subgraph "Cluster Peer 1 Process<br/>(Participates in Consensus)"
47
- G1[ClusterService]
48
- H1[ClusterMember]
49
- I1[StorageRepo]
50
- subgraph "Local Storage 1"
51
- I1A[BlockStorage]
52
- I1B[FileRawStorage]
53
- I1C[Local Filesystem]
54
- end
55
- G1 --> H1
56
- H1 --> I1
57
- I1 --> I1A
58
- I1A --> I1B
59
- I1B --> I1C
60
- end
61
-
62
- subgraph "Cluster Peer 2 Process<br/>(Participates in Consensus)"
63
- G2[ClusterService]
64
- H2[ClusterMember]
65
- I2[StorageRepo]
66
- subgraph "Local Storage 2"
67
- I2A[BlockStorage]
68
- I2B[FileRawStorage]
69
- I2C[Local Filesystem]
70
- end
71
- G2 --> H2
72
- H2 --> I2
73
- I2 --> I2A
74
- I2A --> I2B
75
- I2B --> I2C
76
- end
77
-
78
- subgraph "Cluster Peer N Process<br/>(Participates in Consensus)"
79
- G3[ClusterService]
80
- H3[ClusterMember]
81
- I3[StorageRepo]
82
- subgraph "Local Storage N"
83
- I3A[BlockStorage]
84
- I3B[FileRawStorage]
85
- I3C[Local Filesystem]
86
- end
87
- G3 --> H3
88
- H3 --> I3
89
- I3 --> I3A
90
- I3A --> I3B
91
- I3B --> I3C
92
- end
93
-
94
- %% Network communication
95
- B -.->|"/db-p2p/repo/1.0.0"| C
96
- E -.->|"/optimystic/&lt;network&gt;/cluster/1.0.0"| G1
97
- E -.->|"/optimystic/&lt;network&gt;/cluster/1.0.0"| G2
98
- E -.->|"/optimystic/&lt;network&gt;/cluster/1.0.0"| G3
99
- ```
100
-
101
- ### Repo Layer: External Client Interface
102
-
103
- The repo layer provides the interface for external clients to communicate with the distributed database:
104
-
105
- - **RepoClient**: Allows external clients to connect to any coordinator node
106
- - **RepoService**: Handles incoming requests from external clients
107
- - **CoordinatorRepo**: Orchestrates distributed transactions by coordinating with cluster peers
108
- - **Network transparency**: Same `IRepo` interface whether local or distributed
109
-
110
- ### Cluster Layer: Peer-to-Peer Coordination Interface
111
-
112
- The cluster layer provides the interface for coordinators to communicate with other peers in the cluster:
113
-
114
- - **ClusterClient**: Allows coordinators to send consensus requests to cluster peers
115
- - **ClusterService**: Handles incoming consensus requests from coordinators
116
- - **ClusterMember**: Implements 2-phase commit protocol for distributed consensus
117
- - **Byzantine fault tolerance**: Ensures consistency even with peer failures
118
-
119
- ### Storage Layer: Persistent Data Implementation
120
-
121
- The storage layer provides concrete implementations of the core database abstractions:
122
-
123
- - **StorageRepo**: Implements `IRepo` interface with filesystem-based persistence
124
- - **BlockStorage**: Provides versioned block storage with conflict resolution
125
- - **FileRawStorage**: JSON-based file storage with atomic operations
126
- - **Data restoration**: Pluggable restoration for missing data from network peers
127
-
128
- ## Detailed Component Architecture
129
-
130
- ### Repo Layer Components
131
-
132
- #### `RepoClient`
133
- Allows external clients to connect to coordinator nodes:
134
-
135
- ```typescript
136
- class RepoClient extends ProtocolClient implements IRepo {
137
- static create(peerId: PeerId, peerNetwork: IPeerNetwork): RepoClient
138
- // Implements same IRepo interface as local storage
139
- }
140
- ```
141
-
142
- **Key Features:**
143
- - Network-transparent database operations for external clients
144
- - Automatic protocol handling and message serialization
145
- - Error handling for network failures and timeouts
146
- - Connection pooling and optimization
147
-
148
- #### `RepoService`
149
- Handles incoming repository protocol messages from external clients:
150
-
151
- ```typescript
152
- class RepoService implements Startable {
153
- // Protocol: /db-p2p/repo/1.0.0
154
- // Transport: Length-prefixed JSON over libp2p streams
155
- async start(): Promise<void>
156
- async stop(): Promise<void>
157
- }
158
- ```
159
-
160
- **Key Features:**
161
- - Registers and handles the repository protocol
162
- - Routes operations from external clients to coordinator implementation
163
- - Manages concurrent connections and stream processing
164
- - Provides comprehensive error handling and logging
165
-
166
- #### `CoordinatorRepo`
167
- Orchestrates distributed transactions by coordinating with cluster peers:
168
-
169
- ```typescript
170
- class CoordinatorRepo implements IRepo {
171
- constructor(
172
- keyNetwork: IKeyNetwork,
173
- createClusterClient: (peerId: PeerId) => ClusterClient,
174
- storageRepo: IRepo
175
- )
176
- }
177
- ```
178
-
179
- **Key Features:**
180
- - Manages distributed transactions using cluster consensus
181
- - Integrates with key network for peer discovery
182
- - Uses cluster layer to coordinate with other peers
183
- - Maintains local storage directly while coordinating remote operations
184
-
185
- ### Cluster Layer Components
186
-
187
- #### `ClusterClient`
188
- Allows coordinators to send consensus requests to cluster peers:
189
-
190
- ```typescript
191
- class ClusterClient extends ProtocolClient implements ICluster {
192
- async update(record: ClusterRecord): Promise<ClusterRecord>
193
- }
194
- ```
195
-
196
- **Key Features:**
197
- - Network communication for consensus operations
198
- - Handles cluster record updates and responses
199
- - Manages timeouts and retries for consensus requests
200
-
201
- #### `ClusterService`
202
- Handles incoming consensus requests from coordinators:
203
-
204
- ```typescript
205
- class ClusterService implements Startable {
206
- // Protocol: /optimystic/<network>/cluster/1.0.0 (built from protocolPrefix)
207
- // Handles ClusterRecord updates for consensus
208
- }
209
- ```
210
-
211
- **Key Features:**
212
- - Registers and handles the cluster consensus protocol
213
- - Routes consensus requests to local cluster member
214
- - Manages concurrent consensus operations
215
-
216
- #### `ClusterMember`
217
- Implements 2-phase commit protocol for distributed consensus:
218
-
219
- ```typescript
220
- class ClusterMember implements ICluster {
221
- async update(record: ClusterRecord): Promise<ClusterRecord>
222
- // Handles: Promise collection → Majority consensus → Commit execution
223
- }
224
- ```
225
-
226
- **Key Features:**
227
- - Complete 2-phase commit protocol implementation
228
- - Conflict detection and resolution
229
- - Cryptographic signature verification
230
- - Automatic timeout and cleanup management
231
- - Uses storage layer to execute operations when consensus is reached
232
-
233
- ### Storage Layer Components
234
-
235
- #### `StorageRepo`
236
- Implements `IRepo` interface with filesystem-based persistence:
237
-
238
- ```typescript
239
- class StorageRepo implements IRepo {
240
- async get(blockGets: BlockGets): Promise<GetBlockResults>
241
- async pend(request: PendRequest): Promise<PendResult>
242
- async commit(request: CommitRequest): Promise<CommitResult>
243
- async cancel(trxRef: TrxBlocks): Promise<void>
244
- }
245
- ```
246
-
247
- **Key Features:**
248
- - Concrete implementation of core database abstractions
249
- - Orchestrates transactions across multiple blocks
250
- - Handles revision conflicts and missing transaction detection
251
- - Provides atomic commit operations with proper locking
252
-
253
- #### `BlockStorage`
254
- Provides versioned block storage with conflict resolution:
255
-
256
- ```typescript
257
- class BlockStorage implements IBlockStorage {
258
- async getBlock(rev?: number): Promise<{ block: IBlock, actionRev: ActionRev }>
259
- async savePendingAction(actionId: ActionId, transform: Transform): Promise<void>
260
- async promotePendingAction(actionId: ActionId): Promise<void>
261
- async ensureRevision(rev: number): Promise<void>
262
- }
263
- ```
264
-
265
- **Key Features:**
266
- - Maintains complete revision history for each block
267
- - Reconstructs blocks by applying transforms to base versions
268
- - Integrates with restoration callbacks for missing data
269
- - Uses latches for thread-safe concurrent access
270
-
271
- #### `FileRawStorage`
272
- JSON-based file storage with atomic operations:
273
-
274
- ```typescript
275
- // File system organization
276
- {basePath}/
277
- ├── {blockId}/
278
- │ ├── meta.json # Block metadata and revision ranges
279
- │ ├── revs/{rev}.json # Revision → ActionId mappings
280
- │ ├── pend/{actionId}.json # Pending actions
281
- │ ├── trx/{actionId}.json # Committed actions
282
- │ └── blocks/{actionId}.json # Materialized blocks
283
- ```
284
-
285
- **Key Features:**
286
- - Concrete filesystem implementation of storage interfaces
287
- - JSON serialization for cross-platform compatibility
288
- - Atomic file operations for consistency
289
- - Organized directory structure for efficient access
290
-
291
- ## Integration Patterns
292
-
293
- ### Relationship to `@optimystic/db-core`
294
-
295
- The `db-p2p` package provides concrete implementations of the core database abstractions:
296
-
297
- ```typescript
298
- // Core provides interfaces and abstractions
299
- import { IRepo, IBlock, Transform, IBlockStorage } from '@optimystic/db-core';
300
-
301
- // P2P provides concrete implementations
302
- class StorageRepo implements IRepo { /* filesystem-based repo */ }
303
- class BlockStorage implements IBlockStorage { /* versioned block storage */ }
304
- class RepoClient implements IRepo { /* network-transparent repo */ }
305
- class CoordinatorRepo implements IRepo { /* distributed consensus repo */ }
306
- ```
307
-
308
- **Implementation Points:**
309
- - **Storage Layer**: Provides concrete filesystem-based implementations of core interfaces
310
- - **Network Layer**: Implements peer-to-peer communication using libp2p
311
- - **Consensus Layer**: Adds distributed consensus while maintaining core interfaces
312
- - **API Compatibility**: Same interfaces as core abstractions for seamless integration
313
-
314
- ### Libp2p Integration
315
-
316
- The package integrates deeply with libp2p for networking:
317
-
318
- ```typescript
319
- // Node creation with integrated services
320
- const node = await createLibp2pNode({
321
- services: {
322
- repo: repoService({ protocolPrefix: '/optimystic/<network>' }),
323
- cluster: clusterService({ protocolPrefix: '/optimystic/<network>' })
324
- }
325
- });
326
-
327
- // Key network integration
328
- const keyNetwork = new Libp2pKeyPeerNetwork(node);
329
- ```
330
-
331
- **Integration Features:**
332
- - **Protocol Management**: Registers custom protocols for repo and cluster operations
333
- - **Stream Handling**: Uses libp2p streams for reliable message transport
334
- - **Peer Discovery**: Integrates with libp2p DHT for peer discovery
335
- - **Security**: Leverages libp2p's cryptographic peer identity
336
-
337
- ## Usage Examples
338
-
339
- ### Setting Up a Coordinator Node
340
-
341
- ```typescript
342
- import { createLibp2pNode } from '@optimystic/db-p2p';
343
- import { repoService, clusterService, StorageRepo, CoordinatorRepo } from '@optimystic/db-p2p';
344
-
345
- // Create a libp2p node with database services
346
- const node = await createLibp2pNode({
347
- services: {
348
- repo: repoService(), // Handles external client requests
349
- cluster: clusterService() // Handles peer coordination requests
350
- }
351
- });
352
-
353
- // Set up storage layer with filesystem persistence
354
- const storageRepo = new StorageRepo(/* filesystem config */);
355
-
356
- // Set up coordinator that uses cluster layer for consensus
357
- const coordinatorRepo = new CoordinatorRepo(
358
- keyNetwork, // For peer discovery
359
- createClusterClient, // For communicating with cluster peers
360
- storageRepo // For local storage operations
361
- );
362
-
363
- // Start the node
364
- await node.start();
365
- ```
366
-
367
- ### External Client Operations
368
-
369
- ```typescript
370
- // External clients connect to coordinator nodes
371
- const client = RepoClient.create(coordinatorPeerId, peerNetwork);
372
-
373
- // Operations work the same as core database interfaces
374
- const blocks = await client.get({
375
- blockIds: ['block1', 'block2'],
376
- context: { rev: 10 }
377
- });
378
-
379
- // But automatically coordinate across the cluster
380
- const pendResult = await client.pend({
381
- actionId: 'tx1',
382
- transforms: { block1: [/* operations */] },
383
- rev: 11
384
- });
385
-
386
- const commitResult = await client.commit({
387
- actionId: 'tx1',
388
- blockIds: ['block1'],
389
- rev: 11
390
- });
391
- ```
392
-
393
- ### How Coordination Works
394
-
395
- ```mermaid
396
- sequenceDiagram
397
- participant Client as External Client
398
- participant RC as RepoClient
399
- participant CR as CoordinatorRepo
400
- participant CC as ClusterClient
401
- participant CS as ClusterService
402
- participant CM as ClusterMember
403
- participant SR as StorageRepo
404
-
405
- Client->>RC: pend(transaction)
406
- RC->>CR: pend(transaction)
407
- CR->>CC: consensus request
408
- CC->>CS: cluster protocol
409
- CS->>CM: update(ClusterRecord)
410
- CM->>CM: 2-phase commit
411
- CM->>SR: execute operation
412
- SR-->>CM: result
413
- CM-->>CS: consensus result
414
- CS-->>CC: response
415
- CC-->>CR: consensus complete
416
- CR-->>RC: transaction result
417
- RC-->>Client: pend result
418
- ```
419
-
420
- ## Documentation
421
-
422
- For detailed information about specific components:
423
-
424
- - **[Storage System](./docs/storage.md)**: Versioned block storage and persistence
425
- - **[Repo Interface](./docs/repo.md)**: Distributed database operations
426
- - **[Cluster Consensus](./docs/cluster.md)**: 2-phase commit and distributed consensus
427
-
428
- ## Block Restoration & Arachnode Integration
429
-
430
- The db-p2p package includes **dynamic Arachnode ring discovery** for automatic block restoration across storage tiers.
431
-
432
- ### How Block Restoration Works
433
-
434
- When a node is missing a block or revision, the restoration system:
435
-
436
- 1. **Determines storage rings** - Nodes self-select ring depth based on capacity
437
- 2. **Discovers peers via FRET** - Ring membership propagates via existing neighbor exchange
438
- 3. **Queries rings intelligently** - Tries transaction ring first, then inner storage rings
439
- 4. **Filters by partition** - Only queries peers responsible for the block's keyspace
440
-
441
- ### Ring Selection
442
-
443
- Nodes calculate their appropriate ring depth using:
444
-
445
- ```
446
- ringDepth = ceil(-log2(available_capacity / estimated_demand))
447
- ```
448
-
449
- **Examples:**
450
- - 100% coverage → Ring 0 (full keyspace)
451
- - 50% coverage → Ring 1 (2 partitions)
452
- - 1% coverage → Ring 7 (128 partitions)
453
-
454
- ### Architecture
455
-
456
- ```
457
- FRET (Pure DHT)
458
- - Generic metadata transport
459
- - Peer discovery & routing
460
-
461
- ArachnodeFretAdapter (Plugin Layer)
462
- - Arachnode-specific semantics
463
- - Ring discovery methods
464
-
465
- Restoration Components
466
- - RingSelector: Capacity-based ring selection
467
- - RestorationCoordinator: Multi-ring queries
468
- - StorageMonitor: Capacity tracking
469
- - SyncService/Client: Block request protocol
470
- ```
471
-
472
- ### Configuration
473
-
474
- ```typescript
475
- const node = await createLibp2pNode({
476
- port: 9000,
477
- networkName: 'mynet',
478
- bootstrapNodes: ['...'],
479
- clusterSize: 10, // Cluster size for peer discovery
480
- arachnode: {
481
- enableRingZulu: true // Default: enabled
482
- }
483
- });
484
- ```
485
-
486
- #### Solo / bootstrap / mobile nodes (no remote peers)
487
-
488
- A node can operate fully on its own — no bootstrap peers, no listen addresses, no
489
- connectivity to other peers. This is the common shape for a mobile app on first
490
- launch or for a local-only sanity test.
491
-
492
- ```typescript
493
- const soloNode = await createLibp2pNode({
494
- networkName: 'mynet',
495
- bootstrapNodes: [], // no bootstrap
496
- listenAddrs: [], // no incoming connections
497
- transports: [webSockets()], // transports still required
498
- clusterSize: 1,
499
- });
500
- ```
501
-
502
- In solo mode:
503
-
504
- - The node is its own coordinator. `CoordinatorRepo` short-circuits to local
505
- storage when the cluster size is ≤ 1, so `pend`/`commit` do not require
506
- cluster consensus and do not attempt to dial self.
507
- - Block restoration is skipped when the only discovered peer is self.
508
- `RestorationCoordinator` will return `undefined` immediately rather than
509
- attempt to dial self (which would hang on a node with no listen addrs).
510
- - If an operation fails for some other reason, `findCoordinator` surfaces a
511
- distinguishable `FindCoordinatorError` with `code ===
512
- FIND_COORDINATOR_ERROR_CODES.SELF_COORDINATION_EXHAUSTED` rather than the
513
- generic "all candidates excluded". The aggregate error thrown by
514
- `NetworkTransactor` preserves the ORIGINAL first-attempt cause so callers can
515
- diagnose the real problem.
516
-
517
- Once remote peers become reachable, the node transitions out of solo mode
518
- automatically as FRET discovers them. Persisted network state (HWM, FRET
519
- table) carries across restarts when an `options.persistence` is supplied.
520
-
521
- #### Custom transports (including React Native)
522
-
523
- By default, `createLibp2pNode()` uses TCP + circuit-relay transport.
524
-
525
- To use non-default transports, pass `transports` (and typically `listenAddrs`):
526
-
527
- ```typescript
528
- import { webSockets } from '@libp2p/websockets';
529
- import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
530
- import { createLibp2pNode } from '@optimystic/db-p2p';
531
-
532
- const node = await createLibp2pNode({
533
- networkName: 'mynet',
534
- bootstrapNodes: ['/dns4/relay.example.com/tcp/443/wss/p2p/12D3...'],
535
- transports: [webSockets(), circuitRelayTransport()],
536
- listenAddrs: [], // client-only; no incoming connections
537
- });
538
- ```
539
-
540
- #### React Native
541
-
542
- For React Native, import from `@optimystic/db-p2p/rn` instead of the root entrypoint.
543
- The `/rn` entrypoint does **not** import `@libp2p/tcp`, so Metro/Hermes won't try to
544
- bundle Node-only native modules. It requires `options.transports` explicitly:
545
-
546
- ```typescript
547
- import { webSockets } from '@libp2p/websockets';
548
- import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
549
- import { createLibp2pNode } from '@optimystic/db-p2p/rn';
550
-
551
- const node = await createLibp2pNode({
552
- networkName: 'mynet',
553
- bootstrapNodes: ['/dns4/relay.example.com/tcp/443/wss/p2p/12D3...'],
554
- transports: [webSockets(), circuitRelayTransport()],
555
- });
556
- ```
557
-
558
- RN environments (Hermes) need polyfills for globals that libp2p and its
559
- dependencies expect. Install these early (e.g. in your app's entry file
560
- before any other imports).
561
-
562
- **Global polyfills** (patch `globalThis` before any library code):
563
-
564
- | API | Required by | Notes |
565
- |-----|-------------|-------|
566
- | `crypto.getRandomValues()` | @noble/hashes, @libp2p/crypto | RN 0.76+ New Architecture provides natively; fallback via e.g. `react-native-get-random-values` |
567
- | `crypto.subtle.digest()` | multiformats/hashes/sha2-browser | Async SHA-256/SHA-512 — implement via @noble/hashes |
568
- | `structuredClone()` | @optimystic/db-core | JSON round-trip is sufficient |
569
- | `Promise.withResolvers()` | @libp2p/utils, @chainsafe/libp2p-yamux, it-queue | ES2024 simple shim |
570
- | `AbortSignal.prototype.throwIfAborted()` | libp2p, @libp2p/circuit-relay-v2, it-pushable | DOM spec addition simple shim |
571
- | Timer `.ref()` / `.unref()` | @optimystic/db-p2p, undici | Wrap Hermes numeric timer IDs in objects with no-op `.ref()`/`.unref()` methods; patch `clearTimeout`/`clearInterval` to unwrap |
572
- | `Event`, `CustomEvent`, `EventTarget` | libp2p, @libp2p/interface | Custom shim or npm `event-target-polyfill` |
573
- | `Intl.PluralRules` | moat-maker | English-only ordinal/cardinal shim is sufficient |
574
-
575
- **Node.js built-in module shims** (via Metro `extraNodeModules` or bundler aliases):
576
-
577
- | Module | Required by | Recommended shim |
578
- |--------|-------------|------------------|
579
- | `os` / `node:os` | @libp2p/utils | Custom shim: `networkInterfaces()` → `{}`, `platform()` → `Platform.OS` |
580
- | `crypto` / `node:crypto` | multiformats/hashes/sha2 | Custom shim: `createHash()` via @noble/hashes |
581
- | `stream` / `node:stream` | libp2p | `readable-stream` (npm) |
582
- | `buffer` / `node:buffer` | libp2p, multiformats | `buffer` (npm) |
583
-
584
- **Built-in (no polyfill needed):**
585
- - `TextEncoder` built-in to Hermes
586
- - `TextDecoder` — built-in to Expo SDK 52+ (UTF-8 only)
587
- - `BigInt` — built-in to Hermes since RN 0.70
588
-
589
- See the [Sereus reference-app-rn](https://github.com/gotchoices/sereus/tree/master/packages/reference-app-rn/polyfills) for working polyfill implementations.
590
-
591
- ### Ring Transitions
592
-
593
- Nodes automatically transition between rings based on capacity thresholds:
594
- - **Move OUT** (to more granular ring) when > 85% capacity used
595
- - **Move IN** (to broader ring) when < 40% capacity used
596
-
597
- ## Related Packages
598
-
599
- - **[@optimystic/db-core](../db-core)**: Core database interfaces and local operations
600
- - **[@optimystic/db-quereus](../db-quereus)**: Query engine and data access patterns
601
- - **[p2p-fret](../fret)**: DHT implementation for peer discovery
602
-
603
- ### Internal Architecture
604
-
605
- For developers and AI agents working on internals, see [Internals Guide](../../docs/internals.md) for:
606
- - Data flow diagrams (read/write/commit paths)
607
- - Mutation contracts (which functions mutate vs clone)
608
- - Key invariants and common pitfalls
609
- - Type glossary and debugging tips
1
+ # Optimystic DB-P2P
2
+
3
+ A distributed peer-to-peer database system that provides concrete implementations of the Optimystic database abstractions using pluggable storage backends and libp2p networking. This package transforms the interfaces and abstractions from `@optimystic/db-core` into a fully operational distributed database system.
4
+
5
+ > **Storage is pluggable.** `db-p2p` owns the distributed layer (repo/cluster/coordination) and the storage *interfaces* (`IRawStorage`, `IBlockStorage`, `IKVStore`) plus an in-memory backend. Concrete persistent backends live in separate packages — filesystem persistence is [`@optimystic/db-p2p-storage-fs`](../db-p2p-storage-fs); see also `-storage-ns` (NativeScript/SQLite), `-storage-rn` (React Native/LevelDB), and `-storage-web` (IndexedDB).
6
+
7
+ ## Overview
8
+
9
+ The `@optimystic/db-core` package provides abstractions and interfaces for database operations—immutable blocks, versioned transactions, repository interfaces, and collection data structures. However, it doesn't provide concrete implementations for storage or distribution. `db-p2p` provides concrete implementations of the core abstractions using:
10
+
11
+ - **Versioned block storage** (`StorageRepo` / `BlockStorage`) layered over a pluggable `IRawStorage` backend — an in-memory backend ships here; filesystem persistence lives in `@optimystic/db-p2p-storage-fs`
12
+ - **libp2p networking** for decentralized peer-to-peer communication
13
+ - **Distributed consensus** using 2-phase commit protocols for consistency
14
+ - **Fault-tolerant coordination** with automatic recovery and data restoration
15
+
16
+ The result is a fully operational distributed database system that implements the interfaces defined in the core abstractions to provide a complete distributed database system.
17
+
18
+ ## Architecture Overview
19
+
20
+ The system provides three distinct communication interfaces, each serving different roles in the distributed architecture:
21
+
22
+ ```mermaid
23
+ graph TD
24
+ subgraph "External Client Process"
25
+ A[Application Code]
26
+ B[RepoClient]
27
+ A --> B
28
+ end
29
+
30
+ subgraph "Coordinator Node Process<br/>(Orchestrates Transactions)"
31
+ C[RepoService]
32
+ D[CoordinatorRepo]
33
+ E[ClusterClient]
34
+ F[StorageRepo]
35
+ subgraph "Local Storage"
36
+ F1[BlockStorage]
37
+ F2[IRawStorage backend]
38
+ F3[Backend store]
39
+ end
40
+ C --> D
41
+ D --> E
42
+ D --> F
43
+ F --> F1
44
+ F1 --> F2
45
+ F2 --> F3
46
+ end
47
+
48
+ subgraph "Cluster Peer 1 Process<br/>(Participates in Consensus)"
49
+ G1[ClusterService]
50
+ H1[ClusterMember]
51
+ I1[StorageRepo]
52
+ subgraph "Local Storage 1"
53
+ I1A[BlockStorage]
54
+ I1B[IRawStorage backend]
55
+ I1C[Backend store]
56
+ end
57
+ G1 --> H1
58
+ H1 --> I1
59
+ I1 --> I1A
60
+ I1A --> I1B
61
+ I1B --> I1C
62
+ end
63
+
64
+ subgraph "Cluster Peer 2 Process<br/>(Participates in Consensus)"
65
+ G2[ClusterService]
66
+ H2[ClusterMember]
67
+ I2[StorageRepo]
68
+ subgraph "Local Storage 2"
69
+ I2A[BlockStorage]
70
+ I2B[IRawStorage backend]
71
+ I2C[Backend store]
72
+ end
73
+ G2 --> H2
74
+ H2 --> I2
75
+ I2 --> I2A
76
+ I2A --> I2B
77
+ I2B --> I2C
78
+ end
79
+
80
+ subgraph "Cluster Peer N Process<br/>(Participates in Consensus)"
81
+ G3[ClusterService]
82
+ H3[ClusterMember]
83
+ I3[StorageRepo]
84
+ subgraph "Local Storage N"
85
+ I3A[BlockStorage]
86
+ I3B[IRawStorage backend]
87
+ I3C[Backend store]
88
+ end
89
+ G3 --> H3
90
+ H3 --> I3
91
+ I3 --> I3A
92
+ I3A --> I3B
93
+ I3B --> I3C
94
+ end
95
+
96
+ %% Network communication
97
+ B -.->|"/db-p2p/repo/1.0.0"| C
98
+ E -.->|"/optimystic/&lt;network&gt;/cluster/1.0.0"| G1
99
+ E -.->|"/optimystic/&lt;network&gt;/cluster/1.0.0"| G2
100
+ E -.->|"/optimystic/&lt;network&gt;/cluster/1.0.0"| G3
101
+ ```
102
+
103
+ ### Repo Layer: External Client Interface
104
+
105
+ The repo layer provides the interface for external clients to communicate with the distributed database:
106
+
107
+ - **RepoClient**: Allows external clients to connect to any coordinator node
108
+ - **RepoService**: Handles incoming requests from external clients
109
+ - **CoordinatorRepo**: Orchestrates distributed transactions by coordinating with cluster peers
110
+ - **Network transparency**: Same `IRepo` interface whether local or distributed
111
+
112
+ ### Cluster Layer: Peer-to-Peer Coordination Interface
113
+
114
+ The cluster layer provides the interface for coordinators to communicate with other peers in the cluster:
115
+
116
+ - **ClusterClient**: Allows coordinators to send consensus requests to cluster peers
117
+ - **ClusterService**: Handles incoming consensus requests from coordinators
118
+ - **ClusterMember**: Implements 2-phase commit protocol for distributed consensus
119
+ - **Byzantine fault tolerance**: Ensures consistency even with peer failures
120
+
121
+ ### Storage Layer: Persistent Data Implementation
122
+
123
+ The storage layer implements the core database abstractions over a pluggable raw-storage backend:
124
+
125
+ - **StorageRepo**: Implements the `IRepo` interface on top of `BlockStorage` — backend-agnostic
126
+ - **BlockStorage**: Provides versioned block storage with conflict resolution, reading/writing through an injected `IRawStorage`
127
+ - **`IRawStorage`**: The raw persistence interface. An in-memory backend ships in this package; the filesystem backend (`FileRawStorage`) lives in `@optimystic/db-p2p-storage-fs`
128
+ - **Data restoration**: Pluggable restoration for missing data from network peers
129
+
130
+ ## Detailed Component Architecture
131
+
132
+ ### Repo Layer Components
133
+
134
+ #### `RepoClient`
135
+ Allows external clients to connect to coordinator nodes:
136
+
137
+ ```typescript
138
+ class RepoClient extends ProtocolClient implements IRepo {
139
+ static create(peerId: PeerId, peerNetwork: IPeerNetwork): RepoClient
140
+ // Implements same IRepo interface as local storage
141
+ }
142
+ ```
143
+
144
+ **Key Features:**
145
+ - Network-transparent database operations for external clients
146
+ - Automatic protocol handling and message serialization
147
+ - Error handling for network failures and timeouts
148
+ - Connection pooling and optimization
149
+
150
+ #### `RepoService`
151
+ Handles incoming repository protocol messages from external clients:
152
+
153
+ ```typescript
154
+ class RepoService implements Startable {
155
+ // Protocol: /db-p2p/repo/1.0.0
156
+ // Transport: Length-prefixed JSON over libp2p streams
157
+ async start(): Promise<void>
158
+ async stop(): Promise<void>
159
+ }
160
+ ```
161
+
162
+ **Key Features:**
163
+ - Registers and handles the repository protocol
164
+ - Routes operations from external clients to coordinator implementation
165
+ - Manages concurrent connections and stream processing
166
+ - Provides comprehensive error handling and logging
167
+
168
+ #### `CoordinatorRepo`
169
+ Orchestrates distributed transactions by coordinating with cluster peers:
170
+
171
+ ```typescript
172
+ class CoordinatorRepo implements IRepo {
173
+ constructor(
174
+ keyNetwork: IKeyNetwork,
175
+ createClusterClient: (peerId: PeerId) => ClusterClient,
176
+ storageRepo: IRepo
177
+ )
178
+ }
179
+ ```
180
+
181
+ **Key Features:**
182
+ - Manages distributed transactions using cluster consensus
183
+ - Integrates with key network for peer discovery
184
+ - Uses cluster layer to coordinate with other peers
185
+ - Maintains local storage directly while coordinating remote operations
186
+
187
+ ### Cluster Layer Components
188
+
189
+ #### `ClusterClient`
190
+ Allows coordinators to send consensus requests to cluster peers:
191
+
192
+ ```typescript
193
+ class ClusterClient extends ProtocolClient implements ICluster {
194
+ async update(record: ClusterRecord): Promise<ClusterRecord>
195
+ }
196
+ ```
197
+
198
+ **Key Features:**
199
+ - Network communication for consensus operations
200
+ - Handles cluster record updates and responses
201
+ - Manages timeouts and retries for consensus requests
202
+
203
+ #### `ClusterService`
204
+ Handles incoming consensus requests from coordinators:
205
+
206
+ ```typescript
207
+ class ClusterService implements Startable {
208
+ // Protocol: /optimystic/<network>/cluster/1.0.0 (built from protocolPrefix)
209
+ // Handles ClusterRecord updates for consensus
210
+ }
211
+ ```
212
+
213
+ **Key Features:**
214
+ - Registers and handles the cluster consensus protocol
215
+ - Routes consensus requests to local cluster member
216
+ - Manages concurrent consensus operations
217
+
218
+ #### `ClusterMember`
219
+ Implements 2-phase commit protocol for distributed consensus:
220
+
221
+ ```typescript
222
+ class ClusterMember implements ICluster {
223
+ async update(record: ClusterRecord): Promise<ClusterRecord>
224
+ // Handles: Promise collection → Majority consensus → Commit execution
225
+ }
226
+ ```
227
+
228
+ **Key Features:**
229
+ - Complete 2-phase commit protocol implementation
230
+ - Conflict detection and resolution
231
+ - Cryptographic signature verification
232
+ - Automatic timeout and cleanup management
233
+ - Uses storage layer to execute operations when consensus is reached
234
+
235
+ ### Storage Layer Components
236
+
237
+ #### `StorageRepo`
238
+ Implements the `IRepo` interface over `BlockStorage` — independent of any particular storage backend:
239
+
240
+ ```typescript
241
+ class StorageRepo implements IRepo {
242
+ async get(blockGets: BlockGets): Promise<GetBlockResults>
243
+ async pend(request: PendRequest): Promise<PendResult>
244
+ async commit(request: CommitRequest): Promise<CommitResult>
245
+ async cancel(trxRef: TrxBlocks): Promise<void>
246
+ }
247
+ ```
248
+
249
+ **Key Features:**
250
+ - Concrete implementation of core database abstractions
251
+ - Orchestrates transactions across multiple blocks
252
+ - Handles revision conflicts and missing transaction detection
253
+ - Provides atomic commit operations with proper locking
254
+
255
+ #### `BlockStorage`
256
+ Provides versioned block storage with conflict resolution, reading and writing through an injected `IRawStorage` backend:
257
+
258
+ ```typescript
259
+ class BlockStorage implements IBlockStorage {
260
+ constructor(blockId: BlockId, storage: IRawStorage, /* … */)
261
+ async getBlock(rev?: number): Promise<{ block: IBlock, actionRev: ActionRev }>
262
+ async savePendingAction(actionId: ActionId, transform: Transform): Promise<void>
263
+ async promotePendingAction(actionId: ActionId): Promise<void>
264
+ async ensureRevision(rev: number): Promise<void>
265
+ }
266
+ ```
267
+
268
+ **Key Features:**
269
+ - Maintains complete revision history for each block
270
+ - Reconstructs blocks by applying transforms to base versions
271
+ - Integrates with restoration callbacks for missing data
272
+ - Uses latches for thread-safe concurrent access
273
+
274
+ #### `IRawStorage` backends
275
+ `BlockStorage` persists through the `IRawStorage` interface, so the backing store is pluggable:
276
+
277
+ - **In-memory** (`MemoryRawStorage`) — ships in this package; used for tests and ephemeral nodes.
278
+ - **Filesystem** (`FileRawStorage`) lives in the separate [`@optimystic/db-p2p-storage-fs`](../db-p2p-storage-fs) package; JSON files with atomic writes, organized per block:
279
+
280
+ ```typescript
281
+ // Filesystem backend layout (@optimystic/db-p2p-storage-fs)
282
+ {basePath}/
283
+ ├── {blockId}/
284
+ │ ├── meta.json # Block metadata and revision ranges
285
+ │ ├── revs/{rev}.json # Revision → ActionId mappings
286
+ │ ├── pend/{actionId}.json # Pending actions
287
+ │ ├── trx/{actionId}.json # Committed actions
288
+ │ └── blocks/{actionId}.json # Materialized blocks
289
+ ```
290
+
291
+ - **Other backends** — SQLite (`-storage-ns`), LevelDB (`-storage-rn`), IndexedDB (`-storage-web`), each in its own package.
292
+
293
+ ## Integration Patterns
294
+
295
+ ### Relationship to `@optimystic/db-core`
296
+
297
+ The `db-p2p` package provides concrete implementations of the core database abstractions:
298
+
299
+ ```typescript
300
+ // Core provides interfaces and abstractions
301
+ import { IRepo, IBlock, Transform, IBlockStorage } from '@optimystic/db-core';
302
+
303
+ // P2P provides concrete implementations
304
+ class StorageRepo implements IRepo { /* backend-agnostic versioned repo */ }
305
+ class BlockStorage implements IBlockStorage { /* versioned block storage over IRawStorage */ }
306
+ class RepoClient implements IRepo { /* network-transparent repo */ }
307
+ class CoordinatorRepo implements IRepo { /* distributed consensus repo */ }
308
+ ```
309
+
310
+ **Implementation Points:**
311
+ - **Storage Layer**: Implements the core storage interfaces over a pluggable `IRawStorage` backend (in-memory here; filesystem in `@optimystic/db-p2p-storage-fs`)
312
+ - **Network Layer**: Implements peer-to-peer communication using libp2p
313
+ - **Consensus Layer**: Adds distributed consensus while maintaining core interfaces
314
+ - **API Compatibility**: Same interfaces as core abstractions for seamless integration
315
+
316
+ ### Libp2p Integration
317
+
318
+ The package integrates deeply with libp2p for networking:
319
+
320
+ ```typescript
321
+ // Node creation with integrated services
322
+ const node = await createLibp2pNode({
323
+ services: {
324
+ repo: repoService({ protocolPrefix: '/optimystic/<network>' }),
325
+ cluster: clusterService({ protocolPrefix: '/optimystic/<network>' })
326
+ }
327
+ });
328
+
329
+ // Key network integration
330
+ const keyNetwork = new Libp2pKeyPeerNetwork(node);
331
+ ```
332
+
333
+ **Integration Features:**
334
+ - **Protocol Management**: Registers custom protocols for repo and cluster operations
335
+ - **Stream Handling**: Uses libp2p streams for reliable message transport
336
+ - **Peer Discovery**: Integrates with libp2p DHT for peer discovery
337
+ - **Security**: Leverages libp2p's cryptographic peer identity
338
+
339
+ ## Usage Examples
340
+
341
+ ### Setting Up a Coordinator Node
342
+
343
+ ```typescript
344
+ import { createLibp2pNode } from '@optimystic/db-p2p';
345
+ import { repoService, clusterService, StorageRepo, CoordinatorRepo } from '@optimystic/db-p2p';
346
+
347
+ // Create a libp2p node with database services
348
+ const node = await createLibp2pNode({
349
+ services: {
350
+ repo: repoService(), // Handles external client requests
351
+ cluster: clusterService() // Handles peer coordination requests
352
+ }
353
+ });
354
+
355
+ // Set up storage layer over a raw-storage backend
356
+ // (in-memory here; swap in FileRawStorage from @optimystic/db-p2p-storage-fs for persistence)
357
+ const storageRepo = new StorageRepo(/* IRawStorage backend */);
358
+
359
+ // Set up coordinator that uses cluster layer for consensus
360
+ const coordinatorRepo = new CoordinatorRepo(
361
+ keyNetwork, // For peer discovery
362
+ createClusterClient, // For communicating with cluster peers
363
+ storageRepo // For local storage operations
364
+ );
365
+
366
+ // Start the node
367
+ await node.start();
368
+ ```
369
+
370
+ ### External Client Operations
371
+
372
+ ```typescript
373
+ // External clients connect to coordinator nodes
374
+ const client = RepoClient.create(coordinatorPeerId, peerNetwork);
375
+
376
+ // Operations work the same as core database interfaces
377
+ const blocks = await client.get({
378
+ blockIds: ['block1', 'block2'],
379
+ context: { rev: 10 }
380
+ });
381
+
382
+ // But automatically coordinate across the cluster
383
+ const pendResult = await client.pend({
384
+ actionId: 'tx1',
385
+ transforms: { block1: [/* operations */] },
386
+ rev: 11
387
+ });
388
+
389
+ const commitResult = await client.commit({
390
+ actionId: 'tx1',
391
+ blockIds: ['block1'],
392
+ rev: 11
393
+ });
394
+ ```
395
+
396
+ ### How Coordination Works
397
+
398
+ ```mermaid
399
+ sequenceDiagram
400
+ participant Client as External Client
401
+ participant RC as RepoClient
402
+ participant CR as CoordinatorRepo
403
+ participant CC as ClusterClient
404
+ participant CS as ClusterService
405
+ participant CM as ClusterMember
406
+ participant SR as StorageRepo
407
+
408
+ Client->>RC: pend(transaction)
409
+ RC->>CR: pend(transaction)
410
+ CR->>CC: consensus request
411
+ CC->>CS: cluster protocol
412
+ CS->>CM: update(ClusterRecord)
413
+ CM->>CM: 2-phase commit
414
+ CM->>SR: execute operation
415
+ SR-->>CM: result
416
+ CM-->>CS: consensus result
417
+ CS-->>CC: response
418
+ CC-->>CR: consensus complete
419
+ CR-->>RC: transaction result
420
+ RC-->>Client: pend result
421
+ ```
422
+
423
+ ## Documentation
424
+
425
+ For detailed information about specific components:
426
+
427
+ - **[Storage System](./docs/storage.md)**: Versioned block storage and persistence
428
+ - **[Repo Interface](./docs/repo.md)**: Distributed database operations
429
+ - **[Cluster Consensus](./docs/cluster.md)**: 2-phase commit and distributed consensus
430
+
431
+ ## Block Restoration & Arachnode Integration
432
+
433
+ The db-p2p package includes **dynamic Arachnode ring discovery** for automatic block restoration across storage tiers.
434
+
435
+ ### How Block Restoration Works
436
+
437
+ When a node is missing a block or revision, the restoration system:
438
+
439
+ 1. **Determines storage rings** - Nodes self-select ring depth based on capacity
440
+ 2. **Discovers peers via FRET** - Ring membership propagates via existing neighbor exchange
441
+ 3. **Queries rings intelligently** - Tries transaction ring first, then inner storage rings
442
+ 4. **Filters by partition** - Only queries peers responsible for the block's keyspace
443
+
444
+ ### Ring Selection
445
+
446
+ Nodes calculate their appropriate ring depth using:
447
+
448
+ ```
449
+ ringDepth = ceil(-log2(available_capacity / estimated_demand))
450
+ ```
451
+
452
+ **Examples:**
453
+ - 100% coverage → Ring 0 (full keyspace)
454
+ - 50% coverage → Ring 1 (2 partitions)
455
+ - 1% coverage → Ring 7 (128 partitions)
456
+
457
+ ### Architecture
458
+
459
+ ```
460
+ FRET (Pure DHT)
461
+ - Generic metadata transport
462
+ - Peer discovery & routing
463
+
464
+ ArachnodeFretAdapter (Plugin Layer)
465
+ - Arachnode-specific semantics
466
+ - Ring discovery methods
467
+
468
+ Restoration Components
469
+ - RingSelector: Capacity-based ring selection
470
+ - RestorationCoordinator: Multi-ring queries
471
+ - StorageMonitor: Capacity tracking
472
+ - SyncService/Client: Block request protocol
473
+ ```
474
+
475
+ ### Configuration
476
+
477
+ ```typescript
478
+ const node = await createLibp2pNode({
479
+ port: 9000,
480
+ networkName: 'mynet',
481
+ bootstrapNodes: ['...'],
482
+ clusterSize: 10, // Cluster size for peer discovery
483
+ arachnode: {
484
+ enableRingZulu: true // Default: enabled
485
+ }
486
+ });
487
+ ```
488
+
489
+ #### Solo / bootstrap / mobile nodes (no remote peers)
490
+
491
+ A node can operate fully on its own — no bootstrap peers, no listen addresses, no
492
+ connectivity to other peers. This is the common shape for a mobile app on first
493
+ launch or for a local-only sanity test.
494
+
495
+ ```typescript
496
+ const soloNode = await createLibp2pNode({
497
+ networkName: 'mynet',
498
+ bootstrapNodes: [], // no bootstrap
499
+ listenAddrs: [], // no incoming connections
500
+ transports: [webSockets()], // transports still required
501
+ clusterSize: 1,
502
+ });
503
+ ```
504
+
505
+ In solo mode:
506
+
507
+ - The node is its own coordinator. `CoordinatorRepo` short-circuits to local
508
+ storage when the cluster size is ≤ 1, so `pend`/`commit` do not require
509
+ cluster consensus and do not attempt to dial self.
510
+ - Block restoration is skipped when the only discovered peer is self.
511
+ `RestorationCoordinator` will return `undefined` immediately rather than
512
+ attempt to dial self (which would hang on a node with no listen addrs).
513
+ - If an operation fails for some other reason, `findCoordinator` surfaces a
514
+ distinguishable `FindCoordinatorError` with `code ===
515
+ FIND_COORDINATOR_ERROR_CODES.SELF_COORDINATION_EXHAUSTED` rather than the
516
+ generic "all candidates excluded". The aggregate error thrown by
517
+ `NetworkTransactor` preserves the ORIGINAL first-attempt cause so callers can
518
+ diagnose the real problem.
519
+
520
+ Once remote peers become reachable, the node transitions out of solo mode
521
+ automatically as FRET discovers them. Persisted network state (HWM, FRET
522
+ table) carries across restarts when an `options.persistence` is supplied.
523
+
524
+ #### Custom transports (including React Native)
525
+
526
+ By default, `createLibp2pNode()` uses TCP + circuit-relay transport.
527
+
528
+ To use non-default transports, pass `transports` (and typically `listenAddrs`):
529
+
530
+ ```typescript
531
+ import { webSockets } from '@libp2p/websockets';
532
+ import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
533
+ import { createLibp2pNode } from '@optimystic/db-p2p';
534
+
535
+ const node = await createLibp2pNode({
536
+ networkName: 'mynet',
537
+ bootstrapNodes: ['/dns4/relay.example.com/tcp/443/wss/p2p/12D3...'],
538
+ transports: [webSockets(), circuitRelayTransport()],
539
+ listenAddrs: [], // client-only; no incoming connections
540
+ });
541
+ ```
542
+
543
+ #### React Native
544
+
545
+ For React Native, import from `@optimystic/db-p2p/rn` instead of the root entrypoint.
546
+ The `/rn` entrypoint does **not** import `@libp2p/tcp`, so Metro/Hermes won't try to
547
+ bundle Node-only native modules. It requires `options.transports` explicitly:
548
+
549
+ ```typescript
550
+ import { webSockets } from '@libp2p/websockets';
551
+ import { circuitRelayTransport } from '@libp2p/circuit-relay-v2';
552
+ import { createLibp2pNode } from '@optimystic/db-p2p/rn';
553
+
554
+ const node = await createLibp2pNode({
555
+ networkName: 'mynet',
556
+ bootstrapNodes: ['/dns4/relay.example.com/tcp/443/wss/p2p/12D3...'],
557
+ transports: [webSockets(), circuitRelayTransport()],
558
+ });
559
+ ```
560
+
561
+ RN environments (Hermes) need polyfills for globals that libp2p and its
562
+ dependencies expect. Install these early (e.g. in your app's entry file
563
+ before any other imports).
564
+
565
+ **Global polyfills** (patch `globalThis` before any library code):
566
+
567
+ | API | Required by | Notes |
568
+ |-----|-------------|-------|
569
+ | `crypto.getRandomValues()` | @noble/hashes, @libp2p/crypto | RN 0.76+ New Architecture provides natively; fallback via e.g. `react-native-get-random-values` |
570
+ | `crypto.subtle.digest()` | multiformats/hashes/sha2-browser | Async SHA-256/SHA-512implement via @noble/hashes |
571
+ | `structuredClone()` | @optimystic/db-core | JSON round-trip is sufficient |
572
+ | `Promise.withResolvers()` | @libp2p/utils, @chainsafe/libp2p-yamux, it-queue | ES2024 simple shim |
573
+ | `AbortSignal.prototype.throwIfAborted()` | libp2p, @libp2p/circuit-relay-v2, it-pushable | DOM spec addition simple shim |
574
+ | Timer `.ref()` / `.unref()` | @optimystic/db-p2p, undici | Wrap Hermes numeric timer IDs in objects with no-op `.ref()`/`.unref()` methods; patch `clearTimeout`/`clearInterval` to unwrap |
575
+ | `Event`, `CustomEvent`, `EventTarget` | libp2p, @libp2p/interface | Custom shim or npm `event-target-polyfill` |
576
+ | `Intl.PluralRules` | moat-maker | English-only ordinal/cardinal shim is sufficient |
577
+
578
+ **Node.js built-in module shims** (via Metro `extraNodeModules` or bundler aliases):
579
+
580
+ | Module | Required by | Recommended shim |
581
+ |--------|-------------|------------------|
582
+ | `os` / `node:os` | @libp2p/utils | Custom shim: `networkInterfaces()` → `{}`, `platform()` → `Platform.OS` |
583
+ | `crypto` / `node:crypto` | multiformats/hashes/sha2 | Custom shim: `createHash()` via @noble/hashes |
584
+ | `stream` / `node:stream` | libp2p | `readable-stream` (npm) |
585
+ | `buffer` / `node:buffer` | libp2p, multiformats | `buffer` (npm) |
586
+
587
+ **Built-in (no polyfill needed):**
588
+ - `TextEncoder` — built-in to Hermes
589
+ - `TextDecoder` built-in to Expo SDK 52+ (UTF-8 only)
590
+ - `BigInt` — built-in to Hermes since RN 0.70
591
+
592
+ See the [Sereus reference-app-rn](https://github.com/gotchoices/sereus/tree/master/packages/reference-app-rn/polyfills) for working polyfill implementations.
593
+
594
+ ### Ring Transitions
595
+
596
+ Nodes automatically transition between rings based on capacity thresholds:
597
+ - **Move OUT** (to more granular ring) when > 85% capacity used
598
+ - **Move IN** (to broader ring) when < 40% capacity used
599
+
600
+ ## Related Packages
601
+
602
+ - **[@optimystic/db-core](../db-core)**: Core database interfaces and local operations
603
+ - **[@optimystic/db-p2p-storage-fs](../db-p2p-storage-fs)**: Filesystem `IRawStorage` backend (Node)
604
+ - **[@optimystic/db-p2p-storage-ns](../db-p2p-storage-ns)**: NativeScript SQLite storage backend
605
+ - **[@optimystic/db-p2p-storage-rn](../db-p2p-storage-rn)**: React Native LevelDB storage backend
606
+ - **[@optimystic/db-p2p-storage-web](../db-p2p-storage-web)**: Browser IndexedDB storage backend
607
+ - **[@optimystic/db-quereus](../db-quereus)**: Query engine and data access patterns
608
+ - **[p2p-fret](../fret)**: DHT implementation for peer discovery
609
+
610
+ ### Internal Architecture
611
+
612
+ For developers and AI agents working on internals, see [Internals Guide](../../docs/internals.md) for:
613
+ - Data flow diagrams (read/write/commit paths)
614
+ - Mutation contracts (which functions mutate vs clone)
615
+ - Key invariants and common pitfalls
616
+ - Type glossary and debugging tips