<- cd ~/home
$ cat ./projects/geoattend.md

GeoAttend

NEXT.JSPOSTGISHR-TECH
ROLE
Solo Architect & Full-Stack Engineer
YEAR
2026
TYPE
Attendance Platform
STATUS
FEATURE-COMPLETE
TESTS PASSING
794
Across 56 files, plus 128 end-to-end assertions on a live server.
GEODESIC ERROR
<1mm
Vincenty on WGS84, cross-validated against PostGIS fixtures.
API ENDPOINTS
72
Across 410 TypeScript modules and 20 hand-written SQL migrations.
GeoAttendDrop main screenshot
// 01

OVERVIEW

A production-grade attendance system replacing paper registers and spreadsheets with GPS-verified, auditable time records across multiple sites and timezones — built to one standard: the output has to be trustworthy enough to pay people from.

// 02

THE PROBLEM

HR spent 15–25 hours a month transcribing paper attendance, payroll disputes were settled by argument because no evidence existed, and proxy attendance was undetectable. Sites span countries and timezones, so "yesterday" is not one moment: overnight shifts cross midnight and DST moves the boundary twice a year. Underneath sits the real engineering problem — attendance feeds payroll, so a wrong number is someone's rent, while the primary sensor is consumer GPS: unreliable indoors and near steel, and trivially spoofed. That is a trap with two jaws. Enforce strictly and legitimate employees standing at the gate cannot record their own work, which is worse than the original problem. Enforce loosely and the system is decorative. Resolving that tension was the project.

GeoAttend screenshot 1Screenshot 1
GeoAttend screenshot 2Screenshot 2
GeoAttend screenshot 3Screenshot 3
GeoAttend screenshot 4Screenshot 4
GeoAttend screenshot 5Screenshot 5
GeoAttend screenshot 6Screenshot 6
// 03

THE SOLUTION

An immutable event log with a derived read model. Punches are append-only facts — never edited, only voided, enforced by a database trigger — and daily records are recomputable derivations from them. A nightly job recomputes a sample and alerts on drift rather than repairing it, because silently overwriting the number hides the bug that produced it.

Underneath sits a pure domain layer: 19 modules of geodesy, timezone arithmetic and shift resolution with no I/O and no clock, which is what makes 794 tests meaningful rather than ceremonial. The same discipline decides what the system refuses to do — a forgotten check-out yields NULL hours, not zero and not an estimated departure, because a guess that reaches payroll is indistinguishable from a fact.

Corrections are additive. Nothing overwrites the original: mandatory reason codes, before and after values, and a two-stage workflow where the direct manager decides and HR resolves. Across the API, 33 mutating endpoints emit 31 audit actions to an append-only log.

Fraud detection flags but never blocks. Eleven rules cover impossible travel, implausible accuracy, shared coordinates, fingerprint changes, clock skew and IP mismatch — and their inputs are read outside the punch transaction, so a detection bug can never stop someone recording their work. Enforcement itself is configuration rather than deployment: geofence policy and device binding switch per site without shipping code, because an over-enforcing attendance system has to be fixable in seconds, not in a release cycle.

Two dependencies were refused on purpose. Rate limiting and idempotency run on Postgres rather than Redis — at this load a second datastore buys nothing, and one fewer moving part matters more than theoretical headroom. And the standard XLSX library pulls a chain carrying known high-severity advisories, so export is written directly against Node’s built-in compression in about a hundred tested lines; the CSV path carries a UTF-8 BOM so Excel renders Arabic names, and neutralises formula injection.

Two decisions worth reading

A detection bug once cost every check-out. Anomaly rules originally queried the database inside the punch transaction; one failure left the connection out of protocol sync, so the next statement failed too and the punch was lost. Catching the exception was not enough — rule inputs are now read before the transaction opens, which makes evaluation structurally incapable of touching the database. The end-to-end suite found it, not production.

A timezone bug moved recorded attendance by three hours. Rather than bulk-repairing the affected rows, I wrote a read-only audit script that identifies them by the specific arithmetic signature of the bug and leaves ambiguous cases untouched. Re-adjusting recorded attendance is HR’s decision and has to leave its own audit trail — that is how a payroll figure is allowed to change.

FEATURES

  • Append-only punch log
  • Vincenty geodesy on WGS84
  • Versioned geofence history
  • Idempotent punches
  • Effective-dated shift engine
  • Overnight workday attribution
  • Additive corrections only
  • 11 fraud rules that flag
  • Five-tier scoped roles
  • PDPL coordinate retention
  • Arabic-default bilingual RTL
  • Grounded AI help assistant

STACK

  • Next.js 16
  • React 19
  • TypeScript
  • PostgreSQL 16
  • PostGIS
  • Drizzle
  • Zod 4
  • next-intl
  • MapLibre GL
  • Argon2id
  • Docker
  • Dokploy
  • nginx

OUTCOMES

  • 794 tests across 56 files, with 100% branch coverage on the four modules where a bug is expensive
  • 128 end-to-end assertions against a live server, so no feature regresses silently
  • Geodesic accuracy under 1 mm, cross-validated against PostGIS fixtures
  • 33 mutating endpoints emit 31 audit actions — every state change attributable, proven by test
  • Zero known-vulnerable dependencies, by writing two components rather than importing them
BACK TO HOME