Files
KNELDoorman/README.md
T
mrcharles d853dbf33d docs(doorman): README — architecture, webhook contract, deploy runbook [#355]
Cross-links: Discourse t/318 (docs SoR), Redmine #345/#355/#356,
PFVCluster, LegacyTechops lineage.

https://community.turnsys.com/t/318
2026-09-02 21:13:07 -05:00

100 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) |