Skip to content

← 5. Access rights · Contents · Next → 7. The fleet

6. Going offline

This is the heart of the project. Everything else exists so that this chapter can be short.

A wardn controller — the small box that controls one or several doors — never asks the internet for permission to open a door. It decides on its own, on the spot, using the rules it already has stored locally.

The connection to the server only exists to keep those rules up to date and to report what happened. So when that connection drops, badging keeps working exactly as before.

What an outage actually changes

Almost nothing.

While the internet is down
A badge is read ✅ works
The decision is taken ✅ works, using the rules already stored on site
The door opens ✅ works
The passage is recorded ✅ works, kept locally until it can be sent
A right changed on the server ⏸ waits on the server, delivered once the connection returns
The dashboard shows the passage ⏸ shown once it arrives
The controller appears online ❌ marked offline after ~30 seconds of silence

The controller does not switch to some lesser "offline mode." It has been deciding on its own the whole time. What actually stops, during an outage, is simply telling anyone about it — the server and the dashboard just don't hear from it for a while.

Nothing gets lost while waiting

Every passage — granted, refused, or reported late — is written down on the spot, before anything else happens. It is only crossed off that local record once the server has confirmed it received it.

This isn't a special outage behaviour: a passage that happens while everything is working normally goes through the exact same "write it down, then wait for confirmation" process as one that happens during a week-long outage.

That is deliberate. If sending passages to the server only had to work during rare outages, a bug in that path could go unnoticed for months. Because it is used constantly, a problem shows up immediately.

If the connection drops partway through sending a batch of passages, and only some of them get confirmed, the controller doesn't guess — it re-sends the whole batch rather than risk quietly dropping the ones it wasn't sure about.

The local record has a limit, on purpose

The local record can hold a large number of events (50,000 by default) before anything is at risk of being pushed out.

That might sound like a strange thing to want, but the alternative is worse: a record with no limit would eventually fill up the box's storage completely, and a box that can no longer write anything can no longer let anyone through the door at all. A door that stays shut because its brain ran out of storage is a much bigger problem than losing a handful of very old, already-late records.

So if that limit is ever reached, it's the very oldest waiting passages that get dropped, not the newest — refusing to record what's happening right now, at the door, would be worse. And it's never silent: every drop is counted, that count survives a restart, and it raises an alert so it gets noticed and investigated rather than quietly forgotten.

The controller also warns well before that point, once its local record is about 80% full, so there's time to act before anything is actually at risk.

What an event looks like

Each recorded passage carries:

  • which door and which reader
  • who (or which badge or plate) was involved
  • whether access was granted, and why
  • the exact moment the badge was presented, not the moment the controller got around to sending it

That last one matters: a passage that happened at 9am and only reached the server at 11am because the internet was down should still say 9am, not 11am.

The raw credential value (the badge number, the plate, …) only ever exists in two places: the physical credential itself, and this one message on its way to the server. Everywhere else, including the controller's own local record, only a fingerprint of it is kept, never the value itself.

Seeing the same passage twice is fine

Because the connection to the server can be unreliable, wardn's messaging guarantees a passage is delivered at least once, which in practice occasionally means twice. That's expected, not a fault: the controller doesn't try to be clever about it, and neither does the server. The server simply recognizes an exact repeat of something it already has, and quietly ignores it. Nothing ever gets counted or shown twice.

Losing a passage would be a real problem with no way back. Seeing one twice, and throwing away the duplicate, is not — so wardn accepts the second one as the far cheaper trade-off.

Telling "just now" from "we're only hearing about this now"

When the connection comes back after being down for a while, the controller sends over everything it was holding onto, oldest first. The dashboard makes a point of labelling those as replayed, because "this happened a minute ago" and "this happened three hours ago and we're only being told now" are genuinely different facts, and an operator watching the feed live should be able to tell them apart at a glance.

A controller that doesn't trust a broken clock

A controller times every passage using its own onboard clock — it has to, since it can't ask a time server while offline. But a wrong clock is dangerous: it could let someone in on a right that has already expired, or turn away someone who's still allowed in, all while quietly mislabelling records that will later be trusted as fact.

Without internet, there's no way to know for sure that the clock is right. But there is a simple, reliable way to notice when it's wrong: real time never runs backwards.

The controller keeps track of the latest moment it has already lived through. If its clock ever appears to jump backwards by more than a minute, that's proof, not a guess, that something is broken. From that point on it openly reports its clock as untrustworthy, rather than quietly carrying on as if nothing were wrong, until real time has caught back up to where it already was.

A safety net for the software itself

Separately from all of this, the controller's hardware has its own watchdog: if the software making the decisions were ever to freeze or hang, not crash, just silently stop responding, nothing else would notice on its own, and the door would simply stop working with no obvious sign why.

The watchdog's job is to catch exactly that. It needs a regular "I'm still working properly" signal from the software, and if that signal stops arriving, it resets the box automatically.

Try it

Anyone running wardn locally can see all of this happen for themselves:

make outage

This cuts the connection to the server, presents a badge while it's down, restores the connection, and checks that the passage arrives afterwards with its original time and correctly marked as replayed.


The site survives on its own. Now let us watch it from a distance.

Next → 7. The fleet