NFC door access you can run yourself: an Arduino at the door, a small API, and a web panel to manage who gets in.
Born at XYZ – Sapere, fare, diventare (May 2026, Loro Piceno), a workshop by La Scuola Open Source. Built by Team Y, with the collaboration of Teams X and Z.
People with a registered card tap the reader; the door asks the server and opens or stays shut. From the panel you add users, assign cards, watch the door go online or offline, switch between reader and activation mode, and browse the logs.
Door users never log into the app. Admins and superadmins do, with different permissions (superadmins can create other admin accounts).
| Area | What you get |
|---|---|
| Users | Door users, admins, superadmins |
| Cards | Activated on the reader, assigned in the panel |
| Door | Online/offline status, reader mode sync |
| Logs | Access attempts, card events, door connectivity |
openluma/
├── apps/web/ # Admin UI (Angular)
├── apps/api/ # API (NestJS)
├── shared/ # Shared types
├── hardware/ # Arduino sketches
├── deploy/ # Production nginx + notes
├── docker-compose.yml
└── docker-compose.prod.yml
- Node.js 20+
- Docker (at least for MongoDB in dev)
- Two Arduino UNO R4 boards for the door setup (hardware/README.md)
git clone https://github.com/lascuolaopensource/openluma.git
cd openluma
npm install
cp .env.example .env
docker compose up mongo api -dThen:
npm run dev:webOpen http://localhost:4200 and sign in. On first start the API creates a superadmin from ADMIN_EMAIL, ADMIN_PASSWORD, ADMIN_FIRST_NAME, and ADMIN_LAST_NAME in .env (only if none exists yet).
docker compose up --buildApp at http://localhost:8080.
| Role | Panel login | Opens door |
|---|---|---|
| Door user | No | Yes, with assigned card |
| Admin | Yes | Yes, with card |
| Superadmin | Yes | Yes, full control |
External reader + internal WiFi board, linked over serial. Sketches live in hardware/openluma/external/ and hardware/openluma/internal/. Copy each secrets.h.example to secrets.h before flashing.
Wiring and protocol: hardware/README.md.
cp .env.example .env
docker compose -f docker-compose.prod.yml up -d --buildThe stack listens on 127.0.0.1:8080; put nginx and TLS in front. Details in deploy/README.md.
| Command | |
|---|---|
npm run dev:web |
Angular dev server |
npm run dev:api |
API watch mode |
npm run build |
Build everything |
npm test |
Tests |
npm run deploy:prod |
Production compose up |
Rotate JWT_SECRET, DEVICE_API_KEY, and NFC_AES_KEY before going live. Do not commit .env or secrets.h. The Arduino talks HTTP on port 80; see deploy/nginx-openluma.example.conf for how to proxy that without breaking the device.
Colours and type tokens are in apps/web/src/styles/. Headings use Q-Sci from the XYZ Loro Piceno workshop (OFL 1.1).
Issues and pull requests welcome. See CONTRIBUTING.md.
Application code: AGPL-3.0. Q-Sci font: SIL OFL 1.1.