Cross-links: Discourse t/318 (docs SoR), Redmine #345/#355/#356, PFVCluster, LegacyTechops lineage. https://community.turnsys.com/t/318
100 lines
4.2 KiB
Markdown
100 lines
4.2 KiB
Markdown
# doorman — badge access, modernized
|
||
|
||
Physical access control for the server-room door: badge-reader listener
|
||
for the reader host, dispatching scans to Home Assistant, which owns the
|
||
whitelist, logging, alerts, and unlock decision.
|
||
|
||
**Dedicated docs topic: [Discourse t/318](https://community.turnsys.com/t/318)**
|
||
· Tracking: [Redmine #345](https://projects.knownelement.com/issues/345)
|
||
(umbrella) → [#355](https://projects.knownelement.com/issues/355)
|
||
(code & testing) → [#356](https://projects.knownelement.com/issues/356)
|
||
(deployment) · Ops status: [Discourse t/297](https://community.turnsys.com/t/297)
|
||
· Fleet context: [KNEL/PFVCluster](https://git.knownelement.com/KNEL/PFVCluster)
|
||
|
||
## Lineage
|
||
|
||
Successor of the 2018 Perl doorman
|
||
([KNEL/LegacyTechops `doorman/`](https://git.knownelement.com/KNEL/LegacyTechops/src/branch/main/doorman/doorman.pl)),
|
||
carried verbatim under [`legacy/`](legacy/doorman.pl). Same reader class
|
||
(keyboard-emulating USB RFID), same decode semantics — but the defunct
|
||
plain-HTTP auth portal (`doors.pfv.turnsys.net`) is NOT replicated: HA is
|
||
the single authentication brain.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
USB RFID reader (HID keyboard)
|
||
└─> /dev/input/by-id/*-event-kbd (reader host: pfvsvrpi prod,
|
||
| ultix-field dev)
|
||
v
|
||
bin/doorman.sh od(1) + awk(1) decode — bash and
|
||
| coreutils ONLY, no package installs
|
||
v
|
||
POST {badge_id, reader, ts} -----> Home Assistant webhook
|
||
|– whitelist decision
|
||
|– logbook/recorder history
|
||
|– unknown badge -> instant alert
|
||
'- unlock (e-lock or relay)
|
||
```
|
||
|
||
Fail-closed by construction: if HA is unreachable, the scan is logged and
|
||
dropped — the door does not open. The optional local relay bridge
|
||
(`usbrelay`, 2018 lineage) exists behind `DOORMAN_UNLOCK_ON_2XX` (default
|
||
**off**) for the transition period; native HA webhooks always answer 200,
|
||
so that flag must stay off until HA returns real accept/deny codes.
|
||
|
||
### Webhook contract (for the HA side)
|
||
|
||
`POST <DOORMAN_WEBHOOK_URL>` with JSON:
|
||
|
||
```json
|
||
{"badge_id": "0009399422", "reader": "pfvsvrpi", "ts": "2026-09-02T19:12:44-0500"}
|
||
```
|
||
|
||
- Values are digits + env-provided strings; the JSON cannot carry
|
||
user-controlled quotes/backslashes.
|
||
- Any 2xx satisfies the listener; HA automations do the real work.
|
||
- Configure the HA webhook trigger to this URL; the token lives in the
|
||
URL path — treat it as a secret, keep it in `/etc/default/doorman`
|
||
(0600) on the reader host, never in git.
|
||
|
||
## Deploy (reader host)
|
||
|
||
```bash
|
||
sudo mkdir -p /opt/doorman
|
||
sudo cp bin/doorman.sh /opt/doorman/bin/ # + this repo, or rsync
|
||
sudo cp deploy/doorman.service /etc/systemd/system/
|
||
sudo cp .env.example /etc/default/doorman # fill in, chown root:root
|
||
sudo chmod 600 /etc/default/doorman
|
||
sudo systemctl daemon-reload && sudo systemctl enable --now doorman
|
||
```
|
||
|
||
Record width auto-detects (24 B on 64-bit kernels, 16 B on 32-bit Pis).
|
||
Logs go to syslog/journald (`-t doorman`); `DOORMAN_DEBUG=true` mirrors
|
||
to stderr. Deployment discipline: **ultix-field (dev) first, verify
|
||
end-to-end, then pfvsvrpi (prod)** — never both at once (#356).
|
||
|
||
## Testing (no hardware needed)
|
||
|
||
```bash
|
||
bash scripts/test.sh # decoder suite: synthetic event fixtures
|
||
bash tests/shellcheck.sh # lint gate (docker, zero-warning)
|
||
bash scripts/check-rules.sh # full rule audit
|
||
```
|
||
|
||
`--selftest` mode decodes a fixture file and prints one badge ID per
|
||
line — that is the entire test surface of the decoder, including the
|
||
2018 quirks we intentionally preserve (usage passthrough 0-9, bad-usage
|
||
parity-anchor behavior, empty-scan suppression).
|
||
|
||
## Repo map
|
||
|
||
| Path | What |
|
||
|------|------|
|
||
| `bin/doorman.sh` | The listener daemon |
|
||
| `deploy/doorman.service` | systemd unit (`/etc/default/doorman` env file) |
|
||
| `tests/run-tests.sh` | Offline decoder suite (9 tests) |
|
||
| `legacy/` | Verbatim 2018 Perl snapshot (not maintained) |
|
||
| `scripts/` | Rule engine + hooks (adopted from TSYSGroupAIOS) |
|
||
| `questions-v1.md` | Points to the active round (PFVCluster questions-v4.md) |
|