Skip to content

asso-syntac/wifi-connect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wifi-connect

Entirely vibe-coded, self-contained Go daemon that brings up a captive-portal wifi access point when a Linux host has no internet, lets a user pick a wifi network from their phone, applies it via NetworkManager, then steps aside.

📡 What it does

You've got a headless Linux box — no screen, no keyboard. You've just plugged it in somewhere new : a venue for an event, a customer site, a closet in a building you don't own. It boots fine but it's offline, because nobody ever told it which wifi to join, and there's nowhere on the box itself to type that.

That's the gap wifi-connect fills. When the box has no internet, it broadcasts an open wifi called wifi-setup. You hop on with your phone, your OS goes "oh hey, a captive portal!" and pops a page open all by itself. You pick the wifi you want from the scanned list, type the password, hit submit. The box jumps onto the chosen wifi and the AP disappears. ✨

If nobody interacts, the box retries every 10 min : drops the AP, asks NetworkManager nicely if any known wifi is in range, brings the AP back if not. Patient little thing.

📥 Install

Grab a release binary straight from GitHub releases :

# latest stable
curl -L -o wifi-connect https://github.com/asso-syntac/wifi-connect/releases/latest/download/wifi-connect
chmod +x wifi-connect
sudo install -m 0755 wifi-connect /usr/local/bin/

Statically linked, no extra runtime to install.

🧰 Prerequisites

  • Linux/amd64
  • NetworkManager + nftables (default on modern distros)
  • A wifi adapter that does AP mode — iw list should show * AP under "Supported interface modes"
  • Root, or CAP_NET_ADMIN (we touch nftables and NM)

🚀 Run

Flag Env var Default
--debug false
--interface=<name> WIFI_CONNECT_IFACE auto-detect
--ssid=<name> WIFI_CONNECT_SSID wifi-setup
--recovery-interval=<dur> WIFI_CONNECT_RECOVERY_INTERVAL 10m

Durations accept the usual Go format : 30s, 5m, 1h30m.

Logs are JSON on stdout, slurped happily by journald or any shipper.

🎮 Talk to the running daemon

The same binary doubles as a CLI client over /run/wifi-connect.sock :

sudo wifi-connect status         # current state + last scan + last error
sudo wifi-connect monitor        # tail state transitions live (Ctrl-C to stop)
sudo wifi-connect force-portal   # bring up the AP right now — great for tests
sudo wifi-connect version

Everything except version needs root (the socket is 0600).

🐳 Build from source

Docker only. No local Go install — everything happens inside golang:1.23-alpine.

make build

Spits out a ~6.5 MB static binary at bin/wifi-connect. To bump Go, edit GO_IMAGE in the Makefile.

🧠 What's under the hood

  • NetworkManager creates the AP in shared mode → its internal dnsmasq handles DHCP. We never run our own dnsmasq. (That was the bug we were running away from.)
  • nftables redirects DNS (UDP/TCP 53) and HTTP (TCP 80) from the AP interface to in-process servers : a tiny DNS hijack that answers 192.168.42.1 to absolutely everything, and an HTTP server that 302's any request whose Host isn't the portal. That 302 is what flips the "Sign in to network" notif on Android, the auto-popup on iOS, etc.
  • HTML/JS/CSS are embedded via go:embed — nothing to ship next to the binary.
  • One supervisor goroutine, four states (STARTINGMONITORINGPORTAL_UPCONNECTING), channels for everything else. No global mutex hell.

That's it. Have fun. 📶

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors