diff --git a/blog/dot-from-scratch.md b/blog/dot-from-scratch.md index 9f04cdf..b4bb70b 100644 --- a/blog/dot-from-scratch.md +++ b/blog/dot-from-scratch.md @@ -132,20 +132,41 @@ $ numa setup-phone Numa Phone Setup - Profile URL: http://192.168.1.16:8765/mobileconfig + Profile URL: http://192.168.1.10:8765/mobileconfig - █▀▀▀▀▀▀▀█▀▀██ ██ ▀█▀▀▀▀▀▀▀█ - █ █▀▀▀█ █▀▄▀▀▀▀▄▄█ █▀▀▀█ █ - ... + █████████████████████████████████████ + █████████████████████████████████████ + ████ ▄▄▄▄▄ ██ ▀█ ▀▀▀▄▀ ▀▀█ ▄▄▄▄▄ ████ + ████ █ █ █ ▄▀ ▄█▀▄▀█▄▀█ █ █ ████ + ████ █▄▄▄█ █ ▀▄▄ ▀ █▄▀▀█▀█ █▄▄▄█ ████ + ████▄▄▄▄▄▄▄█ ▀▄▀▄█▄█ █▄█▄█▄▄▄▄▄▄▄████ + ████ ▀▄▄▄▄▄█▀ ▀██▄ ▄ ▄▀█▀█ ▄ ▄▄█▀████ + █████▄▄▀▄▀▄▄█▄ ▀████▀▄▄▀█▀▀▄ ██▀█████ + ████▄██▄ ▀▄ █ █ █▀█▄▄██ ▄▄▀▄▀▄ █▀████ + █████ ▀ ▄▀ ▄▀▄ ▄▄▀ ██ ▄▀██▄▀█████ + ████ ▀▀ █▄█▄▀ ▄ █▄ ▄█▀▄ ▀█▀▀ █▀████ + ████ ██▀█ ▄▄▀█▄▄██▀▄▀ ▀█▄▀ █▀▄▄▀█████ + ████▄█▄▄▄▄▄█▀▄█▄█▀▀ ▀██▀ ▄▄▄ ▀ ████ + ████ ▄▄▄▄▄ █▀▀▀▀ ▄█▀ ▀▄ █▄█ ▄▄▀█████ + ████ █ █ █ ▄ ██▀▄ ▄▄██ ▄ ▄▄▄██████ + ████ █▄▄▄█ █▄ ▄▀▀▄▄█▀▄▀▄ ▀▄▀ ▄█ █████ + ████▄▄▄▄▄▄▄█▄▄█▄▄▄█▄█▄▄██████▄▄██████ + █████████████████████████████████████ On your iPhone: 1. Open Camera, point at the QR code, tap the yellow banner 2. Allow the download when Safari asks - 3. Settings → "Profile Downloaded" → Install - 4. Settings → General → About → Certificate Trust Settings + 3. Open Settings — tap "Profile Downloaded" near the top + (or: Settings → General → VPN & Device Management → Numa DNS) + 4. Tap Install (top right), enter passcode, Install again + 5. Settings → General → About → Certificate Trust Settings Toggle ON "Numa Local CA" — required for DoT to work ``` +The same QR is available in the dashboard — click "Phone Setup" in the header and the popover renders an SVG QR code pointing at the mobileconfig URL. On mobile viewports it shows a direct download link instead. + +Numa dashboard with Phone Setup popover showing QR code and install instructions + Step 4 is non-negotiable. Even though the CA is bundled in the same profile that installs the DNS settings, iOS still requires the user to explicitly toggle trust in Certificate Trust Settings. It's a deliberate iOS policy to prevent profile-based trust injection — annoying, and correct. I've been dogfooding this since v0.10 shipped in early April. The phone resolves through Numa over DoT whenever I'm home; persistent connections are visible in the log as a single source port living through dozens of queries. The one real caveat: if the laptop's LAN IP changes, the profile breaks. [RFC 9462 DDR](https://datatracker.ietf.org/doc/html/rfc9462) fixes that — Numa can respond to `_dns.resolver.arpa IN SVCB` with its current IP and iOS picks it up on each network join. Next piece of work. diff --git a/scripts/record-demo.sh b/scripts/record-demo.sh index bb84ead..30c44fb 100755 --- a/scripts/record-demo.sh +++ b/scripts/record-demo.sh @@ -7,18 +7,19 @@ # The script: # 1. Opens the dashboard in Chrome --app mode (clean, no address bar) # 2. Generates DNS traffic (forward, cache hit, blocked) -# 3. Types "peekm" / "6419" into the Local Services form on camera -# 4. Shows LAN accessibility badge ("local only" / "LAN") -# 5. Checks a blocked domain -# 6. Opens peekm.numa to show the proxy working -# 7. Records via ffmpeg and converts to optimized GIF +# 3. Opens Phone Setup QR popover +# 4. Types "peekm" / "6419" into the Local Services form on camera +# 5. Shows LAN accessibility badge ("local only" / "LAN") +# 6. Checks a blocked domain +# 7. Opens peekm.numa to show the proxy working +# 8. Records via ffmpeg and converts to optimized GIF set -euo pipefail # --------------- Configuration --------------- OUTPUT="${1:-assets/hero-demo.gif}" PORT=5380 -RECORD_SECONDS=20 +RECORD_SECONDS=24 VIEWPORT_W=1800 VIEWPORT_H=1100 FPS=12 @@ -230,8 +231,16 @@ dig @127.0.0.1 github.com +short > /dev/null 2>&1 dig @127.0.0.1 ad.doubleclick.net +short > /dev/null 2>&1 sleep 3 -# --------------- Scene 2: Add peekm service via UI (3-7s) --------------- -log "Scene 2: Adding peekm.numa service..." +# --------------- Scene 2: Phone Setup popover (3-7s) --------------- +log "Scene 2: Phone Setup QR popover..." +run_js "document.querySelector('#phoneSetup button').click();" +sleep 3 +# Dismiss popover +run_js "document.getElementById('phoneSetupPopover').style.display = 'none';" +sleep 1 + +# --------------- Scene 3: Add peekm service via UI (7-11s) --------------- +log "Scene 3: Adding peekm.numa service..." # Services panel is now first — scroll to it run_js " @@ -249,18 +258,18 @@ sleep 0.3 run_js "document.querySelector('#serviceForm .btn-add').click();" sleep 2 -# --------------- Scene 3: Open peekm.numa (7-11s) --------------- -log "Scene 3: Opening peekm.numa in browser..." +# --------------- Scene 4: Open peekm.numa (11-15s) --------------- +log "Scene 4: Opening peekm.numa in browser..." open "http://peekm.numa/view/peekm/README.md" 2>/dev/null || true sleep 4 -# --------------- Scene 4: Back to dashboard (11-14s) --------------- -log "Scene 4: Back to dashboard — LAN badges + LOCAL queries visible..." +# --------------- Scene 5: Back to dashboard (15-18s) --------------- +log "Scene 5: Back to dashboard — LAN badges + LOCAL queries visible..." osascript -e "tell application \"System Events\" to set frontmost of (first process whose unix id is $CHROME_PID) to true" 2>/dev/null || true sleep 3 -# --------------- Scene 5: Check Domain blocker (14-17s) --------------- -log "Scene 5: Check Domain — blocked tracker..." +# --------------- Scene 6: Check Domain blocker (18-21s) --------------- +log "Scene 6: Check Domain — blocked tracker..." # Scroll down to blocking panel run_js " var blockPanel = document.getElementById('blockingPanel'); @@ -273,8 +282,8 @@ sleep 0.3 run_js "document.querySelector('#checkDomainInput').closest('form').querySelector('.btn').click();" sleep 2 -# --------------- Scene 6: Terminal-style dig overlay (17-20s) --------------- -log "Scene 6: dig proof overlay..." +# --------------- Scene 7: Terminal-style dig overlay (21-24s) --------------- +log "Scene 7: dig proof overlay..." DIG_RESULT=$(dig @127.0.0.1 peekm.numa +short 2>/dev/null | head -1) run_js " var overlay = document.createElement('div'); diff --git a/site/blog/phone-setup-dashboard.png b/site/blog/phone-setup-dashboard.png new file mode 100644 index 0000000..bdacb62 Binary files /dev/null and b/site/blog/phone-setup-dashboard.png differ