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.
@@ -1,8 +1,8 @@
1
1
  {
2
- "codeHash": "c256c2701bea12bbb2f50552b268bda6065a58d187f7bde6d6be86d82eb73f06",
3
- "codeHashHex": "0xc256c2701bea12bbb2f50552b268bda6065a58d187f7bde6d6be86d82eb73f06",
4
- "computedAt": "2026-03-01T03:11:08.326Z",
5
- "fileCount": 24,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soulprint-network",
3
- "version": "0.4.6",
3
+ "version": "0.5.0",
4
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",