On the lederera dev box, the nrt self-service identification flow fails with “error during photo processing” when either (1) the vuer_cv container is stopped, or (2) the hairpin-NAT /etc/hosts fix is missing from vuer_oss. Two independent root causes, same symptom. This is a recipe-oriented runbook — a future engineer hitting this should check both.

Dev box only — and the box has MOVED (2026-07-01)

Observed on the old on-prem box (lederera / 192.168.1.93, ssh Facekom), which is now decommissioned. The current dev host is the fk-dev Tailscale VM (command ssh ops@fk-dev.taild4189d.ts.net — see dev-build-host); the class of failure below still applies there. This is a dev-box networking + container-state gotcha, not a product bug. The flow itself is correct — there is no dev config flag that skips face-detection recognition, so a down/unreachable CV service hard-fails the flow.

Symptom

  • Customer-facing UI during nrt self-service identification: “error during photo processing”.
  • vuer_oss logs (exact strings, searchable):
    • Failed to ping CV server: 'cv-lederera.facekomdev.net'
    • Error in ping CV server: 'RequestError: Error: read ECONNRESET'
    • CV server is down: 'cv-lederera.facekomdev.net'
    • CV process error Error: Recipe missing no CV Service!
  • Thrown at server/cv/CVRecipe.js:88 — the recipe is constructed without a cvService because the CV health-check already marked the host down.

Why the flow can’t skip it

The throw propagates up:

SelfServiceV2Service.photoCandidate  (SelfServiceV2Service.js:1043, calls submitTaskRecognition unconditionally)
  -> FlowService.submitTaskRecognition
    -> RecognitionService.runRecognitions
      -> CVRecipe (server/cv/CVRecipe.js:88) throws "Recipe missing no CV Service!"

For Agents

SelfServiceV2Service.js:1043 calls submitTaskRecognition unconditionally — there is NO dev config flag that skips face-detection recognition. selfService.ui.disabledChecks only covers girinfo / emrtd / kau — not face detection. So if CV is unreachable, the whole nrt photo step hard-fails. Fixing reachability is the only path; you cannot config your way around it on the dev box.

Root cause 1 — vuer_cv container stopped

The CV (computer-vision / face-recognition) service runs in its own container vuer_cv (image harbor.techteamer.com/facekom-devel/vuer_cv:4.6.2.DEV-...). On this box it was stopped:

docker ps -a
# vuer_cv   Exited (255) 2 months ago

With it down, nginx returns 502 Bad Gateway for https://cv-lederera.facekomdev.net. Confirm via loopback (bypasses hairpin, hits the box’s own nginx):

curl -k --resolve cv-lederera.facekomdev.net:443:127.0.0.1 https://cv-lederera.facekomdev.net/
# 502 Bad Gateway  -> upstream (vuer_cv) is down

Fix

docker start vuer_cv

It boots cleanly under supervisord (nginx, redis, the CV process, ~10 worker procs) and becomes Up (healthy) after ~2 min. After it’s up, the same loopback curl returns 404 on / (real upstream answering) instead of 502:

curl -k --resolve cv-lederera.facekomdev.net:443:127.0.0.1 https://cv-lederera.facekomdev.net/
# 404  -> upstream is alive (404 on bare / is expected, the real app is responding)

Root cause 2 — hairpin NAT (host-network container box’s own public IP)

vuer_oss runs with host networking. Its container /etc/hosts maps all *-lederera.facekomdev.net names (css / oss / api / cv / esign-* / library / lederera.test) to the box’s own LAN IP 192.168.1.93. A host-network container connecting to the box’s own public/LAN IP resets at the TLS handshake (“Connection reset by peer” right after Client Hello) — classic hairpin-NAT failure.

This is why CV ping fails with read ECONNRESET even when vuer_cv is up: the connection never completes the handshake.

Fix

Point those names at 127.0.0.1 instead of 192.168.1.93 inside vuer_oss’s /etc/hosts. Loopback reaches the same nginx with no hairpin:

docker exec vuer_oss sh -c "sed 's/192\.168\.1\.93/127.0.0.1/g' /etc/hosts > /tmp/hosts.new && cat /tmp/hosts.new > /etc/hosts"

After this, vuer_oss logs:

CV server is available: 'cv-lederera.facekomdev.net'

and the CV ping recovers.

Use truncate+write, NOT sed -i

/etc/hosts is a single-file bind mount. An in-place rewrite must truncate+write the same inode (... > /etc/hosts, or base64 -d > /etc/hosts). sed -i fails with Device or resource busy because it tries to rename a temp file over the bind-mounted path.

CRITICAL caveat — the fix does not survive a restart

Re-apply after EVERY vuer_oss restart

vuer_oss’s /etc/hosts is regenerated by Docker on every container start (it’s a bind-mounted file Docker owns). So the hairpin edit DOES NOT survive docker restart vuer_oss. Any restart re-breaks:

  • CV reachability (re-triggers “error during photo processing”), and
  • the oss-lederera external-API path used by instacash-cli.

Re-apply the /etc/hosts edit after any vuer_oss restart.

Also affects: bin/instacash-cli

The same hairpin reset previously broke bin/instacash-cli calls to oss-lederera.facekomdev.net/external/... (TLS reset, host-network container own LAN IP). Same fix: map oss-lederera 127.0.0.1 in vuer_oss’s /etc/hosts. (The single sed replace above already covers all *-lederera names, including oss-lederera.)

Quick recipe

Hitting "error during photo processing" or "CV server is down"?

  1. Is vuer_cv running? docker ps -a | grep vuer_cv — if Exited, docker start vuer_cv and wait ~2 min for Up (healthy). Verify: loopback curl returns 404 not 502.
  2. Is the hairpin /etc/hosts fix in place in vuer_oss? Especially after any vuer_oss restart (the edit is wiped on every container start). Re-map *-lederera.facekomdev.net 127.0.0.1 via truncate+write (never sed -i). Confirm via CV server is available in the logs.
  • vuer_cv — the CV / face-recognition service that must be Up (healthy)
  • vuer_oss — host-network container whose /etc/hosts carries the hairpin fix (CVRecipe.js:88, SelfServiceV2Service.js:1043)
  • infrastructure — dev-box networking, host-networking model, nginx subdomain routing
  • vuer_docker — container orchestration / docker start operations
  • sms-verification-code-dev-testing — sibling dev-box gotcha discovered in the same nrt self-service testing session