|
| 1 | +# LANtern |
| 2 | + |
| 3 | +Wake on LAN web app. Add devices, tap the power button, they wake up. Installable as a PWA on iOS and Android. |
| 4 | + |
| 5 | +Built for homelabs running on Linux (Raspberry Pi, NAS, mini PC) on the same LAN as your devices. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Why |
| 10 | + |
| 11 | +There are great projects out there like Upsnap that handle WoL along with status monitoring, shutdown, and more. For my setup though, my homelab mini PCs are managed through MeshCentral via Intel AMT, which keeps a persistent connection alive, meaning they always show as online regardless of whether they're actually powered on. That made status-based tools less useful for my specific case. |
| 12 | + |
| 13 | +LANtern does one thing: send a magic packet. Open the app, tap the button, done. |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## Security |
| 18 | + |
| 19 | +LANtern has no built-in authentication beyond the optional PIN. **If you expose this outside your local network, put it behind proper authentication.** Cloudflare Zero Trust, a reverse proxy with basic auth, a VPN. The PIN alone is not enough for internet-facing deployments. |
| 20 | + |
| 21 | +See [Remote access](#remote-access) below for options that work well with this setup. |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## Install on your phone |
| 26 | + |
| 27 | +**iOS:** Open in Safari, tap the Share button, select "Add to Home Screen". |
| 28 | + |
| 29 | +**Android:** Open in Chrome, tap the three-dot menu, select "Add to Home Screen". |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## Deploy |
| 34 | + |
| 35 | +**1. Clone and create config files** |
| 36 | + |
| 37 | +```bash |
| 38 | +git clone https://github.com/allxm4/LANtern.git |
| 39 | +cd LANtern |
| 40 | +touch devices.json config.json |
| 41 | +``` |
| 42 | + |
| 43 | +**2. Start** |
| 44 | + |
| 45 | +```bash |
| 46 | +docker compose up -d |
| 47 | +``` |
| 48 | + |
| 49 | +**3. Open** |
| 50 | + |
| 51 | +``` |
| 52 | +http://<your-server-ip>:3000 |
| 53 | +``` |
| 54 | + |
| 55 | +Devices and PIN config persist in `devices.json` and `config.json` next to `docker-compose.yml`. |
| 56 | + |
| 57 | +--- |
| 58 | + |
| 59 | +## Changing the port |
| 60 | + |
| 61 | +If port `3000` is already in use, change `PORT` in `docker-compose.yml`: |
| 62 | + |
| 63 | +```yaml |
| 64 | +environment: |
| 65 | + - PORT=8080 |
| 66 | +``` |
| 67 | +
|
| 68 | +Then run `docker compose up -d` and access the app at `http://<your-server-ip>:8080`. |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +## Remote access |
| 73 | + |
| 74 | +### Cloudflare Tunnel |
| 75 | + |
| 76 | +Zero open ports. Traffic goes through Cloudflare's network. Pair this with a Cloudflare Access policy to gate it behind authentication. |
| 77 | + |
| 78 | +1. [one.dash.cloudflare.com](https://one.dash.cloudflare.com) → Networks → Tunnels → Create tunnel |
| 79 | +2. Choose Cloudflared, name it, copy the token |
| 80 | +3. In the tunnel's Public Hostname tab, point your domain to `http://localhost:3000` |
| 81 | +4. Add to `.env`: `CLOUDFLARE_TOKEN=your-token` |
| 82 | +5. Uncomment the `cloudflared` block in `docker-compose.yml` |
| 83 | +6. `docker compose up -d` |
| 84 | + |
| 85 | +### NetBird |
| 86 | + |
| 87 | +Mesh VPN. The app is only reachable from devices enrolled in your NetBird network, with no public exposure. |
| 88 | + |
| 89 | +1. [app.netbird.io](https://app.netbird.io) → Setup Keys → Create setup key (reusable) |
| 90 | +2. Add to `.env`: `NETBIRD_SETUP_KEY=your-key` |
| 91 | +3. Uncomment the `netbird` block and the `volumes` block in `docker-compose.yml` |
| 92 | +4. `docker compose up -d` |
| 93 | +5. Find the assigned IP under Peers in the NetBird dashboard, open `http://<netbird-ip>:3000` |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## PIN lock |
| 98 | + |
| 99 | +Set a PIN from the gear icon in the app. Sessions last 8 hours, so you won't be asked again on the same device until the session expires. The TTL is configurable via `SESSION_TTL_HOURS` in `docker-compose.yml`. |
| 100 | + |
| 101 | +**Forgot your PIN:** |
| 102 | + |
| 103 | +```bash |
| 104 | +docker exec LANtern node reset-pin.js # remove PIN |
| 105 | +docker exec LANtern node reset-pin.js 1234 # set a new PIN |
| 106 | +``` |
| 107 | + |
| 108 | +To immediately log out all active sessions: |
| 109 | + |
| 110 | +```bash |
| 111 | +docker compose restart wol |
| 112 | +``` |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +## API |
| 117 | + |
| 118 | +All endpoints require authentication if a PIN is set. First get a session token, then include it in subsequent requests. |
| 119 | + |
| 120 | +**Get a session token** |
| 121 | + |
| 122 | +```bash |
| 123 | +# With PIN enabled |
| 124 | +curl -s -X POST http://localhost:3000/api/auth/login \ |
| 125 | + -H "Content-Type: application/json" \ |
| 126 | + -d '{"pin":"1234"}' |
| 127 | +# -> {"token":"abc123..."} |
| 128 | +
|
| 129 | +# Without PIN, call the same endpoint with no body |
| 130 | +curl -s -X POST http://localhost:3000/api/auth/login |
| 131 | +``` |
| 132 | + |
| 133 | +Pass the token in the `x-session-token` header on all subsequent requests. |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +**Wake by device ID** |
| 138 | + |
| 139 | +Device IDs are UUIDs assigned when you add a device. Retrieve them from: |
| 140 | + |
| 141 | +```bash |
| 142 | +curl -s http://localhost:3000/api/devices \ |
| 143 | + -H "x-session-token: <token>" |
| 144 | +``` |
| 145 | + |
| 146 | +Then wake the device: |
| 147 | + |
| 148 | +```bash |
| 149 | +curl -s -X POST http://localhost:3000/api/wake/<device-id> \ |
| 150 | + -H "x-session-token: <token>" |
| 151 | +``` |
| 152 | + |
| 153 | +**Wake by MAC address** (useful for iOS Shortcuts or home automation) |
| 154 | + |
| 155 | +```bash |
| 156 | +curl -s -X POST http://localhost:3000/api/wake/mac/AA:BB:CC:DD:EE:FF \ |
| 157 | + -H "Content-Type: application/json" \ |
| 158 | + -H "x-session-token: <token>" \ |
| 159 | + -d '{"broadcastAddress":"192.168.1.255"}' |
| 160 | +``` |
| 161 | + |
| 162 | +`broadcastAddress` is optional, defaults to `255.255.255.255`. |
| 163 | + |
| 164 | +**Health check** (no auth required) |
| 165 | + |
| 166 | +```bash |
| 167 | +curl -s http://localhost:3000/api/health |
| 168 | +``` |
0 commit comments