Skip to content
AFM Studio
  • Pages

    AFM Studio
  • Work
  • Lab
  • About
  • CV
  • Contact
  • Projects

    AksaraA searchable library for Otorita IKN's knowledge, where the server checks every reader's access across three levels
  • Daily TaqwaTamper-resistant prayer attendance system for ~2,000 OIKN government employees at Masjid Negara IKN
  • Lantara v2A permit engine for Otorita IKN where administrators add new permit types from a screen, without a developer
  • eKiosk IKNTwo control rooms for Nusantara: kiosk content management and live bus tracking with arrival times that grade themselves
  • E-Monev IKNA monitoring platform for Nusantara's 23-year build, tracking every unit's quarterly progress against 24 national KPIs
  • JDIH OIKNOIKN's public legal document portal, with a controlled publishing workflow and AI summaries that never leave the server
  • Jejak PenanamanA check-in app for IKN tree-planting events that proves each participant was on site with a geofenced selfie
  • Dashboard SAKTIA nightly pipeline and dashboard that replaces OIKN's manual budget downloads for all six of its budget units
  • DoseRxA bedside dose calculator for 127 drugs that rounds each dose to tablets and syrups Indonesian pharmacies sell
  • CubiqA speedcubing trainer with competition-grade timing and a solver I wrote for each of the 8 major puzzles
  • ScimotionAn interactive science library of 86 articles, each explained next to animations the reader can control
  • WA Daily Scrum BotA WhatsApp bot that chases missing standup reports for a 14-person team and flags tasks stuck for days

↑ ↓ to move · Enter to open · Esc to close

Daily Taqwa

Tamper-resistant prayer attendance system for ~2,000 OIKN government employees at Masjid Negara IKN

Screenshots
OIKN employees served
~2,000
check-ins in five minutes, designed for
~500
daily prayers tracked per employee
5
leaderboards, including dawn prayer and directorate
4
On this page

The problem

Otorita IKN wanted to encourage its roughly 2,000 civil servants to pray at Masjid Negara IKN, and to know whether they did. Paper sign-in sheets could be filled in afterwards. A simple digital check-in could be faked from outside the mosque. HR leadership also wanted to see attendance trends across directorates, beyond a list of who showed up.

The timing made it harder than a normal attendance system. Prayer windows are short, and check-ins pile up in the few minutes around each call to prayer. The location check also had to hold up against someone who was trying to fake it.

What I built

Only the mobile app can check in

A browser can't prove its GPS reading came from the phone's own hardware. Free fake-GPS apps and browser tools beat a web-only geofence, however careful the server is. So I split the system. The native mobile app is the only client allowed to submit a check-in. It captures a liveness check and reads GPS with the phone's own fake-location detection. The web portal shows prayer times, rankings, profiles, release notes and the admin panel. Both talk to one backend, which holds every rule.

The server owns the clock

The server calculates each prayer window, caches it, and checks it again at the moment a check-in arrives. The phone never sends a time that counts. IKN runs on Central Indonesia Time, eight hours ahead of UTC. A dawn check-in at 5:00 local time falls on the previous day in UTC. I made every date in the database use local time, so dawn and night prayers never land on the wrong day.

One check-in, one step, no duplicates

A check-in does everything in one step: it uses up a one-time code, checks the prayer window, the geofence, the travel speed since the last check-in and the liveness capture, then saves the record. If any part fails, nothing is saved. The database refuses a second record for the same person, day and prayer, so two taps at once can't create two entries. Daily, monthly and directorate totals update in that same step, so they never drift from the records.

One login for web and mobile

Both apps sign in through OIKN's existing single sign-on. The web uses the standard browser redirect. The mobile app signs in directly, then trades that login for its own session with the backend. I map each employee's ID number, work unit and gender from the login to their profile. Some HR systems send ID numbers in odd formats, and contract and military staff use non-standard ones, so I accept those too.

Rankings, history and tools HR asked for

There are four leaderboards: monthly by gender, yearly, dawn prayer only, and by directorate. Your own rank stays pinned at the bottom if you're outside the top ten. A monthly grid colours all five prayers for each day. Launch brought needs the spec didn't have. The login sends no gender, so people can correct gender and directorate on their profile, and the next login keeps their fix. HR can upload the old paper records from a web page, and admins can see web and mobile logins separately to track app adoption.

Result

Daily Taqwa is in production at OIKN. The mobile app is live and is the only channel for check-ins. The web portal runs rankings and administration on the same backend. It serves about 2,000 employees and is designed for about 500 check-ins in a five-minute midday window. I built it between April and June 2026. The scope grew past the original spec: single sign-on replaced local accounts, and the HR tools above came from launch feedback.

Under the hoodTechnical detail for engineers
  • Prayer windows are computed with the Python praytimes library and cached in Redis. A get_wita_date() helper converts every attendance date to WITA (UTC+8) before it hits a date-keyed constraint.
  • The check-in service runs in one @transaction.atomic block with SELECT FOR UPDATE on the user row: nonce consumption, window validation, Haversine geofence with a configurable radius, velocity check, liveness sanity, photo save and aggregate updates.
  • UNIQUE(user_id, date, prayer) is the hard guard against races. The service catches IntegrityError and returns "already checked in". Nonces are single-use with a two-minute TTL.
  • Web SSO is Keycloak PKCE via mozilla-django-oidc. Mobile uses Direct Access Grant: it gets a Keycloak id_token and exchanges it at a backend endpoint for SimpleJWT tokens. Claims (NIP, unit_kerja, gender) map to the user model with fuzzy eselon matching. NIPs arriving as floats are coerced to strings.
  • A profile_self_edited flag stops the OIDC sync from overwriting a user's corrected directorate on the next login. last_login_web and last_login_mobile replace Django's single last_login.
  • Face matching with dlib was descoped to liveness-only. The server checks frames are present and not identical; face-api.js runs the liveness challenge on the client.
  • Photos are stripped of EXIF with Pillow and served only through an authenticated endpoint. Presigned MinIO URLs pointed at an internal Docker hostname, so the endpoint streams the bytes.
  • import_backfill_csv imports the paper records idempotently with --append and merges SSO accounts by NIP. It is also wrapped in an admin upload page.
  • The admin panel has a Leaflet geofence editor, per-prayer window settings, bulk Excel user import, an attendance log with photo preview, and an audit log.
  • About 15 models, about 30 API endpoints, 23 frontend page files, about 18,800 lines of Python and TypeScript, 85 commits.

Screenshots

Beranda
Peringkat
Profil
Pengaturan
Catatan Rilis
Dashboard Admin

Hiring, or building something similar?

I'm open to full-time roles and select freelance work.