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:
78
.artifacts/2026-02-01_14-30_gitea-coolify-webhook-fix.md
Normal file
78
.artifacts/2026-02-01_14-30_gitea-coolify-webhook-fix.md
Normal 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
|
||||
93
.artifacts/2026-02-01_20-49_nocodb-credentials.md
Normal file
93
.artifacts/2026-02-01_20-49_nocodb-credentials.md
Normal 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()`
|
||||
171
.artifacts/2026-02-01_21-06_gitea-coolify-integration.md
Normal file
171
.artifacts/2026-02-01_21-06_gitea-coolify-integration.md
Normal 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
|
||||
155
.artifacts/2026-02-01_21-08_outline-ai-workflow.md
Normal file
155
.artifacts/2026-02-01_21-08_outline-ai-workflow.md
Normal 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`
|
||||
89
.artifacts/2026-02-01_21-25_vaultwarden-credentials.md
Normal file
89
.artifacts/2026-02-01_21-25_vaultwarden-credentials.md
Normal 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
|
||||
143
.artifacts/2026-02-01_21-35_tailscale-funnel-https.md
Normal file
143
.artifacts/2026-02-01_21-35_tailscale-funnel-https.md
Normal 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`
|
||||
23
.artifacts/2026-02-02_00-15_stalwart-admin-credentials.md
Normal file
23
.artifacts/2026-02-02_00-15_stalwart-admin-credentials.md
Normal 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`
|
||||
17
.artifacts/2026-02-02_00-20_snappymail-credentials.md
Normal file
17
.artifacts/2026-02-02_00-20_snappymail-credentials.md
Normal 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`
|
||||
48
.artifacts/2026-02-02_12-30_whyrating-databases.md
Normal file
48
.artifacts/2026-02-02_12-30_whyrating-databases.md
Normal 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
|
||||
121
.artifacts/2026-02-02_15-20_whyrating-email-setup.md
Normal file
121
.artifacts/2026-02-02_15-20_whyrating-email-setup.md
Normal 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)
|
||||
98
.artifacts/2026-02-02_15-30_stalwart-mcp-server.md
Normal file
98
.artifacts/2026-02-02_15-30_stalwart-mcp-server.md
Normal 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
|
||||
169
.artifacts/2026-02-02_15-35_stalwart-mcp-created-tested.md
Normal file
169
.artifacts/2026-02-02_15-35_stalwart-mcp-created-tested.md
Normal 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
|
||||
73
.artifacts/2026-02-03_12-30_cloudbeaver-setup.md
Normal file
73
.artifacts/2026-02-03_12-30_cloudbeaver-setup.md
Normal 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/
|
||||
69
.artifacts/2026-02-03_22-00_turbostarter-deployment.md
Normal file
69
.artifacts/2026-02-03_22-00_turbostarter-deployment.md
Normal 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
|
||||
Reference in New Issue
Block a user