yakmesh 2.8.2 → 3.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.
Files changed (232) hide show
  1. package/CHANGELOG.md +637 -0
  2. package/CONTRIBUTING.md +42 -0
  3. package/Caddyfile +77 -0
  4. package/README.md +119 -29
  5. package/adapters/adapter-mlv-bible/README.md +124 -0
  6. package/adapters/adapter-mlv-bible/index.js +400 -0
  7. package/adapters/chat-mod-adapter.js +532 -0
  8. package/adapters/content-adapter.js +273 -0
  9. package/content/api.js +50 -41
  10. package/content/index.js +2 -2
  11. package/content/store.js +355 -173
  12. package/dashboard/index.html +19 -3
  13. package/database/replication.js +117 -37
  14. package/docs/CRYPTO-AGILITY.md +204 -0
  15. package/docs/MTLS-RESEARCH.md +367 -0
  16. package/docs/NAMCHE-SPEC.md +681 -0
  17. package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
  18. package/docs/PRECISION-DISCLOSURE.md +96 -0
  19. package/docs/README.md +76 -0
  20. package/docs/ROADMAP-2.4.0.md +447 -0
  21. package/docs/ROADMAP-2.5.0.md +244 -0
  22. package/docs/SECURITY-AUDIT-REPORT.md +306 -0
  23. package/docs/SST-INTEGRATION.md +712 -0
  24. package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
  25. package/docs/TERNARY-AUDIT-REPORT.md +247 -0
  26. package/docs/TME-FAQ.md +221 -0
  27. package/docs/WHITEPAPER.md +623 -0
  28. package/docs/adapters.html +1001 -0
  29. package/docs/advanced-systems.html +1045 -0
  30. package/docs/annex.html +1046 -0
  31. package/docs/api.html +970 -0
  32. package/docs/business/response-templates.md +160 -0
  33. package/docs/c2c.html +1225 -0
  34. package/docs/cli.html +1332 -0
  35. package/docs/configuration.html +1248 -0
  36. package/docs/darshan.html +1085 -0
  37. package/docs/dharma.html +966 -0
  38. package/docs/docs-bundle.html +1075 -0
  39. package/docs/docs.css +3120 -0
  40. package/docs/docs.js +556 -0
  41. package/docs/doko.html +969 -0
  42. package/docs/geo-proof.html +858 -0
  43. package/docs/getting-started.html +840 -0
  44. package/docs/gumba-tutorial.html +1144 -0
  45. package/docs/gumba.html +1098 -0
  46. package/docs/index.html +914 -0
  47. package/docs/jhilke.html +1312 -0
  48. package/docs/karma.html +1100 -0
  49. package/docs/katha.html +1037 -0
  50. package/docs/lama.html +978 -0
  51. package/docs/mandala.html +1067 -0
  52. package/docs/mani.html +964 -0
  53. package/docs/mantra.html +967 -0
  54. package/docs/mesh.html +1409 -0
  55. package/docs/nakpak.html +869 -0
  56. package/docs/namche.html +928 -0
  57. package/docs/nav-order.json +53 -0
  58. package/docs/prahari.html +1043 -0
  59. package/docs/prism-bash.min.js +1 -0
  60. package/docs/prism-javascript.min.js +1 -0
  61. package/docs/prism-json.min.js +1 -0
  62. package/docs/prism-tomorrow.min.css +1 -0
  63. package/docs/prism.min.js +1 -0
  64. package/docs/privacy.html +699 -0
  65. package/docs/quick-reference.html +1181 -0
  66. package/docs/sakshi.html +1402 -0
  67. package/docs/sandboxing.md +386 -0
  68. package/docs/seva.html +911 -0
  69. package/docs/sherpa.html +871 -0
  70. package/docs/studio.html +860 -0
  71. package/docs/stupa.html +995 -0
  72. package/docs/tailwind.min.css +2 -0
  73. package/docs/tattva.html +1332 -0
  74. package/docs/terms.html +686 -0
  75. package/docs/time-server-deployment.md +166 -0
  76. package/docs/time-sources.html +1392 -0
  77. package/docs/tivra.html +1127 -0
  78. package/docs/trademark-policy.html +686 -0
  79. package/docs/tribhuj.html +1183 -0
  80. package/docs/trust-security.html +1029 -0
  81. package/docs/tutorials/backup-recovery.html +654 -0
  82. package/docs/tutorials/dashboard.html +604 -0
  83. package/docs/tutorials/domain-setup.html +605 -0
  84. package/docs/tutorials/host-website.html +456 -0
  85. package/docs/tutorials/mesh-network.html +505 -0
  86. package/docs/tutorials/mobile-access.html +445 -0
  87. package/docs/tutorials/privacy.html +467 -0
  88. package/docs/tutorials/raspberry-pi.html +600 -0
  89. package/docs/tutorials/security-basics.html +539 -0
  90. package/docs/tutorials/share-files.html +431 -0
  91. package/docs/tutorials/troubleshooting.html +637 -0
  92. package/docs/tutorials/trust-karma.html +419 -0
  93. package/docs/tutorials/yak-protocol.html +456 -0
  94. package/docs/tutorials.html +1034 -0
  95. package/docs/vani.html +1270 -0
  96. package/docs/webserver.html +809 -0
  97. package/docs/yak-protocol.html +940 -0
  98. package/docs/yak-timeserver-design.md +475 -0
  99. package/docs/yakapp.html +1015 -0
  100. package/docs/ypc27.html +1069 -0
  101. package/docs/yurt.html +1344 -0
  102. package/embedded-docs/bundle.js +334 -74
  103. package/gossip/protocol.js +247 -27
  104. package/identity/key-resolver.js +262 -0
  105. package/identity/machine-seed.js +632 -0
  106. package/identity/node-key.js +669 -368
  107. package/identity/tribhuj-ratchet.js +506 -0
  108. package/knowledge-base.js +37 -8
  109. package/launcher/yakmesh.bat +62 -0
  110. package/launcher/yakmesh.sh +70 -0
  111. package/mesh/annex.js +462 -108
  112. package/mesh/beacon-broadcast.js +113 -1
  113. package/mesh/darshan.js +1718 -0
  114. package/mesh/gumba.js +1567 -0
  115. package/mesh/jhilke.js +651 -0
  116. package/mesh/katha.js +1012 -0
  117. package/mesh/nakpak-routing.js +8 -5
  118. package/mesh/network.js +724 -34
  119. package/mesh/pulse-sync.js +4 -1
  120. package/mesh/rate-limiter.js +127 -15
  121. package/mesh/seva.js +526 -0
  122. package/mesh/sherpa-discovery.js +89 -8
  123. package/mesh/sybil-defense.js +19 -5
  124. package/mesh/temporal-encoder.js +4 -3
  125. package/mesh/vani.js +1364 -0
  126. package/mesh/yurt.js +1340 -0
  127. package/models/entropy-sentinel.onnx +0 -0
  128. package/models/karma-trust.onnx +0 -0
  129. package/models/manifest.json +43 -0
  130. package/models/sakshi-anomaly.onnx +0 -0
  131. package/oracle/code-proof-protocol.js +7 -6
  132. package/oracle/codebase-lock.js +257 -28
  133. package/oracle/index.js +74 -15
  134. package/oracle/ma902-snmp.js +678 -0
  135. package/oracle/module-sealer.js +5 -3
  136. package/oracle/network-identity.js +16 -0
  137. package/oracle/packet-checksum.js +201 -0
  138. package/oracle/sst.js +579 -0
  139. package/oracle/ternary-144t.js +714 -0
  140. package/oracle/ternary-ml.js +481 -0
  141. package/oracle/time-api.js +239 -0
  142. package/oracle/time-source.js +137 -47
  143. package/oracle/validation-oracle-hardened.js +1111 -1071
  144. package/oracle/validation-oracle.js +4 -2
  145. package/oracle/ypc27.js +211 -0
  146. package/package.json +20 -3
  147. package/protocol/yak-handler.js +35 -9
  148. package/protocol/yak-protocol.js +28 -13
  149. package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
  150. package/reference/cpp/yakmesh_ypc27.cpp +179 -0
  151. package/sbom.json +87 -0
  152. package/scripts/security-audit.mjs +264 -0
  153. package/scripts/update-docs-nav.js +194 -0
  154. package/scripts/update-docs-sidebar.cjs +164 -0
  155. package/security/crypto-config.js +4 -3
  156. package/security/dharma-moderation.js +517 -0
  157. package/security/doko-identity.js +193 -143
  158. package/security/domain-consensus.js +86 -85
  159. package/security/fs-hardening.js +620 -0
  160. package/security/hardware-attestation.js +5 -3
  161. package/security/hybrid-trust.js +227 -87
  162. package/security/karma-rate-limiter.js +692 -0
  163. package/security/khata-protocol.js +22 -21
  164. package/security/khata-trust-integration.js +277 -150
  165. package/security/memory-safety.js +635 -0
  166. package/security/mesh-auth.js +11 -10
  167. package/security/mesh-revocation.js +373 -5
  168. package/security/namche-gateway.js +298 -69
  169. package/security/sakshi.js +460 -3
  170. package/security/sangha.js +770 -0
  171. package/security/secure-config.js +473 -0
  172. package/security/silicon-parity.js +13 -10
  173. package/security/steadywatch.js +1142 -0
  174. package/security/strike-system.js +32 -3
  175. package/security/temporal-signing.js +488 -0
  176. package/security/trit-commitment.js +464 -0
  177. package/server/crypto/annex.js +247 -0
  178. package/server/darshan-api.js +343 -0
  179. package/server/index.js +3259 -362
  180. package/server/komm-api.js +668 -0
  181. package/utils/accel.js +2273 -0
  182. package/utils/ternary-id.js +79 -0
  183. package/utils/verify-worker.js +57 -0
  184. package/webserver/index.js +95 -5
  185. package/assets/yakmesh-logo.png +0 -0
  186. package/assets/yakmesh-logo.svg +0 -80
  187. package/assets/yakmesh-logo2.png +0 -0
  188. package/assets/yakmesh-logo2sm.png +0 -0
  189. package/assets/ymsm.png +0 -0
  190. package/website/assets/silhouettes/adapters.svg +0 -107
  191. package/website/assets/silhouettes/api-endpoints.svg +0 -115
  192. package/website/assets/silhouettes/atomic-clock.svg +0 -83
  193. package/website/assets/silhouettes/base-camp.svg +0 -81
  194. package/website/assets/silhouettes/bridge.svg +0 -69
  195. package/website/assets/silhouettes/docs-bundle.svg +0 -113
  196. package/website/assets/silhouettes/doko-basket.svg +0 -70
  197. package/website/assets/silhouettes/fortress.svg +0 -93
  198. package/website/assets/silhouettes/gateway.svg +0 -54
  199. package/website/assets/silhouettes/gears.svg +0 -93
  200. package/website/assets/silhouettes/globe-satellite.svg +0 -67
  201. package/website/assets/silhouettes/karma-wheel.svg +0 -137
  202. package/website/assets/silhouettes/lama-council.svg +0 -141
  203. package/website/assets/silhouettes/mandala-network.svg +0 -169
  204. package/website/assets/silhouettes/mani-stones.svg +0 -149
  205. package/website/assets/silhouettes/mantra-wheel.svg +0 -116
  206. package/website/assets/silhouettes/mesh-nodes.svg +0 -113
  207. package/website/assets/silhouettes/nakpak.svg +0 -56
  208. package/website/assets/silhouettes/peak-lightning.svg +0 -73
  209. package/website/assets/silhouettes/sherpa.svg +0 -69
  210. package/website/assets/silhouettes/stupa-tower.svg +0 -119
  211. package/website/assets/silhouettes/tattva-eye.svg +0 -78
  212. package/website/assets/silhouettes/terminal.svg +0 -74
  213. package/website/assets/silhouettes/webserver.svg +0 -145
  214. package/website/assets/silhouettes/yak.svg +0 -78
  215. package/website/assets/yakmesh-logo.png +0 -0
  216. package/website/assets/yakmesh-logo.webp +0 -0
  217. package/website/assets/yakmesh-logo128x140.webp +0 -0
  218. package/website/assets/yakmesh-logo2.png +0 -0
  219. package/website/assets/yakmesh-logo2.svg +0 -51
  220. package/website/assets/yakmesh-logo40x44.webp +0 -0
  221. package/website/assets/yakmesh.gif +0 -0
  222. package/website/assets/yakmesh.ico +0 -0
  223. package/website/assets/yakmesh.jpg +0 -0
  224. package/website/assets/yakmesh.pdf +0 -0
  225. package/website/assets/yakmesh.png +0 -0
  226. package/website/assets/yakmesh.svg +0 -70
  227. package/website/assets/yakmesh128.webp +0 -0
  228. package/website/assets/yakmesh32.png +0 -0
  229. package/website/assets/yakmesh32.svg +0 -65
  230. package/website/assets/yakmesh32o.ico +0 -2
  231. package/website/assets/yakmesh32o.svg +0 -65
  232. package/website/assets/yakmesh32o.svgz +0 -0
