soulprint-network 0.3.3 → 0.3.5

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.
@@ -0,0 +1,25 @@
1
+ {
2
+ "codeHash": "559c4b6036fc59ae758016efba130d9147d8a89d5f43aff68a0a00e788aec761",
3
+ "codeHashHex": "0x559c4b6036fc59ae758016efba130d9147d8a89d5f43aff68a0a00e788aec761",
4
+ "computedAt": "2026-02-24T21:00:59.352Z",
5
+ "fileCount": 17,
6
+ "files": [
7
+ "blockchain/blockchain-anchor.ts",
8
+ "blockchain/blockchain-client.ts",
9
+ "code-integrity.ts",
10
+ "consensus/attestation-consensus.ts",
11
+ "consensus/index.ts",
12
+ "consensus/nullifier-consensus.ts",
13
+ "consensus/state-sync.ts",
14
+ "credentials/email.ts",
15
+ "credentials/github.ts",
16
+ "credentials/index.ts",
17
+ "credentials/phone.ts",
18
+ "crypto/gossip-cipher.ts",
19
+ "crypto/peer-router.ts",
20
+ "index.ts",
21
+ "p2p.ts",
22
+ "server.ts",
23
+ "validator.ts"
24
+ ]
25
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * code-integrity.ts — Fix 2: Runtime code integrity verification
3
+ *
4
+ * Carga el hash computado en build-time y lo expone para:
5
+ * 1. Logging al arranque del nodo
6
+ * 2. GET /health (peers pueden verificar)
7
+ * 3. Comparación con hashes aprobados on-chain por governance
8
+ *
9
+ * GARANTÍA:
10
+ * - Si alguien modifica src/ y recompila → el hash cambia
11
+ * - Los peers pueden detectar que un nodo corre código diferente
12
+ * - El GovernanceModule puede registrar hashes aprobados on-chain
13
+ * - Si el hash no coincide con el aprobado → el nodo queda marcado como no confiable
14
+ *
15
+ * LIMITACIÓN (sin TEE):
16
+ * - Un atacante con root puede modificar dist/ directamente o falsificar el hash
17
+ * - La protección completa requiere TEE (Intel SGX / AMD SEV) — fase futura
18
+ */
19
+ export interface CodeIntegrityInfo {
20
+ codeHash: string;
21
+ codeHashHex: string;
22
+ computedAt: string;
23
+ fileCount: number;
24
+ available: boolean;
25
+ }
26
+ /**
27
+ * Carga el code-hash.json generado en build time.
28
+ */
29
+ export declare function getCodeIntegrity(): CodeIntegrityInfo;
30
+ /**
31
+ * Verifica que el code hash del nodo actual coincide con uno aprobado.
32
+ * @param approvedHashes Lista de hashes aprobados por governance
33
+ * @returns true si el hash actual está en la lista de aprobados
34
+ */
35
+ export declare function isCodeApproved(approvedHashes: (string | null | undefined)[]): boolean;
36
+ /**
37
+ * Computa un hash rápido del propio binario en tiempo real (fallback).
38
+ * Menos preciso que el hash del código fuente pero no requiere build step.
39
+ */
40
+ export declare function computeRuntimeHash(): string;
41
+ /**
42
+ * Log de integridad al arranque — llamar desde startServer().
43
+ */
44
+ export declare function logCodeIntegrity(): void;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * code-integrity.ts — Fix 2: Runtime code integrity verification
3
+ *
4
+ * Carga el hash computado en build-time y lo expone para:
5
+ * 1. Logging al arranque del nodo
6
+ * 2. GET /health (peers pueden verificar)
7
+ * 3. Comparación con hashes aprobados on-chain por governance
8
+ *
9
+ * GARANTÍA:
10
+ * - Si alguien modifica src/ y recompila → el hash cambia
11
+ * - Los peers pueden detectar que un nodo corre código diferente
12
+ * - El GovernanceModule puede registrar hashes aprobados on-chain
13
+ * - Si el hash no coincide con el aprobado → el nodo queda marcado como no confiable
14
+ *
15
+ * LIMITACIÓN (sin TEE):
16
+ * - Un atacante con root puede modificar dist/ directamente o falsificar el hash
17
+ * - La protección completa requiere TEE (Intel SGX / AMD SEV) — fase futura
18
+ */
19
+ import { existsSync, readFileSync } from "node:fs";
20
+ import { join, dirname } from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+ import { createHash } from "node:crypto";
23
+ const __dir = dirname(fileURLToPath(import.meta.url));
24
+ let _cached = null;
25
+ /**
26
+ * Carga el code-hash.json generado en build time.
27
+ */
28
+ export function getCodeIntegrity() {
29
+ if (_cached)
30
+ return _cached;
31
+ const hashFile = join(__dir, "code-hash.json");
32
+ if (!existsSync(hashFile)) {
33
+ _cached = {
34
+ codeHash: "unavailable",
35
+ codeHashHex: "0x0000000000000000000000000000000000000000000000000000000000000000",
36
+ computedAt: new Date().toISOString(),
37
+ fileCount: 0,
38
+ available: false,
39
+ };
40
+ return _cached;
41
+ }
42
+ try {
43
+ const raw = JSON.parse(readFileSync(hashFile, "utf8"));
44
+ _cached = {
45
+ codeHash: raw.codeHash ?? "unknown",
46
+ codeHashHex: raw.codeHashHex ?? ("0x" + raw.codeHash),
47
+ computedAt: raw.computedAt ?? "unknown",
48
+ fileCount: raw.fileCount ?? 0,
49
+ available: true,
50
+ };
51
+ }
52
+ catch {
53
+ _cached = {
54
+ codeHash: "parse-error",
55
+ codeHashHex: "0x0000000000000000000000000000000000000000000000000000000000000001",
56
+ computedAt: new Date().toISOString(),
57
+ fileCount: 0,
58
+ available: false,
59
+ };
60
+ }
61
+ return _cached;
62
+ }
63
+ /**
64
+ * Verifica que el code hash del nodo actual coincide con uno aprobado.
65
+ * @param approvedHashes Lista de hashes aprobados por governance
66
+ * @returns true si el hash actual está en la lista de aprobados
67
+ */
68
+ export function isCodeApproved(approvedHashes) {
69
+ const info = getCodeIntegrity();
70
+ if (!info.available)
71
+ return false;
72
+ const h = info.codeHash.toLowerCase().replace("0x", "");
73
+ return approvedHashes.some(a => {
74
+ if (a == null || typeof a !== "string")
75
+ return false;
76
+ return a.toLowerCase().replace("0x", "") === h;
77
+ });
78
+ }
79
+ /**
80
+ * Computa un hash rápido del propio binario en tiempo real (fallback).
81
+ * Menos preciso que el hash del código fuente pero no requiere build step.
82
+ */
83
+ export function computeRuntimeHash() {
84
+ try {
85
+ const selfPath = join(__dir, "validator.js");
86
+ if (!existsSync(selfPath))
87
+ return "no-binary";
88
+ const content = readFileSync(selfPath);
89
+ return createHash("sha256").update(content).digest("hex");
90
+ }
91
+ catch {
92
+ return "hash-error";
93
+ }
94
+ }
95
+ /**
96
+ * Log de integridad al arranque — llamar desde startServer().
97
+ */
98
+ export function logCodeIntegrity() {
99
+ const info = getCodeIntegrity();
100
+ if (info.available) {
101
+ console.log(`[integrity] ✅ Code hash: ${info.codeHash.slice(0, 16)}... (${info.fileCount} files)`);
102
+ console.log(`[integrity] Built at: ${info.computedAt}`);
103
+ }
104
+ else {
105
+ console.warn(`[integrity] ⚠️ Code hash unavailable — run 'pnpm build' to compute`);
106
+ console.warn(`[integrity] Without a code hash, peers cannot verify this node's integrity`);
107
+ }
108
+ }
package/dist/validator.js CHANGED
@@ -10,6 +10,7 @@ import { selectGossipPeers, routingStats } from "./crypto/peer-router.js";
10
10
  import { NullifierConsensus, AttestationConsensus, StateSyncManager } from "./consensus/index.js";
11
11
  import { BlockchainAnchor } from "./blockchain/blockchain-anchor.js";
12
12
  import { SoulprintBlockchainClient, loadBlockchainConfig, } from "./blockchain/blockchain-client.js";
13
+ import { getCodeIntegrity, logCodeIntegrity, computeRuntimeHash } from "./code-integrity.js";
13
14
  import { publishAttestationP2P, onAttestationReceived, getP2PStats, } from "./p2p.js";
14
15
  // ── Config ────────────────────────────────────────────────────────────────────
15
16
  const PORT = parseInt(process.env.SOULPRINT_PORT ?? String(PROTOCOL.DEFAULT_HTTP_PORT));
@@ -748,6 +749,28 @@ export function startValidatorNode(port = PORT) {
748
749
  return handleGetReputation(res, decodeURIComponent(cleanUrl.replace("/reputation/", "")));
749
750
  if (cleanUrl.startsWith("/nullifier/") && req.method === "GET")
750
751
  return handleNullifierCheck(res, decodeURIComponent(cleanUrl.replace("/nullifier/", "")));
752
+ // ── Code integrity + health ───────────────────────────────────────────────
753
+ if (cleanUrl === "/health" && req.method === "GET") {
754
+ const integrity = getCodeIntegrity();
755
+ const govHash = await client?.getCurrentApprovedHash() ?? null;
756
+ return json(res, 200, {
757
+ status: "ok",
758
+ version: VERSION,
759
+ protocolHash: PROTOCOL_HASH,
760
+ codeHash: integrity.codeHash,
761
+ codeHashHex: integrity.codeHashHex,
762
+ codeHashAvailable: integrity.available,
763
+ codeBuiltAt: integrity.computedAt,
764
+ codeFileCount: integrity.fileCount,
765
+ runtimeHash: computeRuntimeHash(),
766
+ governanceApprovedHash: govHash,
767
+ blockchainConnected: !!client?.isConnected,
768
+ nodeCompatible: !govHash ||
769
+ govHash.toLowerCase() === ("0x" + PROTOCOL_HASH).toLowerCase(),
770
+ uptime: process.uptime(),
771
+ ts: Date.now(),
772
+ });
773
+ }
751
774
  // ── Blockchain anchor status ──────────────────────────────────────────────
752
775
  if (cleanUrl === "/anchor/stats" && req.method === "GET") {
753
776
  return json(res, 200, anchor.getStats());
@@ -890,6 +913,7 @@ export function startValidatorNode(port = PORT) {
890
913
  json(res, 404, { error: "Not found" });
891
914
  });
892
915
  server.listen(port, () => {
916
+ logCodeIntegrity();
893
917
  console.log(`\n🌐 Soulprint Validator Node v${VERSION}`);
894
918
  console.log(` Node DID: ${nodeKeypair.did}`);
895
919
  console.log(` Listening: http://0.0.0.0:${port}`);
@@ -900,6 +924,7 @@ export function startValidatorNode(port = PORT) {
900
924
  console.log(` Known peers: ${peers.length}`);
901
925
  console.log(`\n Core endpoints:`);
902
926
  console.log(` POST /verify verify ZK proof + co-sign`);
927
+ console.log(` GET /health code integrity + governance status`);
903
928
  console.log(` GET /info node info`);
904
929
  console.log(` GET /protocol protocol constants (immutable)`);
905
930
  console.log(` GET /nullifier/:n anti-sybil check`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "soulprint-network",
3
- "version": "0.3.3",
4
- "description": "Soulprint validator node HTTP server that verifies ZK proofs, co-signs SPTs, anti-Sybil registry",
3
+ "version": "0.3.5",
4
+ "description": "Soulprint validator node \u2014 HTTP server that verifies ZK proofs, co-signs SPTs, anti-Sybil registry",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "bin": {
@@ -12,8 +12,9 @@
12
12
  "README.md"
13
13
  ],
14
14
  "scripts": {
15
- "build": "tsc",
16
- "start": "node dist/server.js"
15
+ "build": "tsc && node scripts/compute-code-hash.mjs",
16
+ "start": "node dist/server.js",
17
+ "build:hash": "node scripts/compute-code-hash.mjs"
17
18
  },
18
19
  "publishConfig": {
19
20
  "access": "public"
@@ -67,4 +68,4 @@
67
68
  "types": "./dist/index.d.ts"
68
69
  }
69
70
  }
70
- }
71
+ }