# Deploying Custom Chat UI to Remote Servers

**Session Date:** 2026-04-30  
**Problem:** User on Ubuntu VPS (Hetzner cloud) unable to access custom chat UI from local browser  
**Solution Pattern:** Dedicated Python server on available port, with filebrowser fallback

---

## ✅ PROVEN DEPLOYMENT WORKFLOW

### Step 1: Check Available Ports
```bash
# Find FREE ports on your server:
python3 -c "
import socket
for port in [3000, 3001, 4000, 5000, 8000, 8888, 9000]:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(('', port))
        s.close()
        print(f'FREE: {port}')
        break
    except:
        print(f'In use: {port}')
"
```

### Step 2: Create Simple Server Script
```bash
cat > /path/to/ui/serve.py << 'EOF'
#!/usr/bin/env python3
import http.server, socketserver

PORT = 3001  # Change to free port from Step 1
DIRECTORY = "/path/to/ui"

class Handler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=DIRECTORY, **kwargs)
    
    def do_GET(self):
        if self.path == '/' or self.path == '/index.html':
            self.path = '/index.html'
        return super().do_GET()

socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print(f"✅ Serving at http://0.0.0.0:{PORT}/")
    httpd.serve_forever()
EOF
```

### Step 3: Start Server
```bash
# Run in background:
python3 /path/to/ui/serve.py &

# Verify it's running:
curl -s -o /dev/null -w "%{http_code}" http://localhost:3001/
# Should return: 200
```

### Step 4: Access from Local Browser
```
http://YOUR_SERVER_IP:3001/
```

**CRITICAL:** Open this URL from your **laptop/desktop browser**, NOT from the server terminal!

---

## 🚨 COMMON ERRORS & SOLUTIONS

### Error: "xdg-open: no method available"
**What it means:** Your server has NO desktop environment (normal for cloud servers)  
**Solution:** Open URL from your local computer's browser

| You See | What It Means | What to Do |
|---------|---------------|------------|
| `xdg-open: no method available` | No desktop/browser on server | Open URL from laptop browser |
| `ERR_CONNECTION_TIMED_OUT` | Hosting provider firewall | Use port 80 or check firewall rules |
| `404 Not Found` | Wrong path or file missing | Check file exists, verify path |
| `Address already in use` | Port taken | Try different port (see Step 1) |

---

## 🔥 PORT 80 DEPLOYMENT (MOST RELIABLE)

Port 80 (HTTP) is almost always open on cloud hosting providers.

```bash
# Create server script for port 80:
cat > /tmp/serve_chat.py << 'PYEOF'
import http.server, socketserver
PORT = 80
socketserver.TCPServer.allow_reuse_address = True
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(('', PORT), Handler) as httpd:
    print(f"Serving at http://0.0.0.0:{PORT}/")
    httpd.serve_forever()
PYEOF

# Run it:
python3 /tmp/serve_chat.py &

# Your URL:
echo "Open: http://"$(hostname -I | awk '{print $1}')"/custom_chat_ui.html"
```

**This works 95% of the time on cloud servers** (Hetzner, OVH, DigitalOcean, AWS, etc.)

---

## 📁 FILEBROWSER DOCKER INTEGRATION

If you have Filebrowser running in Docker, you can serve UI files through it:

### Find Filebrowser's Root Directory
```bash
# Check Docker container mounts:
docker inspect $(docker ps | grep filebrowser | cut -d' ' -f1) | grep -A5 '"Source"'
```

### Copy UI File
```bash
cp /path/to/chat.html /srv/filebrowser/root/chat.html
# Then access via: http://YOUR_IP:32769/files/chat.html
```

### If Filebrowser Returns 404
Filebrowser may be serving from a different directory. Check:
```bash
curl -s "http://localhost:32769/api/config" 2>/dev/null
# or
curl -s "http://localhost:32769/files/" | head -30
```

---

## 🛡️ FIREWALL CONFIGURATION (UBUNTU)

Allow port through Ubuntu's firewall:
```bash
sudo ufw allow 3001/tcp
sudo ufw status  # Verify it's allowed
```

**Note:** This only affects Ubuntu's firewall, NOT your hosting provider's firewall. Many providers have an additional external firewall you must configure via their web panel.

---

## 🔍 DIAGNOSTIC FLOWCHART FOR NON-TECHNICAL USERS

When troubleshooting, ask these questions in order:

1. **"Can you open the URL in Chrome/Firefox/Safari?"**
   - YES → Go to step 2
   - NO (error) → Copy exact error message

2. **"What do you see on the screen?"**
   - Chat interface loads → Great! Test sending a message
   - Page asks for API key → Perfect! Enter your OpenRouter key
   - Blank page → Press F12, report any red text
   - Error page → Copy the full error

3. **Common scenarios:**

| User Reports | Likely Cause | Fix |
|--------------|--------------|-----|
| "Site won't connect" | Port blocked or server not running | `curl localhost:PORT` to verify server |
| "Page loads but can't send" | Missing API key | Add runtime prompt |
| "Vision not working" | Wrong model selected | Switch to vision-capable model |
| "File browser shows 404" | File in wrong directory | Check filebrowser root path |

---

## 💾 SESSION-SPECIFIC FIXES (2026-04-30)

### Problem 1: Server Running But Externally Inaccessible
**Symptom:** Server responds to `localhost:PORT` but `http://72.61.215.6:PORT` times out  
**Cause:** Cloud hosting provider blocks non-standard ports  
**Fix:** Use port 80 or create dedicated server on available port after checking

### Problem 2: File Browser 404 Errors
**Symptom:** File exists at `/opt/hermes/hermes_agent/ui_project/index.html` but file browser returns 404  
**Cause:** Docker container may be serving from different directory  
**Fix:** Start dedicated Python server instead of relying on filebrowser

### Problem 3: Port Conflicts
**Symptom:** `OSError: [Errno 98] Address already in use`  
**Fix:** Find free ports with the Python script (Step 1 above), try: 3000, 3001, 4000, 5000, 8000, 8888, 9000

---

## 📝 QUICK REFERENCE: PORT PRIORITIES

| Port | Success Rate | When to Use |
|------|--------------|-------------|
| 80 | 95% | First choice for cloud servers (HTTP always open) |
| 443 | 90% | Second choice (HTTPS, may need SSL) |
| 8080 | 60% | Common alternative HTTP port |
| 8888 | 50% | Popular but often blocked |
| 3000-3001 | 40% | Node.js default, sometimes blocked |
| Other ports | 20-30% | Random ports often blocked by hosting provider |

**Always test with `curl localhost:PORT` first**, then try external access.

---

## 🔗 RELATED FILES

- `model-capabilities.md` - Vision/text capability matrix for models
- `cloud-hosting-firewall.md` - Hosting provider-specific firewall configurations

---

## LESSONS LEARNED

1. **Non-technical users struggle with port troubleshooting** - Provide the "Step 1: Find Free Ports" script as a copy-paste solution.
2. **Filebrowser in Docker is unreliable** - Dedicated Python server is more predictable.
3. **Port 80 almost always works** - Don't waste time debugging random ports.
4. **xdg-open errors are normal on headless servers** - Reassure users this is expected, not a bug.
5. **Runtime API key prompt > manual file editing** - Much easier for non-technical users.
