wardn documentation¶
Version: v1.2.0
wardn is a physical access control system. A door opens for a credential. It keeps deciding on its own when the network is down, and the server takes charge again the moment it comes back.
- A door never waits on the network. The decision happens on site, in milliseconds. Cut the uplink for a week and a badge still opens the door, exactly as before.
- Encrypted, end to end. Every link between a controller and the server is mutually authenticated and encrypted. A stolen controller holds hashes, not plaintext badge numbers or plates.
- No ceremony to install. One command starts the whole stack. A badge opens a door within ten minutes, with nothing to configure by hand.
- Runs on modest hardware. A small box on a DIN rail, next to the circuit breakers. No GPU, no constant cloud connection, just enough compute to hold a database and drive a relay.
- Self-hostable, top to bottom. The broker, the database, the object store, the identity provider: every piece runs on infrastructure you control, or in the cloud you already pay for. Nothing calls home.
- Built to be extended. Credential types are free-form, a right carries its own metadata, and hardware sits behind a small interface. A site with a scenario nobody anticipated still has a place to put it.
A door here is anything that opens for a credential. An office door, a parking lane, a yard gate, a turnstile. wardn treats them all the same way. From the controller's point of view, they are the same thing.
What this is not. wardn does not aim to replace a full access control platform such as Nedap AEOS or Genetec Security Center. It solves one problem precisely instead of many problems approximately. The model is small and opinionated on purpose, shaped by years of integrating with software that each did one thing well and left the rest to somebody else. Expect a narrow, well-defined scope. Do not expect the feature checklist of a large access control suite.
This documentation is the reference for the system. It assumes no prior knowledge of the project, of access control, or of the technologies involved. It is written to be read on its own. Everything stated here is what the software actually does today.
Where to start¶
| You are… | Start at… |
|---|---|
| Curious, you want to understand the project | 1. The problem |
| A developer, you want it running this morning | 2. Getting started |
| An architect, evaluating the design | 3. Architecture |
| An integrator, wiring your software into it | 9. The API |
| An operator, keeping it alive | 12. Operating |
| A contributor, about to write code | 13. Developing |
| Ready to run it on AWS | 14. Deploying to production |
| Running a pilot on the smallest possible bill | 15. A near-free deployment |
| Building a physical rig to test controllers against real hardware | 16. The lab suitcase |
| Signing off a deployment as secure, on any platform | 18. The security agreement |
| Building a badge and PIN reader yourself, from zero | 19. A DIY reader |
| Building the door that reader opens | 20. A DIY door |
| Powering the whole bench from one mains plug | 21. A DIY power supply |
The full path¶
flowchart TD
subgraph U["Understand"]
A["1. The problem"] --> B["2. Getting started"] --> C["3. Architecture"]
end
subgraph F["Feature by feature"]
D["4. A decision"] --> E["5. Access rights"] --> G["6. Going offline"]
G --> H["7. The fleet"] --> I["8. The dashboard"] --> J["9. The API"]
end
subgraph X["Cross-cutting"]
K["10. Security"] --> L["11. Personal data"]
L --> M["12. Operating"] --> N["13. Developing"] --> O["14. Deploying to production"]
O --> P["15. A near-free deployment"]
O --> S["18. The security agreement"]
end
subgraph HW["Hardware lab"]
Q["16. The lab suitcase"] --> R["17. The all-in-one reader"]
Q --> T["19. A DIY reader"]
end
P --> Q
C --> D
J --> K Understand
- The problem. Why a door is an interesting problem for software, and the one idea wardn is built around.
- Getting started. Install it, run it, open a door with a badge. Ten minutes, nothing to configure.
- Architecture. The six components, the message channels between them, and why they are kept separate.
Feature by feature
- A decision. What happens between the badge presented and the door rising, rule by rule.
- Access rights. Who may enter, how you say so, how it reaches the controller, and how the two ends stay in agreement.
- Going offline. What happens when the network drops. This is the heart of the project.
- The fleet. Watching controllers, driving them remotely, updating them over the air.
- The dashboard. The screens, and the live feed.
- The API. The complete integration reference: endpoints, authentication, provisioning, errors, pagination, the live socket.
Cross-cutting
- Security. Identities, certificates, confinement between customers, and the limits that are still open.
- Personal data. The three journals, retention, right of access, right to erasure.
- Operating. Deployment, backups, restores, certificates, monitoring, and the complete configuration reference.
- Developing. Repository layout, tests, the executable scenarios, continuous integration.
- Deploying to production. The same stack on AWS: IoT Core and Greengrass, RDS, S3, CloudFront, and a Helm chart for the backend.
- A near-free deployment. The same stack again, on free and near-free tiers: Cloud Run, Supabase, Cloudflare R2 and Pages, Auth0, AWS IoT Core.
Hardware lab
- The lab suitcase. A portable rig for testing a controller against real readers and real doors over a genuine RS-485 bus, component by component, and why each one is there.
- The all-in-one reader. Exploratory: folding the reader and the controller into a single door-mounted unit, and what that would and would not change.
- A DIY reader. A beginner's build log: a Pico, an RC522, a keypad, a buzzer and a screen on one breadboard, then a door pulse and OSDP to a controller, step by step, with every mistake and its fix.
- A DIY door. The sequel: a second Pico plays a door on a half breadboard, opened by the reader's wire or by an OSDP order, reporting what it does, and powered with the reader from one USB port.
- A DIY power supply. The third part: one 12 V brick and three converters, each set with a meter before anything touches it, feed the Raspberry Pi, the reader and the door.
Going live
- The security agreement. What the product secures, what the deployment must secure on top of it, and the checklist to sign before real doors depend on either.
Reference
- OpenAPI reference. The full HTTP surface, generated from the code.
- MQTT reference. Every topic between a controller and the server, and the shape of every message.
Two reading conventions¶
Blocks marked like this explain a design choice that would otherwise look arbitrary:
Why this way? The server never decides an opening. It could, technically. But a door that waits for an answer from the network is a door that stays shut the day the 4G link goes down.
And these state a real limit of the system as it stands today:
⚠️ Known limit. OSDP Secure Channel is not implemented. Frames on the RS-485 bus are in the clear.
Neither is decoration. An access control system that hides its limits is a system whose limits get discovered at the worst possible moment. Every known limit in this documentation is collected in 10. Security → What is not there yet.
Conventions used throughout¶
- Instants are stored in UTC. Every zone carries an IANA timezone, and that is what an instant is rendered in. A passage at the Lisboa site read in Brussels time is the wrong hour.
edge-01is a device identifier, the name painted on the controller and the name it publishes under. The UUID beside it exists only in the database. Both appear, because an operator on site reads one, and a log carries the other.- Shell examples assume the local stack, started with
make up, with the default ports from.env.example.