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
(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
| Container | Image | Role |
|---|---|---|
app | attendance/app:latest | Apache + PHP 8.2. Serves /api/* (Sanctum-authed JSON) and /admin/* (session-authed Blade). |
web | attendance/web:latest | nginx-alpine reverse proxy. Listens on 127.0.0.1:8092 on the host, terminates only HTTP — TLS is the host nginx's job. |
worker | same as app | php artisan queue:work. Reserved for future async jobs (push notifications, reconciliation, payroll export). |
audit | same as app | php artisan audit:consume. XREADGROUP loop that drains sds:audit:events from Redis into sds_audit.audit_events with hash-chained immutability. |
Network paths
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'sthrottle: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)
SDS infra integration
- Postgres: shared cluster
sds-dms-postgres-1. Two databases —attendance_dev(app data) andsds_audit(central audit). App has its own roleattendance_dev_app. - Redis: shared
sds-dms-redis-1. Streamsds:audit:eventsuses a per-app consumer groupattendance. - 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, payloadthrough the AuditClient → Redis stream → audit container. Hash chain anchored daily bysds-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.
