Initial commit - NUC server configuration and docs
- CLAUDE.md: Server instructions and service reference - docs/: Persistent documentation (architecture, guides) - .artifacts/: Session-generated notes - playwriter-browser/: Remote browser container config Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
696
CLAUDE.md
Normal file
696
CLAUDE.md
Normal file
@@ -0,0 +1,696 @@
|
||||
# NUC Server - Claude Code Instructions
|
||||
|
||||
## Server Access
|
||||
|
||||
The NUC server is accessible via SSH:
|
||||
```bash
|
||||
ssh nuc
|
||||
```
|
||||
|
||||
**Connection Details:**
|
||||
- Hostname: `192.168.1.3`
|
||||
- User: `alezmad`
|
||||
- SSH Key: `~/.ssh/id_ed25519_nuc`
|
||||
|
||||
## Service Management
|
||||
|
||||
### Coolify (Primary Service Manager)
|
||||
All services are managed through Coolify at `http://192.168.1.3:8000`
|
||||
|
||||
**Prefer using Coolify MCP** (`mcp__coolify__*`) for service management - it's faster and more reliable than SSH commands.
|
||||
|
||||
### ⚠️ STRICT RULE: Container Deployment Priority
|
||||
|
||||
**ALWAYS attempt Coolify first when adding any container/service:**
|
||||
|
||||
1. **First:** Try `mcp__coolify__service(action="create", type="<service-name>", ...)`
|
||||
2. **If type invalid:** Deploy via docker-compose in Coolify using `docker_compose_raw`
|
||||
3. **Last resort:** Direct Docker commands via SSH (only if Coolify can't handle it)
|
||||
|
||||
```python
|
||||
# Step 1: Try native Coolify service type
|
||||
mcp__coolify__service(action="create", type="servicename", name="ServiceName",
|
||||
server_uuid="qk84w0goo4w48g4ggsoo0oss", project_uuid="a8484ggc88c40w4g4k004ow0",
|
||||
environment_name="production", instant_deploy=True)
|
||||
|
||||
# Step 2: If "Invalid service type", use docker-compose
|
||||
mcp__coolify__service(action="create", name="ServiceName",
|
||||
server_uuid="qk84w0goo4w48g4ggsoo0oss", project_uuid="a8484ggc88c40w4g4k004ow0",
|
||||
environment_name="production",
|
||||
docker_compose_raw="""
|
||||
services:
|
||||
myservice:
|
||||
image: organization/image:tag
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- data:/app/data
|
||||
volumes:
|
||||
data:
|
||||
""", instant_deploy=True)
|
||||
```
|
||||
|
||||
**Why Coolify first:**
|
||||
- Centralized management in one UI
|
||||
- Automatic restarts, health checks
|
||||
- Easy updates and rollbacks
|
||||
- Visible in infrastructure overview
|
||||
- Consistent with existing services
|
||||
|
||||
### ⚠️ STRICT RULE: Browser MCP for Manual Configurations
|
||||
|
||||
**When a task requires manual web UI interaction (OAuth setup, API key generation, admin consoles), ALWAYS use Browser MCP instead of asking the user to do it manually.**
|
||||
|
||||
**Priority order for browser automation:**
|
||||
1. `mcp__playwriter-nuc-01__*` - Remote NUC browser (preferred, no local resources)
|
||||
2. `mcp__chrome-devtools-nuc-01__*` - Chrome DevTools for NUC browser
|
||||
3. `mcp__playwriter-local__*` - Local browser (fallback, uses local resources)
|
||||
|
||||
**Browser MCP Naming Convention:**
|
||||
```
|
||||
playwriter-<location>-<id>
|
||||
chrome-devtools-<location>-<id>
|
||||
|
||||
Examples:
|
||||
- playwriter-local → Local machine browser
|
||||
- playwriter-nuc-01 → First NUC browser container
|
||||
- playwriter-nuc-02 → Second NUC browser (future)
|
||||
- playwriter-cloud-01 → Cloud browser instance (future)
|
||||
```
|
||||
|
||||
**Common use cases:**
|
||||
- Generating API keys/tokens (Tailscale, OAuth apps, etc.)
|
||||
- Configuring OAuth/OIDC providers
|
||||
- Admin console settings not available via API
|
||||
- Any "go to website and click" tasks
|
||||
|
||||
```python
|
||||
# Example: Navigate and interact
|
||||
mcp__chrome-devtools__navigate_page(type="url", url="https://admin.example.com")
|
||||
mcp__chrome-devtools__take_snapshot() # See current state
|
||||
mcp__chrome-devtools__click(uid="<element_uid>")
|
||||
mcp__chrome-devtools__fill(uid="<element_uid>", value="text")
|
||||
```
|
||||
|
||||
**NEVER say "please go to X and do Y manually" - use browser MCP instead.**
|
||||
|
||||
### ⚠️ STRICT RULE: Parallel Subtasks for Multiple Operations
|
||||
|
||||
**When multiple independent services, configurations, or entities need to be set up, ALWAYS use parallel Task agents instead of sequential operations.**
|
||||
|
||||
```python
|
||||
# WRONG - Sequential (slow)
|
||||
# Step 1: Configure Tailscale
|
||||
# Step 2: Configure WireGuard
|
||||
# Step 3: Add to Homepage
|
||||
|
||||
# CORRECT - Parallel subtasks (fast)
|
||||
Task(subagent_type="general-purpose", prompt="Configure Tailscale auth key...", description="Setup Tailscale")
|
||||
Task(subagent_type="general-purpose", prompt="Install WireGuard on router...", description="Setup WireGuard")
|
||||
Task(subagent_type="general-purpose", prompt="Add services to Homepage...", description="Update Homepage")
|
||||
# All three run simultaneously!
|
||||
```
|
||||
|
||||
**When to parallelize:**
|
||||
- Multiple service deployments
|
||||
- Multiple configuration changes across different systems
|
||||
- Independent API calls or browser automations
|
||||
- Any tasks that don't depend on each other's output
|
||||
|
||||
**How to parallelize:**
|
||||
- Use multiple `Task` tool calls in a single message
|
||||
- Each task gets its own agent with full context
|
||||
- Results are collected when all complete
|
||||
|
||||
### Available MCPs for NUC Management
|
||||
|
||||
| MCP | Purpose |
|
||||
|-----|---------|
|
||||
| `mcp__coolify__*` | Service management, deployments, env vars |
|
||||
| `mcp__ssh-manager__*` | Direct SSH commands, file transfers |
|
||||
| `mcp__n8n__*` | Workflow automation (if configured) |
|
||||
| `mcp__playwriter__*` | Browser automation fallback (see below) |
|
||||
|
||||
### ⚠️ STRICT RULE: MCP Research Protocol
|
||||
|
||||
**When asked to find/recommend new MCPs**, read the detailed guide at `docs/mcp-research-guide.md` which contains:
|
||||
- MCP directories (MCP.so, Smithery, MCPHub, etc.)
|
||||
- GitHub verification resources
|
||||
- Evaluation criteria checklist
|
||||
- Research workflow
|
||||
- Category quick reference
|
||||
|
||||
**Quick workflow:**
|
||||
1. Search directories: `mcp.so`, `smithery.ai`, `mcpservers.org`
|
||||
2. Verify on GitHub: stars, last commit, issues
|
||||
3. Evaluate: 50+ stars, <6 months active, 3+ tools, OSS license
|
||||
|
||||
### Playwriter as Fallback
|
||||
|
||||
When SSH, API endpoints, or other MCPs can't accomplish a task (e.g., no API available, UI-only settings), use **Playwriter MCP** to automate browser interactions:
|
||||
|
||||
```javascript
|
||||
// Navigate to service UI
|
||||
await page.goto('http://192.168.1.3:8000');
|
||||
// Get page state
|
||||
console.log(await accessibilitySnapshot({ page }));
|
||||
// Interact with elements
|
||||
await page.locator('aria-ref=e5').click();
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- Configuring services that lack APIs (Coolify UI settings, etc.)
|
||||
- Creating OAuth apps, API keys through web interfaces
|
||||
- Debugging issues by inspecting service dashboards
|
||||
- Any task where clicking through a UI is the only option
|
||||
|
||||
### Remote Browser Container (NUC)
|
||||
|
||||
A dedicated browser container runs on the NUC for AI-controlled browsing without local resources:
|
||||
|
||||
**Access:**
|
||||
- noVNC Web: `http://192.168.1.3:6081/vnc.html`
|
||||
- Playwriter Relay: `ws://192.168.1.3:19988`
|
||||
- Chrome DevTools: `http://192.168.1.3:9222`
|
||||
|
||||
**MCP connects remotely via:**
|
||||
```json
|
||||
{
|
||||
"playwriter-nuc-01": {
|
||||
"_id": "nuc-01",
|
||||
"_host": "192.168.1.3",
|
||||
"args": ["playwriter", "--host", "ws://192.168.1.3:19988", "--token", "nuc-browser-token"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**First-time setup:** Access noVNC, install Playwriter extension, click to activate (turns green).
|
||||
|
||||
**Container location:** `~/playwriter-browser/` on NUC (deployed via docker compose)
|
||||
|
||||
**Coolify CLI Commands:**
|
||||
```bash
|
||||
# Access Coolify's Laravel tinker for direct database/service manipulation
|
||||
ssh nuc "docker exec coolify php artisan tinker --execute=\"<PHP_CODE>\""
|
||||
|
||||
# Restart a service (example for service ID 9 - Outline)
|
||||
ssh nuc "docker exec coolify php artisan tinker --execute=\"
|
||||
use App\Actions\Service\StartService;
|
||||
use App\Models\Service;
|
||||
\\\$service = Service::find(9);
|
||||
StartService::run(\\\$service);
|
||||
\""
|
||||
|
||||
# Update environment variable (encrypted)
|
||||
ssh nuc "docker exec coolify php artisan tinker --execute=\"
|
||||
use App\Models\EnvironmentVariable;
|
||||
\\\$var = EnvironmentVariable::where('key', 'VAR_NAME')->where('resourceable_id', SERVICE_ID)->first();
|
||||
\\\$var->value = encrypt('new_value');
|
||||
\\\$var->save();
|
||||
\""
|
||||
```
|
||||
|
||||
### Docker Commands
|
||||
```bash
|
||||
# List all containers
|
||||
ssh nuc "docker ps -a --format '{{.Names}}\t{{.Status}}'"
|
||||
|
||||
# View container logs
|
||||
ssh nuc "docker logs <container_name> 2>&1 | tail -50"
|
||||
|
||||
# Restart a container
|
||||
ssh nuc "docker restart <container_name>"
|
||||
|
||||
# Execute command in container
|
||||
ssh nuc "docker exec <container_name> <command>"
|
||||
```
|
||||
|
||||
## Services & Ports
|
||||
|
||||
| Service | Port | URL | Container |
|
||||
|---------|------|-----|-----------|
|
||||
| Homepage | 3000 | http://192.168.1.3:3000 | homepage-* |
|
||||
| Coolify | 8000 | http://192.168.1.3:8000 | coolify |
|
||||
| Gitea | 3030 | http://192.168.1.3:3030 | gitea-* |
|
||||
| Outline | 3080 | http://192.168.1.3:3080 | outline-* |
|
||||
| n8n | 5678 | http://192.168.1.3:5678 | n8n-* |
|
||||
| Vaultwarden | 8222 | http://192.168.1.3:8222 | vaultwarden-* |
|
||||
| Ntfy | 8333 | http://192.168.1.3:8333 | ntfy-* |
|
||||
| MinIO Console | 9001 | http://192.168.1.3:9001 | minio-* |
|
||||
| MinIO API | 9000 | http://192.168.1.3:9000 | minio-* |
|
||||
| Authentik | 9090 | http://192.168.1.3:9090 | authentik-* |
|
||||
| FileBrowser | 8085 | http://192.168.1.3:8085 | filebrowser-* |
|
||||
| Adminer | 8088 | http://192.168.1.3:8088 | adminer |
|
||||
| Uptime Kuma | 3001 | http://192.168.1.3:3001 | uptime-kuma |
|
||||
| Kopia | 51515 | http://192.168.1.3:51515 | kopia |
|
||||
| Dozzle | 9999 | http://192.168.1.3:9999 | dozzle |
|
||||
|
||||
## Port Forwarding
|
||||
|
||||
Some services use port forwarding containers (alpine/socat or nginx) to expose internal Coolify services:
|
||||
```bash
|
||||
# Create a port forwarder for a Coolify service
|
||||
ssh nuc "docker run -d --name port-fwd-<service> --network <coolify_network> -p <external_port>:<internal_port> alpine/socat tcp-listen:<internal_port>,fork,reuseaddr tcp-connect:<container_name>:<container_port>"
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
**Homepage Config:**
|
||||
- Location: `/opt/homepage/config/`
|
||||
- Services: `/opt/homepage/config/services.yaml`
|
||||
|
||||
**Coolify Data:**
|
||||
- Location: `/data/coolify/`
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **After Coolify redeploy**: Containers may be in "Created" state - manually start with `docker start <container>`
|
||||
|
||||
2. **Environment Variables in Coolify**: Are encrypted with Laravel encryption. Use `encrypt()` when updating.
|
||||
|
||||
3. **HSTS Issues**: Some services send HSTS headers. Use nginx proxy with `proxy_hide_header Strict-Transport-Security;` to strip them.
|
||||
|
||||
4. **Network Discovery**: Find container's network with `docker inspect <container> --format '{{.NetworkSettings.Networks}}'`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Coolify MCP vs Direct Docker
|
||||
|
||||
**Always verify Coolify status with Docker** - Coolify's status can lag behind actual container state:
|
||||
```bash
|
||||
# Coolify may show "exited" but container is actually running
|
||||
ssh nuc "docker ps -a --format 'table {{.Names}}\t{{.Status}}' | grep <service>"
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Containers stuck in "Created" state**: After Coolify restart/redeploy, containers may not auto-start
|
||||
```bash
|
||||
ssh nuc "docker start <container_name>"
|
||||
```
|
||||
|
||||
2. **Service shows "running:unknown"**: No healthcheck configured. Add one via Coolify service update:
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:<port>"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
```
|
||||
|
||||
3. **Service dependencies not starting**: Services with `depends_on: condition: service_healthy` won't start until dependencies are healthy. Check dependency containers first.
|
||||
|
||||
4. **Stale database entries in Coolify**: Coolify may have database/service entries with no corresponding container. Safe to delete if container doesn't exist:
|
||||
```bash
|
||||
# Verify container doesn't exist
|
||||
ssh nuc "docker ps -a | grep <container_name>"
|
||||
# Then delete via Coolify MCP or UI
|
||||
```
|
||||
|
||||
5. **Embedded vs Standalone databases**: Services like Outline and Authentik have their own PostgreSQL containers (e.g., `postgres-pccg80...`) bundled in the service compose. These are separate from standalone Coolify databases.
|
||||
|
||||
6. **Wrong healthcheck endpoint**: Some services use `/healthz` instead of `/`. Verify with:
|
||||
```bash
|
||||
ssh nuc "docker exec <container> wget -qO- http://127.0.0.1:<port>/healthz"
|
||||
```
|
||||
|
||||
7. **Creating API keys when no UI available** (e.g., n8n):
|
||||
```bash
|
||||
# Stop container, insert directly into SQLite, restart
|
||||
ssh nuc "docker run --rm -v <volume>:/data keinos/sqlite3 sqlite3 /data/database.sqlite \"<INSERT_QUERY>\""
|
||||
```
|
||||
|
||||
### Coolify MCP Quick Reference
|
||||
|
||||
```python
|
||||
# Check infrastructure overview
|
||||
mcp__coolify__get_infrastructure_overview()
|
||||
|
||||
# Start/stop/restart service
|
||||
mcp__coolify__control(resource="service", action="start|stop|restart", uuid="<uuid>")
|
||||
|
||||
# Get service details (including docker_compose)
|
||||
mcp__coolify__get_service(uuid="<uuid>")
|
||||
|
||||
# Update service config (e.g., add healthcheck)
|
||||
mcp__coolify__service(action="update", uuid="<uuid>", docker_compose_raw="<yaml>")
|
||||
|
||||
# Delete stale database
|
||||
mcp__coolify__database(action="delete", uuid="<uuid>", delete_volumes=True)
|
||||
```
|
||||
|
||||
## OpenWrt Router
|
||||
|
||||
The network is managed by an OpenWrt router at `192.168.1.1`.
|
||||
|
||||
### SSH Access
|
||||
```bash
|
||||
# Connect to router
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1
|
||||
|
||||
# Or via NUC as jump host
|
||||
ssh nuc "ssh root@192.168.1.1 '<command>'"
|
||||
```
|
||||
|
||||
**Router Details:**
|
||||
- IP: `192.168.1.1`
|
||||
- User: `root`
|
||||
- SSH Key: `~/.ssh/id_ed25519_nuc`
|
||||
- Firmware: OpenWrt 23.05.0
|
||||
- Architecture: ARM Cortex-A9 (mvebu/cortexa9)
|
||||
- LuCI Web UI: `http://192.168.1.1`
|
||||
|
||||
### OpenWrt MCP Server
|
||||
An MCP server runs on the router for AI integration:
|
||||
- **Location:** `/opt/mcp-server/openwrt-mcp-server`
|
||||
- **Config:** `/opt/mcp-server/config.toml`
|
||||
- **HTTP API:** `http://192.168.1.1:8090`
|
||||
- **API Token:** `openwrt-mcp-secret-2026`
|
||||
- **Init Script:** `/etc/init.d/mcp-server`
|
||||
|
||||
```bash
|
||||
# Control MCP server
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "/etc/init.d/mcp-server start|stop|restart"
|
||||
```
|
||||
|
||||
### Common Router Tasks
|
||||
|
||||
**Port Forwarding:**
|
||||
```bash
|
||||
# List current port forwards
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "uci show firewall | grep redirect"
|
||||
|
||||
# Add port forward
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "
|
||||
uci add firewall redirect
|
||||
uci set firewall.@redirect[-1].name='<name>'
|
||||
uci set firewall.@redirect[-1].src='wan'
|
||||
uci set firewall.@redirect[-1].src_dport='<external_port>'
|
||||
uci set firewall.@redirect[-1].dest='lan'
|
||||
uci set firewall.@redirect[-1].dest_ip='<internal_ip>'
|
||||
uci set firewall.@redirect[-1].dest_port='<internal_port>'
|
||||
uci set firewall.@redirect[-1].proto='tcp udp'
|
||||
uci set firewall.@redirect[-1].target='DNAT'
|
||||
uci commit firewall
|
||||
/etc/init.d/firewall restart
|
||||
"
|
||||
```
|
||||
|
||||
**Firewall Rules:**
|
||||
```bash
|
||||
# Show firewall zones
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "uci show firewall | grep zone"
|
||||
|
||||
# Allow traffic from WAN to specific port
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "
|
||||
uci add firewall rule
|
||||
uci set firewall.@rule[-1].name='Allow-<service>'
|
||||
uci set firewall.@rule[-1].src='wan'
|
||||
uci set firewall.@rule[-1].dest_port='<port>'
|
||||
uci set firewall.@rule[-1].proto='tcp'
|
||||
uci set firewall.@rule[-1].target='ACCEPT'
|
||||
uci commit firewall
|
||||
/etc/init.d/firewall restart
|
||||
"
|
||||
```
|
||||
|
||||
**DNS/DHCP (dnsmasq):**
|
||||
```bash
|
||||
# View DNS/DHCP config
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "uci show dhcp"
|
||||
|
||||
# Force DNS cache refresh
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "/etc/init.d/dnsmasq restart"
|
||||
|
||||
# Add static DHCP lease
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "
|
||||
uci add dhcp host
|
||||
uci set dhcp.@host[-1].name='<hostname>'
|
||||
uci set dhcp.@host[-1].mac='<mac_address>'
|
||||
uci set dhcp.@host[-1].ip='<ip_address>'
|
||||
uci commit dhcp
|
||||
/etc/init.d/dnsmasq restart
|
||||
"
|
||||
|
||||
# Add custom DNS entry
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "
|
||||
uci add dhcp domain
|
||||
uci set dhcp.@domain[-1].name='<hostname>'
|
||||
uci set dhcp.@domain[-1].ip='<ip_address>'
|
||||
uci commit dhcp
|
||||
/etc/init.d/dnsmasq restart
|
||||
"
|
||||
```
|
||||
|
||||
**Network Diagnostics:**
|
||||
```bash
|
||||
# Check WAN status
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "ifstatus wan"
|
||||
|
||||
# View connected clients
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "cat /tmp/dhcp.leases"
|
||||
|
||||
# Check routing table
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "ip route"
|
||||
|
||||
# View system logs
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "logread | tail -50"
|
||||
```
|
||||
|
||||
**Package Management (opkg):**
|
||||
```bash
|
||||
# Update package lists
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "opkg update"
|
||||
|
||||
# Install package
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "opkg install <package>"
|
||||
|
||||
# List installed packages
|
||||
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "opkg list-installed"
|
||||
```
|
||||
|
||||
**Browser Automation (Chrome DevTools MCP):**
|
||||
When SSH commands aren't sufficient, use Chrome DevTools MCP to automate LuCI:
|
||||
```python
|
||||
# Navigate to router UI
|
||||
mcp__chrome-devtools__navigate_page(type="url", url="http://192.168.1.1/cgi-bin/luci/admin/...")
|
||||
|
||||
# Take snapshot of UI state
|
||||
mcp__chrome-devtools__take_snapshot()
|
||||
|
||||
# Interact with elements by uid
|
||||
mcp__chrome-devtools__click(uid="<uid>")
|
||||
mcp__chrome-devtools__fill(uid="<uid>", value="<text>")
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
**Outline OIDC (via Gitea):**
|
||||
- Client ID: `249a3a1d-92d4-47d8-b4a9-81c64e1da6ab`
|
||||
- Auth URL: `http://192.168.1.3:3030/login/oauth/authorize`
|
||||
- Token URL: `http://192.168.1.3:3030/login/oauth/access_token`
|
||||
- Userinfo URL: `http://192.168.1.3:3030/login/oauth/userinfo`
|
||||
|
||||
## Public Access & Security Architecture
|
||||
|
||||
**Full architecture details:** `docs/architecture.md`
|
||||
|
||||
### Why Tailscale Funnel (Not Cloudflare)
|
||||
|
||||
Cloudflare shared IPs get blocked by Spanish ISPs during LaLiga matches. Tailscale Funnel:
|
||||
- Uses different IP infrastructure (not blocked)
|
||||
- Handles dynamic ISP IP changes automatically
|
||||
- No ports exposed on router
|
||||
- HTTPS termination included
|
||||
|
||||
### Tailscale Funnel (Public Access)
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Funnel URL** | `https://nuc-tailscale.tail58f5ad.ts.net` |
|
||||
| **Tailscale IP** | `100.x.x.x` (stable, never changes) |
|
||||
| **Status** | `ssh nuc "tailscale funnel status"` |
|
||||
|
||||
**Start Funnel for a service:**
|
||||
```bash
|
||||
# Expose port 3000 via Funnel
|
||||
ssh nuc "tailscale funnel 3000"
|
||||
|
||||
# Or with background (use screen/tmux)
|
||||
ssh nuc "screen -dmS funnel tailscale funnel 3000"
|
||||
```
|
||||
|
||||
### Current Domain Routes
|
||||
|
||||
| Domain | Destination | Method |
|
||||
|--------|-------------|--------|
|
||||
| whyrating.com | `nuc-tailscale.tail58f5ad.ts.net` | Namecheap 301 redirect |
|
||||
|
||||
### Adding a New Domain
|
||||
|
||||
1. **Check availability** (if needed):
|
||||
```python
|
||||
mcp__namecheap__namecheap_check_domain_availability(domains=["example.com"])
|
||||
```
|
||||
|
||||
2. **Configure redirect at registrar** (Namecheap):
|
||||
- Go to Domain List → Manage → Redirect Domain
|
||||
- Source: `example.com`
|
||||
- Destination: `https://nuc-tailscale.tail58f5ad.ts.net`
|
||||
- Type: Permanent (301)
|
||||
|
||||
3. **Start Funnel** on NUC for the target port
|
||||
|
||||
### Security Layers
|
||||
|
||||
```
|
||||
Internet → Tailscale Funnel (HTTPS) → CrowdSec → Traefik → Container
|
||||
↓
|
||||
Blocks malicious IPs
|
||||
```
|
||||
|
||||
| Layer | Purpose |
|
||||
|-------|---------|
|
||||
| **Tailscale Funnel** | Only entry point, HTTPS termination |
|
||||
| **CrowdSec** | DDoS protection, threat intelligence |
|
||||
| **Traefik** | Domain routing, rate limiting |
|
||||
| **Docker Networks** | Container isolation |
|
||||
|
||||
### Tailscale Mesh (Admin Access)
|
||||
|
||||
Private encrypted access from anywhere:
|
||||
|
||||
| From | Command |
|
||||
|------|---------|
|
||||
| **Remote SSH** | `ssh nuc-tailscale` |
|
||||
| **Remote Coolify** | `http://nuc-tailscale:8000` |
|
||||
| **Home network** | `ssh nuc` or `http://192.168.1.3:8000` |
|
||||
|
||||
**NOT exposed to internet:** SSH (22), Coolify (8000), databases, MinIO, Authentik, router admin.
|
||||
|
||||
### Dynamic IP Handling
|
||||
|
||||
ISP can change your public IP anytime. Tailscale handles this automatically:
|
||||
- Tailscale IP (100.x.x.x) stays stable
|
||||
- Funnel URL stays stable
|
||||
- Domain redirects stay stable
|
||||
- Tunnels auto-reconnect in ~10-30 seconds
|
||||
|
||||
## Artifacts Folder
|
||||
|
||||
**Location:** `.artifacts/`
|
||||
|
||||
This folder stores important information artifacts that should be preserved for future reference. Claude should proactively save artifacts here when relevant information is generated during sessions.
|
||||
|
||||
### Naming Convention
|
||||
Files must use datetime-prefixed names:
|
||||
```
|
||||
YYYY-MM-DD_HH-MM_<description>.md
|
||||
```
|
||||
|
||||
Examples:
|
||||
- `2026-02-01_13-45_coolify-api-token.md`
|
||||
- `2026-02-01_14-30_n8n-mcp-setup.md`
|
||||
- `2026-02-01_15-00_service-health-report.md`
|
||||
|
||||
### When to Save Artifacts
|
||||
|
||||
**Always save artifacts for:**
|
||||
- API tokens, keys, or credentials generated during sessions
|
||||
- Configuration changes made to services
|
||||
- Troubleshooting steps that resolved issues
|
||||
- Infrastructure changes or deployments
|
||||
- MCP server configurations and setup details
|
||||
- Database schema changes or migrations
|
||||
- Important command outputs that may be needed later
|
||||
- Service health reports or diagnostics
|
||||
|
||||
### Artifact Format
|
||||
```markdown
|
||||
# <Title>
|
||||
|
||||
**Date:** YYYY-MM-DD HH:MM
|
||||
**Context:** <Brief description of what was being done>
|
||||
|
||||
## Details
|
||||
|
||||
<Relevant information, configs, tokens, commands, etc.>
|
||||
|
||||
## Related
|
||||
- <Links to services, docs, or other artifacts>
|
||||
```
|
||||
|
||||
## OpenWrt Interaction Methods (Quick Reference)
|
||||
|
||||
| Method | When to Use | Example |
|
||||
|--------|-------------|---------|
|
||||
| **SSH** | Direct commands, config changes, package management | `ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "uci show network"` |
|
||||
| **OpenWrt MCP** | AI-driven automation, status queries | `curl -H "x-api-token: openwrt-mcp-secret-2026" http://192.168.1.1:8090/status` |
|
||||
| **Chrome DevTools MCP** | LuCI UI automation when no CLI/API exists | `mcp__chrome-devtools__navigate_page(url="http://192.168.1.1/...")` |
|
||||
| **LuCI Web UI** | Manual configuration, visual inspection | `http://192.168.1.1` (user: root) |
|
||||
|
||||
**Priority Order:** SSH > OpenWrt MCP > Chrome DevTools > Manual UI
|
||||
|
||||
## Next Steps / Migration Candidates
|
||||
|
||||
### Priority 1: Safe to Delete (Duplicates/Old Versions)
|
||||
| Image | Size | Action | Reason |
|
||||
|-------|------|--------|--------|
|
||||
| `google-reviews-scraper-pro-api` (old) | 3.62GB | ❌ DELETE | Old version, newer exists |
|
||||
| `claudefarm-claudefarm` | 3.87GB | ❌ DELETE | Replaced by claudefarm-browser + claudefarm-api |
|
||||
| `postgres:16` | 657MB | ❌ DELETE | Using `16-alpine` (389MB) instead |
|
||||
| `prom/mysqld-exporter:v0.14.0` | 28MB | ❌ DELETE | 3 years old, likely unused |
|
||||
|
||||
**Savings: ~8.2GB**
|
||||
|
||||
### Priority 2: Migrate to NUC (High Value)
|
||||
| Image | Size | Priority | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| `nocodb/nocodb` | 1.24GB | ⭐ HIGH | Airtable alternative - great for self-hosted data |
|
||||
| `grafana/grafana` | 932MB | ⭐ HIGH | Pairs with existing Uptime Kuma for monitoring |
|
||||
| `prom/prometheus` | 479MB | ⭐ HIGH | Metrics backend for Grafana |
|
||||
| `timescale/timescaledb` | 1.45GB | ⭐ HIGH | Time-series data, useful for IoT/metrics |
|
||||
|
||||
### Priority 3: Migrate to NUC (Medium Value)
|
||||
| Image | Size | Priority | Notes |
|
||||
|-------|------|----------|-------|
|
||||
| `mysql:8` | 1.07GB | 🔶 MEDIUM | Only if you have MySQL-specific apps |
|
||||
| `minio/minio + minio/mc` | 340MB | 🔶 SKIP | Already running on NUC via Coolify |
|
||||
|
||||
### Priority 4: MCP Tools - Evaluate Usage
|
||||
| Image | Size | Recommendation | Notes |
|
||||
|-------|------|----------------|-------|
|
||||
| `mcp/n8n` | 675MB | 🔶 SKIP | n8n already on NUC; this is just MCP wrapper |
|
||||
| `mcp/youtube-transcript` | 321MB | ✅ KEEP LOCAL | Useful for AI workflows |
|
||||
| `mcp/context7` | 423MB | ✅ KEEP LOCAL | Documentation lookup, AI essential |
|
||||
| `mcp/fetch` | ? | ✅ KEEP LOCAL | Web fetching for AI |
|
||||
|
||||
### Priority 5: Review Before Deleting
|
||||
| Image | Size | Action | Why Review |
|
||||
|-------|------|--------|------------|
|
||||
| `mysql:8` | 1.07GB | ⚠️ CHECK | May have local databases; verify before delete |
|
||||
| `timescale/timescaledb` | 1.45GB | ⚠️ CHECK | May have local time-series data |
|
||||
|
||||
### Recommended Coolify Deployments
|
||||
|
||||
```bash
|
||||
# 1. NocoDB (Airtable alternative)
|
||||
mcp__coolify__service(action="create", type="nocodb", name="NocoDB",
|
||||
server_uuid="qk84w0goo4w48g4ggsoo0oss", project_uuid="a8484ggc88c40w4g4k004ow0",
|
||||
environment_name="production", instant_deploy=True)
|
||||
|
||||
# 2. Prometheus + Grafana stack
|
||||
mcp__coolify__service(action="create", type="grafana", ...)
|
||||
mcp__coolify__service(action="create", type="prometheus", ...)
|
||||
```
|
||||
|
||||
### Migration Checklist
|
||||
- [ ] Delete old/duplicate images locally
|
||||
- [ ] Deploy NocoDB to NUC
|
||||
- [ ] Deploy Grafana + Prometheus monitoring stack
|
||||
- [ ] Consider TimescaleDB if IoT/metrics needed
|
||||
- [ ] Verify MySQL data before deleting
|
||||
- [ ] Add CloudBeaver to Uptime Kuma monitoring
|
||||
- [ ] Configure OpenWrt MCP MQTT broker (optional)
|
||||
Reference in New Issue
Block a user