HIGH
- async handlers: Express 4 does not catch rejected promises and Node 22
exits on unhandled rejections — a malformed body ({} to /cropper/dequeue
etc.) could hang a request or kill the process. Every cropper route is
now wrapped (throw → 500 via next), request bodies are validated up
front (400), and JSON.parse of queue items is guarded. Server-wide
error middleware added, honouring err.status (413 for oversized bodies).
- error pages HTML-escape all interpolated values — the login failure page
rendered upstream (portal) error text raw.
MEDIUM
- outbound calls carry AbortSignal timeouts (5s OpenFGA verify, 15s
cropper upstreams) — a hung upstream can no longer pin a request.
- body limits scoped: /cropper gets 50mb (picture data URLs), everything
else 1mb (previously 50mb globally, memory-DoS surface pre-auth).
- cropper save validates the client-supplied domain against the catalog;
picture fetches validate the Minio object key (id-domains/ prefix, no ..).
LOW
- static assets served before the session middleware (no PocketBase load
per asset); landing header builds DOM nodes with textContent instead of
innerHTML; picture load checks upstream ok.
5 new tests (40 total) covering malformed bodies, malformed queue items,
unknown domains, object-key traversal, and error-page escaping.
YOUreka Tools — Fingerprint Service
A small Express app that hosts the YOUreka tools landing page and the
Fingerprint Agent enrollment wizard. Fingerprint capture is performed by
the local fpagent binary (Socket.IO on
127.0.0.1:9000); this app provides the login (YOUreka Auth), the permitted
domain selection, and the a2loft integration that stores the enrolled
fingerprints.
Layout
tools/
├── server.js # Express app: routes, session middleware, proxies
├── yrka-auth.js # YOUreka Auth cookie session → PocketBase (authRefresh)
├── permit.js # OpenFGA domain permission checks + 5-minute cache
├── public/
│ ├── index.html # landing page (Image Cropper + Fingerprint Agent)
│ └── assets/
│ ├── style.css
│ └── fp/ # /fingerprint enrollment wizard (Vue 2 via CDN)
└── test/ # node --test suite (fake portal / PocketBase / a2loft)
Running
Prerequisites: Node.js 22+, fpagent installed and running on this laptop
(see the fingerprint-agent repository for installation), the U.are.U 4500
scanner attached.
cd tools
npm install
npm start # → http://localhost:4173
Open http://localhost:4173 and sign in with your YOUreka account.
fpagent must allow the tools origin. On a dev machine run it with:
fpagent -allowed-origins http://localhost:4173
How enrollment works
- Sign in — YOUreka Auth (portal web-auth redirect). The PocketBase session is kept in an httpOnly cookie and refreshed server-side.
- Pick a domain — the hardcoded catalog (UNICAL, Metropolitan Hotel, UCTH,
UNICAL Radio International, YOUreka) is filtered against the same portal
OpenFGA endpoint fpagent uses (
capture_biometricper domain). Results are cached for 5 minutes; the Refresh domains control unlocks once the cache expires. fpagent re-authorizes every capture regardless. - Locate the staff profile — reference + OTP, validated by
apis.a2loft.netthrough a same-origin proxy (the operator's phone comes from the session). - Capture — right thumb and right index finger, one capture at a time,
with per-scan progress and cancel; the PocketBase token, domain and
reference travel with every
fprint-enrollto fpagent. - Save — both templates are posted to a2loft (with a fresh OTP).
Configuration
Everything is hardcoded in tools/server.js by design — port 4173, portal
https://portal.yrka.app, PocketBase https://pb.itcass.net, a2loft
https://apis.a2loft.net, agent http://127.0.0.1:9000, and the domain
catalog. Change them there; there are no environment variables or config
files.
The Image Cropper
The /cropper page (Smart Photo Editor) lets operators review, crop and save
identity photos. It reuses the YOUreka session for identity, requires the
crop_image permission on domain:idid for every proxied action (verified
against the portal's OpenFGA endpoint — live on save, 5-minute cached on
reads), and keeps the a2loft OTP requirement only on the save step.
Its service credentials are the sole environment variables this app uses
(same names the old id-image-bot used — an existing .env is drop-in):
MINIO_ACCESS_KEY=… MINIO_SECRET_KEY=…
AZURE_STORAGE_CONNECTION_STRING=…
NODERED_HTTP_USERNAME=… NODERED_HTTP_PASSWORD=…
CARVEKIT_API_KEY=…
Without them the rest of the app works and /cropper routes answer 503.
Run locally with cp tools/.env.example tools/.env + your values and
node --env-file=tools/.env tools/server.js (or docker run --env-file …).
Tests
cd tools
npm test # node --test — 35 tests, no network or hardware required
Docker
Docker
The app is stateless, so the container only serves the web front-end; fpagent
keeps running on the operator's laptop (the wizard connects to
http://127.0.0.1:9000 from the browser, wherever this container lives).
docker build -t yreka-tools .
docker run -d -p 4173:4173 --name yreka-tools yreka-tools
Behind a TLS-terminating proxy the app honours X-Forwarded-Proto
(trust proxy is enabled), so session cookies get the Secure flag
automatically.
Field walkthrough (real portal, PocketBase, a2loft, and U.are.U 4500): login → permitted-domain filtering → staff lookup → two scanner captures with cancel path → save → reset, all verified end to end via Playwright.