Deadbolt — a padlock with an amber touch-point
Security engineering · disk encryption · human-in-the-loop

Deadbolt

A physical release gate for my own infrastructure. Data on my NAS lives in an encrypted vault that only opens when I physically touch a YubiKey plugged into the machine — and a desktop security console shows me every request: who asked, from where, and when.

1 touchopens the vault in normal operation — no password, no remote path (recovery passphrase kept offline)
500 GBLUKS2-encrypted vault serving a normal Windows network share
0 trustin reported state — the daemon proves a seal before claiming it

What it does

The problem. A NAS on your network will hand its files to anything that presents the right credentials — malware included. Credentials can be stolen, sessions can be hijacked, and nothing about a network protocol proves a person is present and consenting. The same gap exists one layer up: if you distribute demo builds of your software, a public download link gives you no idea who is taking what.

The solution. Put the data behind hardware. Deadbolt keeps sensitive files in a LUKS2-encrypted container whose key is derived from a YubiKey challenge–response that refuses to answer until the key's touch sensor is physically pressed. A small daemon on the NAS owns the state machine; a desktop console on my PC surfaces every unlock request the moment it arrives — with the requester's name and source address — and makes the "touch now" window unmissable. The same request → notify → approve pattern extends to my website: visitors ask for a demo build by name, my phone pings, and nothing downloads until I approve it.

The value. Data at rest is ciphertext until a human touches hardware. Every release of data — a file over SMB, a demo build over HTTPS — is a deliberate, attributable, logged act. A leaked token can only ask; it shows up in the audit trail either way.

stack: Python (stdlib-only daemon) · PySide6 console · LUKS2 + dm-crypt · YubiKey HMAC-SHA1 challenge–response · Samba · Cloudflare Pages Functions + KV + R2 · ntfy

See it in action

The console during a live unlock — captured against the daemon's mock backend, which exists precisely so the whole UX can be exercised without hardware.

screenshot Deadbolt console showing the amber WAITING FOR TOUCH state with a live countdown bar and audit log
The moment that matters. A request arrived — on the NAS, the key would be blinking — and there are 13 seconds left to touch it. If this wasn't you — you simply don't touch, and the request times out into the audit log.
screenshot Deadbolt console in the LOCKED state
Sealed. The share serves nothing; the vault is ciphertext on disk.
screenshot Deadbolt console in the UNLOCKED state with the audit trail of the unlock
Open — deliberately. The full chain is in the audit log: requested → waiting → touch registered → mounted.
screenshot Deadbolt console Requests tab listing pending demo-download requests with approve and deny buttons
The same gate, one layer up. Demo-build requests from this website land in the console (sample data shown); approving mints a download link good for 24 hours or three downloads, whichever comes first.

From idea to a live vault

Built and deployed onto a stock UGREEN DXP2800 NAS — no OS wipe, no vendor encryption, everything brought along.

Proof, not promises

Every claim above was exercised against the real system, not a slide.

ClaimHow it was verifiedResult
Touch is the gate unlock request → key blinks → touch → decrypted and mounted; no touch → sealed at 15 s verified live
Files exist only while open write over SMB → seal → share lists nothing; kernel mount table + mapper checked verified live
A seal claim is proven, not assumed open-handle EBUSY scenario re-run after the fix; LOCKED is never claimed without /proc/mounts proof verified live
State machine holds under abuse 10-check FSM smoke suite: idempotent re-lock, lock-during-unlock refused (409), timeout revert 10 / 10
Web approval flow end-to-end 82-assertion API suite + rate-cap boundary test + console-to-download finale (real console × local Cloudflare runtime, pre-launch) all green

Engineering notes

The decisions that made it trustworthy.

The first live seal attempt caught a real bug — a vault that claimed to be sealed while it wasn't — because verification ran against the real system. That's the point of building this way: the proof and the product are the same artifact.