Move OpenClaw, Palmr, MinIO, JSX publishing, MCP configs, and migration candidates into dedicated docs/ files. Keep only DevOps-essential content inline (deployment rules, DNS, router, credentials, troubleshooting). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
580 lines
21 KiB
Markdown
580 lines
21 KiB
Markdown
# NUC Server - Claude Code Instructions
|
|
|
|
## Server Access
|
|
|
|
The NUC server is accessible via SSH:
|
|
```bash
|
|
ssh nuc
|
|
```
|
|
|
|
**Connection Details:**
|
|
- Hostname: `192.168.1.3` (local) or `100.113.153.45` (Tailscale)
|
|
- User: `alezmad`
|
|
- SSH Key: `~/.ssh/id_ed25519_nuc`
|
|
|
|
## DNS & Tailscale Setup
|
|
|
|
### DNS Strategy: Dual Resolution (Router DNS + Mac /etc/hosts)
|
|
|
|
**On the NUC's LAN (OpenWrt router):** All `.nuc.lan` domains resolve to `192.168.1.3` via dnsmasq.
|
|
|
|
**On the Mac:** `/etc/hosts` maps `.nuc.lan` domains to Tailscale IP `100.113.153.45` (Mac and NUC are on different physical networks that both use `192.168.1.0/24`).
|
|
|
|
**When adding a new `.nuc.lan` domain, update BOTH:**
|
|
1. OpenWrt router DNS (dnsmasq via `uci`)
|
|
2. Mac `/etc/hosts` → `100.113.153.45 newservice.nuc.lan`
|
|
|
|
### Configured Domains
|
|
|
|
| Domain | Service |
|
|
|--------|---------|
|
|
| `nuc.lan` / `nuc.local` | NUC Portal |
|
|
| `coolify.nuc.lan` | Coolify |
|
|
| `gitea.nuc.lan` | Gitea |
|
|
| `outline.nuc.lan` | Outline Wiki |
|
|
| `files.nuc.lan` | FileBrowser |
|
|
| `mail.nuc.lan` | Snappymail |
|
|
| `vault.nuc.lan` | Vaultwarden |
|
|
| `homepage.nuc.lan` | NUC Portal |
|
|
| `brand.nuc.lan` | Whyrating Brand |
|
|
| `templates.nuc.lan` | Whyrating Templates |
|
|
| `whyrating.nuc.lan` | Whyrating Hub |
|
|
| `whyops.nuc.lan` | WhyOps |
|
|
| `arrio.nuc.lan` | Arrio |
|
|
| `whatsapp.nuc.lan` | WhatsApp MCP |
|
|
|
|
All resolve to `192.168.1.3`.
|
|
|
|
### Traefik Routing
|
|
|
|
Config location: `/data/coolify/proxy/dynamic/nuc-services.yaml`
|
|
|
|
```yaml
|
|
http:
|
|
routers:
|
|
coolify:
|
|
rule: Host(`coolify.nuc.lan`)
|
|
service: coolify
|
|
services:
|
|
coolify:
|
|
loadBalancer:
|
|
servers:
|
|
- url: http://host.docker.internal:8000
|
|
```
|
|
|
|
### Adding a New Domain
|
|
|
|
```bash
|
|
# 1. Add DNS entry on router
|
|
ssh nuc "ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 '
|
|
uci add dhcp domain
|
|
uci set dhcp.@domain[-1].name=\"newservice.nuc.lan\"
|
|
uci set dhcp.@domain[-1].ip=\"192.168.1.3\"
|
|
uci commit dhcp
|
|
/etc/init.d/dnsmasq restart
|
|
'"
|
|
|
|
# 2. Add to Mac /etc/hosts
|
|
echo "924718" | sudo -S sh -c 'echo "100.113.153.45 newservice.nuc.lan" >> /etc/hosts'
|
|
echo "924718" | sudo -S dscacheutil -flushcache && sudo killall -HUP mDNSResponder
|
|
|
|
# 3. Add Traefik route (if needed for port-based service)
|
|
# Edit /data/coolify/proxy/dynamic/nuc-services.yaml
|
|
```
|
|
|
|
### Always-On Tailscale
|
|
|
|
**Keep Tailscale running** - direct connection on home network, mesh routing when remote, ~0% CPU idle.
|
|
|
|
## Service Management
|
|
|
|
### Coolify (Primary Service Manager)
|
|
All services managed through Coolify at `http://coolify.nuc.lan` (or `http://100.113.153.45:8000`).
|
|
**Prefer Coolify MCP** (`mcp__coolify__*`) over 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
|
|
|
|
```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)
|
|
```
|
|
|
|
### STRICT RULE: Browser MCP for Manual Configurations
|
|
|
|
**ALWAYS use Browser MCP instead of asking the user to do it manually.**
|
|
|
|
**Priority order:**
|
|
1. `mcp__playwriter-nuc-01__*` - Remote NUC browser (preferred)
|
|
2. `mcp__chrome-devtools-nuc-01__*` - Chrome DevTools for NUC browser
|
|
3. `mcp__playwriter-local__*` - Local browser (fallback)
|
|
|
|
**NEVER say "please go to X and do Y manually" - use browser MCP instead.**
|
|
|
|
### STRICT RULE: Parallel Subtasks for Multiple Operations
|
|
|
|
**ALWAYS use parallel Task agents for independent operations.**
|
|
|
|
```python
|
|
# CORRECT - Parallel subtasks (fast)
|
|
Task(subagent_type="general-purpose", prompt="Configure Tailscale...", description="Setup Tailscale")
|
|
Task(subagent_type="general-purpose", prompt="Install WireGuard...", description="Setup WireGuard")
|
|
# All run simultaneously!
|
|
```
|
|
|
|
### STRICT RULE: MCP Research Protocol
|
|
|
|
**When asked to find/recommend new MCPs**, read `docs/mcp-research-guide.md` first.
|
|
Quick workflow: Search `mcp.so`, `smithery.ai`, `mcpservers.org` → Verify on GitHub (50+ stars, <6 months active) → Evaluate.
|
|
|
|
### Available MCPs for NUC Management
|
|
|
|
| MCP | Purpose |
|
|
|-----|---------|
|
|
| `mcp__coolify__*` | Service management, deployments, env vars |
|
|
| `mcp__stalwart-mail__*` | Email server management |
|
|
| `mcp__email-client__*` | Read/send emails via IMAP/SMTP |
|
|
| `mcp__nocodb__*` | Database operations, table management |
|
|
| `mcp__ssh-manager__*` | Direct SSH commands, file transfers |
|
|
| `mcp__n8n__*` | Workflow automation |
|
|
| `mcp__playwriter__*` | Browser automation |
|
|
| `mcp__deepgram__*` | Audio transcription (STT) and TTS |
|
|
|
|
> Full MCP config details (Stalwart, Email, Remote MCPs, Browser): `docs/mcp-configs.md`
|
|
|
|
### 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();
|
|
\""
|
|
```
|
|
|
|
### Coolify MCP Quick Reference
|
|
|
|
```python
|
|
mcp__coolify__get_infrastructure_overview()
|
|
mcp__coolify__control(resource="service", action="start|stop|restart", uuid="<uuid>")
|
|
mcp__coolify__get_service(uuid="<uuid>")
|
|
mcp__coolify__service(action="update", uuid="<uuid>", docker_compose_raw="<yaml>")
|
|
mcp__coolify__database(action="delete", uuid="<uuid>", delete_volumes=True)
|
|
```
|
|
|
|
### Docker Commands
|
|
|
|
```bash
|
|
ssh nuc "docker ps -a --format '{{.Names}}\t{{.Status}}'"
|
|
ssh nuc "docker logs <container_name> 2>&1 | tail -50"
|
|
ssh nuc "docker restart <container_name>"
|
|
ssh nuc "docker exec <container_name> <command>"
|
|
```
|
|
|
|
## Services & Ports
|
|
|
|
| Service | Domain | Port-based URL | Container |
|
|
|---------|--------|----------------|-----------|
|
|
| NUC Portal | `http://nuc.lan` | - | nuc-portal-* |
|
|
| Coolify | `http://coolify.nuc.lan` | `http://100.113.153.45:8000` | coolify |
|
|
| Gitea | `http://gitea.nuc.lan` | `http://100.113.153.45:3030` | gitea-* |
|
|
| Outline | `http://outline.nuc.lan` | `http://100.113.153.45:3080` | outline-* |
|
|
| FileBrowser | `http://files.nuc.lan` | `http://100.113.153.45:8085` | filebrowser-* |
|
|
| Snappymail | `http://mail.nuc.lan` | `http://100.113.153.45:8082` | snappymail-* |
|
|
| Vaultwarden | `http://vault.nuc.lan` | `http://100.113.153.45:8222` | vaultwarden-* |
|
|
| Homepage | `http://homepage.nuc.lan` | `http://100.113.153.45:3000` | nuc-portal-* |
|
|
| NocoDB | - | `http://100.113.153.45:8084` | nocodb-* |
|
|
| n8n | - | `http://100.113.153.45:5678` | n8n-* |
|
|
| Ntfy | - | `http://100.113.153.45:8333` | ntfy-* |
|
|
| MinIO Console | - | `http://100.113.153.45:9001` | minio-* |
|
|
| MinIO API | - | `http://100.113.153.45:9000` | minio-* |
|
|
| Authentik | - | `http://100.113.153.45:9090` | authentik-* |
|
|
| Adminer | - | `http://100.113.153.45:8088` | adminer |
|
|
| Uptime Kuma | - | `http://100.113.153.45:3001` | uptime-kuma |
|
|
| Kopia | - | `http://100.113.153.45:51515` | kopia |
|
|
| Dozzle | - | `http://100.113.153.45:9999` | dozzle |
|
|
| CloudBeaver | - | `http://100.113.153.45:8978` | cloudbeaver-* |
|
|
| WhyOps | `http://whyops.nuc.lan` | `http://100.113.153.45:3002` | whyrating-dashboard |
|
|
| Palmr | `https://alezmad-nuc.tail58f5ad.ts.net:8443` | `http://100.113.153.45:3334` | palmr |
|
|
| Arrio | `http://arrio.nuc.lan` | `http://100.113.153.45:3335` | web-tgksg0s8gocko4csggs0808c |
|
|
| WhatsApp MCP | `http://whatsapp.nuc.lan` | `http://100.113.153.45:3100` | whatsapp-mcp |
|
|
| Deepgram MCP | - | `http://100.113.153.45:8009` | deepgram-mcp |
|
|
|
|
**Note:** Use Tailscale IP (`100.113.153.45`) instead of `192.168.1.3` to avoid subnet conflicts when remote.
|
|
|
|
## Port Forwarding
|
|
|
|
```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:** `/opt/homepage/config/` (services: `services.yaml`)
|
|
- **Coolify Data:** `/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**: Encrypted with Laravel. Use `encrypt()` when updating.
|
|
3. **HSTS Issues**: Use nginx proxy with `proxy_hide_header Strict-Transport-Security;` to strip.
|
|
4. **Network Discovery**: `docker inspect <container> --format '{{.NetworkSettings.Networks}}'`
|
|
|
|
## Troubleshooting
|
|
|
|
### Coolify MCP vs Direct Docker
|
|
|
|
**Always verify Coolify status with Docker** - Coolify's status can lag:
|
|
```bash
|
|
ssh nuc "docker ps -a --format 'table {{.Names}}\t{{.Status}}' | grep <service>"
|
|
```
|
|
|
|
### Common Issues
|
|
|
|
1. **Containers stuck in "Created"**: `ssh nuc "docker start <container_name>"`
|
|
2. **Service shows "running:unknown"**: No healthcheck. Add via Coolify:
|
|
```yaml
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-q", "--spider", "http://localhost:<port>"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 30s
|
|
```
|
|
3. **Service dependencies not starting**: Check dependency containers are healthy first.
|
|
4. **Stale Coolify entries**: Verify container exists with `docker ps -a | grep <name>`, then delete if missing.
|
|
5. **Embedded vs Standalone databases**: Services like Outline have their own PostgreSQL containers bundled in compose.
|
|
6. **Wrong healthcheck endpoint**: Some use `/healthz`. Verify: `docker exec <container> wget -qO- http://127.0.0.1:<port>/healthz`
|
|
7. **localhost resolves to IPv6 on NUC**: Always use `127.0.0.1` instead of `localhost` in Funnel/Serve targets.
|
|
8. **Funnel port not working externally**: Only ports **443, 8443, 10000** work for Funnel.
|
|
9. **API keys without UI**: Insert directly into SQLite: `docker run --rm -v <volume>:/data keinos/sqlite3 sqlite3 /data/database.sqlite "<QUERY>"`
|
|
10. **Can't reach NUC on LAN**: Mac and NUC on different networks both using `192.168.1.0/24`. Use Tailscale IP (`100.113.153.45`), never `192.168.1.3` from Mac.
|
|
|
|
## OpenWrt Router
|
|
|
|
Router at `192.168.1.1` — OpenWrt 23.05.0, ARM Cortex-A9.
|
|
|
|
**Priority Order:** SSH > OpenWrt MCP > Chrome DevTools > Manual UI
|
|
|
|
### SSH Access
|
|
```bash
|
|
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1
|
|
# Or via NUC jump host:
|
|
ssh nuc "ssh root@192.168.1.1 '<command>'"
|
|
```
|
|
|
|
### OpenWrt MCP Server
|
|
- HTTP API: `http://192.168.1.1:8090` | Token: `openwrt-mcp-secret-2026`
|
|
- Control: `ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "/etc/init.d/mcp-server start|stop|restart"`
|
|
|
|
### Port Forwarding
|
|
```bash
|
|
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "uci show firewall | grep redirect"
|
|
|
|
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
|
|
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
|
|
# 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
|
|
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "ifstatus wan" # WAN status
|
|
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "cat /tmp/dhcp.leases" # Connected clients
|
|
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "ip route" # Routing table
|
|
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "logread | tail -50" # System logs
|
|
ssh -i ~/.ssh/id_ed25519_nuc root@192.168.1.1 "opkg update && opkg install <package>" # Packages
|
|
```
|
|
|
|
## Credentials & Authentication
|
|
|
|
### CloudBeaver
|
|
| **URL** | `http://192.168.1.3:8978` | **Admin** | `cbadmin` / `CloudBeaver2026!` | **UUID** | `joo4g4k0w08k8kcosgsgswc0` |
|
|
|
|
### Vaultwarden (REQUIRES HTTPS)
|
|
| **HTTPS URL** | `https://nuc-tailscale.tail58f5ad.ts.net:8443` | **Admin** | `admin@nuc.lan` / `NucVault2026!Secure` |
|
|
|
|
### Stalwart Mail Server
|
|
| **Admin URL** | `http://192.168.1.3:8081` | **Creds** | `admin` / `QfKYjCJdxu` | **UUID** | `kw00kok0w0s8gcok008gk04k` |
|
|
|
|
**Mail Users:** `info@whyrating.com` (username: `info`, password: `whyrating2026`)
|
|
**DNS:** SPF, DKIM (Ed25519 + RSA), DMARC, MX configured for `whyrating.com`
|
|
|
|
### Outline OIDC (via Gitea)
|
|
- Client ID: `249a3a1d-92d4-47d8-b4a9-81c64e1da6ab`
|
|
- Auth/Token/Userinfo: `http://192.168.1.3:3030/login/oauth/authorize|access_token|userinfo`
|
|
|
|
### Gitea Users
|
|
| `nedas` | `NedasNUC2026!` | Regular user |
|
|
|
|
## Gitea-Coolify Integration (Git Auto-Deploy)
|
|
|
|
> Full docs: `docs/gitea-coolify-auto-deploy.md`
|
|
|
|
### Key References
|
|
|
|
| Resource | Value |
|
|
|----------|-------|
|
|
| **Deploy Key UUID** | `akssgwowsccgwgoggs4ks8ck` |
|
|
| **Gitea Container** | `gitea-ho0cwgcwos88cwc48g84c0g8` |
|
|
| **Webhook Secret** | `9eb07a77964563378c5d66d99006e06ba3da39d232905d4b12554ff91ca39718` |
|
|
|
|
### CRITICAL: Gitea Webhook Allowed Hosts
|
|
|
|
```bash
|
|
# Gitea's app.ini must have:
|
|
[webhook]
|
|
ALLOWED_HOST_LIST = coolify,10.0.1.5,192.168.1.3,localhost,host.docker.internal,external
|
|
```
|
|
|
|
### CRITICAL: Webhook Secret Must Be Set in BOTH Places
|
|
|
|
**Step 1 — Coolify:**
|
|
```bash
|
|
ssh nuc "docker exec coolify php artisan tinker --execute=\"
|
|
use App\\Models\\Application;
|
|
\\\$app = Application::where('uuid', '<app-uuid>')->first();
|
|
\\\$app->manual_webhook_secret_gitea = '9eb07a77964563378c5d66d99006e06ba3da39d232905d4b12554ff91ca39718';
|
|
\\\$app->save();
|
|
\""
|
|
```
|
|
|
|
**Step 2 — Gitea webhook:**
|
|
- URL: `http://coolify:8080/webhooks/source/gitea/events/manual?uuid=<app-uuid>` (port **8080**, NOT 8000)
|
|
- Secret: `9eb07a77964563378c5d66d99006e06ba3da39d232905d4b12554ff91ca39718`
|
|
- Trigger: Push Events
|
|
|
|
### Webhook URLs
|
|
|
|
| App | UUID |
|
|
|-----|------|
|
|
| nuc-portal | `t80w0cw0oooc4g0soswos4so` |
|
|
| whyrating-hub | `vw4ggc40socwkgwg4osc8wg8` |
|
|
| whyrating-brand | `r80gk0ccgg0okos8cw848kkk` |
|
|
| whyrating-templates | `qw80g4sog0kk8cc4wkcs8sgc` |
|
|
|
|
### Quick Deploy (Next.js)
|
|
|
|
```python
|
|
# 1. Create application with deploy key
|
|
mcp__coolify__application(
|
|
action="create_key", name="my-app",
|
|
project_uuid="a8484ggc88c40w4g4k004ow0", environment_name="production",
|
|
server_uuid="qk84w0goo4w48g4ggsoo0oss",
|
|
git_repository="git@gitea-ho0cwgcwos88cwc48g84c0g8:alezmad/<repo>.git",
|
|
git_branch="main", build_pack="nixpacks", ports_exposes="3000",
|
|
private_key_uuid="akssgwowsccgwgoggs4ks8ck")
|
|
|
|
# 2. Set FQDN via tinker
|
|
# 3. Set webhook secret via tinker
|
|
# 4. Create Gitea webhook (URL with port 8080 + secret)
|
|
# 5. Deploy: mcp__coolify__deploy(tag_or_uuid="<app-uuid>")
|
|
```
|
|
|
|
### Deploy Key (add to each new repo)
|
|
```
|
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGHtsL3jicJTsBekYuwbKjO0EcRadYKhvLSUw/36XF7h coolify-gitea
|
|
```
|
|
Add via Gitea: Repository → Settings → Deploy Keys → **Enable Write Access**
|
|
|
|
### New Repo Auto-Deploy Checklist
|
|
1. [ ] Add deploy key to Gitea repo (Write Access enabled)
|
|
2. [ ] Create Coolify application (`action="create_key"`)
|
|
3. [ ] Set FQDN via tinker
|
|
4. [ ] Set webhook secret via tinker
|
|
5. [ ] Create Gitea webhook (port 8080 + UUID + secret)
|
|
6. [ ] Test webhook (HTTP 200)
|
|
7. [ ] Initial deploy
|
|
|
|
### Current Deployed Apps
|
|
|
|
| App | URL | Repository | UUID |
|
|
|-----|-----|------------|------|
|
|
| nuc-portal | http://nuc.lan | `alezmad/nuc-portal` | `t80w0cw0oooc4g0soswos4so` |
|
|
| whyrating-hub | http://whyrating.nuc.lan | `alezmad/whyrating-hub` | `vw4ggc40socwkgwg4osc8wg8` |
|
|
| whyrating-brand | http://brand.nuc.lan | `alezmad/whyrating-brand` | `r80gk0ccgg0okos8cw848kkk` |
|
|
| whyrating-templates | http://templates.nuc.lan | `alezmad/whyrating-templates` | `qw80g4sog0kk8cc4wkcs8sgc` |
|
|
| turbostarter | https://alezmad-nuc.tail58f5ad.ts.net | `alezmad/turbostarter` | `v4gogwwc8wkk4888ksscc4k4` (service) |
|
|
| arrio | http://arrio.nuc.lan | `alezmad/arrio` | `tgksg0s8gocko4csggs0808c` (service) |
|
|
|
|
### Turbostarter (Knosia) - Build & Deploy
|
|
|
|
Deployed as **Coolify Service** (web + pgvector + minio). Architecture: Tailscale Funnel (HTTPS) → Traefik (HTTP:80) → web container.
|
|
|
|
```bash
|
|
# Build (ARM→AMD), push to Gitea registry, redeploy
|
|
cd /Users/agutierrez/Desktop/turbostarter-export
|
|
docker build --platform linux/amd64 --build-arg NEXT_PUBLIC_URL=https://alezmad-nuc.tail58f5ad.ts.net -t 192.168.1.3:3030/alezmad/turbostarter:latest .
|
|
docker push 192.168.1.3:3030/alezmad/turbostarter:latest
|
|
mcp__coolify__control(resource="service", action="stop", uuid="v4gogwwc8wkk4888ksscc4k4")
|
|
mcp__coolify__control(resource="service", action="start", uuid="v4gogwwc8wkk4888ksscc4k4")
|
|
```
|
|
|
|
**DB access:** `ssh -L 5440:<container_ip>:5432 nuc` → `postgres://turbostarter:turbostarter@localhost:5440/core`
|
|
**Seeded users:** `me+admin@turbostarter.dev` / `Pa$$w0rd`, `me+user@turbostarter.dev` / `Pa$$w0rd`
|
|
|
|
### New Site from nuc-portal Template
|
|
|
|
```bash
|
|
cp -r /path/to/nuc-portal /path/to/new-site && cd /path/to/new-site
|
|
rm -rf .git .next node_modules
|
|
# Update package.json, customize src/app/page.tsx
|
|
npm install && npm run build
|
|
git init && git add -A && git commit -m "Initial commit"
|
|
git remote add origin gitea:alezmad/<repo>.git && git push -u origin main
|
|
```
|
|
Then follow the Auto-Deploy Checklist above.
|
|
|
|
### Local Git SSH Config
|
|
```
|
|
Host gitea
|
|
HostName 192.168.1.3
|
|
Port 22222
|
|
User git
|
|
IdentityFile ~/.ssh/id_ed25519_nuc
|
|
```
|
|
|
|
## Public Access & Security
|
|
|
|
> Full architecture: `docs/architecture.md`
|
|
|
|
### Tailscale Funnel
|
|
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| **Funnel URL** | `https://nuc-tailscale.tail58f5ad.ts.net` |
|
|
| **Supported ports** | **443, 8443, 10000 ONLY** |
|
|
|
|
**CRITICAL:** Always use `127.0.0.1`, NOT `localhost` in Funnel/Serve targets (IPv6 issue).
|
|
|
|
```bash
|
|
# Expose a service via Funnel
|
|
ssh nuc "tailscale funnel --bg --https=8443 http://127.0.0.1:3000"
|
|
```
|
|
|
|
**Current Funnel allocation:**
|
|
| Port | Service | Target |
|
|
|------|---------|--------|
|
|
| 443 | Traefik (main) | `http://192.168.1.3:80` |
|
|
| 8443 | Palmr | `http://127.0.0.1:3334` |
|
|
| 10000 | Palmr MinIO | `http://127.0.0.1:9379` |
|
|
|
|
### Domain Routes
|
|
| Domain | Destination | Method |
|
|
|--------|-------------|--------|
|
|
| whyrating.com | `nuc-tailscale.tail58f5ad.ts.net` | Namecheap 301 redirect |
|
|
|
|
### Security Layers
|
|
```
|
|
Internet → Tailscale Funnel (HTTPS) → CrowdSec → Traefik → Container
|
|
```
|
|
|
|
**NOT exposed to internet:** SSH, Coolify, databases, MinIO, Authentik, router admin.
|
|
|
|
## Artifacts Folder
|
|
|
|
**Location:** `.artifacts/` — stores credentials, configs, reports generated during sessions.
|
|
|
|
**Naming:** `YYYY-MM-DD_HH-MM_<description>.md`
|
|
|
|
**Always save artifacts for:** API tokens, config changes, troubleshooting results, infrastructure changes, health reports.
|
|
|
|
> Full conventions: see artifact template in this folder.
|
|
|
|
## Detailed Documentation (split docs)
|
|
|
|
| Topic | File |
|
|
|-------|------|
|
|
| OpenClaw gateway | `docs/openclaw.md` |
|
|
| Palmr file sharing | `docs/palmr.md` |
|
|
| MinIO storage | `docs/minio.md` |
|
|
| Publishing JSX artifacts | `docs/publishing-artifacts.md` |
|
|
| MCP server configs | `docs/mcp-configs.md` |
|
|
| Gitea auto-deploy deep-dive | `docs/gitea-coolify-auto-deploy.md` |
|
|
| Security architecture | `docs/architecture.md` |
|
|
| MCP research guide | `docs/mcp-research-guide.md` |
|
|
| Turbostarter deployment | `docs/turbostarter-deployment.md` |
|