soulprint-verify 0.1.2 → 0.1.4
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/face/face-match.js +2 -1
- package/dist/face/face_match.py +14 -3
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/face/face_match.py +14 -3
package/dist/face/face-match.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.matchFaceWithDocument = matchFaceWithDocument;
|
|
|
4
4
|
const node_child_process_1 = require("node:child_process");
|
|
5
5
|
const node_path_1 = require("node:path");
|
|
6
6
|
const node_fs_1 = require("node:fs");
|
|
7
|
+
const soulprint_core_1 = require("soulprint-core");
|
|
7
8
|
// Path al script Python que corre on-demand
|
|
8
9
|
const PYTHON_SCRIPT = (0, node_path_1.join)(__dirname, "face_match.py");
|
|
9
10
|
/**
|
|
@@ -19,7 +20,7 @@ const PYTHON_SCRIPT = (0, node_path_1.join)(__dirname, "face_match.py");
|
|
|
19
20
|
* @param documentPhoto Path a la foto del documento (cédula)
|
|
20
21
|
*/
|
|
21
22
|
async function matchFaceWithDocument(selfiePhoto, documentPhoto, opts = {}) {
|
|
22
|
-
const minSim = opts.minSimilarity ??
|
|
23
|
+
const minSim = opts.minSimilarity ?? soulprint_core_1.PROTOCOL.FACE_SIM_DOC_SELFIE;
|
|
23
24
|
// Verificar que existe el script Python
|
|
24
25
|
if (!(0, node_fs_1.existsSync)(PYTHON_SCRIPT)) {
|
|
25
26
|
return {
|
package/dist/face/face_match.py
CHANGED
|
@@ -28,6 +28,17 @@ def eprint(*args):
|
|
|
28
28
|
print(*args, file=sys.stderr)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
# ── Protocol Constants (espejo de PROTOCOL en soulprint-core) ─────────────────
|
|
32
|
+
# IMPORTANTE: estos valores deben mantenerse sincronizados con
|
|
33
|
+
# packages/core/src/protocol-constants.ts → PROTOCOL
|
|
34
|
+
# Son INMUTABLES a nivel de protocolo. No modificar sin un nuevo SIP.
|
|
35
|
+
|
|
36
|
+
FACE_SIM_DOC_SELFIE = 0.35 # PROTOCOL.FACE_SIM_DOC_SELFIE
|
|
37
|
+
FACE_SIM_SELFIE_SELFIE = 0.65 # PROTOCOL.FACE_SIM_SELFIE_SELFIE
|
|
38
|
+
FACE_KEY_DIMS = 32 # PROTOCOL.FACE_KEY_DIMS
|
|
39
|
+
FACE_KEY_PRECISION = 1 # PROTOCOL.FACE_KEY_PRECISION
|
|
40
|
+
|
|
41
|
+
|
|
31
42
|
# ── Pre-procesamiento de imagen ────────────────────────────────────────────────
|
|
32
43
|
|
|
33
44
|
def fix_exif_rotation(img):
|
|
@@ -208,7 +219,7 @@ def cosine_similarity(a, b) -> float:
|
|
|
208
219
|
return float(np.dot(a, b))
|
|
209
220
|
|
|
210
221
|
|
|
211
|
-
def quantize_embedding(embedding, precision: int =
|
|
222
|
+
def quantize_embedding(embedding, precision: int = FACE_KEY_PRECISION):
|
|
212
223
|
"""
|
|
213
224
|
Cuantiza el embedding para derivar nullifier determinístico.
|
|
214
225
|
precision=1 (0.1 steps) absorbe ruido natural de InsightFace (±0.01).
|
|
@@ -285,7 +296,7 @@ def main():
|
|
|
285
296
|
parser = argparse.ArgumentParser(description="Soulprint face match")
|
|
286
297
|
parser.add_argument("--selfie", required=True, help="Path selfie del usuario")
|
|
287
298
|
parser.add_argument("--document", required=True, help="Path foto del documento")
|
|
288
|
-
parser.add_argument("--min-sim", type=float, default=
|
|
299
|
+
parser.add_argument("--min-sim", type=float, default=FACE_SIM_DOC_SELFIE, help="Similitud mínima (PROTOCOL.FACE_SIM_DOC_SELFIE)")
|
|
289
300
|
parser.add_argument("--liveness", action="store_true", help="Verificar liveness")
|
|
290
301
|
args = parser.parse_args()
|
|
291
302
|
|
|
@@ -351,7 +362,7 @@ def main():
|
|
|
351
362
|
)
|
|
352
363
|
|
|
353
364
|
# Embedding cuantizado para derivar nullifier (determinístico entre sesiones)
|
|
354
|
-
quantized = quantize_embedding(selfie_emb, precision=
|
|
365
|
+
quantized = quantize_embedding(selfie_emb, precision=FACE_KEY_PRECISION)
|
|
355
366
|
|
|
356
367
|
result = {
|
|
357
368
|
"match": match,
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ async function verifyIdentity(opts) {
|
|
|
48
48
|
log(`✓ Cédula: ${docResult.cedula_number}`);
|
|
49
49
|
// ── PASO 3: Face match (subprocess Python on-demand) ──────────────────────
|
|
50
50
|
log("Verificando coincidencia facial (iniciando proceso de IA)...");
|
|
51
|
-
const faceResult = await (0, face_match_js_1.matchFaceWithDocument)(opts.selfiePhoto, opts.documentPhoto, { minSimilarity: opts.minFaceSim ??
|
|
51
|
+
const faceResult = await (0, face_match_js_1.matchFaceWithDocument)(opts.selfiePhoto, opts.documentPhoto, { minSimilarity: opts.minFaceSim ?? soulprint_core_1.PROTOCOL.FACE_SIM_DOC_SELFIE, checkLiveness: opts.checkLiveness, verbose: opts.verbose });
|
|
52
52
|
if (!faceResult.match) {
|
|
53
53
|
errors.push(...faceResult.errors);
|
|
54
54
|
steps.face_match = "fail";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "soulprint-verify",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Soulprint local verification — on-demand cedula OCR + InsightFace match, zero persistent memory",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"tesseract.js": "^5.1.0",
|
|
35
35
|
"sharp": "^0.33.3",
|
|
36
36
|
"@noble/hashes": "^1.4.0",
|
|
37
|
-
"soulprint-
|
|
38
|
-
"soulprint-
|
|
37
|
+
"soulprint-zkp": "0.1.4",
|
|
38
|
+
"soulprint-core": "0.1.6"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"typescript": "^5.4.0",
|
package/src/face/face_match.py
CHANGED
|
@@ -28,6 +28,17 @@ def eprint(*args):
|
|
|
28
28
|
print(*args, file=sys.stderr)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
# ── Protocol Constants (espejo de PROTOCOL en soulprint-core) ─────────────────
|
|
32
|
+
# IMPORTANTE: estos valores deben mantenerse sincronizados con
|
|
33
|
+
# packages/core/src/protocol-constants.ts → PROTOCOL
|
|
34
|
+
# Son INMUTABLES a nivel de protocolo. No modificar sin un nuevo SIP.
|
|
35
|
+
|
|
36
|
+
FACE_SIM_DOC_SELFIE = 0.35 # PROTOCOL.FACE_SIM_DOC_SELFIE
|
|
37
|
+
FACE_SIM_SELFIE_SELFIE = 0.65 # PROTOCOL.FACE_SIM_SELFIE_SELFIE
|
|
38
|
+
FACE_KEY_DIMS = 32 # PROTOCOL.FACE_KEY_DIMS
|
|
39
|
+
FACE_KEY_PRECISION = 1 # PROTOCOL.FACE_KEY_PRECISION
|
|
40
|
+
|
|
41
|
+
|
|
31
42
|
# ── Pre-procesamiento de imagen ────────────────────────────────────────────────
|
|
32
43
|
|
|
33
44
|
def fix_exif_rotation(img):
|
|
@@ -208,7 +219,7 @@ def cosine_similarity(a, b) -> float:
|
|
|
208
219
|
return float(np.dot(a, b))
|
|
209
220
|
|
|
210
221
|
|
|
211
|
-
def quantize_embedding(embedding, precision: int =
|
|
222
|
+
def quantize_embedding(embedding, precision: int = FACE_KEY_PRECISION):
|
|
212
223
|
"""
|
|
213
224
|
Cuantiza el embedding para derivar nullifier determinístico.
|
|
214
225
|
precision=1 (0.1 steps) absorbe ruido natural de InsightFace (±0.01).
|
|
@@ -285,7 +296,7 @@ def main():
|
|
|
285
296
|
parser = argparse.ArgumentParser(description="Soulprint face match")
|
|
286
297
|
parser.add_argument("--selfie", required=True, help="Path selfie del usuario")
|
|
287
298
|
parser.add_argument("--document", required=True, help="Path foto del documento")
|
|
288
|
-
parser.add_argument("--min-sim", type=float, default=
|
|
299
|
+
parser.add_argument("--min-sim", type=float, default=FACE_SIM_DOC_SELFIE, help="Similitud mínima (PROTOCOL.FACE_SIM_DOC_SELFIE)")
|
|
289
300
|
parser.add_argument("--liveness", action="store_true", help="Verificar liveness")
|
|
290
301
|
args = parser.parse_args()
|
|
291
302
|
|
|
@@ -351,7 +362,7 @@ def main():
|
|
|
351
362
|
)
|
|
352
363
|
|
|
353
364
|
# Embedding cuantizado para derivar nullifier (determinístico entre sesiones)
|
|
354
|
-
quantized = quantize_embedding(selfie_emb, precision=
|
|
365
|
+
quantized = quantize_embedding(selfie_emb, precision=FACE_KEY_PRECISION)
|
|
355
366
|
|
|
356
367
|
result = {
|
|
357
368
|
"match": match,
|