DEV Community

Cover image for I rebuilt warden's console: 16 tabs down to 6 (and found my own DB world-readable)
Christian Anderson
Christian Anderson

Posted on

I rebuilt warden's console: 16 tabs down to 6 (and found my own DB world-readable)

warden is my homelab security console — it reads the logs my WAF can't see, scans the boxes, watches my AI agents, and proposes bans and patches I approve with a ✅ in Discord. It never does anything on its own. I wrote up what it does before; this is about the cleanup I just did, because it had got messy.

It had grown 16 tabs

Every time I added something — incidents, agent tracking, vulnerabilities, endpoint integrity — it got its own tab. Dashboards grow like that. One day I counted and it was 16 tabs across four groups, and I realised I was hunting for things I used every day.

So I cut it down to 6:

  • Overview — the dashboard
  • Review — the one place for everything waiting on my ✅ / ❌
  • Threats — detections, edge, network, incidents, geo
  • Agents — what my AI agents are reaching out to
  • Posture — attack surface, vulnerabilities, endpoints, health
  • Settings

The related views sit under a little sub-nav inside each tab, so nothing's gone — it's just one click deeper. And instead of a scary number on every tab, a parent tab shows a small dot when something inside actually needs me. The real counts live where they make sense, so I don't get a meaningless "512" on a tab that's really just counting known CVEs.

warden's new six-tab console on demo data

The small things that were quietly annoying

  • The settings page listed raw config keys like patch.expire_hours and cloudflare.zone_id. Now each field has a plain label ("Approval expires after (hours)", "Cloudflare Zone ID") with the raw key dimmed underneath, so I still know what maps to the config file.
  • API tokens and the "how do I connect a tool" bits were off on their own. They live under Account now, where I'd actually look for them.
  • The approvals queue used to sit on top of the Settings page. It's its own Review tab now.

The bit that mattered: my database was world-readable

While I was in there I checked how warden stores anything sensitive. Most of it was already fine — API tokens are only kept as a SHA-256 hash, passwords are scrypt, session cookies are random and only their hash is stored, and actual secrets (bot tokens, API keys) never touch the database at all.

Then I looked at the file permissions on the database itself. 644. World-readable.

On a shared box that's a real problem: the database holds logs, session hashes, and — if you use warden's own 2FA — the TOTP seed. Anything else running on that machine as another user could just read it. In my case that includes the sandboxed coding agent I let loose on my repos, which is exactly the kind of thing warden exists to keep honest.

Fix was simple but I made it stick: warden now forces the database (and its write-ahead files) to 0600 on every start, and each process sets a umask so anything it creates is owner-only from birth. I tested it by deliberately setting it back to 644 and restarting — it put itself straight back to 600.

The lesson I keep relearning: the dangerous gaps aren't the exotic ones. It was a default file permission sitting there the whole time.

Get it

It's on GitHub, AGPL-3.0, dependency-free Python — it'll run on a Pi. The v3.2 release has the full changelog, and there's a 5-minute demo setup if you want to click around before wiring it to anything.

Still detect-only and read-only by default. It tells me what it'd do; I decide.

Top comments (0)