- Add GET/POST /login to hermes for first-party cookie during OAuth popup (fixes browser CHIPS cookie partitioning that broke claude.ai connection) - Add role column to all findCustomer* SQL queries in src/auth.ts - Add claude.ai tab to docs/getting-started.html with OAuth flow steps - Add ARCHITECTURE.md with system diagrams, data flow, and key invariants - Rewrite README.md and DEPLOY.md to reflect actual MicroK8s deployment - Deploy updated docs site (squaremcp-docs sha256 updated) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
150 lines
4.4 KiB
Markdown
150 lines
4.4 KiB
Markdown
# Hermes MCP — Deployment Runbook
|
|
|
|
Production deployment runs on a single-node MicroK8s cluster in the `fetcherpay` namespace. Three services are deployed: `hermes-mcp` (API), `squaremcp-app` (SaaS UI), `squaremcp-docs` (docs).
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- MicroK8s with addons: `dns`, `ingress`, `registry`, `cert-manager`
|
|
- Local image registry at `localhost:32000`
|
|
- `ClusterIssuer` named `letsencrypt-prod` configured
|
|
- MySQL 8 running as Docker container on host (`127.0.0.1:3306`)
|
|
- Redis 7 running as Docker container on host (`127.0.0.1:6379`)
|
|
|
|
---
|
|
|
|
## Deploying hermes-mcp
|
|
|
|
```bash
|
|
# 1. Build TypeScript
|
|
npm run build
|
|
|
|
# 2. Build and push Docker image
|
|
docker build -t localhost:32000/hermes-mcp:latest .
|
|
docker push localhost:32000/hermes-mcp:latest
|
|
|
|
# 3. Get the sha256 digest from push output, or:
|
|
docker inspect localhost:32000/hermes-mcp:latest \
|
|
--format='{{index .RepoDigests 0}}'
|
|
|
|
# 4. Update hermes-k8s.yaml
|
|
# Change: image: localhost:32000/hermes-mcp@sha256:<old>
|
|
# To: image: localhost:32000/hermes-mcp@sha256:<new>
|
|
|
|
# 5. Apply
|
|
microk8s kubectl apply -f hermes-k8s.yaml
|
|
|
|
# 6. Wait for rollout
|
|
microk8s kubectl rollout status deployment/hermes-mcp -n fetcherpay
|
|
```
|
|
|
|
**Important:** `hermes-k8s.yaml` is gitignored — it contains plaintext secrets. Never force-add it to git. Always use sha256 digest (never `:latest`) in the manifest.
|
|
|
|
---
|
|
|
|
## Deploying squaremcp-app (dashboard UI)
|
|
|
|
```bash
|
|
docker build -f product/app/Dockerfile . -t localhost:32000/squaremcp-app:latest
|
|
docker push localhost:32000/squaremcp-app:latest
|
|
|
|
# Update sha256 in product/app/app-k8s.yaml, then:
|
|
microk8s kubectl apply -f product/app/app-k8s.yaml
|
|
```
|
|
|
|
---
|
|
|
|
## Deploying squaremcp-docs
|
|
|
|
```bash
|
|
docker build -f docs/Dockerfile . -t localhost:32000/squaremcp-docs:latest
|
|
docker push localhost:32000/squaremcp-docs:latest
|
|
|
|
# Update sha256 in docs/docs-k8s.yaml, then:
|
|
microk8s kubectl apply -f docs/docs-k8s.yaml
|
|
```
|
|
|
|
---
|
|
|
|
## Useful commands
|
|
|
|
```bash
|
|
# Check pod status
|
|
microk8s kubectl get pods -n fetcherpay
|
|
|
|
# Live logs
|
|
microk8s kubectl logs -n fetcherpay -l app=hermes-mcp -f --tail=100
|
|
|
|
# Health check
|
|
curl https://hermes.squaremcp.com/health
|
|
|
|
# Inject env var without rebuild (takes effect after rollout)
|
|
microk8s kubectl set env deployment/hermes-mcp -n fetcherpay KEY=value
|
|
microk8s kubectl rollout restart deployment/hermes-mcp -n fetcherpay
|
|
|
|
# Restart all pods
|
|
microk8s kubectl rollout restart deployment -n fetcherpay
|
|
|
|
# Check TLS certificates
|
|
microk8s kubectl get certificate -n fetcherpay
|
|
```
|
|
|
|
---
|
|
|
|
## Environment variables (hermes-mcp)
|
|
|
|
Key variables in `hermes-k8s.yaml`:
|
|
|
|
| Variable | Purpose |
|
|
|----------|---------|
|
|
| `PORT` | Server port (3456) |
|
|
| `SERVER_URL` | Public base URL (`https://hermes.squaremcp.com`) |
|
|
| `MCP_API_KEY` | Global superadmin API key |
|
|
| `MYSQL_HOST/PORT/USER/PASSWORD` | MySQL connection |
|
|
| `REDIS_URL` | Redis connection (`redis://127.0.0.1:6379`) |
|
|
| `CREDENTIAL_ENCRYPTION_KEY` | AES-256-GCM key for stored platform credentials |
|
|
| `OAUTH_CLIENT_ID/SECRET` | Pre-registered OAuth app credentials |
|
|
| `OBSIDIAN_VAULT_PATH` | Mount path for Obsidian vault (`/vaults`) |
|
|
| `YAHOO_EMAIL / YAHOO_APP_PASSWORD` | Default Yahoo IMAP account |
|
|
| `GMAIL_EMAIL / GMAIL_APP_PASSWORD` | Default Gmail account |
|
|
| `FETCHERPAY_*` | Fetcherpay email IMAP/SMTP |
|
|
|
|
**Do not rotate `CREDENTIAL_ENCRYPTION_KEY`** without first re-encrypting all stored customer credentials in Redis.
|
|
|
|
---
|
|
|
|
## Schema migrations
|
|
|
|
`src/db.ts` uses `ensureColumn()` — an idempotent helper that `ALTER TABLE ... ADD COLUMN IF NOT EXISTS`. Run migrations by deploying a new image; the startup hook runs automatically.
|
|
|
|
To run a migration manually:
|
|
```bash
|
|
mysql -h 127.0.0.1 -u root -pfetcherpay hermes_oauth
|
|
```
|
|
|
|
---
|
|
|
|
## Domains and TLS
|
|
|
|
| Domain | K8s Ingress | TLS secret |
|
|
|--------|-------------|------------|
|
|
| `hermes.squaremcp.com` | `hermes-ingress` | `hermes-squaremcp-tls` |
|
|
| `app.squaremcp.com` | `squaremcp-app-ingress` | `squaremcp-app-tls` |
|
|
| `docs.squaremcp.com` | `squaremcp-docs-ingress` | `squaremcp-docs-tls` |
|
|
|
|
TLS certificates are auto-provisioned by cert-manager from Let's Encrypt. Check certificate status:
|
|
```bash
|
|
microk8s kubectl describe certificate -n fetcherpay
|
|
```
|
|
|
|
---
|
|
|
|
## Rollback
|
|
|
|
To roll back to the previous image, update the sha256 digest in the manifest to the previous value and re-apply:
|
|
```bash
|
|
microk8s kubectl apply -f hermes-k8s.yaml
|
|
microk8s kubectl rollout status deployment/hermes-mcp -n fetcherpay
|
|
```
|