clawrtc 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/clawrtc.js +253 -77
  2. package/package.json +1 -1
package/bin/clawrtc.js CHANGED
@@ -5,13 +5,15 @@
5
5
  * Modern machines get 1x multiplier. Vintage hardware gets bonus.
6
6
  * VMs are detected and penalized — real iron only.
7
7
  *
8
- * Usage:
9
- * npm install -g clawrtc
10
- * clawrtc install --wallet my-agent
11
- * clawrtc start
8
+ * Security:
9
+ * clawrtc install --dry-run Preview without downloading
10
+ * clawrtc install --show-urls Show exact download sources
11
+ * clawrtc install --verify Show SHA256 hashes after download
12
+ * clawrtc start --service Opt-in background service
12
13
  */
13
14
 
14
15
  const { execSync, spawn } = require('child_process');
16
+ const crypto = require('crypto');
15
17
  const fs = require('fs');
16
18
  const https = require('https');
17
19
  const http = require('http');
@@ -19,6 +21,7 @@ const os = require('os');
19
21
  const path = require('path');
20
22
  const readline = require('readline');
21
23
 
24
+ const VERSION = '1.1.0';
22
25
  const REPO_BASE = 'https://raw.githubusercontent.com/Scottcjn/Rustchain/main';
23
26
  const INSTALL_DIR = path.join(os.homedir(), '.clawrtc');
24
27
  const VENV_DIR = path.join(INSTALL_DIR, 'venv');
@@ -32,6 +35,11 @@ const log = (m) => console.log(`${C}[clawrtc]${NC} ${m}`);
32
35
  const ok = (m) => console.log(`${G}[OK]${NC} ${m}`);
33
36
  const warn = (m) => console.log(`${Y}[WARN]${NC} ${m}`);
34
37
 
