Door Behavior Contract¶
Door and reader behaviour is the single most vendor-divergent area in access control, and the most
commonly misinterpreted one. Vendors use the same words ("open", "unlock", "lock") for
physically different things. This contract fixes what each PQ door state and event means
regardless of vendor, and lists the questions every adapter must answer — with citations —
before mapping a vendor's door model. The state/action vocabulary itself is defined in
reference/functions/door.md; this document is the semantic
checklist for mapping a protocol onto it.
The three independent axes¶
A door reports on three axes that MUST NOT be conflated. A vendor "door open" message can mean any of them:
| Axis | PQ states | Physical meaning |
|---|---|---|
| Strike (access control relay) | lock.secured / lock.unsecured / lock.held |
Is the electric strike currently releasing the door? |
| Lock (entry denial) | lock.deadbolt (locked) / back to lock.secured |
Is entry denied — deadbolt, panel lockout, vendor "disable door"? |
| Position (contact sensor) | position.open / closed |
Is the door leaf physically open? |
The classic trap: mapping a vendor contact event ("door opened") to a strike action
(Unsecured) or vice versa. They are different sensors with different audit meanings — a strike
release without a position change means nobody walked through; a position change without a strike
release is a forced entry (intrusion.forced, detected by Opened() automatically).
Command semantics: pulse vs hold-open vs lock¶
Vendor door commands fall into three families that MUST NOT be mixed up:
- Pulse / momentary release ("open door", "remote open", "strike", "relay pulse", "door
release") →
pq.command.access.open, confirmed byUnsecuredRemote(...). The door re-secures itself after a timeout. - Hold open (hold open by schedule/command) →
pq.command.access.open.permanent(lock.held) — a persistent state until cancelled bypq.command.access.close(display "Close", back tolock.secured). - Lock / unlock —
pq.command.access.lockmeans entry denied, cards included (lock.deadbolt);pq.command.access.unlockis strictly its inverse (back to normal operation,lock.secured) — it does NOT cancel hold-open. Whether the vendor implements the denial as a motorized deadbolt, a panel lockout, or a "disable/block door" mode is a wire detail — a PQ door deliberately has ONE denied state, not a locked/disabled pair. Vendor "disable door" commands map here;pq.command.access.disable/.enableare for non-door devices only (readers, modules, terminals). Registeraccess.lockONLY when the vendor verifiably denies a valid credential while locked — a lock button that does not actually deny is worse than no button (the operator believes the door is denied when it is not). If the protocol has no such command, omitaccess.lockand document the limitation; denying a person is then an access-rights change, not a door command.
Sending a mode command where the operator meant a pulse leaves a door standing open; the reverse leaves an operator believing a door is held open when it has already re-secured. If the vendor docs do not state which family a command belongs to (payload field, separate command codes, duration parameter), that is a gap, not a judgment call. The full vendor-command → PQ-command decision table lives in the architect's Door Control Taxonomy (and is enforced by RULE-038).
Event attribution: who released the door, and who decided¶
Audit value lives in attribution. The contract:
Unsecured(...)— the device released the strike for a credential (access grant by panel or by PQ). Carries the person when known.UnsecuredRemote(...)— an operator released it (command path). Never use it for credential-driven or REX releases.UnsecuredByRex(...)— egress hardware (REX button/PIR). Never collapse REX intoUnsecuredorUnsecuredRemote: "person left" vs "operator opened" vs "credential entered" are three different audit facts.- A vendor "access granted" event must be classified by who decided: the panel autonomously (offline decision — the event is the source of truth, map with the credential identity it carries), or PQ via the adapter (online decision — the adapter already knows the identity; the device event is a confirmation, beware double-publishing).
Assembly link requirements¶
A door is a logical assembly (reader(s) + strike output + optional contact + optional REX) — see
access-control-assembly.md. The contract adds: the
reader↔door link must be explicit (parent-child, deviceRef, or a documented relation
resolver). If the protocol has no first-class door object and the binding is implicit (shared id
namespace, installer-side configuration), that implicitness must be documented in the adapter's
design-notes (known quirks) and surfaced as a gap — never reverse-engineered silently from
one observed device.
Questions to answer (mandatory for any protocol with doors/readers)¶
Every question answered with a citation, or recorded as a gap:
- VQ-DOOR-1 — door object. Does the protocol have a first-class door object, or only implicit wiring (reader + relay + contact addressed separately)?
- VQ-DOOR-2 — binding. How does the protocol link readers to doors (and strike outputs to doors)? Shared id namespace, configuration upload, API call, fixed hardware slots?
- VQ-DOOR-3 — pulse vs hold-open vs lock. Which door commands are momentary (pulse, self-restoring), which hold the door open, and which deny entry (deadbolt, lockout, vendor "disable door")? What is the pulse duration and is it configurable?
- VQ-DOOR-4 — contact vs strike in events. For each door-related event: does it report the strike, the deadbolt, or the position axis? Does the device emit distinct forced-open / held-open alarms, or must the adapter derive them?
- VQ-DOOR-5 — REX. Is REX a distinct event (traceable separately from access grant and from operator open)? Does a REX press release the strike autonomously?
- VQ-DOOR-6 — decision source. Are access decisions made by the panel (offline) or by the host (online)? What identity data do grant/deny events carry in each mode?
Review checklist (enforced by RULE-038 in compliance-rules/commands.md)¶
- Strike, deadbolt, and position events route to different Door actions; no contact event maps to a strike action or vice versa.
- Pulse commands map to
access.open+Unsecured*, hold-open toaccess.open.permanentwith its cancel onaccess.close+Secured*, entry-denial (deadbolt, lockout, vendor "disable door") toaccess.lock+Locked*, and its undo toaccess.unlock+Unlocked*— each with a citation for the vendor command's family.access.disable/.enablenever appear on a door. - REX, operator, and credential releases use their three distinct actions.
- The reader↔door link mechanism is explicit in the model; implicit vendor binding is documented as a known quirk.
- Grant/deny decision source (panel vs host) is stated in the adapter's design-notes; no double-publishing of access events on the online path.