Install oonfeeWRT
Upgrade caution: v0.1.8 keeps v0.1.6 and v0.1.7 on schema 25. Before opening existing state, preserve a matching database, keyring, runtime passphrase, and old binary/image. Returning to v0.1.5 requires its pre-upgrade schema-23 recovery unit. Follow migration and rollback. The optional read-only rpcd helper is manually packaged and installed; ordinary adoption remains agent-free. Firmware checks do not flash routers. The isolated demo is the no-router preview path.
oonfeeWRT is a controller that runs on a computer, NAS, or server. It does not replace OpenWrt firmware and no controller binary runs on a router.
This guide targets v0.1.8, with controller schema 25. Publication and artifact availability are established only by the completed exact-tag workflow and GitHub release, not source preparation or a tag alone. Back up both the controller and each router before using it on a network you cannot afford to interrupt. Upgrade and rollback from historical v0.1.0-rc.1 are documented below.
What can change a router
Nothing is installed merely by starting the controller, scanning, or adding a device address. Router changes are separate, default-off actions:
- Controller access payload during adoption: after an explicit prompt, one rpcd ACL JSON file and one scoped
oonfeewrtlogin are added. This is not a package, executable, daemon, service, or firmware change. The supplied administrator credential is used for that SSH action and is not stored. A Managed device receives the managed ACL group; a Monitor only device receives the distinct read-onlyoonfeewrt-monitorgroup. - LLDP capability: after adoption, a separate workflow may install the official OpenWrt
lldpdpackage and its feed dependencies. Refreshing the package index, installing the displayed exact plan, and configuring physical interfaces each require their own acknowledgement. Rollback restores the recorded configuration and service state and removes only packages recorded as additions. - Network configuration: WLAN, network, DHCP, firewall, and explicit IPv6 policy changes occur only after Preview and Apply. They are controller desired state, not hidden adoption side effects. Existing networks upgrade to Router managed, which leaves their IPv6 settings unchanged. Explicit Prefix delegation or Disabled policy is the sole narrow exception to section ownership: it may patch allowlisted options on the exact existing management LAN, its single matching DHCP section, and conventional
wan/wan6; it never creates, claims, renames, or deletes those foreign sections, and ambiguous or conflicting layouts block Preview.
Monitor-only devices remain available to polling, inventory, telemetry, events, and topology, including across routed management subnets. They are excluded from Preview, Apply, desired/site configuration, optional LLDP installation/configuration/removal, wireless-neighbor mutations, and other package/config/remove operations. Existing LLDP observation, scoped ACL maintenance, and un-adoption remain available. The controller host must already reach their SSH and HTTP or HTTPS /ubus endpoints; oonfeeWRT creates no route or VPN.
A supported Monitor-only radio can still run a separately acknowledged RF scan. This is an active, transient observation that may interrupt clients while the serving radio is off-channel; it has no intended persistent configuration change.
Un-adoption removes the scoped login and ACL. It is blocked until any recorded LLDP capability is rolled back, so package residue cannot be silently orphaned.
Choose a controller host
Use a supported 64-bit Linux or macOS host on the management LAN:
linux/amd64,linux/arm64,darwin/amd64, ordarwin/arm64for binaries;linux/amd64orlinux/arm64for the container image;- network reachability from the controller to each OpenWrt management address.
The controller's HTTP listener has no native TLS. Bind it to loopback by default. Use a trusted reverse proxy for TLS or deliberately bind to a trusted, isolated management LAN.
Install the binary
Set the release and platform. On macOS, uname -m reports x86_64 rather than the archive's amd64, so normalize it:
VERSION=v0.1.8
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$(uname -m)" in
x86_64) ARCH=amd64 ;;
arm64|aarch64) ARCH=arm64 ;;
*) echo "unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac
NAME="oonfeewrt_${VERSION#v}_${OS}_${ARCH}"
BASE="https://github.com/aiden0rchad/oonfeeWRT/releases/download/$VERSION"
curl --fail --location --remote-name "$BASE/$NAME.tar.gz"
curl --fail --location --remote-name "$BASE/SHA256SUMS"
grep " $NAME.tar.gz\$" SHA256SUMS > "$NAME.sha256"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum --check "$NAME.sha256"
else
shasum -a 256 --check "$NAME.sha256"
fi
tar -xzf "$NAME.tar.gz"
"$NAME/oonfeewrtd" -version2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
The macOS binaries are not Developer ID signed or notarized. Use the checksum-verified archive above; do not bypass a checksum mismatch.
Keep the extracted binary in that directory or install it on PATH:
sudo install -m 0755 "$NAME/oonfeewrtd" /usr/local/bin/oonfeewrtd
sudo install -m 0755 "$NAME/oonfeewrt-recoverycheck" \
/usr/local/bin/oonfeewrt-recoverycheck2
3
Create separate private directories for persistent state and the unattended passphrase. The database and keyring.json are created in the data directory. Keep that directory mode 0700. v0.1.0 and later force newly created SQLite database, WAL, and SHM files to mode 0600. Historical v0.1.0-rc.1 relied on the process umask for its sidecars, so keep umask 077 during an RC upgrade.
install -d -m 0700 "$HOME/.local/share/oonfeewrt" "$HOME/.config/oonfeewrt"
umask 077
head -c 32 /dev/urandom | base64 > "$HOME/.config/oonfeewrt/passphrase"
chmod 600 "$HOME/.config/oonfeewrt/passphrase"
oonfeewrtd \
-data-dir "$HOME/.local/share/oonfeewrt" \
-passphrase-file "$HOME/.config/oonfeewrt/passphrase" \
-listen 127.0.0.1:80802
3
4
5
6
7
8
9
Open http://127.0.0.1:8080 and create the first owner. The generated passphrase enables unattended restarts: it is no longer an independent second factor, so protect it with host permissions and backups. Setting OONFEE_PASSPHRASE is rejected; secrets never belong in environment values.
For an interactive start, omit -passphrase-file. A terminal prompts twice on first run and once after each restart.
Run the container
After publication, the version-pinned release image is ghcr.io/aiden0rchad/oonfeewrt:v0.1.8. It is multi-platform, defaults to UID 65532, and has no shell or package manager. The command below instead uses your non-root host UID with bind-mounted state, which keeps permissions and backups straightforward on both Linux and Docker Desktop.
Install cosign from the official Sigstore instructions, then verify the GitHub Actions keyless identity before first use. At publication, the 0.1.8, 0.1, and latest aliases resolve to the same final manifest, but the rolling 0.1 and latest aliases can advance. Pin v0.1.8 for the release version, or its verified digest for immutable deployment identity.
[ "$(id -u)" -ne 0 ] || { echo "run Docker as a non-root user" >&2; exit 1; }
install -d -m 0700 \
"$HOME/.config/oonfeewrt" \
"$HOME/.local/share/oonfeewrt-container"
umask 077
head -c 32 /dev/urandom | base64 > "$HOME/.config/oonfeewrt/passphrase"
chmod 600 "$HOME/.config/oonfeewrt/passphrase"2
3
4
5
6
7
Bridge mode works on Linux and Docker Desktop. It intentionally publishes only to host loopback. The shipped discovery path is a bounded TCP /ubus scan, not ARP or mDNS; a bridge usually does not expose the router's LAN subnet, so adopt by address.
cosign verify \
--certificate-identity "https://github.com/aiden0rchad/oonfeeWRT/.github/workflows/release.yml@refs/tags/v0.1.8" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/aiden0rchad/oonfeewrt:v0.1.8
docker run -d \
--name oonfeewrt \
--restart unless-stopped \
--read-only \
--user "$(id -u):$(id -g)" \
--cap-drop ALL \
--security-opt no-new-privileges:true \
--stop-timeout 150 \
--tmpfs "/tmp:rw,noexec,nosuid,nodev,uid=$(id -u),gid=$(id -g),mode=0700" \
-p 127.0.0.1:8080:8080 \
--mount "type=bind,source=$HOME/.local/share/oonfeewrt-container,target=/data" \
--mount "type=bind,source=$HOME/.config/oonfeewrt/passphrase,target=/run/secrets/oonfee-passphrase,readonly" \
-e OONFEE_DATA_DIR=/data \
-e OONFEE_LISTEN=:8080 \
-e OONFEE_PASSPHRASE_FILE=/run/secrets/oonfee-passphrase \
ghcr.io/aiden0rchad/oonfeewrt:v0.1.82
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
To use Compose instead of the direct docker run command, create a private deployment directory, download the exact v0.1.8 file, and create its dedicated mode-0600 passphrase. The service uses the same release image, a named data volume, and a loopback-default bridge mapping. Its OONFEE_HTTP_BIND override can select one deliberate management address; host networking remains an explicit Linux-only edit documented in the file.
install -d -m 0700 oonfeewrt-compose
cd oonfeewrt-compose
umask 077
curl --fail --location \
--output docker-compose.yml \
https://raw.githubusercontent.com/aiden0rchad/oonfeeWRT/v0.1.8/deploy/docker-compose.yml
head -c 32 /dev/urandom | base64 > passphrase
sudo chown 65532:65532 passphrase
sudo chmod 600 passphrase
printf '%s\n' \
'OONFEE_VERSION=v0.1.8' \
'OONFEE_HTTP_BIND=127.0.0.1' > .env
chmod 600 .env
docker compose up -d2
3
4
5
6
7
8
9
10
11
12
13
14
15
The v0.1.8 Compose file keeps the loopback default but accepts OONFEE_HTTP_BIND=<controller-LAN-IP> when browsers must connect directly from a trusted management LAN. This changes only host-side publishing. OONFEE_HTTP_BIND=0.0.0.0 explicitly publishes on every host IPv4 interface; it is not a browser URL. Prefer one concrete host IP, use a literal IP during first-owner setup, enforce firewall policy, and do not expose the controller's unencrypted port 8080 to the Internet.
On Linux, host networking lets the on-demand subnet scanner enumerate eligible host LAN interfaces. Replace the -p line with --network host and set -e OONFEE_LISTEN=127.0.0.1:8080. The scanner performs a bounded TCP probe for an unauthenticated OpenWrt /ubus endpoint; it does not implement ARP or mDNS. An ordinary bridged container may not see a host-LAN subnet to scan, so use add-by-address there. Adoption, polling, Preview and Apply work over routed L3 in either mode.
Put TLS in front
Keep the controller on loopback and proxy it from the same host. A minimal Caddy site is:
oonfeewrt.example.internal {
reverse_proxy 127.0.0.1:8080
}2
3
Use a name and certificate trusted by your clients. Preserve WebSocket upgrades and X-Forwarded-Proto: https; Caddy does both by default. The controller then marks session cookies Secure. Do not expose port 8080 directly to the Internet.
Back up and upgrade
oonfeewrt.db and its sibling keyring.json are one recovery unit. A copied database without its matching keyring cannot reveal sealed credentials, even with the passphrase. Back up the passphrase separately.
For a live binary install, use SQLite's backup API rather than copying a WAL-mode database file directly:
install -d -m 0700 /path/to/private-backup
sqlite3 "$HOME/.local/share/oonfeewrt/oonfeewrt.db" \
".backup '/path/to/private-backup/oonfeewrt.db'"
cp -p "$HOME/.local/share/oonfeewrt/keyring.json" \
/path/to/private-backup/keyring.json2
3
4
5
Verify a restore pair before depending on it:
OONFEE_PASSPHRASE_FILE="$HOME/.config/oonfeewrt/passphrase" \
oonfeewrt-recoverycheck /path/to/private-backup/oonfeewrt.db2
For a container install, stop it cleanly before copying the bind-mounted state. This avoids requiring SQLite tooling inside the scratch image:
docker stop --time 150 oonfeewrt
install -d -m 0700 /path/to/private-backup
cp -p "$HOME/.local/share/oonfeewrt-container/oonfeewrt.db" \
"$HOME/.local/share/oonfeewrt-container/keyring.json" \
/path/to/private-backup/
docker start oonfeewrt2
3
4
5
6
For a Compose named volume, stop the service and use trusted volume-snapshot tooling to preserve the whole volume before v0.1.8 opens it. Record the exact Compose project/volume identity and retain the matching passphrase file. This raw snapshot is the simplest direct rollback path; never copy only the main SQLite file while WAL may be active.
Verify the copied pair with the release archive's oonfeewrt-recoverycheck before restarting after a real restore.
To upgrade, retain that backup, stop the old process cleanly, replace the binary or container tag, and restart with the same data volume and passphrase file. The controller migrates its database on startup and refuses an unsupported downgrade.
Upgrade from v0.1.6 or v0.1.7
v0.1.8 introduces no database migration over v0.1.6 or v0.1.7. Preserve and verify the matching recovery unit, stop the controller cleanly, replace its binary/image, and restart using the same data volume and runtime passphrase. Reauthenticate and refresh the browser, then verify inventory, health, and the router-write gate before resuming work. No re-adoption, router helper installation, new router permission, LLDP package action, or automatic Apply is required.
If a null LLDP package ledger previously broke the Devices page, confirm it now shows none recorded without reinstalling LLDP. For an affected shared-PHY device, inspect any prior Apply outcome, re-probe, and require distinct UCI radio targets in a fresh Preview before separately authorizing Apply. For recovery checks and a controlled return to the prior deployment, use the complete upgrade checklist.
Upgrade from v0.1.5 and roll back
v0.1.8 applies the schema 23 → 24 → 25 migrations introduced in v0.1.6. Schema 24 stores alert rules, incidents, evaluation state, cooldowns, and a bounded notification outbox; schema 25 stores encrypted AdGuard connection configuration. It does not create rules, enable a webhook, contact AdGuard, install a router helper, or flash firmware. Existing inventory, management modes, configuration, and history are retained.
v0.1.5 cannot open the migrated database. To return to it, stop v0.1.8, preserve its schema-25 data separately, restore the matching pre-upgrade schema-23 database and keyring, and run v0.1.5 with that pair's runtime passphrase. A pre-upgrade portable backup must be restored through a separate clean v0.1.5 instance; a v0.1.8 portable backup cannot be imported into the older release. See the complete upgrade checklist.
Portable restores performed by v0.1.8 separately pause external alert delivery, cancel queued notifications, and reset evaluation continuity. Rules, incidents, cooldown history, and encrypted destination are preserved; an Owner must review and explicitly re-enable delivery. This does not replay cancelled history or replace the independent router-write suppression gate.
Upgrade from v0.1.4 and roll back
v0.1.4 uses schema 20; v0.1.8 first applies the three migrations introduced in v0.1.5, then the schema 24 and 25 additions described above. Schema 21 adds device management_mode and assigns all existing devices Managed, preserving prior authority. Schema 22 adds named policy sets, exact-MAC members, and stable firewall source_set_id references. Schema 23 adds source-relative client_observations, its MAC lookup, and the case-insensitive global-client MAC index used by bounded policy checks. It drops the legacy Gateway index before canonicalizing the compatibility role, then rebuilds the one-managed-Gateway uniqueness guard from functions_json plus role, so those representations cannot disagree and bypass it. The migration invents no sets, does not infer provenance from merged global client rows, and preserves existing policies, configuration, credentials, secrets, telemetry, and topology intervals.
The v0.1.4 daemon cannot open schema 23 or 25. To roll back, stop v0.1.8, retain its schema-25 data separately, restore the matching pre-upgrade schema-20 database and keyring, install v0.1.4, and start it with the corresponding passphrase. Changing only the binary or OONFEE_VERSION is not a valid rollback.
If the portable .oowrtbak is your only schema-20 recovery point, start v0.1.4 against a new empty data directory or volume and restore the artifact through Settings → Backup & Restore with its export passphrase. Confirmation also uses that clean instance's runtime passphrase. Do not point v0.1.4 at the schema-25 live volume; the public recovery helper does not extract a portable backup in place.
The upgrade itself makes no router change. Read-only polling resumes, and all existing devices remain Managed until an Administrator or Owner deliberately changes authority. A monitor-only selection installs the distinct read-only oonfeewrt-monitor ACL through the reviewed ACL lifecycle; it is not a silent startup change.
Stable v0.1.1 through v0.1.3 use schema 19 and can migrate forward to v0.1.8, but rollback to one of those releases requires that version's matching pre-upgrade schema-19 database, keyring, and passphrase. Do not use a schema-20 schema-23, or schema-25 volume with a schema-19 daemon.
Upgrade from v0.1.0-rc.1 and roll back
v0.1.0-rc.1 uses schema 17; v0.1.8 migrates supported state through schemas 18–25. Before the upgrade, stop the RC cleanly and copy its database and matching keyring. Verify that pair with the RC archive's oonfeewrt-recoverycheck, then retain it without opening it with the final daemon. Start v0.1.8 with a copy of the same data pair and unchanged passphrase file.
Rollback is a data restore, not merely an image-tag change: stop v0.1.8, retain the schema-25 state separately, restore the untouched schema-17 database and matching keyring, then restart v0.1.0-rc.1 with its prior passphrase. Never point the RC daemon at a schema-19, schema-20, schema-23, or schema-25 database. Controller migration and rollback make no router request and do not revert router configuration.
Portable backup and restore
v0.1.0 automates the paired recovery workflow above. An owner can export one encrypted native .oowrtbak containing a consistent live-WAL database snapshot and matching wrapped key material from Settings → Backup & Restore. The artifact includes controller state only; it is not a backup of foreign or unmanaged router UCI.
Use TLS through the trusted reverse proxy above, or access the controller directly on loopback. Export start/download and restore mutations require recent account-password reauthentication. Choose a separate export passphrase of at least 16 Unicode characters and at most 4096 UTF-8 bytes; it is not stored by the controller. Restore accepts the raw .oowrtbak, authenticates and migrates only a disposable copy, and shows manifest identity, source/target schema and recovery counts before any replacement.
Confirmation requires that export passphrase again, the current controller runtime/boot passphrase, exact RESTORE CONTROLLER, and all four safety acknowledgements. The runtime passphrase is not the signed-in account password. For a container, keep /data on the same persistent volume and keep the same protected OONFEE_PASSPHRASE_FILE across ordinary restarts; otherwise the database/keyring pair will not reopen. The confirmed restore uses a controlled in-process restart, not an unattended router operation.
Before replacement, the controller retains the prior pair as a mode-0600 encrypted artifact at <data-dir>/.oonfeewrt-recovery/safety-<restore-id>.oowrtbak, protected by the same export passphrase used for confirmation. It is not automatically expired by time. After the applied-restore audit receipt is cleared, retention targets three recognized safety artifacts, fills available slots newest-first and prunes the rest. Artifacts referenced by an active restore marker, receipt or suppression record are always preserved, even if that temporarily exceeds three. For longer retention, copy one to protected backup storage before pruning and document its passphrase separately. It can be uploaded through the same staged restore workflow later.
Successful restore revokes all controller sessions. Restored desired state is never automatically applied to routers, and router writes remain persistently suppressed until an owner reauthenticates and enters exact RESUME ROUTER WRITES. Read-only monitoring of restored devices may resume after restart while that gate remains active. Removing it immediately re-enables automatic 802.11k neighbour maintenance; the reconciler may write hostapd RRM neighbour state even though restored desired configuration is not automatically Applied. Review the restored inventory and intent before removing the gate.
First adoption
- Set a root password in LuCI before adoption. A factory-default passwordless OpenWrt account is unsafe.
- Open Devices, scan or add the router by management address, and inspect the reported model, firmware, SSH host key, and warnings.
- Read the controller-access-payload disclosure. Select it only if you accept the ACL file and scoped-login changes described above.
- Enter the router credential only when prompted. Review the read-only inspection and, when sharing hardware evidence, use its sanitized compatibility-report export rather than the raw response.
- Leave LLDP off unless measured physical topology is worth installing an official router package. If enabled, review every package and interface plan.
The controller never needs a custom OpenWrt image or controller-authored router package. See FRESH-START-VALIDATION.md in this release archive for the factory-reset proof and known evidence gaps.