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 <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-02-18 15:17:04 +01:00
parent 617f200310
commit 59944e9144
14 changed files with 1347 additions and 0 deletions

View File

@@ -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

View File

@@ -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()`

View File

@@ -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="<contents of /tmp/coolify-gitea-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', '<app-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="<app-uuid>",
fqdn="http://myapp.nuc.lan"
)
```
### 8. Deploy
```python
mcp__coolify__deploy(tag_or_uuid="<app-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

View File

@@ -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="<Descriptive Title>",
text="<Markdown content>",
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="<doc-id>",
text="<new content>",
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`

View File

@@ -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/<repo>.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

View File

@@ -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 <command>"
```
## 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`

View File

@@ -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`

View File

@@ -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`

View File

@@ -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

View File

@@ -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: <YOUR_PUBLIC_IP or Tailscale Funnel IP>
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)

View File

@@ -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

View File

@@ -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

View File

@@ -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/

View File

@@ -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