deploy: VPS + Cloudflare bundle (nginx vhosts, release script, setup guide)

The wadamesh.com distribution stack, ready to drop onto a new VPS behind
Cloudflare:
- deploy/nginx/tiles.wadamesh.com.conf — OSM tile proxy (HTTP origin, double-quoted
  location regex, resolver + variable proxy_pass, OSM UA, 14d disk cache)
- deploy/nginx/firmware.wadamesh.com.conf — bins + the beta_<N> update-check listing
- scripts/release.sh — build both boards tagged, refresh the listing, rsync to the VPS
- deploy/README.md — VPS + Cloudflare DNS/SSL/cache setup, publish flow

VPS target supplied via $WADAMESH_VPS at publish time — no IP/SSH in the repo
(Cloudflare fronts the origin). Web flasher + OTA re-enable are the remaining
pre-launch pieces (noted in deploy/README.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kaj Schittecat <kaj@schittecat.com>
This commit is contained in:
Kaj SchittecatandClaude Opus 4.8 committed 2026-06-13 09:04:17 +02:00
1 parent bc8344b219
commit eb07184c3e
4 files changed
+216

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
# wadamesh.com infrastructure
Distribution stack for wadamesh: a **VPS nginx origin behind Cloudflare**.
```
tiles.wadamesh.com →CF (HTTP, edge-cached) → nginx → tile.openstreetmap.org
firmware.wadamesh.com →CF (cache bins) → nginx → /srv/wadamesh/firmware
flasher.wadamesh.com →CF (HTTPS) → web flasher (TODO — see below)
wadamesh.com →CF (HTTPS) → landing page (later)
```
The firmware fetches **tiles + the update-check over plain HTTP** (on-device HTTPS
isn't viable — mbedTLS needs ~30 KB heap, only ~5 KB is free post-Wi-Fi), so the
tile + firmware hosts must stay reachable over HTTP. Cloudflare provides the edge
cache, HTTPS for the flasher, and hides the origin IP (so no IP lives in this repo
or the firmware).
## 1. VPS (origin)
```bash
sudo apt install nginx
sudo mkdir -p /srv/wadamesh/firmware/releases/TOUCH /var/cache/nginx/wadamesh-tiles
sudo cp deploy/nginx/tiles.wadamesh.com.conf /etc/nginx/sites-available/
sudo cp deploy/nginx/firmware.wadamesh.com.conf /etc/nginx/sites-available/
sudo ln -s /etc/nginx/sites-available/tiles.wadamesh.com.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/firmware.wadamesh.com.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```
## 2. Cloudflare
- **DNS:** `A`/`AAAA` records for `tiles`, `firmware`, `flasher`, `@` → the VPS IP,
all **Proxied** (orange cloud).
- **SSL/TLS:** mode **Flexible** (CF↔origin HTTP) is enough since the origin is
HTTP-only. **Do NOT enable "Always Use HTTPS"** on `tiles.` or `firmware.` — the
firmware needs plain HTTP there.
- **Cache Rules:**
- `tiles.wadamesh.com/*` → Eligible for cache, Edge TTL ~14d.
- `firmware.wadamesh.com/releases/*/*.bin` → cache, Edge TTL ~1d.
- `firmware.wadamesh.com/releases/TOUCH` (the listing) → short TTL (~60s) or
Bypass, so new releases appear promptly.
## 3. Publishing a release
From a wadamesh checkout (builds both boards, refreshes the listing, rsyncs up):
```bash
WADAMESH_VPS=user@your-vps scripts/release.sh beta_2
```
The on-device check GETs `http://firmware.wadamesh.com/releases/TOUCH`, finds the
highest `beta_<N>`, and (once OTA-over-Wi-Fi is re-enabled) pulls
`…/releases/TOUCH/beta_<N>/<board>.bin`.
## TODO before public launch
- **Web flasher** (`flasher.wadamesh.com`) — esptool-js / Web Serial install page
with a board picker. Not yet built; the firmware already points "update manually
at flasher.wadamesh.com" here.
- **Re-enable OTA-over-Wi-Fi** in the firmware (currently it version-checks then
defers to manual flashing).
- **Landing page** at `wadamesh.com`.
- Decide tile-proxy sharing: dedicated `tiles.wadamesh.com` (this config) vs
reusing the meshcomod proxy.
> Never commit the VPS IP, SSH keys, or `WADAMESH_VPS`. Cloudflare fronts the
> origin; the deploy target is supplied via the environment at publish time.
+42
View File
@@ -0,0 +1,42 @@
# firmware.wadamesh.com — wadamesh firmware bins + the update-check feed.
#
# Layout on disk (populated by scripts/release.sh, rsynced to the VPS):
# /srv/wadamesh/firmware/
# releases/TOUCH/index.json <- listing the firmware scans for beta_<N>
# releases/TOUCH/beta_1/wadamesh-heltec-v4-tft.bin
# releases/TOUCH/beta_1/wadamesh-heltec-v4-tft-merged.bin
# releases/TOUCH/beta_1/wadamesh-tdeck.bin
# releases/TOUCH/beta_1/wadamesh-tdeck-merged.bin
#
# The on-device version check GETs http://firmware.wadamesh.com/releases/TOUCH and
# scans the body for the highest beta_<N>. We serve index.json at exactly that
# path (no trailing-slash redirect, which the on-device HTTPClient may not follow).
#
# Cloudflare: firmware.wadamesh.com proxied; SSL "Full". Plain HTTP must remain
# reachable (the firmware checks over HTTP) — do NOT force HTTPS on this host.
# Cache Rule: cache .bin aggressively (Edge TTL ~1d); index.json short TTL (~60s)
# or bypass, so a new release shows up promptly.
server {
listen 80;
listen [::]:80;
server_name firmware.wadamesh.com;
root /srv/wadamesh/firmware;
# The update-check feed — served at the no-slash path the firmware requests.
location = /releases/TOUCH {
default_type application/json;
alias /srv/wadamesh/firmware/releases/TOUCH/index.json;
add_header Cache-Control "public, max-age=60";
}
# Bins + everything else under /releases/.
location /releases/ {
autoindex off;
types { application/octet-stream bin; }
add_header Cache-Control "public, max-age=86400";
}
location / { return 404; }
}
+49
View File
@@ -0,0 +1,49 @@
# tiles.wadamesh.com — OSM tile proxy for the wadamesh map tab.
#
# The firmware fetches plain HTTP (on-device HTTPS isn't workable: mbedTLS wants
# ~30 KB heap for a handshake and only ~5 KB is free after Wi-Fi associates), so
# this vhost is HTTP-only by design. Cloudflare sits in front and caches the
# .png tiles at the edge; this origin keeps a second 14-day disk cache.
#
# Deploy: copy to /etc/nginx/sites-available/, symlink into sites-enabled/, then
# nginx -t && systemctl reload nginx
# Cloudflare: tiles.wadamesh.com proxied (orange cloud), SSL/TLS mode "Flexible"
# (CF<->origin HTTP), and a Cache Rule: Eligible for cache + Edge TTL ~14d for
# path "/*". Do NOT enable "Always Use HTTPS" on this hostname.
proxy_cache_path /var/cache/nginx/wadamesh-tiles levels=1:2
keys_zone=wadamesh_tiles:10m max_size=4g inactive=14d use_temp_path=off;
server {
listen 80;
listen [::]:80;
server_name tiles.wadamesh.com;
# Re-resolve the upstream at request time. A literal hostname in proxy_pass
# fails with "no resolver defined" on a fresh box; a variable + resolver works.
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
# The location regex is DOUBLE-QUOTED on purpose: nginx's lexer otherwise
# treats { and } (quantifiers) as block delimiters and refuses to start.
location ~ "^/(\d{1,2})/(\d{1,7})/(\d{1,7})\.png$" {
set $osm_upstream "tile.openstreetmap.org";
proxy_pass https://$osm_upstream/$1/$2/$3.png;
proxy_ssl_server_name on;
proxy_set_header Host tile.openstreetmap.org;
# OSM tile usage policy requires a valid, identifying User-Agent.
proxy_set_header User-Agent "wadamesh-tile-proxy/1.0 (+https://wadamesh.com)";
proxy_set_header Referer "https://wadamesh.com/";
proxy_cache wadamesh_tiles;
proxy_cache_valid 200 302 14d;
proxy_cache_valid 404 1h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
expires 14d;
}
location / { return 404; }
}
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# wadamesh release: build tagged bins for both touch boards, refresh the
# update-check listing, and publish to the firmware VPS (behind Cloudflare).
#
# Usage:
# WADAMESH_VPS=user@your-vps WADAMESH_VPS_PATH=/srv/wadamesh/firmware \
# scripts/release.sh beta_2
#
# The VPS target comes from the environment — never commit it (Cloudflare fronts
# the origin, so the IP isn't needed in the firmware anyway).
set -euo pipefail
TAG="${1:?usage: release.sh <tag> e.g. beta_2}"
PIO="${PIO:-$HOME/Library/Python/3.9/bin/pio}"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
OUT="$ROOT/out/firmware" # local mirror of the VPS firmware root
REL="$OUT/releases/TOUCH"
DEST="${WADAMESH_VPS:-}"; DEST_PATH="${WADAMESH_VPS_PATH:-/srv/wadamesh/firmware}"
declare -A ENVS=(
[heltec_v4_tft_companion_radio_usb_tcp_touch]=wadamesh-heltec-v4-tft
[LilyGo_TDeck_companion_radio_touch]=wadamesh-tdeck
)
# 1. Pull the current published tree so the listing stays complete across tags.
if [ -n "$DEST" ]; then
mkdir -p "$OUT"
rsync -a "$DEST:$DEST_PATH/" "$OUT/" 2>/dev/null || echo "note: first publish (nothing to pull yet)"
fi
mkdir -p "$REL/$TAG"
# 2. Build both boards (app + merged), tag + version embedded.
export PLATFORMIO_BUILD_FLAGS="-DFIRMWARE_RELEASE_TAG='\"${TAG}\"' -DFIRMWARE_VERSION='\"wadamesh ${TAG}\"'"
for env in "${!ENVS[@]}"; do
name="${ENVS[$env]}"
"$PIO" run -t mergebin -e "$env"
cp ".pio/build/$env/firmware.bin" "$REL/$TAG/$name.bin"
cp ".pio/build/$env/firmware-merged.bin" "$REL/$TAG/$name-merged.bin"
done
# 3. Regenerate the update-check listing (the firmware scans the body for the
# highest beta_<N>). JSON array of dir names — GitHub-contents shape.
python3 - "$REL" > "$REL/index.json" <<'PY'
import os, sys, json
rel = sys.argv[1]
betas = sorted(d for d in os.listdir(rel)
if d.startswith("beta_") and os.path.isdir(os.path.join(rel, d)))
print(json.dumps([{"name": b, "type": "dir"} for b in betas], indent=2))
PY
echo "listing: $(python3 -c 'import json,sys;print(", ".join(x["name"] for x in json.load(open(sys.argv[1]))))' "$REL/index.json")"
# 4. Publish to the VPS (Cloudflare caches at the edge).
if [ -n "$DEST" ]; then
rsync -av "$OUT/" "$DEST:$DEST_PATH/"
echo "published $TAG -> $DEST:$DEST_PATH"
else
echo "WADAMESH_VPS not set — built + staged in $REL/$TAG only (no publish)."
fi