| title | Biometric Verify |
|---|---|
| emoji | 🟣 |
| colorFrom | indigo |
| colorTo | purple |
| sdk | docker |
| app_port | 7860 |
| pinned | false |
| short_description | Contactless face verification + identification API |
Confirm who someone is using just a phone or tablet camera — by their face or the palm of their hand. No fingerprint scanner, no cards, no internet required.
Think of it as a universal "prove it's really you" button that any app or organisation can plug in: point a camera at a person, and it either confirms they're who they claim to be, or tells you who they are — in a second or two, on the cheap phones people already have, and it keeps working when the internet is down.
Most identity systems today rely on fingerprint scanners, which have two big problems:
- They exclude people. Farmers, cleaners, builders, traders and the elderly often have worn or damaged fingerprints that scanners can't read — so they get turned away from their own benefits, SIM cards, wages or exams. This is a real, documented failure of large fingerprint-based ID programmes.
- They cost money and break. Every scanner is hardware to buy, install, clean and maintain.
This fixes both:
- Nobody gets excluded — if your palm doesn't read, your face does (and vice-versa). A match is a match.
- No special hardware — it's the camera already in every phone.
- Contactless & hygienic — nothing to touch.
- Works fully offline — for rural clinics, remote sites and field work with no signal (there's an air-gapped Android build too).
- Private by design — it never stores photos, only a scrambled mathematical signature that can't be turned back into a face, and it can be wiped on request.
Anyone who must answer "is this the right person?" at scale, cheaply, without leaving people out:
| Who | Why |
|---|---|
| Employers (factories, farms, schools) | Stop "buddy-punching" on attendance where fingerprint clocks fail on manual workers |
| Governments & NGOs (cash transfers, welfare) | Eliminate ghost/duplicate beneficiaries and pay people out without excluding worn-fingerprint citizens |
| Exam boards & universities | Stop candidates sitting exams for one another |
| Clinics & hospitals | Find a patient's record instantly when the card is lost; avoid duplicate records |
| Banks, microfinance, mobile-money agents | Verify members and stop multi-branch fraud, even in villages with no connectivity |
| Software teams | Add trustworthy identity to their product with a couple of API calls — no biometric work of their own |
See examples/ for four small, working products built on it
(attendance, exams, welfare, clinics) — proof it weaves into anything.
Parts exist — but no one combines them for these people and places, and the most common existing tool (fingerprints) is the problem we solve:
| Existing approach | Gap it leaves |
|---|---|
| Fingerprint / national-ID scanners | Exclude worn fingerprints; need hardware everywhere |
| Cloud face APIs (AWS, Azure, Face++, NEC) | Need constant internet; costly; no palm fallback; data in someone's cloud |
| Palm-payment (Amazon One) | Needs special infra-red scanners — not a phone |
| Digital KYC / onboarding (Smile ID, Onfido, Jumio) | One-time, online account-opening — not repeated, offline, field verification |
We're the only option that is camera-only, face-or-palm, offline, and private — built for the settings the others don't reach. The face-matching tech itself is mature and proven; the edge is the packaging and fit — inclusive, hardware-free, offline, embeddable, and tuned/tested on the target population.
A contactless face + palm verification + identification service: a phone web client, an operator admin console, and a multi-tenant REST API other apps integrate with. ArcFace face embeddings + active (head-turn) liveness, plus contactless palm-print (MediaPipe-Hands ROI → CCNet ONNX) — both behind one auto-routing API, so a user is recognised whether they show their face or their palm (a match is a match). Encrypted at rest, with adaptive enrolment that keeps recognising a person as they change over months/years.
The earlier contactless-fingerprint system is archived under
fingerprint/; phone-camera capture proved unworkable, so the project pivoted to face.
| Surface | Path | Who | Auth |
|---|---|---|---|
| Phone web client | / |
end users (kiosk) | verify open; enrol needs admin login |
| Admin console | /admin |
your operators | admin password |
| Integration API | /v1/* |
other companies' systems | X-API-Key + role |
Full docs in docs/:
Architecture ·
Security & Privacy ·
Integration ·
API errors/codes ·
Operations ·
Deploy ·
Development ·
Android ·
Roadmap ·
Changelog.
Package maps: face/ (recognition core), face_service/ (web API).
python -m venv venv && venv/Scripts/pip install -r requirements.txt
# PowerShell: $env:FACE_ADMIN_PASSWORD="choose-one"
python app.py # dev server, HTTPS (self-signed) on :5000Open https://<this-machine-ip>:5000 on a phone (same network), accept the
self-signed cert, allow the camera. For public 24/7 access see docs/DEPLOY.md.
Auth: header X-API-Key: <key>. Mint keys: python manage_keys.py create "App" --role verify.
| Endpoint | Scope | Purpose |
|---|---|---|
POST /v1/enroll |
enroll | enrol one user from image(s) |
POST /v1/enroll/bulk |
enroll | enrol many users in one call |
POST /v1/verify |
verify | 1:1 (with user_id) or 1:N |
POST /v1/identify |
verify | 1:N — who is this? |
POST /v1/embed |
verify | stateless: image → 512-d embedding |
POST /v1/compare |
verify | stateless: probe vs references |
GET /v1/users · POST /v1/users/delete |
manage · delete | list / delete (single or user_ids[]) |
POST /v1/users/export |
manage | data-subject access (metadata) |
POST /v1/users/purge |
delete | erase all users in a tenant (confirm:true) |
GET /v1/usage |
any | this tenant's usage this month |
GET /v1/challenge · GET /v1/health |
verify · none | liveness token · readiness |
Roles: admin = full control; verify = recognition only (cannot enrol/delete/list).
Each tenant is isolated; verify/compare results are HMAC-signed with the key's secret.
See docs/INTEGRATION.md, openapi.yaml, and the SDKs in sdk/.
from faceverify import FaceVerifyClient
fv = FaceVerifyClient("https://HOST:5000", "fk_yourkey")
fv.enroll("alice", ["a1.jpg", "a2.jpg", "a3.jpg"])
if fv.verify("alice", "probe.jpg")["success"]:
grant_access()python bulk_enroll.py dataset/ --tenant acme # dataset/<person>/<images...>- Probes:
GET /healthz(liveness),GET /readyz(readiness),GET /metrics(Prometheus). - Audit trail per tenant in
audit_logs/; usage counters inusage.json. - Rate limited per caller (
FACE_RATE_LIMIT/FACE_RATE_WINDOW); security headers on every response.
| Var | Default | Purpose |
|---|---|---|
FACE_ADMIN_PASSWORD |
random (printed) | admin console / enrolment password |
FACE_SECRET_KEY |
random per run | signs admin session cookies (set in prod) |
FACE_DB_KEY |
random key file | passphrase for encryption-at-rest |
FACE_SIGNING_SECRET |
— | HMAC-sign first-party verify results |
FACE_CORS_ORIGINS |
same-origin | comma-separated browser origins allowed on /v1 |
FACE_RATE_LIMIT / FACE_RATE_WINDOW |
120 / 60 | requests per window per caller |
FACE_ACTIVE_LIVENESS |
1 | require a live head-turn on verify |
FACE_LIVENESS |
0 | also run the passive single-shot anti-spoof model (opt-in; pairs with active liveness; self-host only — models ship there) |
FACE_LIVENESS_THRESHOLD |
0.55 | passive-liveness strictness (only used when FACE_LIVENESS=1) |
FACE_ATTRIBUTES |
0 | estimate age/gender (returned on /v1/embed) — loads the small genderage model |
FACE_USE_ANN |
0 | use the HNSW ANN index instead of exact (needs hnswlib; for very large tenants) |
FACE_DB_PATH |
face_db |
base data directory (store + per-tenant + index) |
FACE_KEYS_FILE · FACE_ADMINS_FILE · FACE_AUDIT_DIR · FACE_USAGE_FILE |
apikeys.json · admins.json · audit_logs · usage.json |
state locations |
Operators: python manage_admins.py create alice. While no operators exist, the
FACE_ADMIN_PASSWORD bootstrap login (admin / that password) is active; adding the
first operator disables it.
Biometric templates and the search index are encrypted at rest; raw images are
never stored (only embeddings, encrypted). The audit log records actions, not faces.
Right-to-erasure: POST /v1/users/delete / …/purge. Data-subject access:
POST /v1/users/export. Obtain consent before enrolling people.
face/ ArcFace engine: detection, matching, liveness, adaptive, encrypted store + index
face_service/ /v1 API, API keys + roles, admin auth, audit, usage, metrics, security
app.py Flask app: phone client + admin console + mounts /v1
bulk_enroll.py offline dataset importer serve.py production launcher (waitress)
templates/, static/ phone client + admin console UIs
sdk/, openapi.yaml, docs/, Dockerfile
tests/ pytest suite + scale/drift benchmarks
fingerprint/ archived fingerprint system
The service runs fully offline — no internet needed at runtime. There are no
CDN assets, no telemetry, and no outbound calls; the ArcFace models are loaded from
a local cache (pre-baked into the Docker image). For a self-contained kiosk, run the
server and open https://localhost:5000 in a browser on the same device — it
works with zero network. The phone client is a PWA whose shell caches for instant
loads and shows an offline page if the server is unreachable (verification itself
always needs the server, since the model runs server-side).
Tuned for ~100k identities per tenant (exact match, 100% accurate, ~40 ms search,
encrypted). For 1M+ swap the index to FAISS — see face/index.py (_USE_ANN).
python -m pytest tests/ -q
python _scale_test.py 100000 # scale/accuracy benchmarkInsightFace (buffalo_l ArcFace, ONNX Runtime, CPU) · active-liveness head-turn · Flask · Fernet/AES encryption · waitress/gunicorn. No GPU required.