Attendance Sanchalan
Sanchalan Docs
Architecture

Architecture

Attendance Sanchalan is one Laravel 12 application with mobile + web admin clients, hosted on the SDS (Setu Deployment Stack) shared infrastructure. The single backend is registered as one service in the SDS infra catalog (per /infra/ §22.8), with internal modularity that mirrors what would otherwise be 5 microservices (punch / geofence / device / leave / reconcile).

System context

flowchart LR subgraph User["End users"] P["MP / Staff phone
(Flutter app)"] A["Admin browser
(HR / Reporting officer)"] end subgraph Edge["Edge — host nginx"] N["nginx
ft.rajyasabha.digital"] end subgraph App["Attendance app"] W["web container
(nginx-alpine)"] APP["app container
(apache + PHP 8.2)"] Q["worker container
(queue:work)"] AU["audit container
(audit:consume)"] end subgraph SDS["SDS shared infra"] PG[("Postgres 16
attendance_dev
+ sds_audit")] R[("Redis
sds:audit:events stream")] V[("Vault
per-deployment secrets")] IAM["sb-iam
(Parichay)
OAuth2 PKCE"] end P -- HTTPS --> N A -- HTTPS --> N N -- /* --> W W --> APP APP --> PG APP -- xadd --> R R -- xreadgroup --> AU AU -- writeToPostgres --> PG APP -. read at boot .-> V P -. SSO PKCE .-> IAM IAM -. token .-> APP Q --> PG

Container responsibilities

ContainerImageRole
appattendance/app:latestApache + PHP 8.2. Serves /api/* (Sanctum-authed JSON) and /admin/* (session-authed Blade).
webattendance/web:latestnginx-alpine reverse proxy. Listens on 127.0.0.1:8092 on the host, terminates only HTTP — TLS is the host nginx's job.
workersame as appphp artisan queue:work. Reserved for future async jobs (push notifications, reconciliation, payroll export).
auditsame as appphp artisan audit:consume. XREADGROUP loop that drains sds:audit:events from Redis into sds_audit.audit_events with hash-chained immutability.

Network paths

flowchart TD Client["client (mobile / browser)"] -->|"HTTPS
ft.rajyasabha.digital/*"| Nginx["host nginx
(port 443)"] Nginx -->|"proxy_pass
127.0.0.1:8092 (strips prefix)"| WebC["web container
(nginx-alpine)"] WebC -->|"upstream
app:80"| AppC["app container
(apache + php-fpm)"] AppC -->|"DB_HOST=sds-dms-postgres-1"| PG[("Postgres")] AppC -->|"REDIS_HOST=sds-dms-redis-1"| Redis[("Redis")]

Key configuration details:

  • Host nginx has a limit_req zone=attendance burst=40 nodelay; for the path (defense-in-depth on top of Laravel's throttle:60,1).
  • Headers passed through: X-Real-IP, X-Forwarded-For, X-Forwarded-Proto, X-Request-Id.
  • App's APP_URL=https://attendance.rajyasabha.digital + URL::forceScheme('https') in AppServiceProvider so generated URLs / form actions / CSRF tokens use HTTPS even though the container only sees HTTP.

Internal module map (one app, 5-svc-shaped)

flowchart TB subgraph Controllers pc["PunchController"] dc["DeviceController"] lc["LeaveController"] tc["TourController"] plc["PublicLookupController"] sso["SsoController"] adm["Web\\Admin*Controllers"] end subgraph Services gs["GeoFenceCheck"] sig["PunchSignatureVerifier"] asp["AntiSpoofPolicy"] eng["CcsLeaveEngine"] aud["AuditClient"] end subgraph Models M1["AttendanceEmployeeRegister"] M2["AttendanceDevice"] M3["AttendancePunch"] M4["AttendanceLeaveApplication"] M5["AttendanceLeaveBalance"] M6["AttendanceTour"] M7["AttendanceGeoFence"] M8["AttendanceHoliday"] end pc --> sig & gs & asp & aud pc --> M3 & M2 & M7 dc --> aud --> Redis lc --> eng & aud lc --> M4 & M5 tc --> aud tc --> M6

SDS infra integration

  • Postgres: shared cluster sds-dms-postgres-1. Two databases — attendance_dev (app data) and sds_audit (central audit). App has its own role attendance_dev_app.
  • Redis: shared sds-dms-redis-1. Stream sds:audit:events uses a per-app consumer group attendance.
  • Vault: per-deployment secrets (IMEI salt, DB passwords). Currently provisioned via sds-ops/wire-attendance-audit.sh; future versions will pull at boot.
  • sb-iam (Parichay): OAuth2 PKCE. Web flow already wired (/api/auth/sso/{redirect,callback}); mobile flow added (/api/auth/sso/mobile/{redirect,exchange}) — needs operator to register a public client.
  • Audit: every state-changing action emits app=attendance, action=attendance.<domain>.<event>, entity_type, entity_id, payload through the AuditClient → Redis stream → audit container. Hash chain anchored daily by sds-ops/audit-chain-anchor-sign.sh.

Deployment model

Single host (103.224.23.106 in dev), docker-compose. Containers join two networks: default for in-stack and sds-dms_default (external) to reach Postgres/Redis. Volumes: bind-mount ./backend → /var/www/html for live code reload in dev; app_storage named volume for log persistence.

For production: same shape, signed image tags, vendor/ baked into the image (no host bind), Vault-pulled secrets at boot via an entrypoint, uid-aligned chown step instead of chmod 777 on storage.