Skip to content

Architecture

Open Locker is an IoT system made of four parts: a mobile app, a central Laravel backend, an MQTT broker (Mosquitto), and a locker client on a Raspberry Pi at the cabinet. The backend is the source of truth; the hardware is driven exclusively by the locker client.

When a user opens a compartment in the app:

App → Backend (HTTPS/REST) → MQTT broker → Locker client → Modbus → Lock

Status updates (e.g. “door opened”) travel the same path back and are pushed to the app in real time:

Lock → Locker client → MQTT broker → Backend → Push (WebSocket) → App
  • Laravel 12 with a Filament admin panel (the only server-rendered UI; everything else is API-first)
  • Event sourcing: state changes flow as events through aggregates, projectors (read models), and reactors (side effects such as MQTT publishing)
  • REST API with a live-generated OpenAPI specification (Scramble) at /docs/api.json
  • PostgreSQL as the database, Redis for queues
  • Commands go through REST (POST /api/compartments/{id}/opencommand_id)
  • Progress and door state arrive as WebSocket pushes (Laravel Reverb + Echo) on the private channel users.{userId}.compartment-status
  • If the WebSocket connection is unavailable, the app polls GET /api/compartments/open-requests/{commandId} as a fallback

Details and payloads: App Communication Guide.

  • Mosquitto mediates between the backend and cabinet locations
  • Clients authenticate against the backend (mosquitto-go-auth calls /api/mosq/*) — there are no static broker passwords
  • The backend publishes via typed publisher services; topic structure and message contracts are captured in ADRs (see Reference)
  • The backend’s mqtt-listener process reports liveness via a heartbeat and is restarted automatically on failure (see Operations)
  • TypeScript/Node service, runs as a Docker container on a Raspberry Pi at the cabinet
  • Subscribes to commands via MQTT and translates them into Modbus signals (TCP or RTU) for the relay boards
  • Modbus operations are serialized and tolerate unreachable boards — only the client talks to the hardware, never the backend directly
  • React Native (Expo, TypeScript)
  • The API client is generated from the backend’s OpenAPI specification via codegen (RTK Query) — API changes lead to regenerated, type-safe clients