feat(doorman): doorctl unlock endpoint — HA->Pi callback over Tailscale [#345]

Founder-approved 2026-09-03 (relay load side disconnected during
build). systemd socket-activated, Tailscale-bound, token+source
allowlist, EXIT-trap self-securing relay. 9/9 doorctl tests (auth
matrix + relay cycle, mocked relay), full suite + shellcheck clean.

https://projects.knownelement.com/issues/345
This commit is contained in:
2026-09-03 07:04:12 -05:00
parent 37d27f1158
commit 4fefd10abb
3 changed files with 173 additions and 0 deletions
+23
View File
@@ -43,6 +43,29 @@ dropped — the door does not open. The optional local relay bridge
**off**) for the transition period; native HA webhooks always answer 200,
so that flag must stay off until HA returns real accept/deny codes.
### Door control (unlock path — built 2026-09-03, founder-approved)
Decision: HA → Pi authenticated callback over Tailscale.
```
HA automation (badge valid + input_boolean.doorman_unlock_enabled ON)
└─ rest_command.doorman_unlock
└─ GET http://pfvsvrpi.knel.net:8333/unlock/<token>
(token = DOORMAN_UNLOCK_TOKEN, on-box 0600; URL treated as secret)
└─ doorctl.socket — systemd socket activation, bound to the
Pi's Tailscale IP ONLY, per-connection doorctl@.service:
· source IP must be in DOORMAN_ALLOWED_SRC (HA + the Pi)
· relay =1 for DOORMAN_HOLD seconds, then =0
· EXIT trap guarantees the relay returns to 0 (secure)
even if the hold is killed
```
Everything is logged to syslog/journald (`-t doorctl`): every accept,
every deny with source. Disarm = flip `input_boolean.doorman_unlock_enabled`
off — valid badges then log and notify but the door stays shut.
Arming is a founder-level action; the strike side of the relay was
disconnected during build/test (founder confirmed) and gets wired onsite.
### Webhook contract (for the HA side)
`POST <DOORMAN_WEBHOOK_URL>` with JSON: