diff --git a/cloudflared-config.yml b/cloudflared-config.yml index acef7bf2a..a35031520 100644 --- a/cloudflared-config.yml +++ b/cloudflared-config.yml @@ -1,53 +1,157 @@ +# Cloudflare Tunnel Configuration for the Mac Mini production server. +# +# This file is the SINGLE SOURCE OF TRUTH for which public hostnames +# the tunnel exposes. The cloudflared launchd plist is started with +# `--config run` so any change here is one `git pull` + +# `launchctl kickstart -k gui/501/com.cloudflare.cloudflared` away +# from being live in production. +# +# Adding a new public hostname: +# 1. Append the hostname / service line below in the matching section +# 2. Make sure the corresponding Cloudflare DNS record exists (the +# tunnel needs the hostname pointing at its CNAME — see +# `cloudflared tunnel route dns ` if not) +# 3. Run `./scripts/mac-mini/sync-tunnel-config.sh` to copy this file +# onto the Mac Mini and reload cloudflared +# 4. Verify with `curl -sI https:///health` (or the route's +# equivalent) — expect a non-404 status line +# +# Removing a hostname: same steps, just delete the lines. +# +# Catch-all at the bottom returns http_status:404 for any hostname +# Cloudflare routes here that we don't have an explicit ingress rule +# for. This is the desired failure mode. + tunnel: bb0ea86d-8253-4a54-838b-107bb7945be9 credentials-file: /Users/mana/.cloudflared/bb0ea86d-8253-4a54-838b-107bb7945be9.json ingress: - # SSH Access (requires cloudflared on client) + # ============================================ + # SSH (requires cloudflared on the client) + # ============================================ - hostname: ssh.mana.how service: ssh://localhost:22 - # Mana Dashboard (Main) + # ============================================ + # Unified Mana Web App (Port 5000) + # ============================================ + # Every per-product subdomain points at the same SvelteKit container. + # The container's hooks.server.ts reads the host header and renders + # the matching module surface. mana.how itself is the dashboard. - hostname: mana.how service: http://localhost:5000 + - hostname: chat.mana.how + service: http://localhost:5000 + - hostname: todo.mana.how + service: http://localhost:5000 + - hostname: calendar.mana.how + service: http://localhost:5000 + - hostname: clock.mana.how + service: http://localhost:5000 + - hostname: contacts.mana.how + service: http://localhost:5000 + - hostname: zitare.mana.how + service: http://localhost:5000 + - hostname: skilltree.mana.how + service: http://localhost:5000 + - hostname: planta.mana.how + service: http://localhost:5000 + - hostname: cards.mana.how + service: http://localhost:5000 + - hostname: storage.mana.how + service: http://localhost:5000 + - hostname: presi.mana.how + service: http://localhost:5000 + - hostname: nutriphi.mana.how + service: http://localhost:5000 + - hostname: photos.mana.how + service: http://localhost:5000 + - hostname: mukke.mana.how + service: http://localhost:5000 + - hostname: picture.mana.how + service: http://localhost:5000 + - hostname: calc.mana.how + service: http://localhost:5000 + - hostname: citycorners.mana.how + service: http://localhost:5000 + - hostname: inventar.mana.how + service: http://localhost:5000 + - hostname: times.mana.how + service: http://localhost:5000 + - hostname: uload.mana.how + service: http://localhost:5000 + - hostname: memoro.mana.how + service: http://localhost:5000 + - hostname: context.mana.how + service: http://localhost:5000 + - hostname: questions.mana.how + service: http://localhost:5000 + - hostname: moodlit.mana.how + service: http://localhost:5000 - # Auth Service + # ============================================ + # Auth Service (Hono/Bun) + # ============================================ - hostname: auth.mana.how service: http://localhost:3001 + # ============================================ + # Unified Backend API (Hono/Bun, port 3060) + # ============================================ + # apps/api hosts every product compute module (calendar, chat, + # picture, planta, news, who, …) under /api/v1/{module}/*. The + # unified web app's PUBLIC_MANA_API_URL_CLIENT points here. + - hostname: mana-api.mana.how + service: http://localhost:3060 + + # ============================================ # API Gateway (Go) + # ============================================ + # Older gateway in front of the per-service compute layer. New + # services should go directly through mana-api above; this gateway + # only handles legacy entry points. - hostname: api.mana.how service: http://localhost:3016 + # ============================================ # Forgejo (Git + CI/CD) + # ============================================ - hostname: git.mana.how service: http://localhost:3041 - # NOTE: Individual app backends (chat, todo, calendar, contacts, storage, - # nutriphi, music, planta, picture, etc.) have been REMOVED — all migrated - # to local-first architecture. Web apps run as routes under mana.how. - # Only uload-server and memoro-server remain as app-specific backends. + # ============================================ + # Standalone microservices + # ============================================ + - hostname: uload-api.mana.how + service: http://localhost:3070 + - hostname: media.mana.how + service: http://localhost:3011 + - hostname: llm.mana.how + service: http://localhost:3025 + - hostname: sync.mana.how + service: http://localhost:3010 + - hostname: credits.mana.how + service: http://localhost:3002 + - hostname: subscriptions.mana.how + service: http://localhost:3063 - # Games - - hostname: whopxl.mana.how - service: http://localhost:5100 + # ============================================ + # Standalone web apps (separate containers) + # ============================================ + - hostname: playground.mana.how + service: http://localhost:5050 - hostname: arcade.mana.how service: http://localhost:5210 + - hostname: manavoxel.mana.how + service: http://localhost:5028 + - hostname: whopxl.mana.how + service: http://localhost:5100 - # Public Status Page (generated every 60s by mana-status-gen container) + # ============================================ + # Self-hosted landing pages (Nginx on port 4400) + # ============================================ - hostname: status.mana.how service: http://localhost:4400 - - # Monitoring & Tools - - hostname: grafana.mana.how - service: http://localhost:8000 - - hostname: stats.mana.how - service: http://localhost:8010 - - # GlitchTip Error Tracking - - hostname: glitchtip.mana.how - service: http://localhost:8020 - - # Self-Hosted Landing Pages (via Nginx on port 4400) - hostname: it.mana.how service: http://localhost:4400 - hostname: chats.mana.how @@ -63,7 +167,22 @@ ingress: - hostname: docs.mana.how service: http://localhost:4400 - # GPU Server (Windows PC, LAN: 192.168.178.11) + # ============================================ + # Monitoring & observability + # ============================================ + - hostname: grafana.mana.how + service: http://localhost:8000 + - hostname: stats.mana.how + service: http://localhost:8010 + - hostname: glitchtip.mana.how + service: http://localhost:8020 + + # ============================================ + # GPU server forwarders (Windows PC, LAN: 192.168.178.11) + # ============================================ + # The Mac Mini fronts the Windows GPU box's services so they're + # reachable through the same tunnel without exposing the LAN box + # to the public internet directly. - hostname: gpu-llm.mana.how service: http://192.168.178.11:3025 - hostname: gpu-stt.mana.how @@ -77,5 +196,7 @@ ingress: - hostname: gpu-ollama.mana.how service: http://192.168.178.11:11434 - # Catch-all + # ============================================ + # Catch-all (returns 404 for any unmapped hostname) + # ============================================ - service: http_status:404 diff --git a/scripts/mac-mini/sync-tunnel-config.sh b/scripts/mac-mini/sync-tunnel-config.sh new file mode 100755 index 000000000..a19d1a70d --- /dev/null +++ b/scripts/mac-mini/sync-tunnel-config.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# +# Sync the in-repo cloudflared-config.yml onto the Mac Mini and reload +# the tunnel. Run this whenever cloudflared-config.yml changes — it's +# the only step needed to make a new public hostname go live. +# +# Usage: +# ./scripts/mac-mini/sync-tunnel-config.sh +# +# Requires: +# - SSH access to the `mana-server` host (configured in ~/.ssh/config) +# - The launchd plist on the server already started cloudflared with +# `--config /cloudflared-config.yml run`. If not, run +# ./scripts/mac-mini/setup-cloudflared-service.sh on the server +# once first. +# +# Why a kickstart instead of unload+load: launchctl kickstart -k +# preserves the launchd state, doesn't race with KeepAlive, and +# returns when the new process is up. unload/load is the legacy form +# and tends to leave the agent in a stuck state on macOS 14+. + +set -e + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +CONFIG_FILE="$REPO_ROOT/cloudflared-config.yml" +REMOTE_HOST="mana-server" +REMOTE_PATH='~/projects/mana-monorepo/cloudflared-config.yml' + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' + +if [ ! -f "$CONFIG_FILE" ]; then + echo -e "${RED}Error:${NC} $CONFIG_FILE not found" + exit 1 +fi + +echo -e "${GREEN}=== Syncing cloudflared-config.yml ===${NC}" +echo "" + +# 1. Validate the YAML locally before pushing — cloudflared has a +# `tunnel ingress validate` subcommand that catches duplicate +# hostnames, malformed services, and missing tunnel-id. We rely on +# the server's cloudflared install to do the actual validation +# after the file lands so we don't need cloudflared on the dev box. + +echo -e "${YELLOW}1. Pulling latest from origin (in case the local file is stale)...${NC}" +( cd "$REPO_ROOT" && git fetch --quiet origin main && git diff --quiet origin/main -- cloudflared-config.yml ) || \ + echo -e "${YELLOW} warning: local cloudflared-config.yml differs from origin/main${NC}" + +echo -e "${YELLOW}2. Ensuring repo on the server is up to date...${NC}" +ssh "$REMOTE_HOST" 'cd ~/projects/mana-monorepo && git pull --quiet' + +echo -e "${YELLOW}3. Validating the config on the server...${NC}" +if ! ssh "$REMOTE_HOST" "/opt/homebrew/bin/cloudflared tunnel --config $REMOTE_PATH ingress validate"; then + echo -e "${RED}Validation failed — aborting reload.${NC}" + exit 1 +fi + +echo -e "${YELLOW}4. Reloading cloudflared via launchctl kickstart...${NC}" +ssh "$REMOTE_HOST" 'launchctl kickstart -k gui/$(id -u)/com.cloudflare.cloudflared' + +echo -e "${YELLOW}5. Waiting for the tunnel to register...${NC}" +sleep 5 + +echo -e "${YELLOW}6. Sanity-checking the tunnel is back up...${NC}" +if curl -sf -o /dev/null https://mana.how; then + echo -e "${GREEN}✓ https://mana.how is reachable${NC}" +else + echo -e "${RED}✗ https://mana.how is NOT reachable — check 'tail -f /tmp/cloudflared.log' on the server${NC}" + exit 1 +fi + +echo "" +echo -e "${GREEN}✓ Tunnel config synced and reloaded.${NC}" +echo "" +echo "List currently-loaded routes:" +echo " ssh $REMOTE_HOST 'grep INF /tmp/cloudflared.log | grep \"Updated to new configuration\" | tail -1'"