Laravel + Filament control plane for workplace cameras, occupancy, attendance, and AI-assisted alerts.
This application does not decode RTSP video or run face recognition inside PHP. Cameras stream to an NVR (Frigate / go2rtc). That layer detects people, then this app stores events, matches faces, flags anomalies, evaluates custom vision prompts, and reports from aggregated daily data.
[Cameras] --RTSP--> [Frigate / go2rtc]
|
snapshot + webhook
v
[Laravel + Filament]
| |
queues MySQL / Redis
|
Face service · Vision API · Attendance devices
Default integrations are mocks, so you can demo the full pipeline with zero cameras.
Admin branding: Aether Cam — Filament light and dark mode (user-menu sun/moon). AI via imrandevbd/laravel-ai-hub.
| Role | Sees | |
|---|---|---|
super@cms.test |
Super Admin | Everything + /ai-hub Studio |
admin@cms.test |
Admin | Full control plane + Studio |
ops@cms.test |
Operations | Registry, cameras, copilot, alerts |
security@cms.test |
Security | Monitoring, AI triage |
hr@cms.test |
HR | People, attendance, briefings |
viewer@cms.test |
Viewer | Read-only command center |
Works without API keys (demo answers). Add Gemini/OpenAI/Claude keys in /ai-hub (Studio has its own light/dark theme) for live calls with failover and cost tracking.
- AI Copilot — natural-language questions over live occupancy, cameras, alerts, attendance
- Daily / shift / executive briefings — scheduled at 07:00 or on demand
- Alert explain — auto-triage on new anomalies + manual “AI explain”
- Camera vision prompt author — drafts a per-camera activity prompt
- Vision driver
aihub— snapshot prompts through the hub, fallback to mock - 13 providers — Gemini, GPT, Claude, Grok, DeepSeek, Groq, Ollama, OpenRouter, Azure, Together, Fireworks, Perplexity
- Multi-Stream CCTV Grid: Toggle between 1x1 Focus View, 2x2 Wall, and 3x3 Security Matrix.
- Dynamic HUD Overlays: Real-time 30 FPS stream metrics, timestamp telemetry, and AI bounding box overlays.
- Vision Modes: Toggle Thermal Infrared Heat Filter and Green-Phosphor Night Vision directly on live canvas feeds.
- Simulated PTZ Control: Pan, tilt, and zoom controls directly integrated into camera cards.
- Instant AI Triage: 1-click anomaly evaluation directly from individual camera feeds.
- Dedicated full-page matrix available at
/admin/live-camera-matrix.
- Pod-Level Seat Tracking: Real-time desk occupancy visualization for facility operations.
- Spatial Status Indicators: Distinct visual states for Verified Present (Green), Available Vacant (Gray), and Seating Anomaly / Mismatch (Red).
- Interactive Seat Inspector: Click any workstation to view assigned employee, department, presence status, and biometric confidence.
- Accessible on the landing page and via the Filament admin at
/admin/floor-plan-explorer.
- Multi-Model Triage Engine: Dispatches edge frames against custom vision prompts powered by
imrandevbd/laravel-ai-hub. - Pre-configured Edge Scenarios:
- 🦺 PPE Safety Gear Check: Helmet & high-vis vest compliance auditing.
- 🚪 Turnstile Tailgating Alert: Multi-person vestibule pass-through detection.
- 🚨 After-Hours Perimeter Breach: Motion and unauthorized presence alerts with automatic lockdown escalation.
- 🪑 Desk Occupancy Verification: Biometric seat match verification.
- Live Latency Telemetry: Sub-50ms inference simulation with structured JSON audit output.
- Fast demo access to Filament Command Center across 6 tailored permission tiers without entering passwords:
- Super Admin (
super@cms.test): Unrestricted master access across all sites, cameras, audit ledgers, and AI Hub Studio. - Facility Admin (
admin@cms.test): Full management of buildings, zones, cameras, and employees. - Security Officer (
security@cms.test): CCTV monitoring wall, tailgating flags, unauthorized perimeter alerts. - Operations Lead (
ops@cms.test): Heatmaps, seat allocations, AI Copilot, and facility metrics. - HR Manager (
hr@cms.test): People directory, biometric onboarding, daily executive briefings. - Executive Viewer (
viewer@cms.test): Read-only executive dashboard and shift summaries.
- Super Admin (
GET /api/system-telemetry: Live edge camera health, seat occupancy rate, open alerts count, and inference latency.POST /api/simulate-live-event: Dispatches realistic edge payloads to test asynchronous queues, anomaly detection, and movement logging.POST /api/ai-vision-sandbox: Real-time multi-model vision evaluation endpoint.
Map a building the way it actually works: building → floor/zone → seat → camera.
| Resource | What it stores |
|---|---|
| Buildings | Sites / offices |
| Zones | Floors, halls, rooms (floor + parent building) |
| Seats | Desk labels and optional current occupant |
| Cameras | RTSP URL, stream key, IP, vendor, online/offline, desk vs hallway, detection + face-match toggles, optional AI prompt |
| Employees | Code, department, expected seat, face photo, face-registered flag |
Use cases:
- Desk cameras vs hallway / lobby cameras
- Assign an employee to an expected seat
- Turn detection or face matching off per camera
- Attach a custom AI prompt to a specific camera
Filament admin at http://localhost:8000/admin
- Camera health — online / offline / detection-enabled counts
- Live occupancy — seats with an occupant vs empty
- Attendance today — check-ins, check-outs, people still out
- Open anomalies — unresolved alert feed
Navigation groups: Registry, Monitoring, Attendance, AI, Reports.
When a person (or motion) is detected:
- Frigate posts to
POST /webhooks/frigate(or you run the simulate command). - A raw
camera_eventsrow is stored (never process inline). ProcessCameraEventruns on theeventsqueue.- Face matching returns
employee_id+ confidence (mock or CompreFace). - A business
movement_logsrow is written (enter/exit/present/idle/away). - If the camera has a seat, the occupant is updated.
- Anomaly detection compares the camera zone/seat to the employee’s expected seat.
- If the camera has an AI prompt, a vision job may run (rate-limited).
Frigate-shaped payloads (after.camera, after.label, after.snapshot.path) and a simple JSON body both work. The camera can be identified by id, name, or stream key.
- Upload a face photo on an employee →
RegisterFaceEmbeddingjob → embedding row +face_registered = true - On each event,
FaceRecognitionServicematches the snapshot - Mock driver: uses
employee_codein the webhook, or a filename likesnapshots/E1001.jpg.unknownin the hint stays unmatched.FACE_RECOGNITION_FORCE_EMPLOYEE_IDcan pin a match for demos. - CompreFace driver: HTTP recognize/enroll client (set
FACE_RECOGNITION_DRIVER=compreface)
Unmatched people on a camera still create a movement log and can raise an unauthorized-zone alert.
AnomalyDetectionService flags:
- Person not matched to any employee
- Matched employee in the wrong zone
- Matched employee on the wrong desk camera (seat mismatch)
Alert types already in the schema:
| Type | Typical meaning |
|---|---|
unauthorized_zone |
Wrong place, or unmatched person |
idle_too_long |
Vision prompt returned idle / away |
camera_offline |
Health / ops (seeded as an example) |
absent |
Daily rollup / attendance gap |
Severity: info, warning, critical. Alerts can be resolved in Filament (stores resolved_at + resolved_by).
Configure prompts in Filament (name, text, model gpt-4o or gemini-1.5, applies to camera/zone, trigger on_event or scheduled).
- Only sampled / flagged events — never every frame
- Rate limit: one vision call per camera per
AI_PROMPT_RATE_MINUTES(default 10) - Mock driver returns
working/idle/awayfrom payload or filename hints - OpenAI and Gemini driver shells are ready (
AI_PROMPT_DRIVER=openaiorgemini) - Results stored in
ai_prompt_results; idle/away also opens an alert
Example prompt: “Is this person actively working at their desk or away/idle?”
Biometric / RFID / face terminals push or pull into attendance_records.
POST /webhooks/attendance— check-in / check-out by employee code + device name- Same-day punches upsert onto one record
SyncAttendanceDevicehourly pull for devices without push- Drivers:
mock(demo punch),zktecoandhikvisionstubs for vendor SDKs
Methods: fingerprint, RFID, face, PIN, webhook.
Nightly AggregateDailyReport (01:15) writes employee_daily_summaries:
- Estimated time at seat
- Anomaly count
- Absence flag (no check-in that day)
- Raw movement counts (JSON)
The report table is what dashboards should read — not a live scan of millions of raw events.
Seeded demo (php artisan migrate --seed):
- HQ building, Operations + Lobby zones
- Seats A-12 / A-13 / A-14
- Cameras
desk-a12,hall-3f,lobby-1 - Employees
E1001–E1005(some assigned seats, some face-registered) - Sample movement, alerts, attendance, and a daily summary
php artisan cms:simulate-frigate-event desk-a12 --employee=E1001
php artisan cms:simulate-frigate-event lobby-1 --employee=E1002
php artisan cms:simulate-attendance E1001 --type=check_inThe architecture is built for ~1,000 cameras as a control plane, not a video decoder.
| Possibility | How |
|---|---|
| Real NVR | Point Frigate (or go2rtc + detector) webhooks at /webhooks/frigate. Split ~20–40 cameras per Frigate node. |
| Real face matching | Run CompreFace or a FastAPI + InsightFace service; set FACE_RECOGNITION_DRIVER=compreface. Laravel stays source of truth for employees. |
| Real vision QA | Flip AI_PROMPT_DRIVER to openai or gemini for “idle at desk?”, PPE, phone use, etc. Keep the rate limit. |
| Real attendance | ZKTeco / Hikvision push to /webhooks/attendance, or implement pull in the stub drivers. |
| Queue scale | Separate queues so a slow vision API cannot block face matches. Add Horizon on Linux. |
| Fast lookups | CameraLookupService caches camera → zone → seat in Redis. |
| Heavy history | Partition movement_logs by month, or move it to ClickHouse / Timescale. |
| Live occupancy wall | Laravel Reverb / Pusher (not required for this MVP; widgets poll). |
| Multi-site | Extra buildings/zones; same backend. |
| “Who isn’t working” reports | Read employee_daily_summaries, not raw events. |
| Offline camera alerts | Health probe / Frigate status → camera_offline alerts. |
| Scheduled prompts | ai_prompts.trigger = scheduled is modeled; wire a scheduler when needed. |
Intentionally not in this app: RTSP decode, 24/7 frame analysis, Coral/GPU inference, fingerprint hardware, or storing raw video. Those belong at the edge.
| Layer | Choice |
|---|---|
| App | Laravel 13, PHP 8.3+ |
| Admin | Filament v4 (/admin) |
| Database | SQLite by default; MySQL 8 via Docker |
| Cache / queues | Database locally; Redis recommended in production |
| Queues | events, faces, vision, attendance, reports |
Horizon is not installed on Windows (ext-pcntl). On Linux:
composer require laravel/horizon
php artisan horizon:install
php artisan horizoncomposer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed
php artisan storage:link
php artisan serve| Admin | http://localhost:8000/admin |
admin@cms.test |
|
| Password | password |
With QUEUE_CONNECTION=sync (local .env), simulate commands finish immediately. For real queues:
php artisan queue:work --queue=events,faces,vision,attendance,reportsdocker compose up -dThen in .env:
DB_CONNECTION=mysql
DB_DATABASE=cms
DB_USERNAME=cms
DB_PASSWORD=cms
QUEUE_CONNECTION=redis
CACHE_STORE=redisHeader: X-Webhook-Secret when FRIGATE_WEBHOOK_SECRET is set.
Simple body:
{
"camera": "desk-a12",
"event_type": "person",
"snapshot_path": "snapshots/E1001.jpg",
"employee_code": "E1001",
"detected_at": "2026-09-08T10:00:00+06:00"
}event_type: person or motion. Also accepts Frigate’s after object.
{
"device": "Lobby Reader",
"employee_code": "E1001",
"type": "check_in",
"method": "fingerprint",
"occurred_at": "2026-09-08T09:04:00+06:00"
}type: check_in or check_out. method: fingerprint, rfid, face, pin, webhook.
| Variable | Purpose | Default |
|---|---|---|
FACE_RECOGNITION_DRIVER |
mock / compreface |
mock |
FACE_RECOGNITION_FORCE_EMPLOYEE_ID |
Force a match (demo) | — |
COMPREFACE_URL / COMPREFACE_API_KEY |
CompreFace client | — |
AI_PROMPT_DRIVER |
mock / openai / gemini |
mock |
AI_PROMPT_RATE_MINUTES |
Vision call cooldown per camera | 10 |
OPENAI_API_KEY / GEMINI_API_KEY |
Real vision APIs | — |
ATTENDANCE_DRIVER |
mock / zkteco / hikvision |
mock |
FRIGATE_WEBHOOK_SECRET |
Frigate HMAC/shared secret | empty = open |
ATTENDANCE_WEBHOOK_SECRET |
Attendance webhook secret | empty = open |
CMS_LOOKUP_CACHE_SECONDS |
Camera lookup cache TTL | 300 |
See config/cms.php.
app/
Models/ Domain tables
Services/ Face, AI prompt, anomaly, attendance, camera lookup
Jobs/ ProcessCameraEvent, RunFaceMatch, EvaluateAiPrompt,
RegisterFaceEmbedding, SyncAttendanceDevice, AggregateDailyReport
Http/Controllers/Webhooks/
Filament/Resources + Widgets
Console/Commands cms:simulate-frigate-event, cms:simulate-attendance
Scheduled (when schedule:run / a cron is active):
- Hourly: pull attendance devices
- Daily 01:15: aggregate yesterday’s summaries
php artisan test- Registry CRUD + a handful of cameras (done)
- Attendance webhooks (done; plug a real device)
- One Frigate node →
camera_events(done; point a real NVR at the webhook) - CompreFace / InsightFace → live
movement_logs - Anomaly rules + alert routing (email/Slack can sit on
alerts) - Paid vision prompts last (most expensive)
- Dashboards on
employee_daily_summariesonly
Validate at 5 cameras before 500. The pipeline design holds at 1,000; schema and rate limits are what you want to get wrong early.
I'm actively open to:
Remote Senior Full-Stack Roles · Freelance Contracts · Technical Partnerships · Long-Term Collaborations
in Laravel · WordPress · React/Next.js · AI-powered Platforms · Security Audits · SaaS Architecture
📍 Timezone: UTC+6 (Dhaka/Rangpur) — flexible overlap for US, EU & Asia
⚡ Available: Immediately · Production-first · Fast delivery · Transparent communication
| Platform | Link |
|---|---|
| 🌐 Portfolio | imrandev.bd |
| linkedin.com/in/imranbru99 | |
| 🐙 GitHub | github.com/imranbru99 |
| 🐦 X / Twitter | @imrandev_bd |
| 📺 YouTube | @ImranDevBD |
| @imranbru99 | |
| ExpertImranDev | |
| 🎵 TikTok | @imrandev_bd |
| 🧵 Threads | @imranbru99 |
| @imrandev_bd | |
| +880 1576-918420 | |
| me@imrandev.bd | |
| 🔗 All Links | linktr.ee/ExpertImranDev |
"Security isn't an add-on — it's the foundation. Scale, speed, and trust drive every line of code I write."
— Imran Ahmed


