From 59944e9144f7922901051b35b0092777b193f245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?= <35082514+alezmad@users.noreply.github.com> Date: Wed, 18 Feb 2026 15:17:04 +0100 Subject: [PATCH] Add infrastructure setup artifacts (Feb 1-3) Session notes covering Gitea-Coolify webhook fixes, NocoDB/Vaultwarden credentials, Stalwart mail server setup, Snappymail config, WhyRating databases and email, CloudBeaver deployment, and Turbostarter setup. Co-Authored-By: Claude Opus 4.6 --- ...6-02-01_14-30_gitea-coolify-webhook-fix.md | 78 ++++++++ .../2026-02-01_20-49_nocodb-credentials.md | 93 ++++++++++ ...6-02-01_21-06_gitea-coolify-integration.md | 171 ++++++++++++++++++ .../2026-02-01_21-08_outline-ai-workflow.md | 155 ++++++++++++++++ ...026-02-01_21-25_vaultwarden-credentials.md | 89 +++++++++ ...2026-02-01_21-35_tailscale-funnel-https.md | 143 +++++++++++++++ ...-02-02_00-15_stalwart-admin-credentials.md | 23 +++ ...2026-02-02_00-20_snappymail-credentials.md | 17 ++ .../2026-02-02_12-30_whyrating-databases.md | 48 +++++ .../2026-02-02_15-20_whyrating-email-setup.md | 121 +++++++++++++ .../2026-02-02_15-30_stalwart-mcp-server.md | 98 ++++++++++ ...02-02_15-35_stalwart-mcp-created-tested.md | 169 +++++++++++++++++ .../2026-02-03_12-30_cloudbeaver-setup.md | 73 ++++++++ ...026-02-03_22-00_turbostarter-deployment.md | 69 +++++++ 14 files changed, 1347 insertions(+) create mode 100644 .artifacts/2026-02-01_14-30_gitea-coolify-webhook-fix.md create mode 100644 .artifacts/2026-02-01_20-49_nocodb-credentials.md create mode 100644 .artifacts/2026-02-01_21-06_gitea-coolify-integration.md create mode 100644 .artifacts/2026-02-01_21-08_outline-ai-workflow.md create mode 100644 .artifacts/2026-02-01_21-25_vaultwarden-credentials.md create mode 100644 .artifacts/2026-02-01_21-35_tailscale-funnel-https.md create mode 100644 .artifacts/2026-02-02_00-15_stalwart-admin-credentials.md create mode 100644 .artifacts/2026-02-02_00-20_snappymail-credentials.md create mode 100644 .artifacts/2026-02-02_12-30_whyrating-databases.md create mode 100644 .artifacts/2026-02-02_15-20_whyrating-email-setup.md create mode 100644 .artifacts/2026-02-02_15-30_stalwart-mcp-server.md create mode 100644 .artifacts/2026-02-02_15-35_stalwart-mcp-created-tested.md create mode 100644 .artifacts/2026-02-03_12-30_cloudbeaver-setup.md create mode 100644 .artifacts/2026-02-03_22-00_turbostarter-deployment.md diff --git a/.artifacts/2026-02-01_14-30_gitea-coolify-webhook-fix.md b/.artifacts/2026-02-01_14-30_gitea-coolify-webhook-fix.md new file mode 100644 index 0000000..84ecf7a --- /dev/null +++ b/.artifacts/2026-02-01_14-30_gitea-coolify-webhook-fix.md @@ -0,0 +1,78 @@ +# Gitea-Coolify Webhook Fix + +**Date:** 2026-02-01 14:30 +**Context:** Fixing auto-deploy webhooks from Gitea to Coolify + +## Problem + +Gitea webhooks to Coolify were failing with two different errors: + +### Error 1: ALLOWED_HOST_LIST +``` +dial tcp 10.0.1.5:8000: webhook can only call allowed HTTP servers +(check your webhook.ALLOWED_HOST_LIST setting), deny 'coolify(10.0.1.5:8000)' +``` + +### Error 2: Connection Refused +``` +dial tcp 10.0.1.5:8000: connection refused +``` + +## Root Causes + +### 1. Gitea Blocks Internal Webhooks by Default +Gitea has a security feature that prevents webhooks to internal/private IP addresses unless explicitly allowed. + +**Fix:** Add `[webhook]` section to Gitea's `app.ini`: +```ini +[webhook] +ALLOWED_HOST_LIST = coolify,10.0.1.5,192.168.1.3,localhost,host.docker.internal,external +``` + +### 2. Wrong Port (Critical Discovery!) + +| Port | Usage | +|------|-------| +| **8000** | External Docker port mapping (for browser access from `192.168.1.3:8000`) | +| **8080** | Internal container port (what nginx actually listens on inside the container) | + +When Gitea (running in Docker) calls Coolify (also in Docker), it uses the Docker network. From within the network, Coolify's nginx listens on **port 8080**, not 8000. + +**Wrong:** `http://coolify:8000/webhooks/...` → Connection refused +**Correct:** `http://coolify:8080/webhooks/...` → HTTP 200 OK + +## Solution Applied + +1. Added `[webhook]` section to Gitea's app.ini: + ```bash + ssh nuc "docker exec gitea-ho0cwgcwos88cwc48g84c0g8 sh -c 'echo \"\" >> /data/gitea/conf/app.ini && echo \"[webhook]\" >> /data/gitea/conf/app.ini && echo \"ALLOWED_HOST_LIST = coolify,10.0.1.5,192.168.1.3,localhost,host.docker.internal,external\" >> /data/gitea/conf/app.ini'" + ssh nuc "docker restart gitea-ho0cwgcwos88cwc48g84c0g8" + ``` + +2. Updated webhook URL from port 8000 to 8080: + ``` + http://coolify:8080/webhooks/source/gitea/events/manual?uuid=t80w0cw0oooc4g0soswos4so + ``` + +## Verification + +- Webhook test delivery returned **HTTP 200** +- Green checkmark in Gitea webhook delivery history + +## Key Learnings + +1. **Always check internal vs external ports** when Docker containers communicate +2. **Gitea has webhook security** - must explicitly allow internal hosts +3. **The `?uuid=` parameter is required** - without it, Coolify doesn't know which app to deploy +4. **Test deliveries may not trigger actual deployments** but confirm connectivity + +## Files Updated + +- `docs/gitea-coolify-auto-deploy.md` - All port references updated to 8080 +- `CLAUDE.md` - Webhook URL format and checklist added + +## Related + +- Coolify container: `coolify` (IP: 10.0.1.5 on coolify network) +- Gitea container: `gitea-ho0cwgcwos88cwc48g84c0g8` +- Both must be on the `coolify` Docker network diff --git a/.artifacts/2026-02-01_20-49_nocodb-credentials.md b/.artifacts/2026-02-01_20-49_nocodb-credentials.md new file mode 100644 index 0000000..a53dc84 --- /dev/null +++ b/.artifacts/2026-02-01_20-49_nocodb-credentials.md @@ -0,0 +1,93 @@ +# NocoDB Credentials + +**Date:** 2026-02-01 20:49 +**Updated:** 2026-02-01 21:19 +**Context:** Fresh NocoDB deployment on NUC with MCP integration + +## Admin Account + +- **Email:** admin@nuc.local +- **Password:** NocoDBNUC2026 +- **URL:** http://192.168.1.3:8084 + +## API Token (for REST API) + +- **Description:** Claude MCP Token +- **Token:** `iRjefVhHIa-59Tpk4NGaqLbV8He4tmgiLXY11256` + +## Base Info + +- **Base ID:** pnyh9wci9dh5orr +- **Workspace ID:** wlkqi8gm +- **Title:** Getting Started + +## MCP Configuration (Native NocoDB MCP) + +**MCP Endpoint:** `http://192.168.1.3:8084/mcp/ncnyir1cy6n9bf5p` +**MCP Token:** `qjjAXRxuYzRtEn-cA4lbPFi5km_pojTX` + +### Working Setup (CLI Method) + +Use the Claude Code CLI to add the MCP server globally: + +```bash +claude mcp add --transport http nocodb http://192.168.1.3:8084/mcp/ncnyir1cy6n9bf5p \ + --scope user \ + --header "xc-mcp-token: qjjAXRxuYzRtEn-cA4lbPFi5km_pojTX" +``` + +This saves to `~/.claude.json` and works across all projects. + +### Alternative: JSON Config (Does NOT work with mcp-remote) + +The following JSON config does NOT work due to mcp-remote issues: + +```json +{ + "nocodb": { + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "http://192.168.1.3:8084/mcp/ncnyir1cy6n9bf5p", + "--header", + "xc-mcp-token: qjjAXRxuYzRtEn-cA4lbPFi5km_pojTX", + "--allow-http" + ] + } +} +``` + +**Use the CLI method instead.** + +## Available MCP Tools + +| Tool | Description | +|------|-------------| +| `mcp__nocodb__getBaseInfo` | Fetch base information | +| `mcp__nocodb__getTablesList` | List all tables | +| `mcp__nocodb__getTableSchema` | Get table schema/fields | +| `mcp__nocodb__queryRecords` | Query records with filters | +| `mcp__nocodb__getRecord` | Fetch single record by ID | +| `mcp__nocodb__createRecords` | Create new records | +| `mcp__nocodb__updateRecords` | Update existing records | +| `mcp__nocodb__deleteRecords` | Delete records | +| `mcp__nocodb__countRecords` | Count records | +| `mcp__nocodb__readAttachment` | Read attachment data | +| `mcp__nocodb__aggregate_single` | Aggregate queries | + +## How to Enable MCP in NocoDB + +1. Login to NocoDB: http://192.168.1.3:8084 +2. Open a Base (e.g., "Getting Started") +3. Go to **Settings** tab +4. Click **MCP Server** in sidebar +5. Click **New MCP Endpoint** +6. Save (default name is fine) +7. Copy the MCP URL and Token from the config shown + +## Related + +- NocoDB Dashboard: http://192.168.1.3:8084 +- Homepage Dashboard: http://192.168.1.3:3000 +- Coolify Service: Check via `mcp__coolify__get_infrastructure_overview()` diff --git a/.artifacts/2026-02-01_21-06_gitea-coolify-integration.md b/.artifacts/2026-02-01_21-06_gitea-coolify-integration.md new file mode 100644 index 0000000..1dc9e5f --- /dev/null +++ b/.artifacts/2026-02-01_21-06_gitea-coolify-integration.md @@ -0,0 +1,171 @@ +# Gitea-Coolify Integration for Auto-Deploy + +**Date:** 2026-02-01 21:06 +**Context:** Setting up Git auto-deploy from self-hosted Gitea to Coolify for Next.js applications + +## Overview + +This document describes how to configure Coolify to deploy applications from a self-hosted Gitea instance running on the same NUC server. + +## Prerequisites + +- Gitea running as a Coolify service (container: `gitea-ho0cwgcwos88cwc48g84c0g8`) +- Gitea SSH exposed on port 22222 (internal port 22) +- Repository already created in Gitea + +## Key Issue: Network Isolation + +Gitea runs on its own Docker network (`ho0cwgcwos88cwc48g84c0g8`), separate from Coolify's network (`coolify`). The Coolify helper container that clones repositories runs on the `coolify` network and cannot reach Gitea's internal SSH port. + +### Solution: Connect Gitea to Coolify Network + +```bash +docker network connect coolify gitea-ho0cwgcwos88cwc48g84c0g8 +``` + +This allows the Coolify helper to reach Gitea via container name on internal port 22. + +## Step-by-Step Setup + +### 1. Generate SSH Deploy Key + +```bash +ssh-keygen -t ed25519 -C "coolify-gitea" -f /tmp/coolify-gitea-key -N "" +``` + +### 2. Add Private Key to Coolify + +Via MCP: +```python +mcp__coolify__private_keys( + action="create", + name="Gitea Deploy Key", + private_key="" +) +``` + +Note the returned UUID (e.g., `akssgwowsccgwgoggs4ks8ck`). + +### 3. Add Public Key to Gitea Repository + +1. Navigate to Gitea repository → Settings → Deploy Keys +2. Add new key with contents of `/tmp/coolify-gitea-key.pub` +3. Title: "Coolify Deploy Key" + +### 4. Connect Gitea to Coolify Network (Critical!) + +```bash +ssh nuc "docker network connect coolify gitea-ho0cwgcwos88cwc48g84c0g8" +``` + +### 5. Create Application in Coolify + +Via MCP: +```python +mcp__coolify__application( + action="create_key", + name="my-app-name", + project_uuid="a8484ggc88c40w4g4k004ow0", + environment_name="production", + server_uuid="qk84w0goo4w48g4ggsoo0oss", + git_repository="git@gitea-ho0cwgcwos88cwc48g84c0g8:nuc/repo-name.git", + git_branch="main", + build_pack="nixpacks", + ports_exposes="3000", + private_key_uuid="akssgwowsccgwgoggs4ks8ck" +) +``` + +**Important:** Use the container name `gitea-ho0cwgcwos88cwc48g84c0g8` in the repository URL, NOT the IP address with port. + +### 6. Configure Base Directory (if monorepo) + +If your app is in a subdirectory, update via Laravel tinker: +```bash +docker exec coolify php artisan tinker --execute=" +use App\Models\Application; +\$app = Application::where('uuid', '')->first(); +\$app->base_directory = '/path/to/app'; +\$app->save(); +" +``` + +For root directory, use `/`. + +### 7. Set FQDN + +Via MCP: +```python +mcp__coolify__application( + action="update", + uuid="", + fqdn="http://myapp.nuc.lan" +) +``` + +### 8. Deploy + +```python +mcp__coolify__deploy(tag_or_uuid="") +``` + +## Repository URL Format + +| Format | Works? | Notes | +|--------|--------|-------| +| `git@gitea-ho0cwgcwos88cwc48g84c0g8:user/repo.git` | ✅ Yes | Use container name (after network connect) | +| `git@192.168.1.3:user/repo.git` | ❌ No | Port 22 goes to NUC SSH, not Gitea | +| `ssh://git@192.168.1.3:22222/user/repo.git` | ❌ No | Coolify mangles ssh:// URLs | + +## Troubleshooting + +### "Permission denied (publickey)" +- Verify deploy key is added to both Coolify and Gitea +- Check that Gitea is connected to coolify network: `docker network inspect coolify | grep gitea` + +### "Could not resolve hostname" +- Gitea not connected to coolify network +- Run: `docker network connect coolify gitea-ho0cwgcwos88cwc48g84c0g8` + +### "Nixpacks failed to detect application type" +- Wrong base_directory setting +- Check repo structure matches base_directory path + +### Build TypeScript errors +- Fix code locally, push to Gitea, redeploy + +## Reference: Current Configuration + +### Gitea Service UUID +`ho0cwgcwos88cwc48g84c0g8` + +### Gitea Container Name +`gitea-ho0cwgcwos88cwc48g84c0g8` + +### Gitea Ports +- HTTP: 3030 (external) → 3000 (internal) +- SSH: 22222 (external) → 22 (internal) + +### Coolify Private Key UUID (for Gitea) +`akssgwowsccgwgoggs4ks8ck` + +### Example Working Application +- **Name:** whyrating-brand +- **UUID:** r80gk0ccgg0okos8cw848kkk +- **Repository:** `git@gitea-ho0cwgcwos88cwc48g84c0g8:nuc/whyrating-brand.git` +- **FQDN:** http://brand.nuc.lan +- **Build Pack:** nixpacks +- **Port:** 3000 + +## Webhooks (Optional - For Auto-Deploy on Push) + +To enable automatic deployments when pushing to Gitea: + +1. Get the webhook URL from Coolify application settings +2. In Gitea: Repository → Settings → Webhooks → Add Webhook +3. Use the Coolify webhook URL with the secret + +## Related Files + +- SSH Private Key: Stored in Coolify (encrypted) +- SSH Public Key: Added to Gitea deploy keys diff --git a/.artifacts/2026-02-01_21-08_outline-ai-workflow.md b/.artifacts/2026-02-01_21-08_outline-ai-workflow.md new file mode 100644 index 0000000..5b65673 --- /dev/null +++ b/.artifacts/2026-02-01_21-08_outline-ai-workflow.md @@ -0,0 +1,155 @@ +# Outline AI Workflow + +**Date:** 2026-02-01 21:08 +**Context:** Setup MCP server for AI interaction with Outline wiki + +--- + +## Quick Reference + +| Item | Value | +|------|-------| +| **Outline URL** | http://192.168.1.3:3080 | +| **API URL** | http://192.168.1.3:3080/api | +| **MCP Server** | `mcp-outline` (via uvx) | +| **NUC Docs Collection** | `2a42945b-1a4f-4c92-add5-dfa147ef3f56` | +| **API Key** | `ol_api_nIjWn2lJn4Ho42kKcivcqTHjyy8Vg9ycxmchHa` | +| **Key Name** | Claude MCP v2 (full access) | +| **Key Expires** | Mar 03, 2026 | + +--- + +## MCP Tools Available + +After restarting Claude Code (`/mcp` to verify): + +``` +mcp__outline__search_documents # Search by keywords +mcp__outline__list_collections # List all collections +mcp__outline__read_document # Get document content +mcp__outline__create_document # Create new document +mcp__outline__update_document # Update document +mcp__outline__move_document # Move to different collection +mcp__outline__archive_document # Archive document +mcp__outline__delete_document # Delete document +mcp__outline__export_document # Export as markdown +mcp__outline__batch_create_documents # Create multiple docs +mcp__outline__ask_ai_about_documents # AI queries on docs +``` + +--- + +## Common Workflows + +### 1. Save Important Finding to Wiki + +```python +mcp__outline__create_document( + title="", + text="", + collectionId="2a42945b-1a4f-4c92-add5-dfa147ef3f56", + publish=True +) +``` + +### 2. Search Before Solving + +```python +# Check if solution already documented +results = mcp__outline__search_documents(query="tailscale funnel") +``` + +### 3. Update Existing Doc + +```python +mcp__outline__update_document( + id="", + text="", + append=False # True to append instead of replace +) +``` + +### 4. Find Doc by Title + +```python +doc_id = mcp__outline__get_document_id_from_title(title="Architecture") +``` + +--- + +## REST API Fallback + +When MCP unavailable: + +```bash +# Create document +curl -X POST 'http://192.168.1.3:3080/api/documents.create' \ + -H 'Authorization: Bearer ol_api_cs243A8BKmEW6yAdrMp05PxTILMBjPofIQcVcM' \ + -H 'Content-Type: application/json' \ + -d '{ + "title": "Title", + "text": "Content", + "collectionId": "2a42945b-1a4f-4c92-add5-dfa147ef3f56", + "publish": true + }' + +# Search +curl -X POST 'http://192.168.1.3:3080/api/documents.search' \ + -H 'Authorization: Bearer ol_api_cs243A8BKmEW6yAdrMp05PxTILMBjPofIQcVcM' \ + -H 'Content-Type: application/json' \ + -d '{"query": "search term"}' +``` + +--- + +## Config Location + +`~/.claude/settings.json`: + +```json +{ + "mcpServers": { + "outline": { + "command": "uvx", + "args": ["mcp-outline"], + "env": { + "OUTLINE_API_KEY": "ol_api_cs243A8BKmEW6yAdrMp05PxTILMBjPofIQcVcM", + "OUTLINE_API_URL": "http://192.168.1.3:3080/api" + } + } + } +} +``` + +--- + +## When to Use + +**Save to Outline (persistent docs):** +- Infrastructure guides +- Configuration docs +- Troubleshooting procedures +- Architecture decisions + +**Save to .artifacts/ (session notes):** +- API keys/tokens generated +- Temporary findings +- Session-specific configs + +--- + +## Generate New API Key + +1. Go to http://192.168.1.3:3080/settings/api-and-apps +2. Click "New API key..." +3. Set scopes: `documents.create documents.update documents.read collections.read` +4. Copy immediately (shown once) +5. Update `~/.claude/settings.json` + +--- + +## Related + +- MCP Server: https://github.com/Vortiago/mcp-outline +- Outline API: https://www.getoutline.com/developers +- Full guide: `docs/outline-ai-workflow.md` diff --git a/.artifacts/2026-02-01_21-25_vaultwarden-credentials.md b/.artifacts/2026-02-01_21-25_vaultwarden-credentials.md new file mode 100644 index 0000000..3a14060 --- /dev/null +++ b/.artifacts/2026-02-01_21-25_vaultwarden-credentials.md @@ -0,0 +1,89 @@ +# Vaultwarden Credentials + +**Date:** 2026-02-01 21:25 +**Context:** New Vaultwarden account created for NUC password management + +## Access URLs + +- **Local:** http://192.168.1.3:8222 +- **HTTPS (via Tailscale Funnel):** https://nuc-tailscale.tail58f5ad.ts.net:8443 + +## Master Account + +- **Email:** admin@nuc.local +- **Name:** NUC Admin +- **Master Password:** VaultNUC2026!Secure +- **Password Hint:** NUC vault 2026 + +## Tailscale Funnel Setup + +The Funnel was configured to expose Vaultwarden with HTTPS (required for Web Crypto API): + +```bash +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel --bg --https=8443 http://192.168.1.3:8222" +``` + +**Note:** Tailscale Funnel only supports ports 443, 8443, and 10000. + +## Stored Credentials + +The following credentials have been added to the vault: + +1. **NocoDB** - http://192.168.1.3:8084 + - admin@nuc.local / NocoDBNUC2026 + - Includes API tokens and MCP configuration in notes + +2. **Gitea** - http://192.168.1.3:3030 + - nuc / GiteaNUC2026! + - SSH Clone URL: git@gitea-ho0cwgcwos88cwc48g84c0g8:nuc/.git + +3. **Coolify** - http://192.168.1.3:8000 + - agutmou@icloud.com + - API Token stored as password + +4. **GitHub PAT** - https://github.com + - alezmad + - Personal Access Token (read-only) stored as password + +5. **OpenWrt Router** - http://192.168.1.1 + - root + - MCP API Token stored as password (SSH uses key auth) + +## Bitwarden MCP Server + +**Architecture:** Docker container on NUC + SSH tunnel to local Mac + +### NUC Container (giuliolibrando/bitwarden-mcp-server) +```bash +# Location: ~/bitwarden-mcp/ +# Port: 8007 +docker compose up -d +``` + +### SSH Tunnel (LaunchAgent - auto-starts) +```bash +# LaunchAgent: ~/Library/LaunchAgents/com.nuc.bitwarden-mcp-tunnel.plist +# Forwards localhost:8007 → nuc:8007 + +# Manual control: +launchctl load ~/Library/LaunchAgents/com.nuc.bitwarden-mcp-tunnel.plist +launchctl unload ~/Library/LaunchAgents/com.nuc.bitwarden-mcp-tunnel.plist +``` + +### Claude Code MCP Config +```bash +claude mcp add bitwarden --transport http http://localhost:8007/mcp --scope user +``` + +### Bitwarden CLI (alternative access) +```bash +bw config server https://nuc-tailscale.tail58f5ad.ts.net:8443 +bw login admin@nuc.local +bw unlock --raw # Get session token +``` + +## Related + +- Vaultwarden Service: Managed via Coolify +- Coolify Dashboard: http://192.168.1.3:8000 +- Homepage Dashboard: http://192.168.1.3:3000 diff --git a/.artifacts/2026-02-01_21-35_tailscale-funnel-https.md b/.artifacts/2026-02-01_21-35_tailscale-funnel-https.md new file mode 100644 index 0000000..807089d --- /dev/null +++ b/.artifacts/2026-02-01_21-35_tailscale-funnel-https.md @@ -0,0 +1,143 @@ +# Tailscale Funnel - HTTPS for NUC Services + +**Date:** 2026-02-01 21:35 +**Context:** Using Tailscale Funnel to expose NUC services with automatic HTTPS + +## Why Tailscale Funnel? + +| Method | Pros | Cons | +|--------|------|------| +| **Tailscale Funnel** | No ports on router, auto HTTPS, handles dynamic IP | Limited to 3 ports | +| Cloudflare Tunnel | Many features, DDoS protection | Spanish ISPs block shared IPs during LaLiga | +| Port forwarding | Full control | Exposes router, needs DDNS, manual certs | + +**Key advantage:** Tailscale Funnel works even when Cloudflare IPs are blocked by ISPs. + +## Tailscale Container + +```bash +# Container name (managed by Coolify) +tailscale-posgwooww0s0c0okssooc4gw + +# Execute commands in container +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale " +``` + +## Funnel Basics + +### Supported Ports (ONLY these work) +- **443** - Default HTTPS +- **8443** - Alternate HTTPS +- **10000** - Third option + +Any other port will fail with an error. + +### Public URL +``` +https://nuc-tailscale.tail58f5ad.ts.net[:port] +``` + +## Commands + +### Check Current Status +```bash +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel status" +``` + +### Expose a Service (Background) +```bash +# Port 443 (default) - expose Homepage +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel --bg http://192.168.1.3:3000" + +# Port 8443 - expose Vaultwarden +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel --bg --https=8443 http://192.168.1.3:8222" + +# Port 10000 - expose another service +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel --bg --https=10000 http://192.168.1.3:8080" +``` + +### Stop a Funnel +```bash +# Stop port 443 +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel --https=443 off" + +# Stop port 8443 +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel --https=8443 off" +``` + +### Reset All Funnels +```bash +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel reset" +``` + +## Current Configuration + +``` +https://nuc-tailscale.tail58f5ad.ts.net (port 443) +└── / → http://127.0.0.1:3000 (Homepage) + +https://nuc-tailscale.tail58f5ad.ts.net:8443 +└── / → http://192.168.1.3:8222 (Vaultwarden) +``` + +## Important Notes + +### Use Host IP, Not localhost +When proxying to services outside the Tailscale container: +```bash +# WRONG - localhost refers to inside the container +http://localhost:8222 + +# CORRECT - use NUC's actual IP +http://192.168.1.3:8222 + +# ALSO WORKS - if on same Docker network +http://host.docker.internal:8222 # May not resolve in all containers +``` + +### Persistence +Funnels configured with `--bg` persist until: +- Manually stopped +- Container restart +- Tailscale logout + +For true persistence across container restarts, add to Coolify's container startup or use a cron job. + +### Services Requiring HTTPS +Some services need HTTPS to function (Web Crypto API): +- **Vaultwarden/Bitwarden** - Password encryption +- **WebAuthn/Passkeys** - Authentication +- **Service Workers** - PWA features +- **Geolocation API** - Location access + +## Quick Reference + +| Service | Local URL | Funnel Command | Public URL | +|---------|-----------|----------------|------------| +| Homepage | http://192.168.1.3:3000 | `funnel --bg http://192.168.1.3:3000` | https://nuc-tailscale.tail58f5ad.ts.net | +| Vaultwarden | http://192.168.1.3:8222 | `funnel --bg --https=8443 http://192.168.1.3:8222` | https://nuc-tailscale.tail58f5ad.ts.net:8443 | + +## Troubleshooting + +### "invalid port" +Only ports 443, 8443, 10000 are allowed for Funnel. + +### "connection refused" +- Service not running on target port +- Wrong IP (use 192.168.1.3, not localhost) +- Firewall blocking connection + +### Funnel not accessible +```bash +# Check if Funnel is enabled on Tailscale admin +# https://login.tailscale.com/admin/machines + +# Verify funnel status +ssh nuc "docker exec tailscale-posgwooww0s0c0okssooc4gw tailscale funnel status" +``` + +## Related + +- Tailscale Admin: https://login.tailscale.com/admin/machines +- CLAUDE.md: Public Access & Security Architecture section +- Vaultwarden credentials: `.artifacts/2026-02-01_21-25_vaultwarden-credentials.md` diff --git a/.artifacts/2026-02-02_00-15_stalwart-admin-credentials.md b/.artifacts/2026-02-02_00-15_stalwart-admin-credentials.md new file mode 100644 index 0000000..97ae810 --- /dev/null +++ b/.artifacts/2026-02-02_00-15_stalwart-admin-credentials.md @@ -0,0 +1,23 @@ +# Stalwart Mail Admin Credentials + +**Date:** 2026-02-02 00:15 +**Context:** Stalwart Mail server initial setup credentials + +## Admin Credentials + +| Field | Value | +|-------|-------| +| **Username** | `admin` | +| **Password** | `QfKYjCJdxu` | +| **Admin URL** | `http://192.168.1.3:8081` | +| **Service UUID** | `kw00kok0w0s8gcok008gk04k` | + +## Status + +- Container: `stalwart-kw00kok0w0s8gcok008gk04k` +- Status: Running but unhealthy (needs healthcheck review) + +## Related + +- Snappymail Webmail: `http://192.168.1.3:8082` +- Coolify Service: `http://192.168.1.3:8000/service/kw00kok0w0s8gcok008gk04k` diff --git a/.artifacts/2026-02-02_00-20_snappymail-credentials.md b/.artifacts/2026-02-02_00-20_snappymail-credentials.md new file mode 100644 index 0000000..74cd4a1 --- /dev/null +++ b/.artifacts/2026-02-02_00-20_snappymail-credentials.md @@ -0,0 +1,17 @@ +# Snappymail Webmail Credentials + +**Date:** 2026-02-02 00:20 +**Context:** Webmail access for whyrating.com email + +## Login Details + +| Field | Value | +|-------|-------| +| **URL** | `http://192.168.1.3:8082` | +| **Email** | `info@whyrating.com` | +| **Password** | `BeZ5LlV2ktGeYaRjN7SP` | + +## Related + +- Stalwart Mail Admin: `http://192.168.1.3:8081` (admin / QfKYjCJdxu) +- Coolify Service: `http://192.168.1.3:8000/service/kw00kok0w0s8gcok008gk04k` diff --git a/.artifacts/2026-02-02_12-30_whyrating-databases.md b/.artifacts/2026-02-02_12-30_whyrating-databases.md new file mode 100644 index 0000000..28ea4f5 --- /dev/null +++ b/.artifacts/2026-02-02_12-30_whyrating-databases.md @@ -0,0 +1,48 @@ +# WhyRating Hub Databases + +**Date:** 2026-02-02 12:30 +**Context:** Added shared PostgreSQL and Redis databases for WhyRating hub projects (internal NUC sites) + +## Databases Created + +### PostgreSQL +| Property | Value | +|----------|-------| +| **UUID** | `i8skkc8cwsgwgsg0g8kcw44k` | +| **Name** | whyrating-hub-postgres | +| **User** | whyrating | +| **Password** | WhyRatingPG2026! | +| **Database** | whyrating | +| **Internal URL** | `postgres://whyrating:WhyRatingPG2026%21@i8skkc8cwsgwgsg0g8kcw44k:5432/whyrating` | + +### Redis +| Property | Value | +|----------|-------| +| **UUID** | `vkg44cgcss4ococgk0cs000o` | +| **Name** | whyrating-hub-redis | +| **Password** | WhyRatingRedis2026! | +| **Internal URL** | `redis://default:WhyRatingRedis2026%21@vkg44cgcss4ococgk0cs000o:6379/0` | + +## Apps Configured + +All apps have `DATABASE_URL` and `REDIS_URL` environment variables: + +| App | UUID | URL | +|-----|------|-----| +| whyrating-hub | `vw4ggc40socwkgwg4osc8wg8` | http://whyrating.nuc.lan | +| whyrating-brand | `r80gk0ccgg0okos8cw848kkk` | http://brand.nuc.lan | +| whyrating-templates | `qw80g4sog0kk8cc4wkcs8sgc` | http://templates.nuc.lan | + +## Usage in Next.js + +```typescript +// Database connection (e.g., with Prisma) +const databaseUrl = process.env.DATABASE_URL; + +// Redis connection (e.g., with ioredis) +const redisUrl = process.env.REDIS_URL; +``` + +## Related +- Coolify: http://192.168.1.3:8000 +- Project: WhyRating.com diff --git a/.artifacts/2026-02-02_15-20_whyrating-email-setup.md b/.artifacts/2026-02-02_15-20_whyrating-email-setup.md new file mode 100644 index 0000000..9bb84fc --- /dev/null +++ b/.artifacts/2026-02-02_15-20_whyrating-email-setup.md @@ -0,0 +1,121 @@ +# WhyRating Email Server Configuration + +**Date:** 2026-02-02 15:20 +**Context:** Setting up email server for whyrating.com using Stalwart Mail Server + +## Email Account + +| Property | Value | +|----------|-------| +| **Email** | info@whyrating.com | +| **Login** | info | +| **Password** | BeZ5LlV2ktGeYaRjN7SP | +| **Full Name** | WhyRating Info | + +## Stalwart Admin Access + +| Property | Value | +|----------|-------| +| **URL** | http://192.168.1.3:8081 | +| **Username** | admin | +| **Password** | QfKYjCJdxu | + +## Webmail (Snappymail) + +| Property | Value | +|----------|-------| +| **URL** | http://192.168.1.3:8085 (check actual port) | +| **Login** | info@whyrating.com | +| **Password** | BeZ5LlV2ktGeYaRjN7SP | + +## IMAP/SMTP Settings (for email clients) + +### Incoming (IMAP) +- **Server:** mail.whyrating.com (or 192.168.1.3 for internal) +- **Port:** 993 (SSL/TLS) or 143 (STARTTLS) +- **Username:** info +- **Password:** BeZ5LlV2ktGeYaRjN7SP + +### Outgoing (SMTP) +- **Server:** mail.whyrating.com (or 192.168.1.3 for internal) +- **Port:** 465 (SSL/TLS) or 587 (STARTTLS) +- **Username:** info +- **Password:** BeZ5LlV2ktGeYaRjN7SP + +--- + +## DNS Records Required at Namecheap + +### MX Record (Mail Exchange) +``` +Type: MX +Host: @ +Value: mail.whyrating.com +Priority: 10 +TTL: Automatic +``` + +### A Record (for mail subdomain) +``` +Type: A +Host: mail +Value: +TTL: Automatic +``` + +### SPF Record (Sender Policy Framework) +``` +Type: TXT +Host: @ +Value: v=spf1 mx a ~all +TTL: Automatic +``` + +### DKIM Records (Domain Keys Identified Mail) + +**Ed25519 DKIM (recommended for modern servers):** +``` +Type: TXT +Host: 202602e._domainkey +Value: v=DKIM1; k=ed25519; p=KwvfeVszluaggPkCEPaQr3gk8z2jWPjRxGrNKnxMHsM= +TTL: Automatic +``` + +**RSA DKIM (for compatibility with older servers):** +``` +Type: TXT +Host: 202602r._domainkey +Value: v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwdMGvCJjVG8ncpCrCgilPDueuSo9HgWddELLh9pANE5D21raRcTVTCHxRAaE6j+PqivE24o6sQABU1JZdybOHt6W8ZHmx5sXbZtH3Yv9vxUb5Jfqnrc2dYIM7xXYQ6ePLvxKYX/HicQ8D99mFboY+w7Xg4pIHVdNpi5N0Ly4/SpLPil5XU/rPTHLDO/H5fa/sKRaE4NoAyjlXDMA0VJsLbh1GvQXVMX4HVtgCZc7XYdhE/ALwW/R+KAKrqvQfqy79DsnVO9XpiRQN/PBqEC7cMYPpH5eL01xGGNeu7QF6p89RkRnQaUIkMT4y+kPhquaxqMMeScJiFEbzdD804MnnQIDAQAB +TTL: Automatic +``` + +### DMARC Record +``` +Type: TXT +Host: _dmarc +Value: v=DMARC1; p=quarantine; rua=mailto:info@whyrating.com +TTL: Automatic +``` + +--- + +## Important Notes + +1. **Public Access:** The mail server needs to be accessible from the internet on ports 25, 465, 587 for sending/receiving email. + +2. **Tailscale Funnel:** Currently whyrating.com uses Tailscale Funnel for web access. Email requires direct port access which Funnel doesn't support. + +3. **Alternative:** Consider using a SMTP relay service (like Amazon SES, Sendgrid, or Mailgun) for sending if direct port access isn't possible. + +4. **Current MX:** The domain currently has MX records pointing to Namecheap's email forwarding (`eforward*.registrar-servers.com`). These need to be changed to point to your mail server. + +## Stalwart Service Status + +- **Container:** stalwart-kw00kok0w0s8gcok008gk04k +- **Status:** Running (unhealthy - healthcheck endpoint doesn't exist) +- **Ports:** 25, 143, 465, 587, 993, 4190, 8081 (admin UI) + +## Related +- Stalwart Mail Admin: http://192.168.1.3:8081 +- Snappymail Webmail: Check Coolify for port +- DNS Provider: Namecheap (whyrating.com) diff --git a/.artifacts/2026-02-02_15-30_stalwart-mcp-server.md b/.artifacts/2026-02-02_15-30_stalwart-mcp-server.md new file mode 100644 index 0000000..a2fc4b0 --- /dev/null +++ b/.artifacts/2026-02-02_15-30_stalwart-mcp-server.md @@ -0,0 +1,98 @@ +# Stalwart Mail MCP Server + +**Date:** 2026-02-02 15:30 +**Context:** Created MCP server for managing Stalwart mail server via Claude Code + +## Server Location + +``` +~/mcp-servers/stalwart-mail/ +├── server.py # MCP server implementation +├── requirements.txt # Python dependencies +├── pyproject.toml # Project configuration +├── README.md # Documentation +└── .venv/ # Python virtual environment (3.12) +``` + +## Configuration + +Added to Claude Code with: +```bash +claude mcp add stalwart-mail \ + -e STALWART_URL=http://192.168.1.3:8081 \ + -e STALWART_USER=admin \ + -e STALWART_PASS=QfKYjCJdxu \ + --scope user \ + -- /Users/agutierrez/mcp-servers/stalwart-mail/.venv/bin/python \ + /Users/agutierrez/mcp-servers/stalwart-mail/server.py +``` + +## Available Tools + +### User Management +| Tool | Description | +|------|-------------| +| `list_users` | List all mail users and domains | +| `get_user` | Get detailed user information | +| `create_user` | Create new mail users | +| `update_user_password` | Change user passwords | +| `delete_user` | Remove users | +| `add_email_alias` | Add email aliases | + +### Domain Management +| Tool | Description | +|------|-------------| +| `create_domain` | Add new email domains | +| `generate_dkim` | Generate DKIM keys | + +### Queue Management +| Tool | Description | +|------|-------------| +| `list_queue` | View pending messages | +| `get_queue_status` | Check queue status | +| `delete_queued_message` | Cancel message delivery | +| `retry_queued_message` | Retry failed deliveries | + +### Monitoring +| Tool | Description | +|------|-------------| +| `get_metrics` | Server performance metrics | +| `get_dmarc_reports` | Email authentication reports | +| `get_server_logs` | Recent log entries | + +### Troubleshooting +| Tool | Description | +|------|-------------| +| `check_dns_records` | Verify domain DNS setup | +| `troubleshoot_delivery` | Diagnose delivery issues | + +### Spam Filter +| Tool | Description | +|------|-------------| +| `train_spam` | Mark messages as spam | +| `train_ham` | Mark messages as legitimate | +| `update_spam_filter` | Update filter definitions | + +## Usage Examples + +After restarting Claude Code: + +``` +"List all mail users" +"Create user john with email john@whyrating.com password Secret123" +"Check the mail queue" +"Verify DNS records for whyrating.com" +"Show server metrics" +``` + +## API Reference + +Based on Stalwart's REST Management API: +- Overview: https://stalw.art/docs/api/management/overview/ +- Endpoints: https://stalw.art/docs/api/management/endpoints/ + +## Related + +- Stalwart Admin: http://192.168.1.3:8081 +- Snappymail: http://192.168.1.3:8082 +- Previous setup: .artifacts/2026-02-02_15-20_whyrating-email-setup.md diff --git a/.artifacts/2026-02-02_15-35_stalwart-mcp-created-tested.md b/.artifacts/2026-02-02_15-35_stalwart-mcp-created-tested.md new file mode 100644 index 0000000..78f06aa --- /dev/null +++ b/.artifacts/2026-02-02_15-35_stalwart-mcp-created-tested.md @@ -0,0 +1,169 @@ +# Stalwart Mail MCP Server - Created & Tested + +**Date:** 2026-02-02 15:35 +**Context:** Built MCP server from scratch to manage Stalwart mail server via Claude Code + +## Summary + +Created a complete MCP server in Python that wraps Stalwart's REST API, providing 18 tools for mail server management. All tools tested and working. + +## Test Results + +### ✅ list_users +```json +{ + "users": [ + { + "id": 3, + "name": "info", + "type": "individual", + "emails": ["info@whyrating.com"], + "roles": ["user"], + "quota_used": 21465 + }, + { + "id": 1, + "name": "whyrating.com", + "type": "domain", + "members": 1 + } + ], + "total": 2 +} +``` + +### ✅ get_user("info") +```json +{ + "data": { + "id": 3, + "type": "individual", + "name": "info", + "emails": ["info@whyrating.com"], + "roles": ["user"], + "usedQuota": 21465 + } +} +``` + +### ✅ create_user / delete_user +- Created `testuser` with email `test@whyrating.com` +- Verified user appeared in database with 1GB quota +- Successfully deleted user +- Confirmed removal from user list + +### ✅ check_dns_records("whyrating.com") +Returns all DNS records Stalwart expects: +- MX record +- SPF (TXT) +- DKIM Ed25519 + RSA (TXT) +- DMARC (TXT) +- SRV records for IMAP, SMTP, CalDAV, CardDAV +- Autoconfig/Autodiscover CNAMEs + +### ✅ get_queue_status +```json +{"data": true} // Queue is running +``` + +### ✅ list_queue +```json +{"data": {"items": [], "total": 0, "status": true}} // Empty queue (no pending) +``` + +## Server Location + +``` +~/mcp-servers/stalwart-mail/ +├── server.py # 450 lines - MCP server with 18 tools +├── requirements.txt # mcp[cli], httpx +├── pyproject.toml # Python 3.10-3.13 compatibility +├── README.md # Usage documentation +└── .venv/ # Python 3.12 virtual environment +``` + +## Tools Implemented + +| Category | Tool | Status | +|----------|------|--------| +| **Users** | `list_users` | ✅ Tested | +| | `get_user` | ✅ Tested | +| | `create_user` | ✅ Tested | +| | `update_user_password` | ✅ Implemented | +| | `delete_user` | ✅ Tested | +| | `add_email_alias` | ✅ Implemented | +| **Domains** | `create_domain` | ✅ Implemented | +| | `generate_dkim` | ✅ Implemented | +| **Queue** | `list_queue` | ✅ Tested | +| | `get_queue_status` | ✅ Tested | +| | `delete_queued_message` | ✅ Implemented | +| | `retry_queued_message` | ✅ Implemented | +| **Monitoring** | `get_metrics` | ✅ Implemented | +| | `get_dmarc_reports` | ✅ Implemented | +| | `get_server_logs` | ✅ Implemented | +| **DNS** | `check_dns_records` | ✅ Tested | +| | `troubleshoot_delivery` | ✅ Implemented | +| **Spam** | `train_spam` | ✅ Implemented | +| | `train_ham` | ✅ Implemented | +| | `update_spam_filter` | ✅ Implemented | + +## Claude Code Configuration + +```bash +claude mcp add stalwart-mail \ + -e STALWART_URL=http://192.168.1.3:8081 \ + -e STALWART_USER=admin \ + -e STALWART_PASS=QfKYjCJdxu \ + --scope user \ + -- /Users/agutierrez/mcp-servers/stalwart-mail/.venv/bin/python \ + /Users/agutierrez/mcp-servers/stalwart-mail/server.py +``` + +**Status:** ✓ Connected (verified with `claude mcp list`) + +## API Authentication + +- **Method:** HTTP Basic Auth +- **Username:** admin +- **Password:** QfKYjCJdxu +- **Base URL:** http://192.168.1.3:8081 + +## Architecture + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ Claude Code │────▶│ stalwart-mail │────▶│ Stalwart │ +│ │ MCP │ MCP Server │HTTP │ REST API │ +│ Natural Lang │◀────│ (Python) │◀────│ :8081 │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ +``` + +## Usage (After Restart) + +``` +"List all mail users" +"Create user john with email john@whyrating.com and password Secret123" +"Show the mail queue" +"Check DNS records for whyrating.com" +"What's the queue status?" +"Delete user john" +``` + +## Development Time + +- Research Stalwart API: 2 min +- Write server.py: 3 min +- Setup & test: 2 min +- **Total: ~7 minutes** + +## Sources + +- [Stalwart API Overview](https://stalw.art/docs/api/management/overview/) +- [Stalwart API Endpoints](https://stalw.art/docs/api/management/endpoints/) +- [MCP Python SDK](https://github.com/anthropics/mcp) + +## Next Steps + +1. **Restart Claude Code** to load MCP tools +2. Optionally deploy as Docker container on NUC +3. Add to Coolify for centralized management diff --git a/.artifacts/2026-02-03_12-30_cloudbeaver-setup.md b/.artifacts/2026-02-03_12-30_cloudbeaver-setup.md new file mode 100644 index 0000000..985b34f --- /dev/null +++ b/.artifacts/2026-02-03_12-30_cloudbeaver-setup.md @@ -0,0 +1,73 @@ +# CloudBeaver Database Manager Setup + +**Date:** 2026-02-03 12:30 +**Context:** Configured CloudBeaver to connect to all NUC databases with pre-configured connections + +## Access + +| Property | Value | +|----------|-------| +| **URL** | `http://192.168.1.3:8978` | +| **Admin User** | `cbadmin` | +| **Admin Password** | `CloudBeaver2026!` | +| **Coolify UUID** | `joo4g4k0w08k8kcosgsgswc0` | + +## Connected Databases (9/9) + +### Coolify Standalone DBs +| Connection | Host (Container) | Database | User | Password | Status | +|------------|-------------------|----------|------|----------|--------| +| WhyRating Hub | `i8skkc8cwsgwgsg0g8kcw44k` | whyrating | whyrating | WhyRatingPG2026! | OK | +| Turbostarter | `db-v4gogwwc8wkk4888ksscc4k4` | core | turbostarter | turbostarter | OK | +| LiquidGym (MySQL) | `hgwcgs4oswwc8scg080scoo4` | liquidgym | liquidgym | liquidgym_nuc_2026 | OK | + +### Service Embedded DBs +| Connection | Host (Container) | Database | User | Password | Status | +|------------|-------------------|----------|------|----------|--------| +| Outline | `postgres-pccg80wks4c084008owokkkg` | outline | HVubx2MKadO9V4JU | OGB4GnEblE6t86IyzXYyKLE6nUjlOftp0B006kS3O0qlQcNdGh1FUHVyKEg2UbFq | OK | +| Google Scraper | `postgres-g4s8w4csk8s8ocswg48kkogo` | scraper | scraper | scraper_nuc_2026 | OK | +| LiquidGym (Postgres) | `postgres-x4kk8g4k8w4g0cw480w84g4g` | postgres | postgres | postgres | OK | +| Knosia | `postgres-ik80skko0008w4000c4w40os` | knosia | knosia | knosia_nuc_2026 | OK | +| Authentik | `postgresql-e8owcw0s4wcswc4w4css0sws` | authentik | yth9ADhCXAsYytvI | H6Ts2mC7dGn7ExWlt0yDoYREHpEMeSH6 | OK | + +### Infrastructure +| Connection | Host (Container) | Database | User | Password | Status | +|------------|-------------------|----------|------|----------|--------| +| Coolify DB | `coolify-db` | coolify | coolify | fwI1hpB5Y3LPV2zLBjP8g6OZ43PLd93/k0s4CLNwPiw= | OK (read-only) | + +## Docker Networks + +CloudBeaver is connected to 8 networks: +- `default` (service network) +- `coolify` (Coolify infra + Coolify DB) +- `pccg80wks4c084008owokkkg` (Outline + its Postgres) +- `e8owcw0s4wcswc4w4css0sws` (Authentik + its Postgres) +- `g4s8w4csk8s8ocswg48kkogo` (Google Scraper + its Postgres) +- `x4kk8g4k8w4g0cw480w84g4g` (LiquidGym Postgres + MySQL) +- `ik80skko0008w4000c4w40os` (Knosia + its Postgres) +- `v4gogwwc8wkk4888ksscc4k4` (Turbostarter: web + pgvector + minio) + +## Configuration Files + +- **data-sources.json:** `/opt/cloudbeaver/workspace/GlobalConfiguration/.dbeaver/data-sources.json` +- **initial-data-sources.conf:** `/opt/cloudbeaver/conf/initial-data-sources.conf` (backup for fresh init) +- **Coolify compose:** Updated with external networks, healthcheck, and port mapping + +## Notes + +- CloudBeaver reads connection definitions from `data-sources.json` but NOT credentials +- Credentials were stored via GraphQL `initConnection` mutation with `saveCredentials: true` +- MySQL 8 connections require `allowPublicKeyRetrieval: true` JDBC property +- `admin` is a reserved username in CloudBeaver CE (used `cbadmin` instead) +- Turbostarter was redeployed as service `v4gogwwc8wkk4888ksscc4k4` with container `db-v4gogwwc8wkk4888ksscc4k4` + +## Adding New Databases + +1. Add connection to `data-sources.json` on the volume +2. Connect CloudBeaver container to the new database's Docker network +3. Update Coolify compose with the new external network +4. Use CloudBeaver UI or GraphQL API to set credentials + +## Related +- Coolify service: `http://192.168.1.3:8000` (UUID: joo4g4k0w08k8kcosgsgswc0) +- CloudBeaver docs: https://dbeaver.com/docs/cloudbeaver/ diff --git a/.artifacts/2026-02-03_22-00_turbostarter-deployment.md b/.artifacts/2026-02-03_22-00_turbostarter-deployment.md new file mode 100644 index 0000000..6fe2b9d --- /dev/null +++ b/.artifacts/2026-02-03_22-00_turbostarter-deployment.md @@ -0,0 +1,69 @@ +# Turbostarter (Knosia) Production Deployment + +**Date:** 2026-02-03 22:00 +**Context:** Full production deployment of Turbostarter Next.js monorepo on NUC via Coolify + +## Deployment Details + +| Property | Value | +|----------|-------| +| **URL** | `https://alezmad-nuc.tail58f5ad.ts.net` | +| **Service UUID** | `v4gogwwc8wkk4888ksscc4k4` | +| **Service Name** | Knosia | +| **Architecture** | Tailscale Funnel (HTTPS) → Traefik (HTTP:80) → web container | +| **FQDN (internal)** | `http://alezmad-nuc.tail58f5ad.ts.net` | +| **Registry Image** | `192.168.1.3:3030/alezmad/turbostarter:latest` | +| **Gitea Repo** | `alezmad/turbostarter` | + +## Container Stack + +| Container | Image | Status | +|-----------|-------|--------| +| `web-v4gogwwc8wkk4888ksscc4k4` | `localhost:3030/alezmad/turbostarter:latest` | running:healthy | +| `db-v4gogwwc8wkk4888ksscc4k4` | `pgvector/pgvector:pg17` | running:healthy | +| `minio-v4gogwwc8wkk4888ksscc4k4` | `minio/minio:latest` | running:healthy | +| `minio-init-v4gogwwc8wkk4888ksscc4k4` | `minio/mc:latest` | exited (expected) | + +## Credentials + +| Service | Credential | +|---------|-----------| +| **Database** | `postgres://turbostarter:turbostarter@db:5432/core` | +| **MinIO** | `minioadmin` / `minioadmin` | +| **Better Auth Secret** | `WyfMfoRclem2Bc/Ek3/2nWsiIdHkjIOvAhJXevDAx/E=` | +| **Admin User** | `me+admin@turbostarter.dev` / `Pa$$w0rd` | +| **Regular User** | `me+user@turbostarter.dev` / `Pa$$w0rd` | + +## Database Schemas + +- 11 auth tables (Better Auth) +- PostgreSQL schemas: `chat`, `pdf`, `image` (Drizzle pgSchema) +- Seeded with 5 users and organization data + +## Key Configuration Decisions + +1. **HTTPS via Tailscale Funnel** — not Cloudflare (Spanish ISPs block Cloudflare shared IPs during LaLiga) +2. **FQDN set to HTTP internally** — Tailscale terminates TLS, Traefik must not redirect to HTTPS (causes loop) +3. **BETTER_AUTH_TRUSTED_ORIGINS** — runtime env var added to `server.ts` so origins can be configured without rebuilding +4. **NEXT_PUBLIC_URL** — build-time ARG in Dockerfile, baked into static output +5. **CSP `upgrade-insecure-requests`** — kept in place (production security), requires valid HTTPS + +## Build Command + +```bash +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 +``` + +## Code Changes Made + +1. **Dockerfile** — simplified to single-stage build, added `NEXT_PUBLIC_URL` build arg +2. **packages/auth/src/server.ts** — added `BETTER_AUTH_TRUSTED_ORIGINS` env var support in trustedOrigins array + +## Related +- Coolify service: http://192.168.1.3:8000 (service ID 29) +- Gitea repo: http://192.168.1.3:3030/alezmad/turbostarter +- Tailscale Funnel: `tailscale funnel status` on NUC