diff --git a/cmd/meshtender/main.go b/cmd/meshtender/main.go index cc9477e..dbf90b8 100644 --- a/cmd/meshtender/main.go +++ b/cmd/meshtender/main.go @@ -202,7 +202,7 @@ func run(logger *slog.Logger) error { } // Shutdown doesn't close hijacked/WebSocket connections, so close the active - // console/confirm sockets and give their handlers time to unwind and stamp + // console sockets and give their handlers time to unwind and stamp // their session's ended_at (see core.WSDrainTimeout) before we stop the flusher // and close the pool. Too short and drained sessions stay "in progress" forever. wsCtx, wsCancel := context.WithTimeout(context.Background(), core.WSDrainTimeout) diff --git a/internal/core/console.go b/internal/core/console.go index 93ab2ff..980b3bd 100644 --- a/internal/core/console.go +++ b/internal/core/console.go @@ -268,8 +268,7 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { debug := r.URL.Query().Get("debug") == "1" if debug { // Dump every inbound KISS frame as hex so we can see exactly what the modem - // reports back (e.g. whether the repeater replies at all). Same aid the - // dedicated confirm page used to offer. + // reports back (e.g. whether the repeater replies at all). bridge.SetObserver(func(f *hardware.KissFrame) { _ = bridge.Status("debug", fmt.Sprintf("rx frame cmd=0x%02x len=%d data=%x", f.Command, len(f.Data), f.Data)) }) @@ -378,9 +377,9 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { if userPathSet { reportPathOutcome(bridge, lr) } - // A successful login proves we reached the repeater, so treat connecting from - // the console as a confirmation (the same as the dedicated confirm flow) — for - // guest access too, which records the access level. This is cheap (no extra + // A successful login proves we reached the repeater, so connecting the console + // IS how a repeater becomes confirmed — for guest access too, which records the + // access level. This is cheap (no extra // packets). Fetching the location is deferred to an explicit "getloc" request // so a plain console session doesn't pay for a location round-trip it doesn't // need; the page offers a "Fetch location" button that sends it. @@ -389,7 +388,8 @@ func (s *Handlers) wsConsole(w http.ResponseWriter, r *http.Request) { } else if lr.IsAdmin { _ = bridge.Status("confirmed", "Repeater confirmed with admin access. ✓") } else { - // Guests can't run CLI commands, so warn as the confirm flow did. + // Guests can't run CLI commands, so say so rather than leaving the user to + // discover it one refused command at a time. _ = bridge.Status("warning", fmt.Sprintf("Repeater reached, but MeshTender only has GUEST access (permissions=%d). Guest is open to anyone with a blank password, so MeshTender can't administer this repeater — re-run `%s` to grant admin.", lr.Permissions, s.Identity.SetPermCommand())) } } diff --git a/internal/core/console_integration_test.go b/internal/core/console_integration_test.go index 7eacf52..4dedf82 100644 --- a/internal/core/console_integration_test.go +++ b/internal/core/console_integration_test.go @@ -180,7 +180,7 @@ func TestConsoleRoundTrip(t *testing.T) { } // Connecting from the console with a successful admin login confirms the - // repeater — the same as running the dedicated confirm flow. + // repeater: connecting the console is what confirms it. confirmed, err := st.GetRepeaterForUser(ctx, user.ID, rep.ID) must(err, "reload repeater") if !confirmed.Confirmed { @@ -343,7 +343,7 @@ func TestConsoleGetLatUpdatesLocation(t *testing.T) { // TestConsoleGuestLoginConfirms: connecting the console to a repeater that only // grants GUEST access still records the confirmation (with is_admin=false) and -// warns the user — the same as the dedicated confirm flow did. It must NOT emit a +// warns the user. It must NOT emit a // "confirmed" (admin) status. func TestConsoleGuestLoginConfirms(t *testing.T) { t.Parallel() diff --git a/internal/core/console_location_test.go b/internal/core/console_location_test.go index 3b2d887..5135e42 100644 --- a/internal/core/console_location_test.go +++ b/internal/core/console_location_test.go @@ -91,8 +91,8 @@ func TestConsoleFetchesLocation(t *testing.T) { shared, _ := repeater.SharedSecret(serverID) _ = ws.Write(rw, websocket.MessageText, []byte(`{"type":"ready"}`)) - // The console fetches location only on request (unlike the old confirm flow's - // eager fetch). Queue the getloc; the console processes it after login. + // The console fetches location only on request. Queue the getloc; the console + // processes it after login. _ = ws.Write(rw, websocket.MessageText, []byte(`{"type":"getloc"}`)) // Reply to login (PATH) then to get lat / get lon (TXT_MSG). @@ -166,9 +166,9 @@ func TestConsoleFetchesLocation(t *testing.T) { break // socket closed; check the DB below } if typ == websocket.MessageText { - // Unlike the old confirm flow, the console stays open after fetching - // location (it's an interactive session), so it never closes the socket - // on its own. It signals success with a "location" status — stop then. + // The console is an interactive session: it stays open after fetching the + // location and never closes the socket on its own. It signals success with + // a "location" status — stop then. var m struct{ State, Message string } if json.Unmarshal(data, &m) == nil && m.State == "location" { break diff --git a/internal/core/templates/dashboard.html b/internal/core/templates/dashboard.html index cfe2b3f..f6417d7 100644 --- a/internal/core/templates/dashboard.html +++ b/internal/core/templates/dashboard.html @@ -137,7 +137,7 @@ {{else}} -
No repeater locations yet. Enable “store location” when editing a repeater and confirm it with a modem to place it on the map.
No repeater locations yet. Open a repeater's console, connect your modem, and use “Fetch location” to read its coordinates onto the map — or set them when you add it.
The long value is MeshTender's public key — not your repeater's. If you see a guest-access warning after confirming a repeater, this is the fix: run the command above, then confirm again.
+The long value is MeshTender's public key — not your repeater's. If you see a guest-access warning when the console connects, this is the fix: run the command above, then connect again.
{{else}}Run setperm <MeshTender's public key> 3 on your repeater. Sign in and start adding a repeater to see the exact command with the key filled in.
There are two ways in, matching how you reach the repeater:
setperm command yourself first, then confirm it here.setperm command yourself first, then register it here.Confirming does a live login round-trip to prove MeshTender can reach and administer the repeater. If it reports guest access, the setperm grant hasn't been applied yet.
A registered repeater shows as confirmed once MeshTender has actually reached it. There's nothing to click for that: open the repeater's console and connect your modem, and the signed login that opens every session doubles as the proof — it confirms the repeater the first time it replies. If the reply reports guest access, the setperm grant hasn't been applied yet; run it, then connect again.
Either way, the device must already be running MeshCore's Repeater firmware — MeshTender configures repeaters, it doesn't flash them. A MeshCore device runs one firmware at a time (Repeater, a Companion variant, or KISS modem), so switching roles means re-flashing.
diff --git a/internal/seed/seed.go b/internal/seed/seed.go index 8069fe1..f16d78e 100644 --- a/internal/seed/seed.go +++ b/internal/seed/seed.go @@ -251,7 +251,7 @@ func seedRepeaters(ctx context.Context, st *store.Store, f *gofakeit.Faker, user _ = st.UpdateRepeater(ctx, u.ID, rep.ID, rep.Name, preset.freq, preset.bw, preset.sf, preset.cr, showOnOrg, true) } _ = st.UpdateRepeaterDocs(ctx, u.ID, rep.ID, f.Paragraph(1, 3, 14, " "), f.Paragraph(1, 2, 10, " ")) - // ~70% confirmed with admin access (perms 3 = admin, per the confirm flow). + // ~70% confirmed with admin access (perms 3 = admin). if chance(f, 70) { _ = st.SetRepeaterConfirmed(ctx, rep.ID, u.ID, true, 3) } diff --git a/internal/web/env.go b/internal/web/env.go index df732ff..5bc5046 100644 --- a/internal/web/env.go +++ b/internal/web/env.go @@ -435,7 +435,7 @@ var compressHTML = middleware.Compress(5, compressibleTypes...) // command lists, markdown docs) — so 1 MiB is generous while stopping a client // from streaming an arbitrarily large body into memory before per-field limits // can apply. WebSocket upgrades carry no request body, so this doesn't affect -// the console/confirm sockets. +// the console socket. const maxRequestBody = 1 << 20 // 1 MiB // limitBody caps r.Body so an oversized request fails fast — a read past the