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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user