Employment — built within the current role · 2026, actively developed
BiometricAttendance Platform
A fingerprint terminal, a legacy system to replace, and the discipline to ship both.
- NestJS (TypeScript)
- TypeORM + MySQL
- Angular
- pnpm monorepo
- ZKTeco terminal (UDP)
- Docker Compose
- nginx
- n8n
The fingerprint terminal by the door looks like an appliance. It's a networked device with a fragile UDP protocol, and careless traffic freezes it for the whole office. This system replaced a legacy attendance stack with one built around that reality, and it's in production now.
For hiring managers
This is current, in-production, full-stack work with a hardware edge — the newest system in this portfolio, still under active development.
- A real device driver problem, solved in two phases. The terminal froze on its "Working…" screen because every sync bracketed reads with disable/enable commands over a single fragile connection. The fix was architectural: device I/O moved to a single-owner, on-demand model — a worker process is the only thing in the system allowed to open a device socket; the API never does. Handshake failures close the socket instead of leaking it; a boot-time recovery pass handles whatever state the device was left in; device timestamps are decoded timezone-aware, with the terminal's unreliable clock auto-sync disabled.
- Session security. Refresh-token rotation with family detection — a stolen refresh token that gets reused revokes the whole token family. Idle timeout, secure-cookie handling behind an nginx same-origin proxy, and an environment-gated first-admin setup flow.
- Secrets encrypted at rest. SMTP credentials entered through the UI are encrypted in the database with a dedicated key — never stored in plaintext or env files.
- Operational shape. Compose stack with health-gated startup order (database → migrations → API/worker → web), a documented systemd alternative for Docker-averse hosts, nightly
mysqldumpcron, and real-time device-status indicators pushed to the dashboard over authenticated WebSockets. - HR-grade features, localized. Flexible shift timing (floating check-out, split grace periods), weekends and holidays, localized reports, a timesheet builder, and a monthly report scheduler.
How AI was involved: the build ran plan-first — five written implementation plans, executed by AI agents under my direction.
For clients
The old attendance system was unmaintained and starting to fail. The replacement had one hard requirement: acceptance is gated on the new system's reports matching the old one's over a week of live data. Staff clock in on the same terminal as before, and HR gets reports it can trust. The organization gets a system one person can operate and extend.
The hard problem
A device that can't be replaced
The terminal is closed hardware with a UDP protocol that has strict expectations: sockets leak on failed handshakes, the firmware locks up when reads are bracketed with disable/enable commands, and the clock reports the wrong timezone. Hardware like that punishes a shared polling loop — concurrent access is exactly what it can't tolerate.
The redesign inverts control. One process owns the device. Everything else — the API, the dashboard, admin actions — asks that owner, on demand, and gets an answer or a queued action. The device goes from a shared resource to a serialized one with a recovery path. That's the same discipline as a database connection pool or a hardware bus arbiter, applied to a fingerprint reader by the door.
The other hard problem was the replacement itself. A payroll-adjacent system can't be wrong during the cutover. Gating acceptance on report parity with the legacy system over live data made the migration safe to approve.
Trade-offs
What it cost
| Decision | Bought | Paid |
|---|---|---|
| Single-owner, on-demand device I/O | Device access serialized; failures contained and recoverable | Every device interaction pays a hop through the worker; "live" data is as fresh as the last read |
| Report parity as acceptance gate | Cutover backed by evidence | The new system inherits the old one's report semantics, including their quirks |
| Secrets encrypted in DB (UI-managed) | Operators rotate SMTP credentials without a deploy | Key management becomes your problem; a lost key orphans the secrets |
| Compose + documented systemd path | Runs on the org's Docker hosts and on bare metal | Two deployment paths to keep honest |
| MySQL + TypeORM | Boring and migratable | None worth arguing — I picked boring on purpose |
Where it stands
In production for the organization's staff, actively developed through July 2026. Built solo in the directing-engineer sense: the plans, the architecture, and the final say on every change are mine. A large share of the implementation was executed by AI agents against those plans.