@@ -0,0 +1,620 @@
1
+ /**
2
+ * Yakmesh File System Hardening — SANGHA-FS Extension
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * PHILOSOPHY: FILES AS COLLECTIVE PARTICIPANTS
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ *
8
+ * Traditional FS security: chmod 400, read-only mounts, ACLs.
9
+ * SANGHA-FS: Critical files JOIN the collective as attestation participants.
10
+ *
11
+ * Each protected file has a GUARDIAN that:
12
+ * - Periodically hashes the file and attests its integrity to SANGHA
13
+ * - Participates in collective circulation (antibodies visit file guardians)
14
+ * - Triggers collective response if tampering is detected
15
+ *
16
+ * This means:
17
+ * - Tampering is detected within one circulation cycle (≤5s)
18
+ * - All components are alerted simultaneously
19
+ * - The mesh can quarantine a compromised node
20
+ *
21
+ * ═══════════════════════════════════════════════════════════════════════════════
22
+ * PROTECTED ASSETS
23
+ * ═══════════════════════════════════════════════════════════════════════════════
24
+ *
25
+ * 1. IDENTITY FILES
26
+ * - machine-seed.json — Hardware-encrypted seed (CRITICAL)
27
+ * - node-key.json — Public identity (HIGH)
28
+ *
29
+ * 2. CONFIGURATION
30
+ * - yakmesh.config.js — Runtime config (oracle-verified)
31
+ *
32
+ * 3. DATABASE
33
+ * - yakmesh.db — SQLite database (integrity header)
34
+ *
35
+ * @module security/fs-hardening
36
+ * @version 1.0.0
37
+ */
38
+
39
+ import { createLogger } from '../utils/logger.js';
40
+ import { sha3_256 } from '../utils/accel.js';
41
+ import { bytesToHex } from '@noble/hashes/utils.js';
42
+ import { readFileSync, chmodSync, statSync, existsSync, watch } from 'fs';
43
+ import { join, resolve } from 'path';
44
+ import { platform } from 'os';
45
+ import { EventEmitter } from 'events';
46
+
47
+ const log = createLogger('security:fs-hardening');
48
+
49
+ // =============================================================================
50
+ // CONSTANTS
51
+ // =============================================================================
52
+
53
+ /** Protection levels */
54
+ export const PROTECTION_LEVEL = Object.freeze({
55
+ CRITICAL: 'CRITICAL', // Identity seed, private keys — IMMUTABLE after startup
56
+ HIGH: 'HIGH', // Public identity, config — LOCKED after startup
57
+ NORMAL: 'NORMAL', // Database, logs — MONITORED for integrity
58
+ });
59
+
60
+ /** File permission modes (POSIX) */
61
+ const POSIX_MODES = Object.freeze({
62
+ CRITICAL: 0o400, // r-------- (owner read only)
63
+ HIGH: 0o600, // rw------- (owner read/write)
64
+ NORMAL: 0o644, // rw-r--r-- (owner rw, others read)
65
+ });
66
+
67
+ /** Default files to protect */
68
+ const DEFAULT_PROTECTED_FILES = [
69
+ { path: 'data/machine-seed.json', level: PROTECTION_LEVEL.CRITICAL },
70
+ { path: 'data/node-key.json', level: PROTECTION_LEVEL.HIGH },
71
+ { path: 'data/yakmesh.db', level: PROTECTION_LEVEL.NORMAL },
72
+ ];
73
+
74
+ // =============================================================================
75
+ // FILE GUARDIAN
76
+ // =============================================================================
77
+
78
+ /**
79
+ * FileGuardian — Protects a single file and attests to SANGHA
80
+ *
81
+ * Each guardian:
82
+ * - Takes an initial hash at startup
83
+ * - Periodically re-hashes and compares
84
+ * - Participates in SANGHA attestation cycles
85
+ * - Triggers collective alert on tampering
86
+ */
87
+ export class FileGuardian extends EventEmitter {
88
+ #filePath;
89
+ #level;
90
+ #baselineHash;
91
+ #baselineMtime;
92
+ #baselineSize;
93
+ #locked;
94
+ #watcher;
95
+ #checkInterval;
96
+ #lastCheck;
97
+
98
+ /**
99
+ * @param {string} filePath - Absolute path to the file
100
+ * @param {string} level - Protection level (CRITICAL/HIGH/NORMAL)
101
+ */
102
+ constructor(filePath, level = PROTECTION_LEVEL.NORMAL) {
103
+ super();
104
+ this.#filePath = resolve(filePath);
105
+ this.#level = level;
106
+ this.#baselineHash = null;
107
+ this.#baselineMtime = null;
108
+ this.#baselineSize = null;
109
+ this.#locked = false;
110
+ this.#watcher = null;
111
+ this.#checkInterval = null;
112
+ this.#lastCheck = 0;
113
+
114
+ Object.seal(this);
115
+ }
116
+
117
+ /** Get file path */
118
+ get path() { return this.#filePath; }
119
+
120
+ /** Get protection level */
121
+ get level() { return this.#level; }
122
+
123
+ /** Get baseline hash */
124
+ get baselineHash() { return this.#baselineHash; }
125
+
126
+ /** Is file locked (immutable)? */
127
+ get isLocked() { return this.#locked; }
128
+
129
+ /**
130
+ * Initialize guardian — take baseline snapshot
131
+ * @returns {Promise<void>}
132
+ */
133
+ async init() {
134
+ if (!existsSync(this.#filePath)) {
135
+ log.warn('Protected file does not exist yet', { path: this.#filePath });
136
+ return;
137
+ }
138
+
139
+ // Take baseline snapshot
140
+ await this.#takeBaseline();
141
+
142
+ // Apply restrictive permissions on POSIX systems
143
+ this.#applyPermissions();
144
+
145
+ // Set up file watcher for real-time detection
146
+ this.#startWatcher();
147
+
148
+ log.info('FileGuardian initialized', {
149
+ path: this.#filePath,
150
+ level: this.#level,
151
+ hash: this.#baselineHash?.slice(0, 16) + '...',
152
+ size: this.#baselineSize,
153
+ });
154
+ }
155
+
156
+ /**
157
+ * Take baseline snapshot of file
158
+ */
159
+ async #takeBaseline() {
160
+ const content = readFileSync(this.#filePath);
161
+ const hash = sha3_256(content);
162
+ const stat = statSync(this.#filePath);
163
+
164
+ this.#baselineHash = bytesToHex(hash);
165
+ this.#baselineMtime = stat.mtimeMs;
166
+ this.#baselineSize = stat.size;
167
+ this.#lastCheck = Date.now();
168
+ }
169
+
170
+ /**
171
+ * Apply restrictive file permissions (POSIX only)
172
+ */
173
+ #applyPermissions() {
174
+ if (platform() === 'win32') {
175
+ // Windows uses NTFS ACLs — handled differently
176
+ // TODO: Use icacls for Windows permission hardening
177
+ return;
178
+ }
179
+
180
+ const mode = POSIX_MODES[this.#level] || POSIX_MODES.NORMAL;
181
+ try {
182
+ chmodSync(this.#filePath, mode);
183
+ log.debug('Applied file permissions', {
184
+ path: this.#filePath,
185
+ mode: mode.toString(8),
186
+ });
187
+ } catch (e) {
188
+ log.warn('Failed to set file permissions', {
189
+ path: this.#filePath,
190
+ error: e.message,
191
+ });
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Start file system watcher for real-time tampering detection
197
+ */
198
+ #startWatcher() {
199
+ // NORMAL-level files (database) change constantly — only watch for deletion
200
+ // CRITICAL/HIGH files get full change monitoring
201
+ try {
202
+ this.#watcher = watch(this.#filePath, (eventType) => {
203
+ if (eventType === 'change' && this.#level !== PROTECTION_LEVEL.NORMAL) {
204
+ // File was modified — verify integrity (CRITICAL/HIGH only)
205
+ this.verify().catch(e => {
206
+ log.error('Watch-triggered verification failed', { error: e.message });
207
+ });
208
+ } else if (eventType === 'rename') {
209
+ // File was renamed/deleted — always report regardless of level
210
+ this.emit('tamper', {
211
+ type: 'FILE_REMOVED',
212
+ path: this.#filePath,
213
+ level: this.#level,
214
+ timestamp: Date.now(),
215
+ });
216
+ }
217
+ });
218
+ } catch (e) {
219
+ log.warn('Could not set up file watcher', {
220
+ path: this.#filePath,
221
+ error: e.message,
222
+ });
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Lock the file (prevent further modifications)
228
+ * For CRITICAL files, this makes them effectively read-only after startup.
229
+ */
230
+ lock() {
231
+ if (this.#locked) return;
232
+
233
+ this.#locked = true;
234
+
235
+ // Re-apply most restrictive permissions
236
+ if (platform() !== 'win32') {
237
+ try {
238
+ chmodSync(this.#filePath, 0o400); // Read-only
239
+ } catch (e) {
240
+ log.warn('Could not lock file', { path: this.#filePath, error: e.message });
241
+ }
242
+ }
243
+
244
+ log.info('File locked (read-only)', { path: this.#filePath });
245
+ }
246
+
247
+ /**
248
+ * Re-take the baseline snapshot.
249
+ * Used after startup grace period so that HIGH-level files capture
250
+ * their post-initialization state (e.g. node-key.json after derivation).
251
+ */
252
+ async rebaseline() {
253
+ if (!existsSync(this.#filePath)) return;
254
+ await this.#takeBaseline();
255
+ log.info('FileGuardian re-baselined', {
256
+ path: this.#filePath,
257
+ level: this.#level,
258
+ hash: this.#baselineHash?.slice(0, 16) + '...',
259
+ size: this.#baselineSize,
260
+ });
261
+ }
262
+
263
+ /**
264
+ * Verify file integrity against baseline
265
+ * @returns {Promise<{ valid: boolean, error?: string }>}
266
+ */
267
+ async verify() {
268
+ if (!this.#baselineHash) {
269
+ return { valid: true, error: 'No baseline (file not yet created)' };
270
+ }
271
+
272
+ if (!existsSync(this.#filePath)) {
273
+ const tamperEvent = {
274
+ type: 'FILE_DELETED',
275
+ path: this.#filePath,
276
+ level: this.#level,
277
+ timestamp: Date.now(),
278
+ };
279
+ this.emit('tamper', tamperEvent);
280
+ return { valid: false, error: 'File deleted' };
281
+ }
282
+
283
+ // NORMAL-level files (e.g. yakmesh.db) are mutable by design.
284
+ // Only monitor for existence/deletion — NOT content hashing.
285
+ // The database changes constantly during normal operation.
286
+ if (this.#level === PROTECTION_LEVEL.NORMAL) {
287
+ this.#lastCheck = Date.now();
288
+ return { valid: true };
289
+ }
290
+
291
+ try {
292
+ const content = readFileSync(this.#filePath);
293
+ const hash = bytesToHex(sha3_256(content));
294
+ const stat = statSync(this.#filePath);
295
+
296
+ this.#lastCheck = Date.now();
297
+
298
+ // Check hash (CRITICAL and HIGH only)
299
+ if (hash !== this.#baselineHash) {
300
+ const tamperEvent = {
301
+ type: 'HASH_MISMATCH',
302
+ path: this.#filePath,
303
+ level: this.#level,
304
+ expected: this.#baselineHash.slice(0, 16) + '...',
305
+ actual: hash.slice(0, 16) + '...',
306
+ timestamp: Date.now(),
307
+ };
308
+ this.emit('tamper', tamperEvent);
309
+ return { valid: false, error: 'Hash mismatch' };
310
+ }
311
+
312
+ // Check size (belt + suspenders)
313
+ if (stat.size !== this.#baselineSize) {
314
+ const tamperEvent = {
315
+ type: 'SIZE_MISMATCH',
316
+ path: this.#filePath,
317
+ level: this.#level,
318
+ expected: this.#baselineSize,
319
+ actual: stat.size,
320
+ timestamp: Date.now(),
321
+ };
322
+ this.emit('tamper', tamperEvent);
323
+ return { valid: false, error: 'Size mismatch' };
324
+ }
325
+
326
+ return { valid: true };
327
+ } catch (e) {
328
+ return { valid: false, error: e.message };
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Get state for SANGHA attestation
334
+ * @returns {Promise<object>}
335
+ */
336
+ async getState() {
337
+ const verification = await this.verify();
338
+ return {
339
+ path: this.#filePath,
340
+ level: this.#level,
341
+ hash: this.#baselineHash?.slice(0, 16),
342
+ locked: this.#locked,
343
+ valid: verification.valid,
344
+ lastCheck: this.#lastCheck,
345
+ };
346
+ }
347
+
348
+ /**
349
+ * Stop the guardian
350
+ */
351
+ stop() {
352
+ if (this.#watcher) {
353
+ this.#watcher.close();
354
+ this.#watcher = null;
355
+ }
356
+ if (this.#checkInterval) {
357
+ clearInterval(this.#checkInterval);
358
+ this.#checkInterval = null;
359
+ }
360
+ }
361
+ }
362
+
363
+ // =============================================================================
364
+ // FS HARDENING MANAGER
365
+ // =============================================================================
366
+
367
+ /**
368
+ * FSHardening — Manages all file guardians and SANGHA integration
369
+ */
370
+ export class FSHardening extends EventEmitter {
371
+ #guardians;
372
+ #dataDir;
373
+ #sangha;
374
+ #started;
375
+ #verifyInterval;
376
+
377
+ /**
378
+ * @param {string} dataDir - Base data directory
379
+ */
380
+ constructor(dataDir = './data') {
381
+ super();
382
+ this.#guardians = new Map();
383
+ this.#dataDir = resolve(dataDir);
384
+ this.#sangha = null;
385
+ this.#started = false;
386
+ this.#verifyInterval = null;
387
+
388
+ Object.seal(this);
389
+ }
390
+
391
+ /**
392
+ * Register a file for protection
393
+ * @param {string} relativePath - Path relative to data directory
394
+ * @param {string} level - Protection level
395
+ */
396
+ async protect(relativePath, level = PROTECTION_LEVEL.NORMAL) {
397
+ const fullPath = join(this.#dataDir, '..', relativePath);
398
+
399
+ if (this.#guardians.has(fullPath)) {
400
+ log.warn('File already protected', { path: fullPath });
401
+ return;
402
+ }
403
+
404
+ const guardian = new FileGuardian(fullPath, level);
405
+
406
+ // Forward tamper events
407
+ guardian.on('tamper', (event) => {
408
+ this.#handleTamper(event);
409
+ });
410
+
411
+ await guardian.init();
412
+ this.#guardians.set(fullPath, guardian);
413
+
414
+ // Lock CRITICAL files immediately
415
+ if (level === PROTECTION_LEVEL.CRITICAL) {
416
+ guardian.lock();
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Handle tampering event
422
+ */
423
+ #handleTamper(event) {
424
+ log.error('🚨 FILE TAMPERING DETECTED', event);
425
+
426
+ // Emit for external handlers
427
+ this.emit('tamper', event);
428
+
429
+ // If SANGHA is connected, trigger collective response
430
+ if (this.#sangha) {
431
+ try {
432
+ // Record as anomaly in current antibody circulation
433
+ const anomaly = {
434
+ componentId: 'fs',
435
+ type: event.type,
436
+ details: event,
437
+ timestamp: Date.now(),
438
+ };
439
+ this.emit('anomaly', anomaly);
440
+ } catch (e) {
441
+ log.error('Failed to report to SANGHA', { error: e.message });
442
+ }
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Initialize with default protected files
448
+ */
449
+ async init() {
450
+ for (const file of DEFAULT_PROTECTED_FILES) {
451
+ try {
452
+ await this.protect(file.path, file.level);
453
+ } catch (e) {
454
+ log.warn('Could not protect file', { path: file.path, error: e.message });
455
+ }
456
+ }
457
+
458
+ log.info('FS Hardening initialized', {
459
+ guardians: this.#guardians.size,
460
+ });
461
+ }
462
+
463
+ /**
464
+ * Bind to SANGHA collective
465
+ * @param {Sangha} sangha - The SANGHA instance
466
+ */
467
+ bindSangha(sangha) {
468
+ this.#sangha = sangha;
469
+ log.info('FS Hardening bound to SANGHA collective');
470
+ }
471
+
472
+ /**
473
+ * Start periodic verification
474
+ * @param {number} intervalMs - Verification interval (default: 30s)
475
+ */
476
+ start(intervalMs = 30000) {
477
+ if (this.#started) return;
478
+
479
+ this.#started = true;
480
+
481
+ // Periodic verification
482
+ this.#verifyInterval = setInterval(async () => {
483
+ await this.verifyAll();
484
+ }, intervalMs);
485
+
486
+ // After startup grace period (5s):
487
+ // - Re-baseline HIGH files (they may have been written during init)
488
+ // - Then lock them
489
+ setTimeout(async () => {
490
+ for (const [path, guardian] of this.#guardians) {
491
+ if (guardian.level === PROTECTION_LEVEL.HIGH && !guardian.isLocked) {
492
+ await guardian.rebaseline();
493
+ guardian.lock();
494
+ }
495
+ }
496
+ log.info('Startup grace period ended — HIGH files re-baselined and locked');
497
+ }, 5000);
498
+
499
+ log.info('FS Hardening started', { interval: intervalMs });
500
+ }
501
+
502
+ /**
503
+ * Verify all protected files
504
+ * @returns {Promise<{ valid: boolean, errors: object[] }>}
505
+ */
506
+ async verifyAll() {
507
+ const errors = [];
508
+
509
+ for (const [path, guardian] of this.#guardians) {
510
+ const result = await guardian.verify();
511
+ if (!result.valid) {
512
+ errors.push({
513
+ path,
514
+ level: guardian.level,
515
+ error: result.error,
516
+ });
517
+ }
518
+ }
519
+
520
+ if (errors.length > 0) {
521
+ log.error('File verification failures', { count: errors.length, errors });
522
+ }
523
+
524
+ return {
525
+ valid: errors.length === 0,
526
+ errors,
527
+ };
528
+ }
529
+
530
+ /**
531
+ * Get state for SANGHA attestation (FS component)
532
+ * @returns {Promise<object>}
533
+ */
534
+ async getState() {
535
+ const guardianStates = [];
536
+
537
+ for (const [path, guardian] of this.#guardians) {
538
+ guardianStates.push(await guardian.getState());
539
+ }
540
+
541
+ return {
542
+ component: 'fs',
543
+ guardianCount: this.#guardians.size,
544
+ guardians: guardianStates,
545
+ allValid: guardianStates.every(g => g.valid),
546
+ };
547
+ }
548
+
549
+ /**
550
+ * Get status summary
551
+ */
552
+ getStatus() {
553
+ const files = [];
554
+
555
+ for (const [path, guardian] of this.#guardians) {
556
+ files.push({
557
+ path: guardian.path,
558
+ level: guardian.level,
559
+ locked: guardian.isLocked,
560
+ hash: guardian.baselineHash?.slice(0, 16) + '...',
561
+ });
562
+ }
563
+
564
+ return {
565
+ started: this.#started,
566
+ sanghaConnected: !!this.#sangha,
567
+ files,
568
+ };
569
+ }
570
+
571
+ /**
572
+ * Stop FS hardening
573
+ */
574
+ stop() {
575
+ if (!this.#started) return;
576
+
577
+ this.#started = false;
578
+
579
+ if (this.#verifyInterval) {
580
+ clearInterval(this.#verifyInterval);
581
+ this.#verifyInterval = null;
582
+ }
583
+
584
+ for (const guardian of this.#guardians.values()) {
585
+ guardian.stop();
586
+ }
587
+
588
+ log.info('FS Hardening stopped');
589
+ }
590
+ }
591
+
592
+ // =============================================================================
593
+ // SINGLETON & EXPORTS
594
+ // =============================================================================
595
+
596
+ let _instance = null;
597
+
598
+ /**
599
+ * Get the FSHardening singleton
600
+ * @param {string} dataDir - Data directory (only used on first call)
601
+ * @returns {FSHardening}
602
+ */
603
+ export function getFSHardening(dataDir = './data') {
604
+ if (!_instance) {
605
+ _instance = new FSHardening(dataDir);
606
+ }
607
+ return _instance;
608
+ }
609
+
610
+ /**
611
+ * Quick protection helper
612
+ * @param {string} filePath - File path
613
+ * @param {string} level - Protection level
614
+ */
615
+ export async function protectFile(filePath, level = PROTECTION_LEVEL.NORMAL) {
616
+ const fs = getFSHardening();
617
+ await fs.protect(filePath, level);
618
+ }
619
+
620
+ export default FSHardening;
@@ -18,9 +18,11 @@
18
18
  */
19
19
 
20
20
  import { createCipheriv, randomBytes } from 'crypto';
21
- import { sha3_256 } from '@noble/hashes/sha3.js';
21
+ import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
22
22
  import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
23
23
  import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
24
+ // ACCEL: Hardware-accelerated crypto
25
+ import { sha3_256, mlDsa65Sign, mlDsa65Verify } from '../utils/accel.js';
24
26
  import { createLogger } from '../utils/logger.js';
25
27
  import os from 'os';
26
28
 
@@ -610,7 +612,7 @@ export class HardwareAttestation {
610
612
 
611
613
  // Sign the response
612
614
  const responseBytes = new TextEncoder().encode(JSON.stringify(responseData));
613
- const signature = ml_dsa65.sign(responseBytes, privateKey);
615
+ const signature = mlDsa65Sign(responseBytes, privateKey);
614
616
 
615
617
  return {
616
618
  ...responseData,
@@ -639,7 +641,7 @@ export class HardwareAttestation {
639
641
  const signature = hexToBytes(response.signature);
640
642
  const pubKeyBytes = typeof publicKey === 'string' ? hexToBytes(publicKey) : publicKey;
641
643
 
642
- if (!ml_dsa65.verify(signature, responseBytes, pubKeyBytes)) {
644
+ if (!mlDsa65Verify(signature, responseBytes, pubKeyBytes)) {
643
645
  return { valid: false, reason: 'INVALID_SIGNATURE' };
644
646
  }
645
647