Skip to content
This repository was archived by the owner on Aug 24, 2026. It is now read-only.

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hearth

I built this to replace the stock web UI on my GL-MT6000 after flashing vanilla OpenWrt onto it. It's a Vue 3 single-page app talking to ten rpcd exec plugins written in ucode, over ubus. No Lua, no LuCI, nothing vendored.

This is a snapshot of what was running in my house on the day I pushed it. It isn't maintained. Read Status and SECURITY.md before you put it on anything.

What it looks like

The dashboard, the WiFi page, and the client list.

hearth dashboard

hearth WiFi page

hearth client list

Every address, MAC, SSID and device name in those is invented. They were captured from my own router and then rewritten: the subnets are the ones profiles/example-three-zone.sh sets, the WAN address comes from TEST-NET-3, and the MACs are locally administered.

What it replaces

GL.iNet's stock UI. Their firmware ran behind upstream OpenWrt, and the panel showed a subset of what the box could do while hiding the parts I wanted to reach. Flashing vanilla fixed the first half of that and left me with LuCI, which is capable and general and a great deal larger than one house needs.

So hearth covers what a home router actually gets used for: wireless, LAN addressing and DHCP, DNS, VLANs, the WAN uplink, port forwards, a client list with naming and blocking, Tailscale, WireGuard as both client and server with policy routing, a backup uplink with automatic failover, firmware flashing and config backup, plus the usual system settings. It doesn't try to match LuCI's breadth. It does replace LuCI rather than sit beside it, because the install removes the LuCI stack: two admin planes on one box means the smaller one's safety rails can be walked around by way of the larger one.

Any change that could cut you off from the router (WiFi, LAN address, WAN, VLANs, blocking a client) arms a 90-second confirm-or-revert window. If the change locks you out, it rolls back on its own. A boot-time unit restores the snapshot too, so losing power inside the window doesn't strand you either.

What it runs on

A GL-MT6000 (Flint 2, mediatek/filogic) on OpenWrt 25.12.5, r33051-f5dae5ece4. The backend is plain ucode against ubus, uci, and the standard OpenWrt service scripts, so in principle nothing here is tied to that board. In practice I've run it on exactly one model, and if you're porting it, the places to look are the LAN port names in panel.network (lan1 through lan5), the LED helper, and anywhere two radios are assumed.

packaging/bootstrap.sh installs the runtime packages the panel expects: uhttpd-mod-ubus, ucode-mod-ubus, ucode-mod-uci, iwinfo, cgi-io, wireguard-tools, qrencode, mwan3, owut, pbr, stubby.

Layout

frontend/       Vue 3 SPA, built with Vite
backend/        ten rpcd exec plugins (ucode), one per panel section
acl/            rpcd ACL: per-object, per-method grants for the hearth role
packaging/      bootstrap, deploy, the boot-revert init script, helper binaries
profiles/       executable router identities and a verification suite
Documentation/  the user guide, also served inside the panel under Help

Installing

You'll need a router already flashed with vanilla OpenWrt and reachable over SSH, an SSH keypair on your workstation, and Node to build the frontend. Set aside an hour, and do it while you can reach the router with a cable, because the profile renumbers your LAN partway through and you will lose your connection when it does.

Build the frontend. This produces a static bundle that deploy.sh pushes later:

cd frontend && npm ci && npm run build

Set your WiFi passphrases. The profile reads them from a file that isn't in the repo and shouldn't be:

cp profiles/production.secrets.sh.example profiles/production.secrets.sh
$EDITOR profiles/production.secrets.sh

Now open profiles/example-three-zone.sh and read the whole thing. It's the worked example: a trusted LAN, an isolated segment for anything with a WAN forward pointed at it, and a guest network, with the firewall policy that keeps the three apart. Everything marked EDIT is a placeholder that will not work on your network, and the SSH key block will stop the run if you leave it alone. If you want to understand what the panel assumes about the world around it, that file teaches more than the Vue code does.

Edit the subnets and SSIDs at the top of profiles/verify.sh to match while you're there. Bootstrap puts that script on the router before it applies anything, so what it says at that point is what you'll be checking against afterward. Left alone it asserts the shipped placeholders and fails on nearly everything.

