yakmesh 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -0
- package/adapters/base-adapter.js +177 -0
- package/assets/yakmesh-logo.png +0 -0
- package/assets/yakmesh-logo2.png +0 -0
- package/cli/index.js +307 -0
- package/dashboard/index.html +547 -0
- package/gossip/protocol.js +464 -0
- package/identity/node-key.js +180 -0
- package/mesh/network.js +407 -0
- package/oracle/README.md +221 -0
- package/oracle/code-proof-protocol.js +458 -0
- package/oracle/consensus-engine.js +609 -0
- package/oracle/genesis-network-v2.js +338 -0
- package/oracle/genesis-network.js +342 -0
- package/oracle/index.js +123 -0
- package/oracle/module-sealer.js +313 -0
- package/oracle/network-identity.js +442 -0
- package/oracle/phase-epoch.js +482 -0
- package/oracle/time-source.js +826 -0
- package/oracle/validation-oracle-hardened.js +836 -0
- package/oracle/validation-oracle.js +757 -0
- package/package.json +52 -0
- package/scripts/start-full-stack.bat +25 -0
- package/scripts/start-full-stack.sh +28 -0
- package/security/mesh-auth.js +123 -0
- package/server/index.js +936 -0
- package/webserver/cli.js +63 -0
- package/webserver/full-stack-example.js +56 -0
- package/webserver/index.js +209 -0
- package/yakmesh.config.example.js +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://csrc.nist.gov/projects/post-quantum-cryptography)
|
|
11
|
+
[](https://www.npmjs.com/package/yakmesh)
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
YAKMESH is a high-resiliency, decentralized networking layer designed for the 2026 threat landscape. Built with quantum-resistant cryptography at its core and anchored by PCIe atomic timing synchronization, YAKMESH provides a "sturdy" substrate for distributed systems that cannot afford to fail.
|
|
17
|
+
|
|
18
|
+
## Why YAKMESH?
|
|
19
|
+
|
|
20
|
+
In an era where traditional ECDSA is increasingly vulnerable and network jitter can desynchronize global state, YAKMESH offers a three-pillar solution:
|
|
21
|
+
|
|
22
|
+
🌿 **Yielding Resilience**: A self-healing mesh topology that adapts to node failure and adversarial interference without central authority.
|
|
23
|
+
|
|
24
|
+
⚛️ **Atomic Precision**: Integrated support for PCIe atomic clock hardware, enabling nanosecond-level hardware timestamping for low-latency synchronization.
|
|
25
|
+
|
|
26
|
+
🔐 **Quantum Hardened**: Fully compatible with Project Zond and the QRL (Quantum Resistant Ledger) ecosystem, utilizing stateless lattice-based signatures (ML-DSA) from Genesis.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## The Y.A.K.M.E.S.H. Philosophy
|
|
31
|
+
|
|
32
|
+
| Letter | Principle | Description |
|
|
33
|
+
|--------|-----------|-------------|
|
|
34
|
+
| **Y** | **Yielding** | Not brittle; flexible enough to absorb network shocks |
|
|
35
|
+
| **A** | **Atomic** | Grounded in the absolute truth of physical time |
|
|
36
|
+
| **K** | **Kernel** | The essential, innermost part of the secure stack |
|
|
37
|
+
| **M** | **Modular** | Swap out encryption primitives or transport layers as tech evolves |
|
|
38
|
+
| **E** | **Encryption** | Privacy and integrity by default |
|
|
39
|
+
| **S** | **Secured** | Hardened against both classical and quantum vectors |
|
|
40
|
+
| **H** | **Hub** | A nexus for decentralized data and peer-to-peer logic |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- 🔒 **Post-Quantum Secure** - ML-DSA-65 (NIST FIPS 204) signatures
|
|
47
|
+
- 🔮 **Self-Verifying Oracle** - Deterministic validation without external trust
|
|
48
|
+
- 🌐 **Mesh Networking** - P2P WebSocket communication with gossip protocol
|
|
49
|
+
- ⏱️ **Precision Timing** - Support for atomic clocks, GPS, PTP, NTP
|
|
50
|
+
- 🔌 **Plugin Architecture** - Adapters for any database or API
|
|
51
|
+
- 🛡️ **Phase Modulation** - Time-based anti-replay protection
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install yakmesh
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
import { YakmeshNode } from 'yakmesh';
|
|
61
|
+
|
|
62
|
+
const node = new YakmeshNode({
|
|
63
|
+
node: { name: 'My Node' },
|
|
64
|
+
network: { httpPort: 3000, wsPort: 9001 },
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await node.start();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## CLI
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Initialize a new node
|
|
74
|
+
npx yakmesh init
|
|
75
|
+
|
|
76
|
+
# Start the node
|
|
77
|
+
npx yakmesh start
|
|
78
|
+
|
|
79
|
+
# Check status
|
|
80
|
+
npx yakmesh status
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Documentation
|
|
84
|
+
|
|
85
|
+
Full documentation available at **[yakmesh.dev](https://yakmesh.dev)**
|
|
86
|
+
|
|
87
|
+
## Architecture
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
yakmesh/
|
|
91
|
+
├── oracle/ # Self-verifying validation engine
|
|
92
|
+
├── mesh/ # WebSocket P2P networking
|
|
93
|
+
├── gossip/ # Epidemic-style message propagation
|
|
94
|
+
├── identity/ # Post-quantum key management
|
|
95
|
+
├── database/ # SQLite replication engine
|
|
96
|
+
├── adapters/ # Platform integration plugins
|
|
97
|
+
├── security/ # Pro authentication & encryption
|
|
98
|
+
├── webserver/ # Embedded Caddy web server
|
|
99
|
+
└── server/ # HTTP/WS server
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Network Identity
|
|
103
|
+
|
|
104
|
+
Each YAKMESH network has a unique identity derived from configurable salts:
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
import { setIdentityConfig } from 'yakmesh/oracle/network-identity.js';
|
|
108
|
+
|
|
109
|
+
setIdentityConfig({
|
|
110
|
+
networkPrefix: 'my', // Network ID prefix
|
|
111
|
+
identitySalt: 'my-app-v1', // Unique network salt
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Different salt = different network (cannot interoperate)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Time Source Trust Levels
|
|
118
|
+
|
|
119
|
+
| Level | Source | Tolerance | Oracle Capable |
|
|
120
|
+
|-------|--------|-----------|----------------|
|
|
121
|
+
| ATOMIC | PCIe atomic clock | ±100ms | ✅ Yes |
|
|
122
|
+
| GPS | GPS with PPS | ±500ms | ✅ Yes |
|
|
123
|
+
| PTP | IEEE 1588 (Meinberg) | ±500ms | ⚠️ Partial |
|
|
124
|
+
| NTP | Standard NTP | ±5000ms | ❌ No |
|
|
125
|
+
|
|
126
|
+
## Adapters
|
|
127
|
+
|
|
128
|
+
Create custom adapters by extending `BaseAdapter`:
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
import { BaseAdapter } from 'yakmesh/adapters/base-adapter.js';
|
|
132
|
+
|
|
133
|
+
class MyAdapter extends BaseAdapter {
|
|
134
|
+
async init() { /* Connect to your database */ }
|
|
135
|
+
getSchema() { return { tables: ['users', 'orders'] }; }
|
|
136
|
+
async fetchChanges(since) { /* Return changed records */ }
|
|
137
|
+
async applyChange(table, record, op) { /* Write to database */ }
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Official Adapters
|
|
142
|
+
|
|
143
|
+
- `@yakmesh/adapter-peerquanta` - PeerQuanta phpBB marketplace
|
|
144
|
+
|
|
145
|
+
## API Endpoints
|
|
146
|
+
|
|
147
|
+
| Endpoint | Method | Description |
|
|
148
|
+
|----------|--------|-------------|
|
|
149
|
+
| `/health` | GET | Node health status |
|
|
150
|
+
| `/node` | GET | Node identity info |
|
|
151
|
+
| `/peers` | GET | Connected peers |
|
|
152
|
+
| `/oracle/status` | GET | Oracle integrity check |
|
|
153
|
+
| `/network/identity` | GET | Network identity (hash obfuscated) |
|
|
154
|
+
| `/time/status` | GET | Time source detection |
|
|
155
|
+
| `/time/capabilities` | GET | Time oracle eligibility |
|
|
156
|
+
| `/connect` | POST | Connect to a peer |
|
|
157
|
+
|
|
158
|
+
## Pro Features
|
|
159
|
+
|
|
160
|
+
YAKMESH Pro includes additional security features:
|
|
161
|
+
|
|
162
|
+
- 🔐 **WebSocket Authentication** - Challenge-response auth with signatures
|
|
163
|
+
- 🔒 **Message Encryption** - XChaCha20-Poly1305 encrypted messages
|
|
164
|
+
- 📋 **Peer Allowlist/Blocklist** - Access control for private networks
|
|
165
|
+
- 🛡️ **Connection Rate Limiting** - DDoS protection
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
- **Community Edition**: MIT License
|
|
170
|
+
- **Pro Edition**: Proprietary License
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
<div align="center">
|
|
175
|
+
<sub>Built with quantum principles. Secured by math.</sub>
|
|
176
|
+
<br><br>
|
|
177
|
+
<strong><a href="https://yakmesh.dev">yakmesh.dev</a></strong>
|
|
178
|
+
<br><br>
|
|
179
|
+
<sub>© 2026 YAKMESH Project. Sturdy & Secure.</sub>
|
|
180
|
+
</div>
|
|
181
|
+
]]>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yakmesh - Base Adapter Interface
|
|
3
|
+
*
|
|
4
|
+
* All platform adapters must extend this class.
|
|
5
|
+
* Adapters bridge external data sources with the Yakmesh network.
|
|
6
|
+
*
|
|
7
|
+
* @module adapters/base-adapter
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Abstract base class for Yakmesh adapters
|
|
15
|
+
*
|
|
16
|
+
* Implement this to create integrations with:
|
|
17
|
+
* - phpBB forums (PeerQuanta)
|
|
18
|
+
* - WordPress/WooCommerce
|
|
19
|
+
* - Custom REST APIs
|
|
20
|
+
* - PostgreSQL databases
|
|
21
|
+
* - MongoDB collections
|
|
22
|
+
*/
|
|
23
|
+
export class BaseAdapter extends EventEmitter {
|
|
24
|
+
constructor(YakmeshNode, config = {}) {
|
|
25
|
+
super();
|
|
26
|
+
|
|
27
|
+
if (new.target === BaseAdapter) {
|
|
28
|
+
throw new Error('BaseAdapter is abstract and cannot be instantiated directly');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.node = YakmeshNode;
|
|
32
|
+
this.config = config;
|
|
33
|
+
this.syncInterval = null;
|
|
34
|
+
this.isInitialized = false;
|
|
35
|
+
|
|
36
|
+
// Statistics
|
|
37
|
+
this.stats = {
|
|
38
|
+
recordsProcessed: 0,
|
|
39
|
+
recordsValidated: 0,
|
|
40
|
+
recordsRejected: 0,
|
|
41
|
+
lastSyncTime: null,
|
|
42
|
+
errors: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Initialize the adapter
|
|
48
|
+
* Must be implemented by subclasses
|
|
49
|
+
* @abstract
|
|
50
|
+
*/
|
|
51
|
+
async init() {
|
|
52
|
+
throw new Error('init() must be implemented by subclass');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get the schema definition for replicated tables
|
|
57
|
+
* Must be implemented by subclasses
|
|
58
|
+
* @abstract
|
|
59
|
+
* @returns {Object} Schema definition
|
|
60
|
+
*/
|
|
61
|
+
getSchema() {
|
|
62
|
+
throw new Error('getSchema() must be implemented by subclass');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Fetch records that have changed since last sync
|
|
67
|
+
* Must be implemented by subclasses
|
|
68
|
+
* @abstract
|
|
69
|
+
* @param {Date} since - Timestamp of last sync
|
|
70
|
+
* @returns {Promise<Array>} Changed records
|
|
71
|
+
*/
|
|
72
|
+
async fetchChanges(since) {
|
|
73
|
+
throw new Error('fetchChanges() must be implemented by subclass');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Apply a record change from the mesh to the local database
|
|
78
|
+
* Must be implemented by subclasses
|
|
79
|
+
* @abstract
|
|
80
|
+
* @param {string} table - Table name
|
|
81
|
+
* @param {Object} record - Record data
|
|
82
|
+
* @param {string} operation - 'INSERT', 'UPDATE', 'DELETE'
|
|
83
|
+
*/
|
|
84
|
+
async applyChange(table, record, operation) {
|
|
85
|
+
throw new Error('applyChange() must be implemented by subclass');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validate a record using custom rules
|
|
90
|
+
* Override to add platform-specific validation
|
|
91
|
+
* @param {string} type - Record type
|
|
92
|
+
* @param {Object} data - Record data
|
|
93
|
+
* @returns {Object} { valid: boolean, errors: string[] }
|
|
94
|
+
*/
|
|
95
|
+
validate(type, data) {
|
|
96
|
+
return { valid: true, errors: [] };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Start periodic synchronization
|
|
101
|
+
* @param {number} intervalMs - Sync interval in milliseconds
|
|
102
|
+
*/
|
|
103
|
+
startSync(intervalMs = 60000) {
|
|
104
|
+
if (this.syncInterval) {
|
|
105
|
+
this.stopSync();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log('Starting sync every ' + (intervalMs / 1000) + 's');
|
|
109
|
+
this.sync();
|
|
110
|
+
|
|
111
|
+
this.syncInterval = setInterval(() => {
|
|
112
|
+
this.sync();
|
|
113
|
+
}, intervalMs);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Stop periodic synchronization
|
|
118
|
+
*/
|
|
119
|
+
stopSync() {
|
|
120
|
+
if (this.syncInterval) {
|
|
121
|
+
clearInterval(this.syncInterval);
|
|
122
|
+
this.syncInterval = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Perform a sync cycle
|
|
128
|
+
*/
|
|
129
|
+
async sync() {
|
|
130
|
+
try {
|
|
131
|
+
const changes = await this.fetchChanges(this.stats.lastSyncTime);
|
|
132
|
+
|
|
133
|
+
for (const change of changes) {
|
|
134
|
+
const validation = this.validate(change.type, change.data);
|
|
135
|
+
|
|
136
|
+
if (validation.valid) {
|
|
137
|
+
this.node.gossip.spreadRumor('adapter:' + change.type, {
|
|
138
|
+
table: change.table,
|
|
139
|
+
operation: change.operation,
|
|
140
|
+
data: change.data,
|
|
141
|
+
});
|
|
142
|
+
this.stats.recordsValidated++;
|
|
143
|
+
} else {
|
|
144
|
+
this.stats.recordsRejected++;
|
|
145
|
+
this.emit('validation-error', { change, errors: validation.errors });
|
|
146
|
+
}
|
|
147
|
+
this.stats.recordsProcessed++;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.stats.lastSyncTime = new Date();
|
|
151
|
+
this.emit('sync-complete', { processed: changes.length });
|
|
152
|
+
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.stats.errors.push({ time: new Date(), error: error.message });
|
|
155
|
+
this.emit('sync-error', error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
getStats() {
|
|
160
|
+
return {
|
|
161
|
+
...this.stats,
|
|
162
|
+
isRunning: this.syncInterval !== null,
|
|
163
|
+
adapterType: this.constructor.name,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async handleRumor(topic, data, origin) {
|
|
168
|
+
if (topic.startsWith('adapter:')) {
|
|
169
|
+
const { table, operation, data: recordData } = data;
|
|
170
|
+
await this.applyChange(table, recordData, operation);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default BaseAdapter;
|
|
176
|
+
|
|
177
|
+
|
|
Binary file
|
|
Binary file
|
package/cli/index.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Yakmesh CLI
|
|
5
|
+
* Command-line interface for managing Yakmesh nodes
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
11
|
+
import { join, resolve } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
|
|
14
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
15
|
+
const VERSION = '0.1.0';
|
|
16
|
+
|
|
17
|
+
const program = new Command();
|
|
18
|
+
|
|
19
|
+
// Banner
|
|
20
|
+
function showBanner() {
|
|
21
|
+
console.log(chalk.cyan(`
|
|
22
|
+
🦬 Yakmesh Network v${VERSION}
|
|
23
|
+
Post-Quantum Secure • Decentralized • Federated
|
|
24
|
+
`));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ===== INIT COMMAND =====
|
|
28
|
+
program
|
|
29
|
+
.command('init')
|
|
30
|
+
.description('Initialize a new Yakmesh Node in the current directory')
|
|
31
|
+
.option('-n, --name <name>', 'Node name', 'My Yakmesh Node')
|
|
32
|
+
.option('-r, --region <region>', 'Geographic region', 'local')
|
|
33
|
+
.option('-p, --port <port>', 'HTTP port', '3000')
|
|
34
|
+
.option('-w, --ws-port <port>', 'WebSocket port', '9001')
|
|
35
|
+
.option('--bootstrap <urls>', 'Comma-separated bootstrap node URLs')
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
showBanner();
|
|
38
|
+
console.log(chalk.yellow('Initializing new Yakmesh Node...\n'));
|
|
39
|
+
|
|
40
|
+
const dataDir = resolve('./data');
|
|
41
|
+
const configPath = resolve('./yakmesh.config.js');
|
|
42
|
+
|
|
43
|
+
// Check if already initialized
|
|
44
|
+
if (existsSync(configPath)) {
|
|
45
|
+
console.log(chalk.red('✗ Error: yakmesh.config.js already exists'));
|
|
46
|
+
console.log(chalk.gray(' Use --force to reinitialize'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Create data directory
|
|
51
|
+
if (!existsSync(dataDir)) {
|
|
52
|
+
mkdirSync(dataDir, { recursive: true });
|
|
53
|
+
console.log(chalk.green('✓ Created data directory'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Parse bootstrap nodes
|
|
57
|
+
const bootstrapNodes = options.bootstrap
|
|
58
|
+
? options.bootstrap.split(',').map(s => s.trim())
|
|
59
|
+
: ['wss://peerquanta.com:9001'];
|
|
60
|
+
|
|
61
|
+
// Generate config
|
|
62
|
+
const config = `/**
|
|
63
|
+
* Yakmesh Node Configuration
|
|
64
|
+
* Generated by: npx yakmesh init
|
|
65
|
+
*/
|
|
66
|
+
export default {
|
|
67
|
+
// Node identity
|
|
68
|
+
node: {
|
|
69
|
+
name: '${options.name}',
|
|
70
|
+
region: '${options.region}',
|
|
71
|
+
capabilities: ['listings', 'chat', 'forum', 'qcoa'],
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Network settings
|
|
75
|
+
network: {
|
|
76
|
+
httpPort: ${options.port},
|
|
77
|
+
wsPort: ${options.wsPort},
|
|
78
|
+
publicHost: 'localhost',
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// Bootstrap nodes (well-known entry points)
|
|
82
|
+
bootstrap: ${JSON.stringify(bootstrapNodes, null, 4).replace(/\n/g, '\n ')},
|
|
83
|
+
|
|
84
|
+
// Database
|
|
85
|
+
database: {
|
|
86
|
+
path: './data/peerquanta.db',
|
|
87
|
+
replication: {
|
|
88
|
+
enabled: true,
|
|
89
|
+
syncInterval: 30000, // 30 seconds
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// Security
|
|
94
|
+
security: {
|
|
95
|
+
maxPeers: 50,
|
|
96
|
+
requireAuth: false,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
writeFileSync(configPath, config);
|
|
102
|
+
console.log(chalk.green('✓ Created yakmesh.config.js'));
|
|
103
|
+
|
|
104
|
+
console.log(chalk.cyan('\n📋 Node Configuration:'));
|
|
105
|
+
console.log(chalk.gray(` Name: ${options.name}`));
|
|
106
|
+
console.log(chalk.gray(` Region: ${options.region}`));
|
|
107
|
+
console.log(chalk.gray(` HTTP Port: ${options.port}`));
|
|
108
|
+
console.log(chalk.gray(` WS Port: ${options.wsPort}`));
|
|
109
|
+
console.log(chalk.gray(` Bootstrap: ${bootstrapNodes.length} node(s)`));
|
|
110
|
+
|
|
111
|
+
console.log(chalk.green('\n✓ Initialization complete!\n'));
|
|
112
|
+
console.log(chalk.white('Next steps:'));
|
|
113
|
+
console.log(chalk.gray(' 1. Edit yakmesh.config.js to customize settings'));
|
|
114
|
+
console.log(chalk.gray(' 2. Run: npx yakmesh start'));
|
|
115
|
+
console.log('');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ===== START COMMAND =====
|
|
119
|
+
program
|
|
120
|
+
.command('start')
|
|
121
|
+
.description('Start the Yakmesh Node')
|
|
122
|
+
.option('-c, --config <path>', 'Path to config file', './yakmesh.config.js')
|
|
123
|
+
.option('-d, --daemon', 'Run as background daemon')
|
|
124
|
+
.action(async (options) => {
|
|
125
|
+
showBanner();
|
|
126
|
+
|
|
127
|
+
const configPath = resolve(options.config);
|
|
128
|
+
|
|
129
|
+
if (!existsSync(configPath)) {
|
|
130
|
+
console.log(chalk.red('✗ Error: yakmesh.config.js not found'));
|
|
131
|
+
console.log(chalk.gray(' Run: npx yakmesh init'));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(chalk.yellow('Starting Yakmesh Node...\n'));
|
|
136
|
+
|
|
137
|
+
// Dynamic import of server - use the package's server module
|
|
138
|
+
const serverPath = join(__dirname, '..', 'server', 'index.js');
|
|
139
|
+
const serverURL = new URL(`file:///${serverPath.replace(/\\/g, '/')}`);
|
|
140
|
+
const { YakmeshNode } = await import(serverURL.href);
|
|
141
|
+
|
|
142
|
+
// Load config from current working directory
|
|
143
|
+
const configURL = new URL(`file:///${configPath.replace(/\\/g, '/')}`);
|
|
144
|
+
const { default: config } = await import(configURL.href);
|
|
145
|
+
|
|
146
|
+
const node = new YakmeshNode(config);
|
|
147
|
+
|
|
148
|
+
// Handle shutdown
|
|
149
|
+
process.on('SIGINT', async () => {
|
|
150
|
+
console.log(chalk.yellow('\n\nShutting down...'));
|
|
151
|
+
await node.stop();
|
|
152
|
+
process.exit(0);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
process.on('SIGTERM', async () => {
|
|
156
|
+
await node.stop();
|
|
157
|
+
process.exit(0);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
await node.start();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// ===== STATUS COMMAND =====
|
|
164
|
+
program
|
|
165
|
+
.command('status')
|
|
166
|
+
.description('Show node status')
|
|
167
|
+
.option('-p, --port <port>', 'HTTP port of local node', '3000')
|
|
168
|
+
.action(async (options) => {
|
|
169
|
+
try {
|
|
170
|
+
const response = await fetch(`http://localhost:${options.port}/health`);
|
|
171
|
+
const data = await response.json();
|
|
172
|
+
|
|
173
|
+
showBanner();
|
|
174
|
+
console.log(chalk.green('✓ Node is running\n'));
|
|
175
|
+
console.log(chalk.cyan('📊 Status:'));
|
|
176
|
+
console.log(chalk.gray(` Node ID: ${data.nodeId}`));
|
|
177
|
+
console.log(chalk.gray(` Peers: ${data.peers}`));
|
|
178
|
+
console.log(chalk.gray(` Algorithm: ${data.algorithm}`));
|
|
179
|
+
console.log('');
|
|
180
|
+
} catch (e) {
|
|
181
|
+
console.log(chalk.red('✗ Node is not running'));
|
|
182
|
+
console.log(chalk.gray(` Could not connect to http://localhost:${options.port}`));
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// ===== PEERS COMMAND =====
|
|
188
|
+
program
|
|
189
|
+
.command('peers')
|
|
190
|
+
.description('List connected peers')
|
|
191
|
+
.option('-p, --port <port>', 'HTTP port of local node', '3000')
|
|
192
|
+
.action(async (options) => {
|
|
193
|
+
try {
|
|
194
|
+
const response = await fetch(`http://localhost:${options.port}/peers`);
|
|
195
|
+
const peers = await response.json();
|
|
196
|
+
|
|
197
|
+
showBanner();
|
|
198
|
+
|
|
199
|
+
if (peers.length === 0) {
|
|
200
|
+
console.log(chalk.yellow('No peers connected\n'));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log(chalk.cyan(`📡 Connected Peers (${peers.length}):\n`));
|
|
205
|
+
|
|
206
|
+
for (const peer of peers) {
|
|
207
|
+
const ago = Math.floor((Date.now() - peer.lastSeen) / 1000);
|
|
208
|
+
console.log(chalk.white(` ${peer.name}`));
|
|
209
|
+
console.log(chalk.gray(` ID: ${peer.nodeId.slice(0, 24)}...`));
|
|
210
|
+
console.log(chalk.gray(` Endpoint: ${peer.endpoint || 'inbound'}`));
|
|
211
|
+
console.log(chalk.gray(` Last seen: ${ago}s ago`));
|
|
212
|
+
console.log('');
|
|
213
|
+
}
|
|
214
|
+
} catch (e) {
|
|
215
|
+
console.log(chalk.red('✗ Could not fetch peers'));
|
|
216
|
+
console.log(chalk.gray(` Is the node running on port ${options.port}?`));
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// ===== INFO COMMAND =====
|
|
222
|
+
program
|
|
223
|
+
.command('info')
|
|
224
|
+
.description('Show detailed node information')
|
|
225
|
+
.option('-p, --port <port>', 'HTTP port of local node', '3000')
|
|
226
|
+
.action(async (options) => {
|
|
227
|
+
try {
|
|
228
|
+
const [nodeRes, replRes] = await Promise.all([
|
|
229
|
+
fetch(`http://localhost:${options.port}/node`),
|
|
230
|
+
fetch(`http://localhost:${options.port}/replication`),
|
|
231
|
+
]);
|
|
232
|
+
|
|
233
|
+
const node = await nodeRes.json();
|
|
234
|
+
const repl = await replRes.json();
|
|
235
|
+
|
|
236
|
+
showBanner();
|
|
237
|
+
console.log(chalk.cyan('🦬 Node Information:\n'));
|
|
238
|
+
|
|
239
|
+
console.log(chalk.white(' Identity:'));
|
|
240
|
+
console.log(chalk.gray(` Node ID: ${node.nodeId}`));
|
|
241
|
+
console.log(chalk.gray(` Name: ${node.name}`));
|
|
242
|
+
console.log(chalk.gray(` Region: ${node.region}`));
|
|
243
|
+
console.log(chalk.gray(` Algorithm: ${node.algorithm} (NIST Level ${node.nistLevel})`));
|
|
244
|
+
console.log(chalk.gray(` Created: ${new Date(node.createdAt).toISOString()}`));
|
|
245
|
+
|
|
246
|
+
console.log(chalk.white('\n Capabilities:'));
|
|
247
|
+
for (const cap of node.capabilities) {
|
|
248
|
+
console.log(chalk.gray(` • ${cap}`));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
console.log(chalk.white('\n Replication:'));
|
|
252
|
+
console.log(chalk.gray(` Log entries: ${repl.replicationLogSize}`));
|
|
253
|
+
console.log(chalk.gray(` Peer states: ${repl.peerStates.length}`));
|
|
254
|
+
console.log(chalk.gray(` Tables: ${repl.replicatedTables.join(', ')}`));
|
|
255
|
+
|
|
256
|
+
console.log(chalk.white('\n Public Key (ML-DSA-65):'));
|
|
257
|
+
console.log(chalk.gray(` ${node.publicKey.slice(0, 64)}...`));
|
|
258
|
+
console.log('');
|
|
259
|
+
} catch (e) {
|
|
260
|
+
console.log(chalk.red('✗ Could not fetch node info'));
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// ===== JOIN COMMAND =====
|
|
266
|
+
program
|
|
267
|
+
.command('join <endpoint>')
|
|
268
|
+
.description('Connect to a peer node')
|
|
269
|
+
.option('-p, --port <port>', 'HTTP port of local node', '3000')
|
|
270
|
+
.action(async (endpoint, options) => {
|
|
271
|
+
try {
|
|
272
|
+
const response = await fetch(`http://localhost:${options.port}/connect`, {
|
|
273
|
+
method: 'POST',
|
|
274
|
+
headers: { 'Content-Type': 'application/json' },
|
|
275
|
+
body: JSON.stringify({ endpoint }),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const result = await response.json();
|
|
279
|
+
|
|
280
|
+
if (result.success) {
|
|
281
|
+
console.log(chalk.green(`✓ Connected to ${endpoint}`));
|
|
282
|
+
} else {
|
|
283
|
+
console.log(chalk.red(`✗ Failed to connect: ${result.error}`));
|
|
284
|
+
}
|
|
285
|
+
} catch (e) {
|
|
286
|
+
console.log(chalk.red('✗ Could not connect'));
|
|
287
|
+
console.log(chalk.gray(` ${e.message}`));
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// ===== VERSION =====
|
|
293
|
+
program
|
|
294
|
+
.version(VERSION)
|
|
295
|
+
.description('Yakmesh Network - Post-Quantum Decentralized Node');
|
|
296
|
+
|
|
297
|
+
// Parse arguments
|
|
298
|
+
program.parse();
|
|
299
|
+
|
|
300
|
+
// Show help if no command
|
|
301
|
+
if (!process.argv.slice(2).length) {
|
|
302
|
+
showBanner();
|
|
303
|
+
program.outputHelp();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|