# 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: # To: image: localhost:32000/hermes-mcp@sha256: # 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 ```