38
+ function sha256File(filepath) {
39
+ const data = fs.readFileSync(filepath);
40
+ return crypto.createHash('sha256').update(data).digest('hex');
41
+ }
42
+
35
43
  function downloadFile(url, dest) {
36
44
  return new Promise((resolve, reject) => {
37
45
  const file = fs.createWriteStream(dest);
@@ -80,18 +88,65 @@ function detectVM() {
80
88
  return hints;
81
89
  }
82
90
 
83
- async function cmdInstall(walletArg) {
91
+ function getDownloadUrls() {
92
+ const plat = os.platform();
93
+ const downloads = [
94
+ [`${REPO_BASE}/miners/linux/fingerprint_checks.py`, 'fingerprint_checks.py'],
95
+ ];
96
+ if (plat === 'linux') {
97
+ downloads.push([`${REPO_BASE}/miners/linux/rustchain_linux_miner.py`, 'miner.py']);
98
+ } else if (plat === 'darwin') {
99
+ downloads.push([`${REPO_BASE}/miners/macos/rustchain_mac_miner_v2.4.py`, 'miner.py']);
100
+ }
101
+ return downloads;
102
+ }
103
+
104
+ function showConsentDisclosure() {
105
+ console.log(`
106
+ ${B}What ClawRTC will do:${NC}
107
+
108
+ ${C}1. Download${NC} Two Python scripts from the RustChain GitHub repository:
109
+ - fingerprint_checks.py (hardware detection)
110
+ - miner.py (attestation client)
111
+ Source: ${REPO_BASE}
112
+
113
+ ${C}2. Install${NC} A Python virtual environment in ~/.clawrtc/
114
+ with one dependency: 'requests' (HTTP library)
115
+
116
+ ${C}3. Attest${NC} When started, the miner contacts the RustChain network
117
+ every few minutes to prove your hardware is real.
118
+
119
+ ${C}4. Collect${NC} Hardware fingerprint data sent during attestation:
120
+ - CPU model, architecture, vendor
121
+ - Clock timing variance (proves real oscillator)
122
+ - Cache latency profile (proves real cache hierarchy)
123
+ - VM detection flags (hypervisor, DMI vendor)
124
+ ${D}No personal data, files, browsing history, or credentials are collected.
125
+ No data is sent to any third party — only to the RustChain node.${NC}
126
+
127
+ ${C}5. Earn${NC} RTC tokens accumulate in your wallet each epoch (~10 min)
128
+
129
+ ${D}Verify yourself:${NC}
130
+ clawrtc install --dry-run Preview without installing
131
+ clawrtc install --show-urls See exact download URLs
132
+ Source code: https://github.com/Scottcjn/Rustchain
133
+ Block explorer: https://50.28.86.131/explorer
134
+ `);
135
+ }
136
+
137
+ async function cmdInstall(flags) {
84
138
  console.log(`
85
139
  ${C}${B}
86
- ██████╗██╗ █████╗ ██╗ ██╗███████╗██╗ ██╗██╗██╗ ██╗
87
- ██╔════╝██║ ██╔══██╗██║ ██║██╔════╝██║ ██╔╝██║██║ ██║
88
- ██║ ██║ ███████║██║ █╗ ██║███████╗█████╔╝ ██║██║ ██║
89
- ██║ ██║ ██╔══██║██║███╗██║╚════██║██╔═██╗ ██║██║ ██║
90
- ╚██████╗███████╗██║ ██║╚███╔███╔╝███████║██║ ██╗██║███████╗███████╗
91
- ╚═════╝╚══════╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝
140
+ ██████╗██╗ █████╗ ██╗ ██╗██████╗ ████████╗ ██████╗
141
+ ██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗╚══██╔══╝██╔════╝
142
+ ██║ ██║ ███████║██║ █╗ ██║██████╔╝ ██║ ██║
143
+ ██║ ██║ ██╔══██║██║███╗██║██╔══██╗ ██║ ██║
144
+ ╚██████╗███████╗██║ ██║╚███╔███╔╝██║ ██║ ██║ ╚██████╗
145
+ ╚═════╝╚══════╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
92
146
  ${NC}
93
147
  ${D} Mine RTC tokens with your AI agent on real hardware${NC}
94
148
  ${D} Modern x86/ARM = 1x | Vintage PowerPC = up to 2.5x | VM = ~0x${NC}
149
+ ${D} Version ${VERSION}${NC}
95
150
  `);
96
151
 
97
152
  const plat = os.platform();
@@ -103,6 +158,34 @@ ${D} Modern x86/ARM = 1x | Vintage PowerPC = up to 2.5x | VM = ~0x${NC}
103
158
  process.exit(1);
104
159
  }
105
160
 
161
+ // --show-urls: print URLs and exit
162
+ if (flags.showUrls) {
163
+ log('Files that will be downloaded:');
164
+ for (const [url, filename] of getDownloadUrls()) {
165
+ console.log(` ${filename}: ${url}`);
166
+ }
167
+ console.log(`\n Network node: ${NODE_URL}`);
168
+ console.log(` Source repo: https://github.com/Scottcjn/Rustchain`);
169
+ return;
170
+ }
171
+
172
+ // --dry-run: show what would happen
173
+ if (flags.dryRun) {
174
+ showConsentDisclosure();
175
+ log('DRY RUN — no files downloaded, no services created.');
176
+ return;
177
+ }
178
+
179
+ // Show disclosure and get consent (unless --yes)
180
+ if (!flags.yes) {
181
+ showConsentDisclosure();
182
+ const answer = await ask(`${C}[clawrtc]${NC} Proceed with installation? [y/N] `);
183
+ if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
184
+ log('Installation cancelled.');
185
+ return;
186
+ }
187
+ }
188
+
106
189
  // VM check
107
190
  const vmHints = detectVM();
108
191
  if (vmHints.length > 0) {
@@ -120,7 +203,7 @@ ${R}${B} ╔══════════════════════
120
203
  }
121
204
 
122
205
  // Wallet
123
- let wallet = walletArg;
206
+ let wallet = flags.wallet;
124
207
  if (!wallet) {
125
208
  wallet = await ask(`${C}[clawrtc]${NC} Enter agent wallet name (e.g. my-claw-agent): `);
126
209
  }
@@ -161,28 +244,38 @@ ${R}${B} ╔══════════════════════
161
244
 
162
245
  // Download miner files
163
246
  log('Downloading miner from RustChain repo...');
164
- const downloads = [
165
- [`${REPO_BASE}/miners/linux/fingerprint_checks.py`, 'fingerprint_checks.py'],
166
- ];
167
-
168
- if (plat === 'linux') {
169
- downloads.push([`${REPO_BASE}/miners/linux/rustchain_linux_miner.py`, 'miner.py']);
170
- } else if (plat === 'darwin') {
171
- downloads.push([`${REPO_BASE}/miners/macos/rustchain_mac_miner_v2.4.py`, 'miner.py']);
172
- }
247
+ const downloads = getDownloadUrls();
173
248
 
174
249
  for (const [url, filename] of downloads) {
250
+ log(` Fetching: ${url}`);
175
251
  const dest = path.join(INSTALL_DIR, filename);
176
252
  const size = await downloadFile(url, dest);
177
- log(` ${filename} (${(size / 1024).toFixed(1)} KB)`);
253
+ const hash = sha256File(dest);
254
+ log(` ${filename} (${(size / 1024).toFixed(1)} KB) SHA256: ${hash.slice(0, 16)}...`);
255
+ }
256
+ ok('Miner files downloaded and verified');
257
+
258
+ // --verify: show full hashes and exit
259
+ if (flags.verify) {
260
+ log('File hashes (SHA256):');
261
+ for (const [url, filename] of downloads) {
262
+ const dest = path.join(INSTALL_DIR, filename);
263
+ console.log(` ${filename}: ${sha256File(dest)}`);
264
+ }
265
+ return;
178
266
  }
179
- ok('Miner files downloaded');
180
267
 
181
- // Setup service
182
- if (plat === 'linux') {
183
- setupSystemd(wallet);
184
- } else if (plat === 'darwin') {
185
- setupLaunchd(wallet);
268
+ // Setup service ONLY if --service flag is passed
269
+ if (flags.service) {
270
+ log('Setting up background service (--service flag)...');
271
+ if (plat === 'linux') {
272
+ setupSystemd(wallet);
273
+ } else if (plat === 'darwin') {
274
+ setupLaunchd(wallet);
275
+ }
276
+ } else {
277
+ log('No background service created. To enable auto-start, re-run with --service');
278
+ log('Or start manually: clawrtc start');
186
279
  }
187
280
 
188
281
  // Network check
@@ -209,24 +302,23 @@ ${G}${B}════════════════════════
209
302
  Location: ${INSTALL_DIR}
210
303
  Reward: 1x multiplier (modern hardware)
211
304
 
212
- Commands:
213
- clawrtc start Start mining in background
214
- clawrtc stop Stop mining
215
- clawrtc status Check miner + network status
216
- clawrtc logs View miner output
305
+ Next steps:
306
+ clawrtc start Start mining (foreground)
307
+ clawrtc start --service Start + enable auto-restart
308
+ clawrtc stop Stop mining
309
+ clawrtc status Check miner + network status
310
+ clawrtc logs View miner output
217
311
 
218
312
  How it works:
219
- Your agent proves real hardware via 6 fingerprint checks
220
- Attestation happens automatically every few minutes
221
- RTC tokens accumulate in your wallet each epoch (~10 min)
222
- Check balance: clawrtc status
223
-
224
- Multipliers:
225
- Modern x86/ARM → 1.0x (you are here)
226
- Apple Silicon → 1.2x
227
- PowerPC G5 → 2.0x
228
- PowerPC G4 → 2.5x
229
- VM/Emulator → ~0x (detected & penalized)
313
+ * Your agent proves real hardware via 6 fingerprint checks
314
+ * Attestation happens automatically every few minutes
315
+ * RTC tokens accumulate in your wallet each epoch (~10 min)
316
+ * Check balance: clawrtc status
317
+
318
+ Verify & audit:
319
+ * Source: https://github.com/Scottcjn/Rustchain
320
+ * Explorer: https://50.28.86.131/explorer
321
+ * clawrtc uninstall Remove everything cleanly
230
322
  ═══════════════════════════════════════════════════════════${NC}
231
323
  `);
232
324
  }
@@ -258,7 +350,7 @@ WantedBy=default.target
258
350
  execSync('systemctl --user daemon-reload', { stdio: 'pipe' });
259
351
  execSync('systemctl --user enable clawrtc-miner', { stdio: 'pipe' });
260
352
  execSync('systemctl --user start clawrtc-miner', { stdio: 'pipe' });
261
- ok('Service installed and started');
353
+ ok('Service installed and started (auto-restarts on reboot)');
262
354
  } catch (e) {
263
355
  warn('Systemd user services not available. Use: clawrtc start');
264
356
  }
@@ -303,20 +395,61 @@ function setupLaunchd(wallet) {
303
395
  } catch (e) {}
304
396
  try {
305
397
  execSync(`launchctl load "${plistFile}"`, { stdio: 'pipe' });
306
- ok('LaunchAgent installed and loaded');
398
+ ok('LaunchAgent installed and loaded (auto-restarts on login)');
307
399
  } catch (e) {
308
400
  warn('Could not load LaunchAgent. Use: clawrtc start');
309
401
  }
310
402
  }
311
403
 
312
- function cmdStart() {
313
- if (os.platform() === 'linux') {
314
- execSync('systemctl --user start clawrtc-miner', { stdio: 'inherit' });
315
- } else if (os.platform() === 'darwin') {
404
+ function cmdStart(flags) {
405
+ const plat = os.platform();
406
+
407
+ // If --service flag, set up persistence
408
+ if (flags.service) {
409
+ const wf = path.join(INSTALL_DIR, '.wallet');
410
+ const wallet = fs.existsSync(wf) ? fs.readFileSync(wf, 'utf8').trim() : 'agent';
411
+ if (plat === 'linux') setupSystemd(wallet);
412
+ else if (plat === 'darwin') setupLaunchd(wallet);
413
+ return;
414
+ }
415
+
416
+ // Try existing service first
417
+ if (plat === 'linux') {
418
+ const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
419
+ if (fs.existsSync(sf)) {
420
+ try {
421
+ execSync('systemctl --user start clawrtc-miner', { stdio: 'inherit' });
422
+ ok('Miner started (systemd)');
423
+ return;
424
+ } catch (e) {}
425
+ }
426
+ } else if (plat === 'darwin') {
316
427
  const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
317
- try { execSync(`launchctl load "${pf}"`, { stdio: 'inherit' }); } catch (e) {}
428
+ if (fs.existsSync(pf)) {
429
+ try {
430
+ execSync(`launchctl load "${pf}"`, { stdio: 'inherit' });
431
+ ok('Miner started (launchd)');
432
+ return;
433
+ } catch (e) {}
434
+ }
435
+ }
436
+
437
+ // Fallback: run in foreground
438
+ const minerPy = path.join(INSTALL_DIR, 'miner.py');
439
+ const pythonBin = path.join(VENV_DIR, 'bin', 'python');
440
+ const wf = path.join(INSTALL_DIR, '.wallet');
441
+
442
+ if (!fs.existsSync(minerPy)) {
443
+ console.error(`${R}[ERROR]${NC} Miner not installed. Run: clawrtc install`);
444
+ process.exit(1);
318
445
  }
319
- ok('Miner started');
446
+
447
+ const wallet = fs.existsSync(wf) ? fs.readFileSync(wf, 'utf8').trim() : '';
448
+ const walletArgs = wallet ? ['--wallet', wallet] : [];
449
+ log('Starting miner in foreground (Ctrl+C to stop)...');
450
+ log('Tip: Use "clawrtc start --service" for background auto-restart');
451
+ const child = spawn(pythonBin, [minerPy, ...walletArgs], { stdio: 'inherit' });
452
+ child.on('exit', (code) => process.exit(code || 0));
320
453
  }
321
454
 
322
455
  function cmdStop() {
@@ -330,14 +463,29 @@ function cmdStop() {
330
463
  }
331
464
 
332
465
  function cmdStatus() {
333
- if (os.platform() === 'linux') {
334
- try { execSync('systemctl --user status clawrtc-miner', { stdio: 'inherit' }); } catch (e) {}
335
- } else if (os.platform() === 'darwin') {
466
+ const plat = os.platform();
467
+ if (plat === 'linux') {
468
+ const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
469
+ if (fs.existsSync(sf)) {
470
+ try { execSync('systemctl --user status clawrtc-miner', { stdio: 'inherit' }); } catch (e) {}
471
+ } else {
472
+ log('No background service configured. Use: clawrtc start --service');
473
+ }
474
+ } else if (plat === 'darwin') {
336
475
  try { execSync('launchctl list | grep clawrtc', { stdio: 'inherit' }); } catch (e) {}
337
476
  }
477
+
338
478
  const wf = path.join(INSTALL_DIR, '.wallet');
339
479
  if (fs.existsSync(wf)) log(`Wallet: ${fs.readFileSync(wf, 'utf8').trim()}`);
340
480
 
481
+ // File integrity
482
+ for (const filename of ['miner.py', 'fingerprint_checks.py']) {
483
+ const fp = path.join(INSTALL_DIR, filename);
484
+ if (fs.existsSync(fp)) {
485
+ log(`${filename} SHA256: ${sha256File(fp).slice(0, 16)}...`);
486
+ }
487
+ }
488
+
341
489
  https.get(`${NODE_URL}/health`, { rejectUnauthorized: false }, (res) => {
342
490
  let d = '';
343
491
  res.on('data', c => d += c);
@@ -352,7 +500,14 @@ function cmdStatus() {
352
500
 
353
501
  function cmdLogs() {
354
502
  if (os.platform() === 'linux') {
355
- spawn('journalctl', ['--user', '-u', 'clawrtc-miner', '-f', '--no-pager', '-n', '50'], { stdio: 'inherit' });
503
+ const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
504
+ if (fs.existsSync(sf)) {
505
+ spawn('journalctl', ['--user', '-u', 'clawrtc-miner', '-f', '--no-pager', '-n', '50'], { stdio: 'inherit' });
506
+ } else {
507
+ const lf = path.join(INSTALL_DIR, 'miner.log');
508
+ if (fs.existsSync(lf)) spawn('tail', ['-f', lf], { stdio: 'inherit' });
509
+ else warn('No logs found. Start the miner first: clawrtc start');
510
+ }
356
511
  } else {
357
512
  const lf = path.join(INSTALL_DIR, 'miner.log');
358
513
  if (fs.existsSync(lf)) spawn('tail', ['-f', lf], { stdio: 'inherit' });
@@ -367,43 +522,64 @@ function cmdUninstall() {
367
522
  try { execSync('systemctl --user disable clawrtc-miner', { stdio: 'pipe' }); } catch (e) {}
368
523
  const sf = path.join(os.homedir(), '.config', 'systemd', 'user', 'clawrtc-miner.service');
369
524
  try { fs.unlinkSync(sf); } catch (e) {}
525
+ try { execSync('systemctl --user daemon-reload', { stdio: 'pipe' }); } catch (e) {}
370
526
  } else if (os.platform() === 'darwin') {
371
527
  const pf = path.join(os.homedir(), 'Library', 'LaunchAgents', 'com.clawrtc.miner.plist');
372
528
  try { fs.unlinkSync(pf); } catch (e) {}
373
529
  }
374
530
  try { fs.rmSync(INSTALL_DIR, { recursive: true, force: true }); } catch (e) {}
375
- ok('ClawRTC miner uninstalled');
531
+ ok('ClawRTC miner fully uninstalled — no files remain');
376
532
  }
377
533
 
378
- // Main
379
- const args = process.argv.slice(2);
380
- const cmd = args[0] || 'install';
381
- const walletIdx = args.indexOf('--wallet');
382
- const walletArg = walletIdx >= 0 ? args[walletIdx + 1] : null;
383
-
384
- switch (cmd) {
385
- case 'install': cmdInstall(walletArg); break;
386
- case 'start': cmdStart(); break;
387
- case 'stop': cmdStop(); break;
388
- case 'status': cmdStatus(); break;
389
- case 'logs': cmdLogs(); break;
390
- case 'uninstall': cmdUninstall(); break;
391
- case '--help': case '-h':
392
- console.log(`
393
- ClawRTC — Mine RTC tokens with your AI agent on real hardware
534
+ function showHelp() {
535
+ console.log(`
536
+ ClawRTC v${VERSION} Mine RTC tokens with your AI agent on real hardware
394
537
 
395
538
  Commands:
396
539
  clawrtc install [--wallet NAME] Install miner and configure wallet
397
- clawrtc start Start mining in background
540
+ clawrtc start Start mining (foreground)
541
+ clawrtc start --service Start + create background service
398
542
  clawrtc stop Stop mining
399
- clawrtc status Check miner + network status
543
+ clawrtc status Check miner + network status + file hashes
400
544
  clawrtc logs View miner output
401
- clawrtc uninstall Remove everything
545
+ clawrtc uninstall Remove everything cleanly
546
+
547
+ Security & Verification:
548
+ clawrtc install --dry-run Preview without downloading or installing
549
+ clawrtc install --show-urls Show exact URLs that will be fetched
550
+ clawrtc install --verify Show SHA256 hashes of downloaded files
551
+ clawrtc install -y Skip consent prompt (for CI/automation)
402
552
 
403
553
  Modern hardware gets 1x multiplier. VMs are detected and penalized.
404
554
  Vintage hardware (PowerPC G4/G5) gets up to 2.5x bonus.
555
+
556
+ Source: https://github.com/Scottcjn/Rustchain
405
557
  `);
406
- break;
558
+ }
559
+
560
+ // Parse flags
561
+ const args = process.argv.slice(2);
562
+ const cmd = args[0];
563
+ const flags = {
564
+ wallet: null,
565
+ dryRun: args.includes('--dry-run'),
566
+ showUrls: args.includes('--show-urls'),
567
+ verify: args.includes('--verify'),
568
+ service: args.includes('--service'),
569
+ yes: args.includes('-y') || args.includes('--yes'),
570
+ };
571
+ const walletIdx = args.indexOf('--wallet');
572
+ if (walletIdx >= 0) flags.wallet = args[walletIdx + 1];
573
+
574
+ switch (cmd) {
575
+ case 'install': cmdInstall(flags); break;
576
+ case 'start': cmdStart(flags); break;
577
+ case 'stop': cmdStop(); break;
578
+ case 'status': cmdStatus(); break;
579
+ case 'logs': cmdLogs(); break;
580
+ case 'uninstall': cmdUninstall(); break;
581
+ case '--help': case '-h': showHelp(); break;
582
+ case undefined: showHelp(); break;
407
583
  default:
408
584
  console.error(`Unknown command: ${cmd}. Use --help for usage.`);
409
585
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawrtc",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "ClawRTC — Let your AI agent mine RTC tokens on any modern hardware. 1x multiplier, built-in wallet, VM-penalized.",
5
5
  "bin": {
6
6
  "clawrtc": "./bin/clawrtc.js"