Skip to content

Commit 7dc63ce

Browse files
Alex MafteiAlex Maftei
authored andcommitted
Initial commit
0 parents  commit 7dc63ce

File tree

16 files changed

+2124
-0
lines changed

16 files changed

+2124
-0
lines changed

.dockerignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
devices.json
3+
config.json
4+
.env
5+
.git
6+
*.log

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules/
2+
.env
3+
.claude/
4+
.DS_Store
5+
*.log
6+
devices.json
7+
config.json

Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM node:20-alpine
2+
3+
WORKDIR /app
4+
5+
COPY package.json ./
6+
RUN npm install --omit=dev && npm cache clean --force
7+
8+
COPY server.js reset-pin.js ./
9+
COPY public/ ./public/
10+
11+
EXPOSE 3000
12+
13+
CMD ["node", "server.js"]

README.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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+
```

docker-compose.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
services:
2+
wol:
3+
build: .
4+
image: lantern:latest
5+
container_name: LANtern
6+
network_mode: host
7+
environment:
8+
- PORT=3000
9+
- DEVICES_FILE=/config/devices.json
10+
- CONFIG_FILE=/config/config.json
11+
# - SESSION_TTL_HOURS=8
12+
volumes:
13+
- ./devices.json:/config/devices.json
14+
- ./config.json:/config/config.json
15+
restart: unless-stopped
16+
17+
# cloudflared:
18+
# image: cloudflare/cloudflared:latest
19+
# restart: unless-stopped
20+
# network_mode: host
21+
# command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TOKEN}
22+
# depends_on:
23+
# - wol
24+
25+
# netbird:
26+
# image: netbirdio/netbird:latest
27+
# restart: unless-stopped
28+
# network_mode: host
29+
# cap_add:
30+
# - NET_ADMIN
31+
# - SYS_MODULE
32+
# devices:
33+
# - /dev/net/tun:/dev/net/tun
34+
# environment:
35+
# - NB_SETUP_KEY=${NETBIRD_SETUP_KEY}
36+
# volumes:
37+
# - netbird_data:/etc/netbird
38+
# depends_on:
39+
# - wol
40+
41+
# volumes:
42+
# netbird_data:

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "wake-on-lan",
3+
"version": "1.0.0",
4+
"description": "Self-hosted Wake on LAN web application",
5+
"main": "server.js",
6+
"scripts": {
7+
"start": "node server.js"
8+
},
9+
"dependencies": {
10+
"express": "^4.18.2"
11+
}
12+
}

0 commit comments

Comments
 (0)