soulprint-network 0.4.6 → 0.5.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/dist/code-hash.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"codeHash": "
|
|
3
|
-
"codeHashHex": "
|
|
4
|
-
"computedAt": "2026-03-01T03:
|
|
5
|
-
"fileCount":
|
|
2
|
+
"codeHash": "5ed4412b9c31b286dd991a4f35189b14284d3ec4f724b06b9dde681dd097042a",
|
|
3
|
+
"codeHashHex": "0x5ed4412b9c31b286dd991a4f35189b14284d3ec4f724b06b9dde681dd097042a",
|
|
4
|
+
"computedAt": "2026-03-01T03:36:12.743Z",
|
|
5
|
+
"fileCount": 25,
|
|
6
6
|
"files": [
|
|
7
7
|
"blockchain/NullifierRegistryClient.ts",
|
|
8
8
|
"blockchain/PeerRegistryClient.ts",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"credentials/github.ts",
|
|
20
20
|
"credentials/index.ts",
|
|
21
21
|
"credentials/phone.ts",
|
|
22
|
+
"credentials/registraduria.ts",
|
|
22
23
|
"crypto/gossip-cipher.ts",
|
|
23
24
|
"crypto/peer-router.ts",
|
|
24
25
|
"index.ts",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registraduría Nacional del Estado Civil — cédula validation
|
|
3
|
+
*
|
|
4
|
+
* Verifies that a Colombian cédula is VIGENTE (active) by querying
|
|
5
|
+
* the official Registraduría certificate service.
|
|
6
|
+
*
|
|
7
|
+
* Endpoint: GET /verify/cedula?numero=XXXXXXXX&fechaNac=YYYY-MM-DD
|
|
8
|
+
*/
|
|
9
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
10
|
+
export interface RegistraduriaResult {
|
|
11
|
+
vigente: boolean;
|
|
12
|
+
status: "VIGENTE" | "NO_VIGENTE" | "NOT_FOUND" | "ERROR";
|
|
13
|
+
raw?: string;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validates a cédula against Registraduría Nacional.
|
|
18
|
+
* @param numero - Cédula number (digits only)
|
|
19
|
+
* @param fechaNac - Birth date in YYYY-MM-DD format
|
|
20
|
+
* @returns RegistraduriaResult
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateCedula(numero: string, fechaNac: string): Promise<RegistraduriaResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Handle GET /verify/cedula?numero=...&fechaNac=...
|
|
25
|
+
* Returns: { ok, vigente, status, numero, fechaNac }
|
|
26
|
+
* Graceful degradation: if Registraduría is unreachable, returns ok=true with status=ERROR and a warning.
|
|
27
|
+
*/
|
|
28
|
+
export declare function handleCedulaRoute(req: IncomingMessage, res: ServerResponse, url: string): Promise<boolean>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registraduría Nacional del Estado Civil — cédula validation
|
|
3
|
+
*
|
|
4
|
+
* Verifies that a Colombian cédula is VIGENTE (active) by querying
|
|
5
|
+
* the official Registraduría certificate service.
|
|
6
|
+
*
|
|
7
|
+
* Endpoint: GET /verify/cedula?numero=XXXXXXXX&fechaNac=YYYY-MM-DD
|
|
8
|
+
*/
|
|
9
|
+
import https from "node:https";
|
|
10
|
+
import { URL } from "node:url";
|
|
11
|
+
const REGISTRADURIA_URL = "https://certvigenciacedula.registraduria.gov.co/Datos.aspx";
|
|
12
|
+
/**
|
|
13
|
+
* Validates a cédula against Registraduría Nacional.
|
|
14
|
+
* @param numero - Cédula number (digits only)
|
|
15
|
+
* @param fechaNac - Birth date in YYYY-MM-DD format
|
|
16
|
+
* @returns RegistraduriaResult
|
|
17
|
+
*/
|
|
18
|
+
export async function validateCedula(numero, fechaNac) {
|
|
19
|
+
// Convert fechaNac YYYY-MM-DD → DD/MM/YYYY expected by Registraduría
|
|
20
|
+
const [year, month, day] = fechaNac.split("-");
|
|
21
|
+
const fechaFormatted = `${day}/${month}/${year}`;
|
|
22
|
+
const postBody = new URLSearchParams({
|
|
23
|
+
NumeroDocumento: numero.trim(),
|
|
24
|
+
FechaNacimiento: fechaFormatted,
|
|
25
|
+
BtnConsultar: "Consultar",
|
|
26
|
+
}).toString();
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
const urlObj = new URL(REGISTRADURIA_URL);
|
|
29
|
+
const options = {
|
|
30
|
+
hostname: urlObj.hostname,
|
|
31
|
+
path: urlObj.pathname + urlObj.search,
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
35
|
+
"Content-Length": Buffer.byteLength(postBody),
|
|
36
|
+
"User-Agent": "Mozilla/5.0 (compatible; SoulprintValidator/0.5.0; +https://github.com/manuelariasfz/soulprint)",
|
|
37
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
38
|
+
"Accept-Language": "es-CO,es;q=0.9",
|
|
39
|
+
},
|
|
40
|
+
timeout: 10_000,
|
|
41
|
+
};
|
|
42
|
+
const req = https.request(options, (res) => {
|
|
43
|
+
let data = "";
|
|
44
|
+
res.on("data", (chunk) => {
|
|
45
|
+
data += chunk;
|
|
46
|
+
if (data.length > 500_000) {
|
|
47
|
+
req.destroy();
|
|
48
|
+
resolve({ vigente: false, status: "ERROR", error: "Response too large" });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
res.on("end", () => {
|
|
52
|
+
try {
|
|
53
|
+
const upper = data.toUpperCase();
|
|
54
|
+
if (upper.includes("VIGENTE") && !upper.includes("NO VIGENTE") && !upper.includes("NO_VIGENTE")) {
|
|
55
|
+
resolve({ vigente: true, status: "VIGENTE", raw: data.slice(0, 2000) });
|
|
56
|
+
}
|
|
57
|
+
else if (upper.includes("NO VIGENTE") || upper.includes("NO_VIGENTE") || upper.includes("CANCELADA") || upper.includes("ANULADA")) {
|
|
58
|
+
resolve({ vigente: false, status: "NO_VIGENTE", raw: data.slice(0, 2000) });
|
|
59
|
+
}
|
|
60
|
+
else if (upper.includes("NO EXISTE") || upper.includes("NO SE ENCONTR") || upper.includes("NOT FOUND")) {
|
|
61
|
+
resolve({ vigente: false, status: "NOT_FOUND", raw: data.slice(0, 2000) });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Ambiguous response — treat as not found
|
|
65
|
+
resolve({ vigente: false, status: "NOT_FOUND", raw: data.slice(0, 2000) });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
resolve({ vigente: false, status: "ERROR", error: e.message });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
req.on("error", (err) => {
|
|
74
|
+
resolve({ vigente: false, status: "ERROR", error: err.message });
|
|
75
|
+
});
|
|
76
|
+
req.on("timeout", () => {
|
|
77
|
+
req.destroy();
|
|
78
|
+
resolve({ vigente: false, status: "ERROR", error: "Request timed out" });
|
|
79
|
+
});
|
|
80
|
+
req.write(postBody);
|
|
81
|
+
req.end();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
|
85
|
+
function jsonResp(res, status, body) {
|
|
86
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
87
|
+
res.end(JSON.stringify(body));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Handle GET /verify/cedula?numero=...&fechaNac=...
|
|
91
|
+
* Returns: { ok, vigente, status, numero, fechaNac }
|
|
92
|
+
* Graceful degradation: if Registraduría is unreachable, returns ok=true with status=ERROR and a warning.
|
|
93
|
+
*/
|
|
94
|
+
export async function handleCedulaRoute(req, res, url) {
|
|
95
|
+
if (!url.startsWith("/verify/cedula"))
|
|
96
|
+
return false;
|
|
97
|
+
if ((req.method ?? "GET") !== "GET")
|
|
98
|
+
return false;
|
|
99
|
+
const qs = new URL(url, "http://localhost").searchParams;
|
|
100
|
+
const numero = qs.get("numero");
|
|
101
|
+
const fechaNac = qs.get("fechaNac");
|
|
102
|
+
if (!numero) {
|
|
103
|
+
jsonResp(res, 400, { ok: false, error: "Missing query param: numero" });
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
if (!fechaNac || !/^\d{4}-\d{2}-\d{2}$/.test(fechaNac)) {
|
|
107
|
+
jsonResp(res, 400, { ok: false, error: "Missing or invalid query param: fechaNac (expected YYYY-MM-DD)" });
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const result = await validateCedula(numero, fechaNac);
|
|
112
|
+
if (result.status === "ERROR") {
|
|
113
|
+
// Graceful degradation — Registraduría unreachable
|
|
114
|
+
console.warn(`[Registraduría] Unreachable or error for cédula ${numero}: ${result.error}`);
|
|
115
|
+
jsonResp(res, 200, {
|
|
116
|
+
ok: true,
|
|
117
|
+
vigente: null,
|
|
118
|
+
status: "ERROR",
|
|
119
|
+
warning: "Registraduría is currently unreachable. Verification skipped.",
|
|
120
|
+
numero,
|
|
121
|
+
fechaNac,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
jsonResp(res, 200, {
|
|
126
|
+
ok: true,
|
|
127
|
+
vigente: result.vigente,
|
|
128
|
+
status: result.status,
|
|
129
|
+
numero,
|
|
130
|
+
fechaNac,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
// Should not happen, but handle just in case
|
|
136
|
+
console.warn(`[Registraduría] Unexpected error: ${e.message}`);
|
|
137
|
+
jsonResp(res, 200, {
|
|
138
|
+
ok: true,
|
|
139
|
+
vigente: null,
|
|
140
|
+
status: "ERROR",
|
|
141
|
+
warning: "Registraduría verification failed unexpectedly. Verification skipped.",
|
|
142
|
+
numero,
|
|
143
|
+
fechaNac,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
package/dist/validator.js
CHANGED
|
@@ -7,6 +7,7 @@ import { generateKeypair, keypairFromPrivateKey, decodeToken, sign, createToken,
|
|
|
7
7
|
import { verifyProof, deserializeProof } from "soulprint-zkp";
|
|
8
8
|
import { buildChallengeResponse, verifyPeerBehavior, } from "./peer-challenge.js";
|
|
9
9
|
import { handleCredentialRoute } from "./credentials/index.js";
|
|
10
|
+
import { handleCedulaRoute } from "./credentials/registraduria.js";
|
|
10
11
|
import { encryptGossip, decryptGossip } from "./crypto/gossip-cipher.js";
|
|
11
12
|
import { selectGossipPeers, routingStats } from "./crypto/peer-router.js";
|
|
12
13
|
import { NullifierConsensus, AttestationConsensus, StateSyncManager } from "./consensus/index.js";
|
|
@@ -1038,6 +1039,12 @@ export function startValidatorNode(port = PORT) {
|
|
|
1038
1039
|
if (handled)
|
|
1039
1040
|
return;
|
|
1040
1041
|
}
|
|
1042
|
+
// ── Registraduría cédula validation ────────────────────────────────────
|
|
1043
|
+
if (cleanUrl.startsWith("/verify/cedula")) {
|
|
1044
|
+
const handled = await handleCedulaRoute(req, res, url);
|
|
1045
|
+
if (handled)
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1041
1048
|
if (cleanUrl === "/info" && req.method === "GET")
|
|
1042
1049
|
return handleInfo(res, nodeKeypair);
|
|
1043
1050
|
if (cleanUrl === "/protocol" && req.method === "GET")
|
package/package.json
CHANGED