Then run the bootstrap, which does everything on the router:

sh packaging/bootstrap.sh <router-ip> profiles/example-three-zone.sh

It installs your SSH public key first, then stops twice to ask for passwords. The first is root's, the second is the panel's own hearth account. Both are typed straight through to the router over an interactive session, so neither one passes through the script, a variable, a log, or this repository. If your key isn't at ~/.ssh/id_ed25519.pub, point HEARTH_SSH_KEY at it. Bootstrap turns off SSH password authentication right after setting the root password, so that key is your only way back in.

After the passwords it pulls the runtime packages, removes the LuCI stack, drops the default root rpcd login so /ubus authenticates only the scoped hearth user, and generates a self-signed certificate.

Then it stages /root on the router with the profile, your secrets file, and verify.sh, and only then applies the profile. The profile renumbers the LAN at the end and the ssh session running it dies there, so the window for copying anything to the box closes at the same moment you start needing things on it. /root rather than /tmp for the same reason one step further out: /tmp is tmpfs, and rebooting the box is exactly what you'll try when it stops answering.

The apply runs detached, so the renumber kills the session without killing the run. It logs to /root/hearth-profile.log, ending in an EXIT= line. Renew your DHCP lease, and you'll find the router at whatever address your profile set:

ssh root@<new-router-ip> cat /root/hearth-profile.log

EXIT=0 means the profile finished. Bootstrap deletes the staged secrets file once the run ends, and leaves the profile and verify.sh in /root so you can re-run either one from the console if the network side goes wrong.

Then push the panel itself, at the new address:

sh packaging/deploy.sh <new-router-ip>

That copies the backend plugins, the ACL, the helper binaries, the built frontend, and the boot-revert unit, then restarts rpcd and uhttpd.

The panel is at https://<new-router-ip>/hearth/. Sign in as hearth with the password bootstrap asked you for. Your browser will complain about the certificate, because it's self-signed; profiles/make-panel-cert.sh writes one with a proper subjectAltName if that bothers you.

Last, check what you actually got. verify.sh is already on the router from the staging step, and it's read-only:

ssh root@<new-router-ip> sh /root/verify.sh

It prints PASS or FAIL per check against the values you put at the top of it, and exits non-zero if anything failed.

The deployment this assumes

Everything above, and the security review behind it, assumes a particular posture. Depart from it and the reasoning stops holding.

The panel binds the LAN address only, never 0.0.0.0 and never the WAN address. It isn't reachable from the internet and no part of it was designed to be. The WAN zone rejects input.

There's one administrator, which is me. No multi-user model, no roles, no audit trail of who changed what. The hearth account is an authentication principal rather than a privilege boundary: rpcd runs every plugin as root, so an authenticated session has root-equivalent control of the router through the panel's own verbs.

SSH is key-only, password auth disabled for root and everyone else. That carries more weight than it sounds like, because SSH is your way back in when a panel change goes wrong.

Status

A snapshot as of its commit date, built for one house, running in production on exactly one router.

It isn't maintained. No releases, no fixes, no security updates. The repo is archived on GitHub, so issues and pull requests are closed by construction and patches aren't accepted. If you want to take it further, fork it. That's what the license is for.

Security

I ran a bounded review before publishing this, and it didn't cover everything. What I examined, what I skipped, and the limitations you inherit by installing it are in SECURITY.md. There's no vulnerability reporting path here, and that file says what to do instead.

License

GPL-2.0, matching OpenWrt itself. Copyright (C) 2026 denmojo. Full text in LICENSE.

The copyleft is deliberate. hearth exists because a vendor shipped a closed UI that fell short of the hardware underneath it, and GPL-2.0 is what stops the replacement being closed and shipped on the next box.

Dependencies are Vue at runtime, plus Vite and its Vue plugin to build, all MIT. Nothing else is vendored.

About

A small OpenWrt admin panel: Vue 3 SPA over ten rpcd ucode plugins. Archived, unmaintained snapshot from one household router.

Topics

Resources

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages