diff --git a/k8s/apps/gotify/README.md b/k8s/apps/gotify/README.md new file mode 100644 index 0000000..91d4408 --- /dev/null +++ b/k8s/apps/gotify/README.md @@ -0,0 +1,107 @@ +# Gotify — Push Notifications + Email Relay + +Self-hosted notification server with SMTP email forwarding sidecar. + +## Architecture + +``` +Forgejo webhook ──POST──→ Gotify API (:80/message) + │ + ┌─────────┼─────────┐ + ▼ ▼ + Push notification SMTP emailer sidecar + (mobile/desktop) (polls → sends email) +``` + +## Setup (one-time, after first deploy) + +### 1. Encrypt secrets before committing + +```bash +# Edit secrets.yaml with real values first, then: +sops -e -i k8s/apps/gotify/secrets.yaml +``` + +### 2. Create Gotify app + client tokens + +1. Login to `https://gotify.riotpiao.com` with admin creds +2. **Applications** → Create `forgejo` → copy **app token** +3. **Clients** → Create `smtp-emailer` → copy **client token** +4. Update `gotify-tokens` secret: + ```bash + kubectl -n notifications create secret generic gotify-tokens \ + --from-literal=app-token= \ + --from-literal=client-token= \ + --dry-run=client -o yaml | kubectl apply -f - + ``` + +### 3. Configure Forgejo webhook + +In each Forgejo repo → **Settings** → **Webhooks** → **Add Webhook** → **Gotify**: + +| Field | Value | +|-------|-------| +| Target URL | `http://gotify.notifications.svc.cluster.local/message` | +| Token | The **app token** from step 2 | +| Events | Pull Request (Created, Merged, Closed) | + +Or via API: +```bash +FORGEJO_TOKEN="" +APP_TOKEN="" + +curl -s -X POST "https://forgejo.riotpiao.com/api/v1/repos/rock/homelab/hooks" \ + -H "Authorization: token $FORGEJO_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "gotify", + "active": true, + "config": { + "content_type": "json", + "url": "http://gotify.notifications.svc.cluster.local/message?token='"$APP_TOKEN"'" + }, + "events": ["pull_request", "pull_request_assign", "pull_request_review"], + "authorization_header": "" + }' +``` + +### 4. Add CoreDNS rewrite (if accessing via public hostname) + +Only needed if Cloudflare Tunnel is used for gotify.riotpiao.com: + +``` +# terraform/files/coredns/Corefile — add rewrite: +rewrite name gotify.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local +``` + +Then: `cd terraform && terraform apply && cd .. && make apply-cp` + +### 5. SMTP providers + +| Provider | Host | Port | Notes | +|----------|------|------|-------| +| Gmail | smtp.gmail.com | 587 | Use App Password (2FA required) | +| Resend | smtp.resend.com | 587 | Free 100 emails/day | +| Sendgrid | smtp.sendgrid.net | 587 | Free 100 emails/day | +| Mailgun | smtp.mailgun.org | 587 | Free 5000/month | + +## Notification priority levels + +| Priority | Meaning | Email forwarded? | +|----------|---------|-----------------| +| 0-4 | Low (info) | No (below MIN_PRIORITY=5) | +| 5-7 | Normal (PR created) | Yes | +| 8-10 | High (PR merged, failures) | Yes | + +## Verify + +```bash +# Test push notification +APP_TOKEN="" +curl -X POST "https://gotify.riotpiao.com/message?token=$APP_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"title":"Test","message":"Hello from homelab","priority":5}' + +# Check email sidecar logs +kubectl -n notifications logs deployment/gotify -c smtp-emailer --tail=20 +``` diff --git a/k8s/apps/gotify/db.yaml b/k8s/apps/gotify/db.yaml new file mode 100644 index 0000000..ef53f6e --- /dev/null +++ b/k8s/apps/gotify/db.yaml @@ -0,0 +1,35 @@ +# CNPG Postgres for Gotify. Lightweight — 2 instances, 2Gi storage. +# CNPG generates secret `gotify-db-app` + service `gotify-db-rw` in ns notifications. +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: gotify-db + namespace: notifications + annotations: + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true +spec: + instances: 2 + imageName: ghcr.io/cloudnative-pg/postgresql:16.2 + bootstrap: + initdb: + database: gotify + owner: app + encoding: UTF8 + localeCollate: C + localeCType: C + enableSuperuserAccess: false + resources: + requests: { memory: "256Mi", cpu: "100m" } + limits: { memory: "512Mi", cpu: "500m" } + storage: + size: 2Gi + storageClass: longhorn-cnpg + monitoring: + enablePodMonitor: true + affinity: + podAntiAffinityType: preferred + topologyKey: kubernetes.io/hostname + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule diff --git a/k8s/apps/gotify/deployment.yaml b/k8s/apps/gotify/deployment.yaml new file mode 100644 index 0000000..f3efee2 --- /dev/null +++ b/k8s/apps/gotify/deployment.yaml @@ -0,0 +1,204 @@ +# Gotify — self-hosted push notification server + SMTP email relay. +# Forgejo webhooks → Gotify → push notifications + email forwarding. +# Runs on control plane (no GPU needed), lightweight. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gotify + namespace: notifications + labels: + app: gotify +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: gotify + template: + metadata: + labels: + app: gotify + spec: + containers: + # --- Gotify server --- + - name: gotify + image: ghcr.io/gotify/server:2.6.1 + command: ["/bin/sh", "-c"] + args: + - | + export GOTIFY_DATABASE_DIALECT=postgres + export GOTIFY_DATABASE_CONNECTION="host=gotify-db-rw.notifications port=5432 user=${DB_USER} password=${DB_PASS} dbname=gotify sslmode=disable" + exec /app/gotify-app + ports: + - containerPort: 80 + protocol: TCP + env: + - name: GOTIFY_DEFAULTUSER_NAME + valueFrom: + secretKeyRef: + name: gotify-admin + key: username + - name: GOTIFY_DEFAULTUSER_PASS + valueFrom: + secretKeyRef: + name: gotify-admin + key: password + - name: DB_USER + valueFrom: + secretKeyRef: + name: gotify-db-app + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: gotify-db-app + key: password + - name: GOTIFY_SERVER_PORT + value: "80" + - name: GOTIFY_SERVER_KEEPALIVEPERIODSECONDS + value: "0" + - name: TZ + value: Asia/Tokyo + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi + livenessProbe: + httpGet: + path: /health + port: 80 + periodSeconds: 30 + initialDelaySeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 80 + periodSeconds: 10 + initialDelaySeconds: 5 + + # --- SMTP emailer sidecar --- + # Watches Gotify WebSocket stream, forwards messages as email. + # https://github.com/eternal-flame-AD/gotify-broadcast + - name: smtp-emailer + image: ghcr.io/gotify/server:2.6.1 + command: + - /bin/sh + - -c + - | + # Wait for Gotify to be ready + until wget -qO- http://localhost:80/health >/dev/null 2>&1; do + echo "Waiting for Gotify..." + sleep 2 + done + echo "Gotify is ready, starting email relay..." + + # Poll Gotify messages and forward via SMTP using msmtp + # Install msmtp for lightweight SMTP sending + apk add --no-cache msmtp curl jq + + # Configure msmtp + cat > /tmp/msmtprc </dev/null) + + if [ -n "$MESSAGES" ]; then + echo "$MESSAGES" | jq -r '.messages[]? | @base64' | while read -r MSG; do + DECODED=$(echo "$MSG" | base64 -d) + ID=$(echo "$DECODED" | jq -r '.id') + TITLE=$(echo "$DECODED" | jq -r '.title // "Notification"') + BODY=$(echo "$DECODED" | jq -r '.message // ""') + PRIORITY=$(echo "$DECODED" | jq -r '.priority // 5') + APP=$(echo "$DECODED" | jq -r '.appid // 0') + DATE=$(echo "$DECODED" | jq -r '.date // ""') + + # Only forward messages with priority >= configured threshold + if [ "$PRIORITY" -ge "${MIN_PRIORITY:-0}" ]; then + printf "Subject: [Gotify] %s\nFrom: %s\nTo: %s\nContent-Type: text/plain; charset=UTF-8\n\n%s\n\n---\nPriority: %s\nDate: %s" \ + "$TITLE" "$SMTP_FROM" "$NOTIFY_EMAIL" "$BODY" "$PRIORITY" "$DATE" | \ + msmtp -C /tmp/msmtprc "$NOTIFY_EMAIL" && \ + echo "Email sent for message $ID: $TITLE" || \ + echo "Failed to send email for message $ID" + fi + + # Update last seen ID + if [ "$ID" -gt "$LAST_ID" ]; then + LAST_ID=$ID + fi + done + fi + + sleep ${POLL_INTERVAL:-30} + done + env: + - name: GOTIFY_CLIENT_TOKEN + valueFrom: + secretKeyRef: + name: gotify-tokens + key: client-token + - name: SMTP_HOST + valueFrom: + secretKeyRef: + name: gotify-smtp + key: host + - name: SMTP_PORT + valueFrom: + secretKeyRef: + name: gotify-smtp + key: port + - name: SMTP_FROM + valueFrom: + secretKeyRef: + name: gotify-smtp + key: from + - name: SMTP_USER + valueFrom: + secretKeyRef: + name: gotify-smtp + key: user + - name: SMTP_PASS + valueFrom: + secretKeyRef: + name: gotify-smtp + key: password + - name: NOTIFY_EMAIL + valueFrom: + secretKeyRef: + name: gotify-smtp + key: notify-email + - name: MIN_PRIORITY + value: "5" + - name: POLL_INTERVAL + value: "15" + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + cpu: 100m + memory: 64Mi + # No volumes — Postgres handles persistence diff --git a/k8s/apps/gotify/ingress.yaml b/k8s/apps/gotify/ingress.yaml new file mode 100644 index 0000000..fde01cf --- /dev/null +++ b/k8s/apps/gotify/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: gotify + namespace: notifications + annotations: + nginx.ingress.kubernetes.io/proxy-read-timeout: "600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "600" + # WebSocket support for Gotify client connections + nginx.ingress.kubernetes.io/proxy-http-version: "1.1" + nginx.ingress.kubernetes.io/configuration-snippet: | + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; +spec: + ingressClassName: nginx + rules: + - host: gotify.riotpiao.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: gotify + port: + number: 80 diff --git a/k8s/apps/gotify/kustomization.yaml b/k8s/apps/gotify/kustomization.yaml new file mode 100644 index 0000000..51633ed --- /dev/null +++ b/k8s/apps/gotify/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - namespace.yaml + - db.yaml + - secrets.yaml + - deployment.yaml + - service.yaml + - ingress.yaml diff --git a/k8s/apps/gotify/namespace.yaml b/k8s/apps/gotify/namespace.yaml new file mode 100644 index 0000000..e6acc35 --- /dev/null +++ b/k8s/apps/gotify/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: notifications + labels: + kubernetes.io/metadata.name: notifications diff --git a/k8s/apps/gotify/secrets.yaml b/k8s/apps/gotify/secrets.yaml new file mode 100644 index 0000000..379c8b5 --- /dev/null +++ b/k8s/apps/gotify/secrets.yaml @@ -0,0 +1,92 @@ +#ENC[AES256_GCM,data:zdor03HqWha5KnsrNNfM+NT96bZafKhVJ1vfARlqbXzd56QTMq6tScippCbJWCtqLhK5,iv:shItz1uc3RYNBo9Xmq4M+0/Yjw6PbpF4x1tfZZQOSUM=,tag:8Bcwz5XkgLtjuMrDxFXOZQ==,type:comment] +#ENC[AES256_GCM,data:QuQeqD21UgcVOUjlJTzQOAXioYWmnc4b38UTPAClL3WIf7/MqDZ2SQ==,iv:PHw7QplRXB4XBQh3fEolZpdvMMxLucaZx/aY8K5LE20=,tag:Vp3BUb2JPOBffwZW0jPfOg==,type:comment] +# +#ENC[AES256_GCM,data:3JOwvqkN0IsF4ogdR6SK9xZKCs7I,iv:uB3qIR9r2AZ7pewPzV+5anulFHnnmUmlBhfhUdM8A2U=,tag:w4g3k9bBcqO6o0pGfCRjuA==,type:comment] +#ENC[AES256_GCM,data:mMQ0VWb11cOPA5QgdUqNGgeeLyzbFRKU7YOhFHmUx7Zb3NmePRXZ2gT6Y9vuieiMxCL/GeNXeg==,iv:URCI0VXAlcT5QI01L4yBB+P3+VT7lEfbmQiMQhVTUYk=,tag:CiyVNUzFZBmydrK9I861Og==,type:comment] +#ENC[AES256_GCM,data:AX/p8bnjaddZFwR3hkvZNsm8op3nx6b8jnWEj6mNvPryq/MGpoS9SkNvlyACQ5KYMIYG6QtltG9buv4n5CVJzyYX,iv:YKdhnl+YtvvOFjpNXp3Ap3mnDPaDa0YoWupviRwPjwE=,tag:etGle/aGv7RemlmUeKEkCg==,type:comment] +#ENC[AES256_GCM,data:Rs33VKbpeUCDrUnblHfBXiVjZa7tlxFV+QWiMYMv/uLzmXBS7NCpU7fIgNKG,iv:ZrZug41B6AysyVylQ6T6Z+Txc2nBUvblX8pRP2r12iQ=,tag:SvrNa0N0mprMq/Wqf77tZg==,type:comment] +#ENC[AES256_GCM,data:sBzMx0RT9FAdtE4pEyWUkNX/nKDgHoEHk4IITSKpAl0/3i2uWgWs9NO4Sp1USAQaTtIa+Os=,iv:Zqj2GAtfw3UsIDgrTZ8erug+KgjP2ckNcas7NaZei+E=,tag:6xRTvzspIVLVhQJMQinPOQ==,type:comment] +#ENC[AES256_GCM,data:bD+GzYKonJecM6Pym1powOYISWJDPIEVT8V1Hu8AM1U/wCdmq70TNq6DRu1FDSG+,iv:a/p1nM+rkie+mZ7E9A1Zg3qQloGTdtRWSIUJPiFNk24=,tag:ymyVypKvIUWmcZeHc5BQFQ==,type:comment] +apiVersion: ENC[AES256_GCM,data:4vs=,iv:JAbEScwusLVWdsz4AuuYsBQDuusynhsbstrEyihrZ/k=,tag:b1IY2bxT6TL7djcFCiyMlg==,type:str] +kind: ENC[AES256_GCM,data:1+fCn5DR,iv:HvI/Id59qQC//Uh3Q0P3oJo8ZSWZnhTXv3LTox3BuEU=,tag:i9aPbsqch9YKekzbf5G4cg==,type:str] +metadata: + name: ENC[AES256_GCM,data:4528BwgvzAtwiHyK,iv:/vz1jcRbLJmAoqDvgTmDbxvE5gYxpInSEGBP7IUFvlI=,tag:yZPEJNrHht9IVrlzsAUqFA==,type:str] + namespace: ENC[AES256_GCM,data:FZMvrE3BV3iD3As59A==,iv:PfOFjLpZspP6JeTdNwa1IwjRl4JE1XKeAOab+8S8imk=,tag:yg3bWjkPngilwirv1CscDw==,type:str] +type: ENC[AES256_GCM,data:QnoC8qce,iv:8paBbJZtmEbKSKlkwhy+3lLiRLoy1I+lB0xMBPdcNMg=,tag:p4/aUaSZHBzeU1CRqJ5rLw==,type:str] +stringData: + username: ENC[AES256_GCM,data:ANiWzQM=,iv:I3LrmBV5HEtjO+aysXuZEOmbvhyOfuOJCqZHx1ykWyU=,tag:zEV4gu2125L0bHGUkgGdNQ==,type:str] + password: ENC[AES256_GCM,data:E5OW/ydSmskcnR8UYZsi9SZM5kpbiAE=,iv:NTgtzAK8P0jeczb8Egrrkmlf1Ejulabxh2/rLWzwNTk=,tag:kF2luernkCvnNl8o3EEvfA==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3b3VUNi9oRFB3ZjFyeVQr + a2dTR3AvWnFDNi9YZ0QvaVNLU3dUSnlEMVdZCklhZjAzNURsZDBNWEI1MzZ1LzFl + cUNhUWJUcVhNSjZqSG43MWdudStaRUkKLS0tIEpuc2kwR2hpUzRiSXZyb1I0V3lE + YWJPcHFZU1lBOFFrQ3ErdzF4RzRqYzgK7q0N+ZcF/plIHR7HeRTF2qRmE4Za+eO+ + mMCo01fxd3ybf8gXnC9lrYmIK4oCkS8cg/B8mfIIBqts7XDzHiCaGA== + -----END AGE ENCRYPTED FILE----- + recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla + lastmodified: "2026-09-10T01:34:10Z" + mac: ENC[AES256_GCM,data:SURFYfHkesBFpdzeoMSdASSduGefetZ9sPY+0KgeUP4lMRX70kH6vnZdt5UyXe4hMgRCDA38sF+UgITW1pxhngqp8tL8QK5GBj+wTX9/zGmbtYl0zPvjvvXLx1vXGvUtfcEyxHW90EfDWae3JxQHCNlUcEH61hNj1ccZX+ceufM=,iv:mCQp3110nD3tg1eaf6P4e9XiMPbO+bhchC0zZ5+vqX8=,tag:vWGl3iaEi3KEoW87JBl3/g==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.2 +--- +apiVersion: ENC[AES256_GCM,data:too=,iv:DFFOApoqaZDGmJe7NcGBrvE69kp85YpLwJUsImKSxmk=,tag:sKWVbV1U3oI656Sb2+iWew==,type:str] +kind: ENC[AES256_GCM,data:Lp1+y5tL,iv:pHbNnGCX6qsAIMSjkdHNyuHZct63jcDatnaek6Z5rjg=,tag:k/0Y7noIi/nAAVLpCv9GTw==,type:str] +metadata: + name: ENC[AES256_GCM,data:QHAhhTHIxeyBXCfE2g==,iv:Q/2HuRPlbEC5AMONhi6POcCDdCJv3vHWMvmm6ZXGrr0=,tag:B+3Jhut9I7NzfpGtF969dQ==,type:str] + namespace: ENC[AES256_GCM,data:VnXAy9fR8ixXanheUw==,iv:Uvkcm7zqgLCWlzcx9fO2/ccytzM7MlD0Zl1X8WNaz7Q=,tag:JsWelai4T+aNg1UabZPMTg==,type:str] +type: ENC[AES256_GCM,data:I3UeAwER,iv:mtIMz/kuzbHx0HMSPVaqzHMLvhCGzvkdNLRfr7xNKBM=,tag:QCgh1anTe7oN2j3kkUM2/g==,type:str] +stringData: + #ENC[AES256_GCM,data:1XpJNypHreS+gUaRgEZe+CaJykvp3JJgI6Q=,iv:g/loGxxl6hvVvODmqwtip6om92df0tWVVg/msZvDOrg=,tag:ATCy0nG4PpGNoPQDRkXRcA==,type:comment] + #ENC[AES256_GCM,data:EmitK30ZlOZz7MruQ/ItDim2LjU3uUZlOQhfr2KO65t2lcAw3xcGZAdaQpQfyZ0RDUQucUgnl+g8ckhH6rZ7+/N9,iv:4655+CO98ak8cG/dZI0H2RCykLS29AWAZ4LjrH3Tg2M=,tag:eqhcnuY6rbyy98ABZrtPrw==,type:comment] + #ENC[AES256_GCM,data:iBIgf2Ru/cwE8D1L7uTdCmcii3lJxny1JE84jntuhROkCK1bqZU2cyf8eC0rq1zONiWwPuTokqFv18v+pdmbzQ==,iv:cnOCtS1qGy/w7ZkG2zRjqpgSeLU0FgvuV4tuBJ2SQ5U=,tag:3uxrBDiH/zMpLvFxZVfn1Q==,type:comment] + app-token: ENC[AES256_GCM,data:NNpIl+DOZ9hjP5TY0bmHWRfSC88GTy1O,iv:NDnz4i/rYbbUxpxUSHFyXc31iI4PQSasjEcHV3u9E70=,tag:8mBEOwhXxVPmoX85fAVKdg==,type:str] + client-token: ENC[AES256_GCM,data:nMSuoalWILpFijzVcB7sfA+d4EDb7HzL,iv:cgqkBnBUDqYMBXpQVCMzIPvpVvMu3BKl1CDqtY04xrk=,tag:Eql+ZAqPdvzm2M19HOoOHA==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3b3VUNi9oRFB3ZjFyeVQr + a2dTR3AvWnFDNi9YZ0QvaVNLU3dUSnlEMVdZCklhZjAzNURsZDBNWEI1MzZ1LzFl + cUNhUWJUcVhNSjZqSG43MWdudStaRUkKLS0tIEpuc2kwR2hpUzRiSXZyb1I0V3lE + YWJPcHFZU1lBOFFrQ3ErdzF4RzRqYzgK7q0N+ZcF/plIHR7HeRTF2qRmE4Za+eO+ + mMCo01fxd3ybf8gXnC9lrYmIK4oCkS8cg/B8mfIIBqts7XDzHiCaGA== + -----END AGE ENCRYPTED FILE----- + recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla + lastmodified: "2026-09-10T01:34:10Z" + mac: ENC[AES256_GCM,data:SURFYfHkesBFpdzeoMSdASSduGefetZ9sPY+0KgeUP4lMRX70kH6vnZdt5UyXe4hMgRCDA38sF+UgITW1pxhngqp8tL8QK5GBj+wTX9/zGmbtYl0zPvjvvXLx1vXGvUtfcEyxHW90EfDWae3JxQHCNlUcEH61hNj1ccZX+ceufM=,iv:mCQp3110nD3tg1eaf6P4e9XiMPbO+bhchC0zZ5+vqX8=,tag:vWGl3iaEi3KEoW87JBl3/g==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.2 +--- +apiVersion: ENC[AES256_GCM,data:Ub4=,iv:t0/4neL1XNR9sc2a0rZ7c4fOnZjRw0ACOY2Pp2A61IY=,tag:X8W/L0wfQLw4JF5aQ1dteg==,type:str] +kind: ENC[AES256_GCM,data:54r8Je+L,iv:feX1TVQNZ7VrYIFiYKvopB0hYEIl9iw0ly2A6u/j+1Y=,tag:gD7iCoOCxIUT5uj1bGfikQ==,type:str] +metadata: + name: ENC[AES256_GCM,data:ub9FC+uwioKPMXg=,iv:1YbPJQle7t60Onnh77R8ffc8M87DNU1cmBQbVA72B28=,tag:3DsCpXMHcTIYN3uBh6Ztug==,type:str] + namespace: ENC[AES256_GCM,data:dLIZlOoIo9YF2VyHew==,iv:YxQFWoMhVMG3jUJqmAtAUwzRr7MkSRjiWlq92rnymIE=,tag:iw5AI7kKlp+wKcgg2GGMxw==,type:str] +type: ENC[AES256_GCM,data:4+CN/8f0,iv:Gmnh4TP0oCAiJ2VTrBT/Q1PWkPZks6XnzWu3io32hvQ=,tag:m5SNbovIU7pjwGACc4Q50g==,type:str] +stringData: + #ENC[AES256_GCM,data:bGrL1ia7o4ADA4J7soyOLCMVq3Y7kUU0BHhdz9ID45/q,iv:fwMHTJ68cYgBrzeAQ/CNdpfH14bAv7Exu7H6/QUk1ok=,tag:Lwc+FnbCd48oErLp5qEmcw==,type:comment] + #ENC[AES256_GCM,data:bQdj5OgHL60gffBB5DZPOL6+RFWxrrBxa8lPkXbYR7viOf0v0xb9iBqjS/o5kIOjjcdeJibgGhQ=,iv:DJQBppj4w3KqFOC1MTEYwoc1LT0Qz17fKb611hgH+AE=,tag:D+l7BE+FEgxZFTIDLpKsiA==,type:comment] + host: ENC[AES256_GCM,data:kaPQN225/tgQLLmejLQ=,iv:Xn69DoAa7Zop6EjZwIRgO/zVPdxvARgKkoacIE4Fcwc=,tag:hlCapggLwTsXr4DeejAYkQ==,type:str] + port: ENC[AES256_GCM,data:vkBH,iv:uCA8fhnMZDg/qiSQYRYmhuc7dnteoavcuJxfSCE8GU0=,tag:ujosmW6GjrEa30mBCEFXgw==,type:str] + from: ENC[AES256_GCM,data:kiWkZ7zmIT6gOHCH6hHI8fiJcQ==,iv:tqpYbLohLYbQ6kotOC5vDw6cZEFvNK1UEiKIa+nFzuQ=,tag:POZAxMpiGItr1lRY1yhW6w==,type:str] + user: ENC[AES256_GCM,data:Vdew4lqPsOD2,iv:1zJE/hEDbchie4crugcjCfvACklXoeSzqopXbPulD/E=,tag:QdJvWQRi9Tc7pgMBigKsBg==,type:str] + password: ENC[AES256_GCM,data:JcjnnXFkAXmI,iv:VgpYDrTn6o3l7tAk0lGMB2rpu+kB1IuAFu2VZMyJkX4=,tag:QR+En9nv/30yyt09GTcyqg==,type:str] + notify-email: ENC[AES256_GCM,data:zMvA9KXy6rK2HFRrjAmZd5c/ay8=,iv:BtCXUsHneVc1QzvPKRwBjmbYc39I3Xy/zFoMg4l22Ks=,tag:NTZKDoPaLARJoTIX9u6oGQ==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3b3VUNi9oRFB3ZjFyeVQr + a2dTR3AvWnFDNi9YZ0QvaVNLU3dUSnlEMVdZCklhZjAzNURsZDBNWEI1MzZ1LzFl + cUNhUWJUcVhNSjZqSG43MWdudStaRUkKLS0tIEpuc2kwR2hpUzRiSXZyb1I0V3lE + YWJPcHFZU1lBOFFrQ3ErdzF4RzRqYzgK7q0N+ZcF/plIHR7HeRTF2qRmE4Za+eO+ + mMCo01fxd3ybf8gXnC9lrYmIK4oCkS8cg/B8mfIIBqts7XDzHiCaGA== + -----END AGE ENCRYPTED FILE----- + recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla + lastmodified: "2026-09-10T01:34:10Z" + mac: ENC[AES256_GCM,data:SURFYfHkesBFpdzeoMSdASSduGefetZ9sPY+0KgeUP4lMRX70kH6vnZdt5UyXe4hMgRCDA38sF+UgITW1pxhngqp8tL8QK5GBj+wTX9/zGmbtYl0zPvjvvXLx1vXGvUtfcEyxHW90EfDWae3JxQHCNlUcEH61hNj1ccZX+ceufM=,iv:mCQp3110nD3tg1eaf6P4e9XiMPbO+bhchC0zZ5+vqX8=,tag:vWGl3iaEi3KEoW87JBl3/g==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.2 diff --git a/k8s/apps/gotify/service.yaml b/k8s/apps/gotify/service.yaml new file mode 100644 index 0000000..1d998b0 --- /dev/null +++ b/k8s/apps/gotify/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: gotify + namespace: notifications + labels: + app: gotify +spec: + selector: + app: gotify + ports: + - port: 80 + targetPort: 80 + protocol: TCP diff --git a/k8s/argocd/apps/59-gotify.yaml b/k8s/argocd/apps/59-gotify.yaml new file mode 100644 index 0000000..abc1f25 --- /dev/null +++ b/k8s/argocd/apps/59-gotify.yaml @@ -0,0 +1,32 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: gotify + namespace: argocd + labels: + app.kubernetes.io/name: gotify + app.kubernetes.io/component: notifications + annotations: + argocd.argoproj.io/sync-wave: "8" +spec: + project: homelab + revisionHistoryLimit: 3 + source: + repoURL: https://forgejo.riotpiao.com/rock/homelab.git + targetRevision: main + path: k8s/apps/gotify + destination: + server: https://kubernetes.default.svc + namespace: notifications + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m