wiki-vault/Sources/Homelab/wifi-join-webapp.md

4.8 KiB

created path project tags type
2026-06-28 Sources/Homelab wifi-join-webapp
dns
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.