diff --git a/Sources/Homelab/wifi-join-webapp.md b/Sources/Homelab/wifi-join-webapp.md new file mode 100644 index 0000000..bd76e2f --- /dev/null +++ b/Sources/Homelab/wifi-join-webapp.md @@ -0,0 +1,76 @@ +--- +created: '2026-06-28' +path: Sources/Homelab +project: wifi-join-webapp +tags: +- dns +type: project-plan +--- + +## Goal + +Replace the ten-click LuCI ceremony for joining a new WiFi network on the road with a one-screen, browser-based scan-and-join page. Universal access (phone/tablet/laptop, no SSH, no app), served off the all-in-one travel Pi. Tap a network, enter the password, done — the page writes the travelmate uplink and reconnects behind the scenes. + +Secondary: it's a genuine portfolio piece — a custom web tool talking to OpenWRT's `ubus`/UCI API. + +## Problem + +Current join flow is all manual LuCI: type router IP (browser never remembers it) → log in → open travelmate → navigate to its page → scan radio0 → find network → add → apply → wait. Interface is fine; the ceremony is the problem. Most of the pain is front-half (reach + login + navigation depth) plus a scan/add/apply tail. + +Deep-link bookmark + password-manager autofill would kill the front half cheaply (considered as Option A), but the decision was to build the real thing — a custom page that collapses the entire flow. + +## Locked Decisions + +- **Architecture: thin backend shim (not pure-frontend-against-ubus).** Privileged work (write config + reconnect travelmate) stays server-side in one place; avoids wrestling rpcd/ubus ACLs from browser JS. Frontend just hits two simple endpoints. +- **Runs on the all-in-one travel Pi.** OpenWRT + travelmate + Pi-hole are all on the one Raspberry Pi, so the shim talks to ubus/UCI locally — same-origin, no CORS, no cross-box auth. +- **Backend language: lua** (native to OpenWRT, uhttpd speaks it, zero extra packages). Python kept in reach as fallback if travelmate config-writing gets gnarly in lua. Travis is new to lua — pace accordingly. +- **Rides existing uhttpd** (already running for LuCI). No new web server. +- **Backup before any router change** (Phase 0, same rule as the captive-portal plan — same router). +- **Build + curl-test the shim before any frontend.** Prove backend, then UI is trivial. + +## Open Items + +- [ ] Verify exact `config wifi-config` field shape against live `/etc/config/travelmate` before trusting the join write (don't write from memory). +- [ ] Decide whether a `/status` endpoint is worth it for "waiting to connect" feedback, or poll something simpler. +- [ ] ubus ACL: confirm the shim can call iwinfo scan + uci commit + travelmate restart under whatever user uhttpd/rpcd runs it as. +- [ ] Decide auth on the page itself (LAN-only? any gate?) — currently assumed trusted-LAN, no extra auth. + +## Phases + +### Phase 0 — Backup (non-negotiable, first) +- [ ] `sysupgrade -b /tmp/backup-$(date +%Y%m%d).tar.gz` +- [ ] `scp` tarball off the Pi to laptop; confirm present before any change + +### Phase 1 — Scan endpoint +- [ ] lua shim: `GET /cgi-bin/wifi-join/scan` → triggers radio0 scan, returns JSON `[{ssid, signal, encryption}]` +- [ ] curl-test from laptop; prove ubus scan works before any UI + +### Phase 2 — Join endpoint +- [ ] Reference live `cat /etc/config/travelmate` for exact uplink shape +- [ ] lua shim: `POST /cgi-bin/wifi-join/join` body `{ssid, key}` → writes travelmate `wifi-config` uplink, commits, reconnects +- [ ] curl-test headless; prove travelmate write + reconnect works + +### Phase 3 — Frontend (one page) +- [ ] Vanilla HTML/JS single file served by uhttpd: Scan button → network list w/ signal → tap → password field → Join → status +- [ ] Wire to the two proven endpoints + +### Phase 4 — Polish +- [ ] Signal bars, connect-status feedback +- [ ] Bookmark target; effectively replaces LuCI for the common case +- [ ] Optional `/status` endpoint if feedback needs it + +## Notes + +- Architecture: + ``` + Browser ──HTTP same-origin──► uhttpd ──► static frontend (one HTML/JS page) + └──/cgi-bin/wifi-join──► lua shim + ├─ ubus call iwinfo scan + ├─ uci set wireless / travelmate wifi-config + ├─ uci commit + └─ /etc/init.d/travelmate restart + ``` +- travelmate wrinkle: 2.1.3 stores uplinks as `config wifi-config` sections in `/etc/config/travelmate`, NOT raw `wireless.@wifi-iface`. "Join" = add a travelmate uplink entry and let its interface handler pick it up. Version-specific; verify against live config. +- Environment (confirmed this session): all-in-one Pi. travelmate `2.1.3-r3`, luci-app-travelmate `25.157.41932~3b521ef`, OpenWRT `24.10.1` r28597. +- Build discipline: backend proven by curl before a line of frontend. Same one-step-at-a-time interactive mode for the shim work. +- Related plan: `travel-pi-router` (captive-portal fix) — same Pi, shares the Phase 0 backup rule. This webapp is a separate deliverable from the captive-portal hook but lives on the same box. \ No newline at end of file