summaryrefslogtreecommitdiff
path: root/prompts/skills/protonbridge-aerc/SKILL.md
diff options
context:
space:
mode:
Diffstat (limited to 'prompts/skills/protonbridge-aerc/SKILL.md')
-rw-r--r--prompts/skills/protonbridge-aerc/SKILL.md321
1 files changed, 321 insertions, 0 deletions
diff --git a/prompts/skills/protonbridge-aerc/SKILL.md b/prompts/skills/protonbridge-aerc/SKILL.md
new file mode 100644
index 0000000..8731f06
--- /dev/null
+++ b/prompts/skills/protonbridge-aerc/SKILL.md
@@ -0,0 +1,321 @@
+---
+name: protonbridge-aerc
+description: "Manages the local aerc connection to Proton Mail Bridge running in the f3s k3s cluster through a persistent kubectl port-forward, pinned Bridge certificate, and systemd user service. Use when setting up, starting, validating, or troubleshooting aerc, Proton Bridge IMAP/SMTP, certificate errors, credentials, or the protonbridge-k3s-tunnel service. Triggers on: protonbridge aerc, aerc mail, Proton Bridge tunnel, aerc IMAP, aerc SMTP."
+---
+
+# Proton Bridge for aerc
+
+Maintain the secure path from aerc on `earth` to Proton Mail Bridge in the f3s
+k3s cluster. Do not expose the Bridge-generated password in output, prompts,
+logs, Git, or task annotations.
+
+## Architecture
+
+```text
+aerc
+ ├─ IMAP STARTTLS 127.0.0.1:1143
+ └─ SMTP STARTTLS 127.0.0.1:1025
+ │
+ ▼
+protonbridge-k3s-tunnel.service
+ └─ kubectl port-forward service/protonbridge -n services
+ │
+ ▼
+Proton Bridge pod in f3s k3s
+```
+
+The port-forward is preferable to a fixed NodePort address because Kubernetes
+selects the healthy pod and the setup survives r0/r1/r2 placement changes.
+
+## Canonical files
+
+| Purpose | Path |
+|---|---|
+| aerc account | `~/.config/aerc/accounts.conf` |
+| Bridge password | `~/.config/aerc/protonbridge-password` (mode `0600`) |
+| Pinned Bridge certificate | `~/.config/aerc/protonbridge-ca-bundle.pem` |
+| aerc wrapper setting `SSL_CERT_FILE` | `~/bin/aerc` |
+| Persistent tunnel | `~/.config/systemd/user/protonbridge-k3s-tunnel.service` |
+| Disabled old local Bridge autostart | `~/.config/autostart/Proton Mail Bridge.desktop` |
+| k3s Deployment | `/home/paul/git/conf/f3s/protonbridge/helm-chart/templates/deployment.yaml` |
+| k3s Service | `/home/paul/git/conf/f3s/protonbridge/helm-chart/templates/service.yaml` |
+| Argo CD Application | `/home/paul/git/conf/f3s/argocd-apps/services/protonbridge.yaml` |
+
+Load the [`f3s-k3s`](../f3s-k3s/SKILL.md) skill for cluster access, Argo CD,
+node, or control-plane problems. This skill owns only the Proton Bridge/aerc
+path.
+
+## f3s Argo CD deployment
+
+The cluster-side Proton Bridge is a GitOps workload in the `conf` repository,
+not an independently managed pod:
+
+- Argo CD Application: `protonbridge` in namespace `cicd`
+- Application manifest:
+ `/home/paul/git/conf/f3s/argocd-apps/services/protonbridge.yaml`
+- Argo source repository:
+ `http://git-server.cicd.svc.cluster.local/conf.git`
+- Source revision and path: `master`, `f3s/protonbridge/helm-chart`
+- Destination: the in-cluster API, namespace `services`
+- Desired policy: automated sync with `prune: true` and `selfHeal: true`
+
+The Helm chart owns:
+
+- `templates/deployment.yaml` — headless Bridge CLI, persistent account/update
+ state, probes, and loopback-to-pod forwarding
+- `templates/service.yaml` — IMAP/SMTP Service ports plus LAN NodePorts
+- `templates/persistent-volumes.yaml` — retained Bridge state on the f3s volume
+
+Treat these files as the source of truth. Make durable cluster-side fixes in
+the chart, validate them, commit them to the `conf` repository, and push through
+the established repository workflow so Argo CD deploys them. A direct
+`kubectl apply` is acceptable for a short diagnostic only; reproduce any valid
+fix in Git and remove live drift afterward.
+
+Validate a chart change before pushing:
+
+```bash
+cd /home/paul/git/conf
+helm lint f3s/protonbridge/helm-chart
+helm template protonbridge f3s/protonbridge/helm-chart \
+ | kubectl apply --dry-run=server -f -
+```
+
+Inspect reconciliation and the deployed revision:
+
+```bash
+kubectl get application -n cicd protonbridge \
+ -o jsonpath='{.status.sync.status}{" "}{.status.health.status}{" "}{.status.sync.revision}{"\n"}'
+kubectl get deployment,pod -n services -l app=protonbridge
+kubectl get service,endpoints -n services protonbridge
+```
+
+If automated sync was temporarily disabled during diagnosis, restore the
+declared Application and verify the policy rather than leaving it disabled:
+
+```bash
+kubectl apply -f /home/paul/git/conf/f3s/argocd-apps/services/protonbridge.yaml
+kubectl get application -n cicd protonbridge \
+ -o jsonpath='{.spec.syncPolicy.automated}{"\n"}{.status.sync.status}{" "}{.status.health.status}{"\n"}'
+```
+
+Expected output includes `{"prune":true,"selfHeal":true}` and
+`Synced Healthy`. Never delete the retained PV/PVC or Bridge vault as a generic
+troubleshooting step; they contain the persisted account session, generated
+credentials, certificate, and self-update state.
+
+## Bootstrap order
+
+When recreating the setup on `earth`:
+
+1. Confirm `kubectl` can reach the f3s cluster and Proton Bridge is healthy.
+2. Create and enable the systemd port-forward described below.
+3. Attach to Bridge, run `info`, and store its generated password without
+ printing or committing it.
+4. Capture and pin the current Bridge certificate through the localhost tunnel.
+5. Create the aerc account with credential commands instead of inline secrets.
+6. Install the `~/bin/aerc` wrapper and confirm it is first on `PATH`.
+7. Disable the obsolete laptop-local Bridge autostart to prevent port conflicts.
+8. Run TLS, IMAP login/folder, SMTP authentication, and aerc startup checks.
+
+Do not continue to a later layer when an earlier one is unhealthy.
+
+## Expected aerc account
+
+The account uses STARTTLS through localhost and obtains the password through a
+credential command:
+
+```ini
+[paul]
+source = imap://mail%40paul.buetow.org@127.0.0.1:1143
+source-cred-cmd = cat ~/.config/aerc/protonbridge-password
+outgoing = smtp://mail%40paul.buetow.org@127.0.0.1:1025
+outgoing-cred-cmd = cat ~/.config/aerc/protonbridge-password
+default = INBOX
+from = Paul Buetow <mail@paul.buetow.org>
+copy-to = Sent
+cache-headers = true
+```
+
+Never put the password back into either URI. It is the Bridge-generated
+password shown by the Bridge `info` command, not the Proton account password.
+
+The `~/bin/aerc` wrapper must precede `/usr/bin` on `PATH` and contain:
+
+```sh
+#!/bin/sh
+export SSL_CERT_FILE="$HOME/.config/aerc/protonbridge-ca-bundle.pem"
+exec /usr/bin/aerc "$@"
+```
+
+## Start and inspect
+
+Start or restart the persistent tunnel:
+
+```bash
+systemctl --user enable --now protonbridge-k3s-tunnel.service
+systemctl --user restart protonbridge-k3s-tunnel.service
+```
+
+Check it without printing secrets:
+
+```bash
+systemctl --user status protonbridge-k3s-tunnel.service --no-pager
+journalctl --user -u protonbridge-k3s-tunnel.service --since '-15 min' --no-pager
+ss -ltn | rg '127\.0\.0\.1:(1025|1143)\b'
+type -a aerc
+```
+
+Expected state:
+
+- service is `enabled` and `active`
+- `kubectl port-forward` listens on localhost ports 1143 and 1025
+- `~/bin/aerc` resolves before `/usr/bin/aerc`
+- no laptop-local `/usr/lib/protonmail/bridge/` process is running
+
+Launch normally with `aerc`.
+
+## End-to-end health check
+
+Check the cluster first:
+
+```bash
+kubectl get pod -n services -l app=protonbridge
+kubectl get service,endpoints -n services protonbridge
+kubectl get application -n cicd protonbridge \
+ -o jsonpath='{.status.sync.status}{" "}{.status.health.status}{"\n"}'
+```
+
+The pod should be `1/1 Running`, endpoints should include IMAP and SMTP, and
+Argo CD should report `Synced Healthy`.
+
+Validate TLS without authenticating:
+
+```bash
+/usr/bin/openssl s_client -starttls imap \
+ -connect 127.0.0.1:1143 \
+ -verify_return_error \
+ -CAfile ~/.config/aerc/protonbridge-ca-bundle.pem \
+ -brief </dev/null
+
+/usr/bin/openssl s_client -starttls smtp \
+ -connect 127.0.0.1:1025 \
+ -verify_return_error \
+ -CAfile ~/.config/aerc/protonbridge-ca-bundle.pem \
+ -brief </dev/null
+```
+
+Both must report `Verification: OK`. Do not work around failures with
+`imap+insecure` or `smtp+insecure`: in aerc 0.21 those schemes disable STARTTLS
+and expose credentials as plaintext.
+
+For an authenticated check, read the password from the credential file inside
+the test process; never echo it. Use Python `imaplib.IMAP4` followed by
+`starttls()`, and `smtplib.SMTP` followed by `starttls()`, with an SSL context
+created from `protonbridge-ca-bundle.pem`. Confirm IMAP login/folder listing and
+SMTP login, then log out without sending mail.
+
+## Recreate the tunnel
+
+The systemd user unit should contain:
+
+```ini
+[Unit]
+Description=Forward local aerc ports to the Proton Bridge k3s service
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/kubectl port-forward --address 127.0.0.1 --namespace services service/protonbridge 1143:1143 1025:1025
+Restart=always
+RestartSec=5
+
+[Install]
+WantedBy=default.target
+```
+
+After creating or changing it:
+
+```bash
+systemctl --user daemon-reload
+systemctl --user enable --now protonbridge-k3s-tunnel.service
+```
+
+## Refresh credentials
+
+Refresh only after an authenticated check proves the saved password is stale.
+The Bridge pod runs an attachable CLI:
+
+```bash
+pod=$(kubectl get pod -n services -l app=protonbridge \
+ -o jsonpath='{.items[0].metadata.name}')
+kubectl attach -it -n services "$pod"
+```
+
+Press Enter if needed, run `info`, and use its Bridge-generated password. Write
+it directly to `~/.config/aerc/protonbridge-password`, apply mode `0600`, and do
+not show it in command output. Typing `quit` exits Bridge; Kubernetes restarts
+the pod and reconnects with its persisted session.
+
+If the account itself is absent, run `login` in the attached CLI. The user must
+enter the Proton credentials and 2FA interactively. Never request or retain the
+Proton account password.
+
+## Refresh the pinned certificate
+
+Bridge uses a self-signed certificate whose SAN is only `127.0.0.1`. This is
+why aerc connects through the localhost port-forward instead of directly to a
+NodePort. If Bridge rotates the certificate, replace the pinned leaf after
+verifying that the cluster pod and account are expected:
+
+```bash
+umask 077
+tmp=$(mktemp)
+timeout 10 /usr/bin/openssl s_client \
+ -starttls imap -connect 127.0.0.1:1143 -showcerts \
+ </dev/null 2>/dev/null \
+ | /usr/bin/openssl x509 -outform PEM >"$tmp"
+test -s "$tmp"
+install -m 0644 "$tmp" ~/.config/aerc/protonbridge-ca-bundle.pem
+rm -f "$tmp"
+```
+
+Then rerun both TLS checks. Keep this file limited to the current Bridge
+certificate; combining it with Fedora's CA bundle can select an older
+self-signed Bridge certificate with the same `127.0.0.1` subject.
+
+## Troubleshooting order
+
+1. **aerc binary** — `type -a aerc`; ensure `~/bin/aerc` is first.
+2. **Local listeners** — check ports 1143/1025 with `ss`.
+3. **Tunnel service** — inspect status and journal; restart it once after
+ reading the error.
+4. **kubectl access** — run `kubectl get service -n services protonbridge`.
+ Fix kube context/network access before changing aerc.
+5. **Cluster health** — inspect pod readiness, restarts, logs, endpoints, and
+ Argo CD status.
+6. **TLS** — run the pinned-certificate checks. Refresh the pin only if the
+ live certificate changed intentionally.
+7. **Authentication** — run a non-printing IMAP/SMTP login check. Refresh the
+ Bridge-generated password only if login fails after transport and TLS pass.
+
+Common failures:
+
+- `connection refused`: tunnel inactive, port collision, or pod unavailable.
+- `error upgrading connection` / port-forward exits: stale kube context,
+ unreachable API server, or no ready Proton Bridge pod.
+- `certificate signed by unknown authority`: wrapper bypassed or pin missing.
+- certificate name mismatch: aerc is connecting directly to an r-node address
+ instead of `127.0.0.1`.
+- `no such user` / SMTP authentication failure: stale Bridge-generated
+ password or wrong full email address.
+- local port already in use: stop old laptop-local Bridge processes and keep
+ `~/.config/autostart/Proton Mail Bridge.desktop` disabled (`Hidden=true`).
+- pod `0/1` with `No active accounts`: attach and perform interactive `login`.
+- pod restart after `kubectl attach`: EOF stopped the CLI; wait for Kubernetes
+ to restart it and confirm readiness before retrying.
+
+Do not patch around cluster failures by running a second local Bridge. Repair
+the owning layer, validate the full path, and preserve the single persisted
+Bridge session in k3s.