diff --git a/.claude/HISTORY.md b/.claude/HISTORY.md index 3a954aa6..769e2024 100644 --- a/.claude/HISTORY.md +++ b/.claude/HISTORY.md @@ -663,3 +663,21 @@ _Last updated: 2026-02-07_ - RPCD `fix_ports` method wrapping CLI command - Visual feedback with modal spinner - Updated container.sh to include `dovecot-pop3d` in initial package list. + +44. **MetaBlogizer KISS ULTIME MODE (2026-02-07)** + - Added `metablogizerctl emancipate ` — one-command full exposure workflow. + - **Workflow steps** (automated in sequence): + - DNS Registration: Creates A record via `dnsctl` (Gandi/OVH based on availability) + - Vortex Mesh: Publishes to mesh via `vortexctl mesh publish` + - HAProxy: Creates backend, server, and vhost with SSL/ACME enabled + - SSL Certificate: Requests ACME cert via `haproxyctl cert add` (webroot mode) + - Zero-downtime Reload: Applies HAProxy config via SIGUSR2 + - **Helper functions**: + - `_emancipate_dns()`: Public IP detection, subdomain extraction, dnsctl integration + - `_emancipate_vortex()`: Mesh publication if vortex-dns enabled + - `_emancipate_haproxy()`: UCI backend/server/vhost creation, haproxyctl generate + - `_emancipate_ssl()`: ACME certificate request with status feedback + - `_emancipate_reload()`: Graceful HAProxy reload with restart fallback + - **Usage**: `metablogizerctl create myblog blog.example.com && metablogizerctl emancipate myblog` + - **Tracking**: Stores `emancipated=1` and `emancipated_at` timestamp in UCI + - Part of Punk Exposure architecture (multi-channel emancipation). diff --git a/.claude/WIP.md b/.claude/WIP.md index 80036d52..b093197b 100644 --- a/.claude/WIP.md +++ b/.claude/WIP.md @@ -51,7 +51,17 @@ _Last updated: 2026-02-07_ - Gossip-based exposure config sync via secubox-p2p - Created `luci-app-vortex-dns` dashboard -### Just Completed (2026-02-06) +### Just Completed (2026-02-07) + +- **MetaBlogizer KISS ULTIME MODE** — DONE (2026-02-07) + - Added `metablogizerctl emancipate` command + - One-command workflow: DNS + Vortex + HAProxy + SSL + Reload + - DNS registration via dnsctl (Gandi/OVH based on availability) + - Vortex DNS mesh publication + - HAProxy vhost with SSL and ACME + - Zero-downtime reload via SIGUSR2 + +### Completed (2026-02-06) - **AI Insights Dashboard** — DONE - Created `luci-app-ai-insights` - unified view across all AI agents diff --git a/package/secubox/secubox-app-metablogizer/Makefile b/package/secubox/secubox-app-metablogizer/Makefile index 14e59e3d..e0dca35f 100644 --- a/package/secubox/secubox-app-metablogizer/Makefile +++ b/package/secubox/secubox-app-metablogizer/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=secubox-app-metablogizer -PKG_VERSION:=1.0.0 +PKG_VERSION:=1.1.0 PKG_RELEASE:=1 include $(INCLUDE_DIR)/package.mk @@ -17,6 +17,7 @@ endef define Package/secubox-app-metablogizer/description Static site publisher with auto-vhost creation. Supports uhttpd (default) and nginx LXC runtimes. + KISS ULTIME MODE: One-command DNS + SSL + Mesh workflow. endef define Package/secubox-app-metablogizer/install diff --git a/package/secubox/secubox-app-metablogizer/files/usr/sbin/metablogizerctl b/package/secubox/secubox-app-metablogizer/files/usr/sbin/metablogizerctl index 382cf2ab..c908fb44 100644 --- a/package/secubox/secubox-app-metablogizer/files/usr/sbin/metablogizerctl +++ b/package/secubox/secubox-app-metablogizer/files/usr/sbin/metablogizerctl @@ -38,6 +38,12 @@ Site Commands: delete Delete site sync Sync site from git repo publish Publish site (create HAProxy vhost) + emancipate KISS ULTIME MODE - Full exposure workflow: + 1. DNS A record (Gandi/OVH) + 2. Vortex DNS mesh publication + 3. HAProxy vhost with SSL + 4. ACME certificate + 5. Zero-downtime reload Runtime Commands: runtime Show current runtime @@ -51,6 +57,10 @@ Runtime Selection: auto - Auto-detect (uhttpd preferred) uhttpd - Use uhttpd instances (lightweight) nginx - Use nginx LXC container (more features) + +Examples: + metablogizerctl create myblog blog.example.com + metablogizerctl emancipate myblog # Full exposure in one command EOF } @@ -512,6 +522,199 @@ EOF log_info "Start with: lxc-start -n $NGINX_LXC -d" } +# =========================================== +# KISS ULTIME MODE - Emancipate +# =========================================== + +_emancipate_dns() { + local name="$1" + local domain="$2" + local zone=$(uci -q get dns-provider.main.zone) + local provider=$(uci -q get dns-provider.main.provider) + + [ -z "$zone" ] && { log_warn "[DNS] No zone configured, skipping external DNS"; return 1; } + + # Extract subdomain from domain + local subdomain=$(echo "$domain" | sed "s/\.${zone}$//") + + # Get public IP + local public_ip=$(curl -s --connect-timeout 5 https://ipv4.icanhazip.com 2>/dev/null | tr -d '\n') + [ -z "$public_ip" ] && { log_warn "[DNS] Cannot detect public IP, skipping DNS"; return 1; } + + log_info "[DNS] Registering $subdomain.$zone -> $public_ip via $provider" + + # Check if dnsctl is available + if ! command -v dnsctl >/dev/null 2>&1; then + log_warn "[DNS] dnsctl not found, skipping external DNS" + return 1 + fi + + # Check if provider is available + if ! dnsctl test >/dev/null 2>&1; then + log_warn "[DNS] Provider $provider not configured or credentials invalid" + log_warn "[DNS] Skipping external DNS registration" + return 1 + fi + + # Add A record + dnsctl add A "$subdomain" "$public_ip" 3600 + + # Verify propagation (non-blocking) + log_info "[DNS] Verify with: dnsctl verify $domain" +} + +_emancipate_vortex() { + local name="$1" + local domain="$2" + + # Check if vortexctl is available + if ! command -v vortexctl >/dev/null 2>&1; then + log_info "[VORTEX] vortexctl not found, skipping mesh publication" + return 0 + fi + + # Check if vortex-dns is enabled + local vortex_enabled=$(uci -q get vortex-dns.main.enabled) + + if [ "$vortex_enabled" = "1" ]; then + log_info "[VORTEX] Publishing $name as $domain to mesh" + vortexctl mesh publish "$name" "$domain" 2>/dev/null + else + log_info "[VORTEX] Vortex DNS disabled, skipping mesh publication" + fi +} + +_emancipate_haproxy() { + local name="$1" + local domain="$2" + local port=$(uci_get site_${name}.port) + + log_info "[HAPROXY] Creating vhost for $domain" + + # Create backend + local backend_name="metablog_${name}" + uci set haproxy.${backend_name}=backend + uci set haproxy.${backend_name}.name="$backend_name" + uci set haproxy.${backend_name}.mode="http" + uci set haproxy.${backend_name}.balance="roundrobin" + uci set haproxy.${backend_name}.enabled="1" + + # Create server + local server_name="${backend_name}_srv" + uci set haproxy.${server_name}=server + uci set haproxy.${server_name}.backend="$backend_name" + uci set haproxy.${server_name}.name="uhttpd" + uci set haproxy.${server_name}.address="192.168.255.1" + uci set haproxy.${server_name}.port="$port" + uci set haproxy.${server_name}.weight="100" + uci set haproxy.${server_name}.check="1" + uci set haproxy.${server_name}.enabled="1" + + # Create vhost with SSL + local vhost_name=$(echo "$domain" | tr '.-' '_') + uci set haproxy.${vhost_name}=vhost + uci set haproxy.${vhost_name}.domain="$domain" + uci set haproxy.${vhost_name}.backend="$backend_name" + uci set haproxy.${vhost_name}.ssl="1" + uci set haproxy.${vhost_name}.ssl_redirect="1" + uci set haproxy.${vhost_name}.acme="1" + uci set haproxy.${vhost_name}.enabled="1" + + uci commit haproxy + + # Generate HAProxy config + if command -v haproxyctl >/dev/null 2>&1; then + haproxyctl generate 2>/dev/null + fi +} + +_emancipate_ssl() { + local domain="$1" + + log_info "[SSL] Requesting certificate for $domain" + + # Check if haproxyctl is available + if ! command -v haproxyctl >/dev/null 2>&1; then + log_warn "[SSL] haproxyctl not found, skipping SSL" + return 1 + fi + + # haproxyctl cert add handles ACME webroot mode (no HAProxy restart needed) + haproxyctl cert add "$domain" 2>&1 | while read line; do + echo " $line" + done + + if [ -f "/srv/haproxy/certs/$domain.pem" ]; then + log_info "[SSL] Certificate obtained successfully" + else + log_warn "[SSL] Certificate request may still be pending" + log_warn "[SSL] Check with: haproxyctl cert verify $domain" + fi +} + +_emancipate_reload() { + log_info "[RELOAD] Applying HAProxy configuration" + /etc/init.d/haproxy reload 2>/dev/null || { + log_warn "[RELOAD] Reload failed, restarting..." + /etc/init.d/haproxy restart 2>/dev/null + } +} + +cmd_emancipate() { + local name="$1" + [ -z "$name" ] && { log_error "Site name required"; usage; return 1; } + + if ! site_exists "$name"; then + log_error "Site '$name' not found" + log_error "Create first: metablogizerctl create $name " + return 1 + fi + + local domain=$(uci_get site_${name}.domain) + [ -z "$domain" ] && { log_error "Site domain not configured"; return 1; } + + echo "" + echo "==============================================" + echo " KISS ULTIME MODE: Emancipating $name" + echo "==============================================" + echo "" + + # Step 1: DNS Registration (external provider) + _emancipate_dns "$name" "$domain" + + # Step 2: Vortex DNS (mesh registration) + _emancipate_vortex "$name" "$domain" + + # Step 3: HAProxy vhost + backend + _emancipate_haproxy "$name" "$domain" + + # Step 4: SSL Certificate + _emancipate_ssl "$domain" + + # Step 5: Reload HAProxy + _emancipate_reload + + # Mark site as emancipated + uci set ${CONFIG}.site_${name}.emancipated="1" + uci set ${CONFIG}.site_${name}.emancipated_at="$(date -Iseconds)" + uci commit ${CONFIG} + + echo "" + echo "==============================================" + echo " EMANCIPATION COMPLETE" + echo "==============================================" + echo "" + echo " Site: https://$domain" + echo " Status: Published and SSL-protected" + echo " Mesh: $(uci -q get vortex-dns.main.enabled | grep -q 1 && echo 'Published' || echo 'Disabled')" + echo "" + echo " Verify:" + echo " curl -v https://$domain" + echo " dnsctl verify $domain" + echo " haproxyctl cert verify $domain" + echo "" +} + # =========================================== # Main # =========================================== @@ -522,6 +725,7 @@ case "${1:-}" in delete) shift; cmd_delete "$@" ;; sync) shift; cmd_sync "$@" ;; publish) shift; cmd_publish "$@" ;; + emancipate) shift; cmd_emancipate "$@" ;; runtime) shift; cmd_runtime "$@" ;; status) shift; cmd_status "$@" ;; install-nginx) shift; cmd_install_nginx "$@" ;;