diff -u -N /etc/nginx/sites-available/batam.hmpbi.my.id.conf.orig /etc/nginx/sites-available/batam.hmpbi.my.id.conf --- /etc/nginx/sites-available/batam.hmpbi.my.id.conf.orig 2026-10-17 21:03:00.000000000 +0700 +++ /etc/nginx/sites-available/batam.hmpbi.my.id.conf 2026-10-19 12:05:00.000000000 +0700 @@ -1,5 +1,5 @@ server { listen 80; listen [::]:80; server_name batam.hmpbi.my.id; location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name batam.hmpbi.my.id; ssl_certificate /etc/ssl/hmpbi.my.id/cert.pem; ssl_certificate_key /etc/ssl/hmpbi.my.id/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets on; sendfile on; sendfile_max_chunk 2m; tcp_nopush on; tcp_nodelay on; aio threads; directio 8m; + charset utf-8; keepalive_timeout 65s; keepalive_requests 10000; open_file_cache max=10000 inactive=60s; open_file_cache_valid 120s; open_file_cache_min_uses 1; open_file_cache_errors on; gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 5; gzip_min_length 256; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript text/markdown; + # Instant response for favicon requests (prevents browser tab spinner from getting stuck) + location = /favicon.ico { + access_log off; + log_not_found off; + return 204; + } + + # Redirect /local (without trailing slash) to canonical /local/ (prevents ERR_INVALID_RESPONSE) + location = /local { + return 301 /local/; + } + + # Canonical download & file viewing route jailed strictly to ~/local/ location /local/ { - alias /home/ubuntu/direct/; + alias /home/ubuntu/local/; autoindex on; autoindex_exact_size off; autoindex_localtime on; - add_header Accept-Ranges bytes; add_header Access-Control-Allow-Origin *; - add_header Cache-Control "no-cache, no-store, must-revalidate"; + add_header Cache-Control "no-cache"; add_header X-Accel-Buffering no; + add_header Content-Disposition $content_disposition always; + add_header X-Content-Type-Options "nosniff" always; } - location /direct/ { - alias /home/ubuntu/direct/; - ... - } - location = / { return 302 /local/; } location / { - return 403 "Forbidden: Access restricted strictly to /home/ubuntu/direct/\n"; + default_type text/plain; + return 403 "Forbidden: Access restricted strictly to /local/\n"; } } --- /dev/null 2026-10-18 23:55:00.000000000 +0700 +++ /etc/nginx/conf.d/disposition_map.conf 2026-10-18 23:55:00.000000000 +0700 @@ -0,0 +1,18 @@ +map $uri $content_disposition { + # 1. Directory autoindex requests (ending with /) MUST NOT be downloaded as file + ~/$ ""; + + # 2. Text, Markdown, code, configs, logs -> View inline in browser + ~*\.(md|markdown|mdown|mkd|txt|log|conf|cfg|ini|env|example|py|sh|bash|zsh|diff|patch|smali|java|kt|kts|rs|go|c|cpp|h|hpp|lua|properties|service|sql|r|tsx|jsx|csv|tsv)$ "inline"; + + # 3. Web markup, scripts, stylesheets, structured data -> View inline + ~*\.(html|htm|css|js|mjs|svg|png|jpg|jpeg|gif|webp|ico|bmp|tiff|heic|avif)$ "inline"; + ~*\.(json|jsonl|geojson|yaml|yml|toml|xml)$ "inline"; + ~*\.pdf$ "inline"; + + # 4. Modern audio and video -> Play inline + ~*\.(mp3|wav|ogg|opus|flac|m4a|aac|mp4|webm|mkv|mov)$ "inline"; + + # 5. Known binary packages, archives, installers, executables -> Force auto-download + ~*\.(7z|zip|tar|gz|tgz|bz2|xz|zst|rar|iso|img|bin|dat|exe|msi|apk|xapk|apks|dex|so|dll|deb|rpm)$ "attachment"; + + # 6. Default for any other unknown file extension default "attachment"; +} --- /dev/null 2026-10-18 23:33:00.000000000 +0700 +++ /etc/nginx/conf.d/custom_types.conf 2026-10-18 23:33:00.000000000 +0700 @@ -0,0 +1,15 @@ +types { + text/plain md markdown mdown mkd log conf cfg ini env example py pyw sh bash zsh diff patch smali java kt kts rs go c cpp h hpp lua properties service sql r tsx jsx csv tsv; + application/json jsonl geojson; + application/yaml yaml yml; + application/toml toml; + audio/opus opus; + audio/flac flac; + application/vnd.android.package-archive apk xapk apks; + application/x-tar tar; + application/gzip gz tgz; + application/x-bzip2 bz2; + application/x-xz xz; + application/zstd zst; +} --- /dev/null 2026-10-17 21:00:00.000000000 +0700 +++ /etc/sysctl.d/99-high-throughput.conf 2026-10-17 21:00:00.000000000 +0700 @@ -0,0 +1,13 @@ +net.core.default_qdisc = fq +net.ipv4.tcp_congestion_control = bbr +net.core.rmem_max = 33554432 +net.core.wmem_max = 33554432 +net.core.rmem_default = 1048576 +net.core.wmem_default = 1048576 +net.ipv4.tcp_rmem = 4096 1048576 33554432 +net.ipv4.tcp_wmem = 4096 1048576 33554432 +net.core.somaxconn = 65535 +net.core.netdev_max_backlog = 16384 +net.ipv4.tcp_max_syn_backlog = 8192 +net.ipv4.tcp_slow_start_after_idle = 0 +net.ipv4.tcp_tw_reuse = 1 +net.ipv4.tcp_fin_timeout = 15 +net.ipv4.tcp_mtu_probing = 1 --- /dev/null 2026-10-17 20:30:00.000000000 +0700 +++ /etc/systemd/system/telegram-bot-api.service 2026-10-17 20:30:00.000000000 +0700 @@ -0,0 +1,14 @@ +[Unit] +Description=Telegram Bot API Server (Local Mode) +After=network.target + +[Service] +Type=simple +User=ubuntu +Group=ubuntu +ExecStart=/usr/local/bin/telegram-bot-api --api-id=2040 --api-hash=b18441a1ff607e10a989891a5462e627 --http-port=8082 --local --dir=/var/lib/telegram-bot-api --temp-dir=/tmp +Restart=always +RestartSec=3s +LimitNOFILE=65535 + +[Install] +WantedBy=multi-user.target --- a/~/.hermes/scripts/wa_telegram_pair.py +++ b/~/.hermes/scripts/wa_telegram_pair.py @@ -8,7 +8,7 @@ from pathlib import Path # Add scripts directory to path for telegram_bot_api -sys.path.insert(0, '/home/ubuntu/.hermes/scripts') +sys.path.insert(0, str(Path.home() / '.hermes/scripts')) from telegram_bot_api import TelegramBot try: @@ -18,9 +18,9 @@ sys.exit(1) CHAT_ID = '-1004327669652' -SESSION_DIR = Path('/home/ubuntu/.hermes/whatsapp/session') -BRIDGE_DIR = Path('/home/ubuntu/.hermes/hermes-agent/scripts/whatsapp-bridge') -QR_IMG_PATH = '/home/ubuntu/direct/qr.png' +SESSION_DIR = Path.home() / '.hermes/whatsapp/session' +BRIDGE_DIR = Path.home() / '.hermes/hermes-agent/scripts/whatsapp-bridge' +QR_IMG_PATH = Path.home() / 'local/qr.png' --- /dev/null 2026-10-21 02:40:00.000000000 +0700 +++ ~/.config/systemd/user/oci-budget.service 2026-10-21 02:40:00.000000000 +0700 @@ -0,0 +1,6 @@ +[Unit] +Description=OCI Budget & Cost Over-usage Monitor + +[Service] +Type=oneshot +ExecStart=/usr/bin/python3 %h/.hermes/scripts/oci_budget_alert.py --- /dev/null 2026-10-21 02:40:00.000000000 +0700 +++ ~/.config/systemd/user/oci-budget.timer 2026-10-21 02:40:00.000000000 +0700 @@ -0,0 +1,9 @@ +[Unit] +Description=Run OCI Budget Monitor Every 6 Hours + +[Timer] +OnCalendar=*-*-* 00/6:00:00 +Persistent=true + +[Install] +WantedBy=timers.target --- a/~/.hermes/scripts/hermes_backup_cron.py +++ b/~/.hermes/scripts/hermes_backup_cron.py @@ -128,6 +128,20 @@ print(f"[OK] Archive created: {archive_name} ({compressed_mb:.1f} MB, SHA-256: {sha256_hash[:16]}...)") + # Resolve live OCI budget spend dynamically + oci_status_str = "SGD 0.00 / 1.00 (Safe)" + try: + sys.path.insert(0, str(HERMES_DIR / "scripts")) + from oci_budget_alert import get_tenancy_ocid, get_active_budget_id, get_oci_budget_info + t_id = get_tenancy_ocid() + b_id = get_active_budget_id(t_id) + b_info = get_oci_budget_info(b_id) + spend = b_info.get("actual_spend", 0.0) + limit = b_info.get("budget_amount", 1.0) + label = "Safe" if spend == 0.0 else "OVERAGE" + oci_status_str = f"SGD {spend:.2f} / {limit:.2f} ({label})" + except Exception: + pass + # 6. Update EXPORT_RESULTS.md --- /dev/null 2026-10-21 15:44:00.000000000 +0700 +++ ~/.hermes/scripts/renew_ssl.sh 2026-10-21 15:44:00.000000000 +0700 @@ -0,0 +1,50 @@ +#!/bin/bash +# Precise H-1 Auto-Renewal Script for Let's Encrypt +# User rule: Renew strictly when less than 24 hours before expiry (H-1) + +DOMAINS=("batam.hmpbi.my.id") +RENEW_HOURS_BEFORE=24 +LOG_FILE="/home/ubuntu/.hermes/logs/ssl-renewal.log" + +mkdir -p "." + +log() { + echo "2026-10-21 15:52:48: " >> "" +} + +CURRENT_EPOCH=1792572768 + +for DOMAIN in ""; do + CERT_FILE="/etc/letsencrypt/live//fullchain.pem" + if [ ! -f "" ]; then + log "WARN: Certificate not found for at " + continue + fi + + EXPIRY_DATE= + if [ -z "" ]; then + log "ERROR: Could not read expiry date for " + continue + fi + + EXPIRY_EPOCH= + DIFF_SECONDS=0 + HOURS_LEFT=0 + MINUTES_LEFT=0 + DAYS_LEFT=0 + + log "[] Expiry: (d h m remaining)" + + if [ "" -le 0 ]; then + log "[] Certificate expires in < h ( hours left). Executing renewal..." + if /usr/bin/certbot renew --cert-name "" --quiet --no-self-upgrade >> "" 2>&1; then + log "[] Renewal succeeded. Reloading nginx..." + /usr/bin/systemctl reload nginx + log "[] Nginx reloaded successfully." + else + log "[] ERROR: Renewal failed." + fi + else + log "[] Certificate OK. Status healthy (> h). Skip renewal." + fi +done --- /dev/null 2026-10-21 15:45:00.000000000 +0700 +++ /etc/cron.d/ssl-renewal 2026-10-21 15:45:00.000000000 +0700 @@ -0,0 +1,2 @@ +# Check SSL certificate expiry twice daily and auto-renew strictly at H-1 (< 24h) +30 2,14 * * * root /home/ubuntu/.hermes/scripts/renew_ssl.sh