diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2f3ecc14032cdefbbba42df887da68ba32e65db3..2727ec80e4a39ad70d30053fb75723c8f90d9f49 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,45 @@
+stages:
+  - build
+  - deploy
+
+variables:
+  BBBATSCALE_SUPPORT_NOTIFY_IMAGE: $CI_REGISTRY_IMAGE/bbbatscale-support-notify
+
+include:
+  - project: its/infra-utils
+    file: gitlab/ci/templates/get-openshift-kubeconfig.gitlab-ci.yml
+  - project: its/infra-utils
+    file: gitlab/ci/templates/install-helmfile.gitlab-ci.yml
+
 build:
+  stage: build
+  interruptible: true
   image:
     name: gcr.io/kaniko-project/executor:debug
-    entrypoint: [""]
+    entrypoint: [ "" ]
   script:
-    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
-    - /kaniko/executor --context "$CI_PROJECT_DIR" --dockerfile "$CI_PROJECT_DIR"/Dockerfile --destination "$CI_REGISTRY_IMAGE:latest"
+    - mkdir -p /kaniko/.docker
+    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" >/kaniko/.docker/config.json
+    - /kaniko/executor
+      --context "$CI_PROJECT_DIR"
+      --dockerfile "$CI_PROJECT_DIR/Dockerfile"
+      --destination "$BBBATSCALE_SUPPORT_NOTIFY_IMAGE:$CI_COMMIT_SHA"
+  needs: [ ]
+
+deploy:
+  stage: deploy
+  interruptible: false
+  image:
+    name: alpine
+  rules:
+    - when: manual
+  variables:
+    NAMESPACE: rooms
+    SERVICE_ACCOUNT: bbbatscale-support-notify-admin
+  before_script:
+    - !reference [ .get openshift kubeconfig - alpine, before_script ]
+    - !reference [ .install helmfile - alpine, before_script ]
+  script:
+    - helmfile sync --file "$CI_PROJECT_DIR/helmfile.yaml.gotmpl"
+  needs:
+    - job: build
diff --git a/.sops.yaml b/.sops.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..682abad51cf1d15bb0c77df95334fe92e19ca0a4
--- /dev/null
+++ b/.sops.yaml
@@ -0,0 +1,11 @@
+creation_rules:
+  - # Using key_groups since otherwise all keys must be
+    # in one string which does not allow comments.
+    key_groups:
+      - age:
+          # GitLab
+          - age17mvdf0vkccylt7lqgjgsm2p6y9wc4gfmnwupn6waet9mq26av9zqkhrw6v
+          # Jakob Probst - Dell XPS 13
+          - age1yhpzsmm3lx2hukfkcv84ww3aky3hxm6g9fksfl53wzsavlps3awsz9te80
+          # Lars Seipel
+          - age1nx0vlfy6w5qvz0nrl3hhdmyx77cjqy757cf9p5jglfvfp7xll95scwkl7f
diff --git a/charts/bbbatscale-support-notify/.helmignore b/charts/bbbatscale-support-notify/.helmignore
deleted file mode 100644
index 0e8a0eb36f4ca2c939201c0d54b5d82a1ea34778..0000000000000000000000000000000000000000
--- a/charts/bbbatscale-support-notify/.helmignore
+++ /dev/null
@@ -1,23 +0,0 @@
-# Patterns to ignore when building packages.
-# This supports shell glob matching, relative path matching, and
-# negation (prefixed with !). Only one pattern per line.
-.DS_Store
-# Common VCS dirs
-.git/
-.gitignore
-.bzr/
-.bzrignore
-.hg/
-.hgignore
-.svn/
-# Common backup files
-*.swp
-*.bak
-*.tmp
-*.orig
-*~
-# Various IDEs
-.project
-.idea/
-*.tmproj
-.vscode/
diff --git a/charts/bbbatscale-support-notify/Chart.yaml b/charts/bbbatscale-support-notify/Chart.yaml
deleted file mode 100644
index 6a413a4b476d658db48b03102991acd07781c802..0000000000000000000000000000000000000000
--- a/charts/bbbatscale-support-notify/Chart.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-apiVersion: v2
-name: bbbatscale-support-notify
-description: A Helm chart for Kubernetes
-
-# A chart can be either an 'application' or a 'library' chart.
-#
-# Application charts are a collection of templates that can be packaged into versioned archives
-# to be deployed.
-#
-# Library charts provide useful utilities or functions for the chart developer. They're included as
-# a dependency of application charts to inject those utilities and functions into the rendering
-# pipeline. Library charts do not define any templates and therefore cannot be deployed.
-type: application
-
-# This is the chart version. This version number should be incremented each time you make changes
-# to the chart and its templates, including the app version.
-# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 0.1.0
-
-# This is the version number of the application being deployed. This version number should be
-# incremented each time you make changes to the application. Versions are not expected to
-# follow Semantic Versioning. They should reflect the version the application is using.
-appVersion: latest
diff --git a/charts/bbbatscale-support-notify/templates/hpa.yaml b/charts/bbbatscale-support-notify/templates/hpa.yaml
deleted file mode 100644
index c606e511c4718addb3f44603a0bede29b5fe762f..0000000000000000000000000000000000000000
--- a/charts/bbbatscale-support-notify/templates/hpa.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-{{- if .Values.autoscaling.enabled }}
-apiVersion: autoscaling/v2beta1
-kind: HorizontalPodAutoscaler
-metadata:
-  name: {{ include "bbbatscale-support-notify.fullname" . }}
-  labels:
-    {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
-spec:
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: {{ include "bbbatscale-support-notify.fullname" . }}
-  minReplicas: {{ .Values.autoscaling.minReplicas }}
-  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
-  metrics:
-    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
-    - type: Resource
-      resource:
-        name: cpu
-        targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
-    {{- end }}
-    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
-    - type: Resource
-      resource:
-        name: memory
-        targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
-    {{- end }}
-{{- end }}
diff --git a/charts/bbbatscale-support-notify/templates/imagestream.yaml b/charts/bbbatscale-support-notify/templates/imagestream.yaml
deleted file mode 100644
index 5e4c2122646e7befcf3a4724d66f6e300c7c5d16..0000000000000000000000000000000000000000
--- a/charts/bbbatscale-support-notify/templates/imagestream.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-{{- if .Values.imageStream.enabled -}}
-apiVersion: image.openshift.io/v1
-kind: ImageStream
-metadata:
-  name: {{ include "bbbatscale-support-notify.fullname" . }}
-  labels:
-    {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
-spec:
-  tags:
-    - name: latest
-      from:
-        kind: DockerImage
-        name: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
-      importPolicy:
-        scheduled: {{ .Values.imageStream.periodicImports }}
-{{- end -}}
diff --git a/charts/bbbatscale-support-notify/templates/secret.yaml b/charts/bbbatscale-support-notify/templates/secret.yaml
deleted file mode 100644
index c9d76baa69e68ddc59d8bcfa83f900d165005191..0000000000000000000000000000000000000000
--- a/charts/bbbatscale-support-notify/templates/secret.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: v1
-kind: Secret
-metadata:
-  name: {{ include "bbbatscale-support-notify.fullname" . }}
-  labels:
-    {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
-data:
-  MATRIX_HOMESERVER: {{ .Values.matrixHomeserver | b64enc | quote }}
-  MATRIX_ROOMID: {{ .Values.matrixRoomId | b64enc | quote }}
-  MATRIX_TOKEN: {{ .Values.matrixToken | b64enc | quote }}
-  MATRIX_USERID: {{ .Values.matrixUserId | b64enc | quote }}
-  NOTIFY_HOOKSECRET: {{ .Values.hookSecret | b64enc | quote }}
diff --git a/charts/bbbatscale-support-notify/values.yaml b/charts/bbbatscale-support-notify/values.yaml
deleted file mode 100644
index 51a6d0bf361877dfcf30d336d5bbeacf60e9a18a..0000000000000000000000000000000000000000
--- a/charts/bbbatscale-support-notify/values.yaml
+++ /dev/null
@@ -1,83 +0,0 @@
-# Default values for bbbatscale-support-notify.
-# This is a YAML-formatted file.
-# Declare variables to be passed into your templates.
-
-replicaCount: 1
-
-image:
-  repository: registry.code.fbi.h-da.de/its/bbbatscale-support-notify
-  pullPolicy: IfNotPresent
-  # Overrides the image tag whose default is the chart appVersion.
-  tag: ""
-
-# Create an ImageStream that can be updated to trigger deployment of new
-# versions.
-imageStream:
-  enabled: true
-  periodicImports: true
-
-imagePullConfig: {}
-# auths:
-#   registry.example.org:
-#     username: example-username
-#     password: secret-password
-#     email: optional-email@example.org
-
-matrixHomeserver: 'https://matrix.fbi.h-da.de'
-matrixUserId: '@rooms-support-notify:matrix.fbi.h-da.de'
-matrixRoomId: '!bHhAvflNDQVScrZdFv:matrix.fbi.h-da.de'
-# matrixToken: SECRET
-
-nameOverride: ""
-fullnameOverride: ""
-
-serviceAccount:
-  # Specifies whether a service account should be created
-  create: true
-  # Annotations to add to the service account
-  annotations: {}
-  # The name of the service account to use.
-  # If not set and create is true, a name is generated using the fullname template
-  name: ""
-
-podAnnotations: {}
-
-podSecurityContext: {}
-  # fsGroup: 2000
-
-securityContext: {}
-  # capabilities:
-  #   drop:
-  #   - ALL
-  # readOnlyRootFilesystem: true
-  # runAsNonRoot: true
-  # runAsUser: 1000
-
-service:
-  type: ClusterIP
-  port: 80
-
-resources: {}
-  # We usually recommend not to specify default resources and to leave this as a conscious
-  # choice for the user. This also increases chances charts run on environments with little
-  # resources, such as Minikube. If you do want to specify resources, uncomment the following
-  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
-  # limits:
-  #   cpu: 100m
-  #   memory: 128Mi
-  # requests:
-  #   cpu: 100m
-  #   memory: 128Mi
-
-autoscaling:
-  enabled: false
-  minReplicas: 1
-  maxReplicas: 100
-  targetCPUUtilizationPercentage: 80
-  # targetMemoryUtilizationPercentage: 80
-
-nodeSelector: {}
-
-tolerations: []
-
-affinity: {}
diff --git a/helm/bbbatscale-support-notify/.gitignore b/helm/bbbatscale-support-notify/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..45d63c4c5bf94861afd2c40b21bcb821eda46fd1
--- /dev/null
+++ b/helm/bbbatscale-support-notify/.gitignore
@@ -0,0 +1 @@
+/Chart.lock
diff --git a/helm/bbbatscale-support-notify/Chart.yaml b/helm/bbbatscale-support-notify/Chart.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..2fc69fd56976f29b426a13d27a329d9b69df8520
--- /dev/null
+++ b/helm/bbbatscale-support-notify/Chart.yaml
@@ -0,0 +1,11 @@
+apiVersion: v2
+name: bbbatscale-support-notify
+description: ""
+
+type: application
+version: 0.0.0
+
+dependencies:
+  - name: its-infra-utils
+    version: 0.0.0
+    repository: git+https://code.fbi.h-da.de/its/infra-utils.git@charts?ref=main
diff --git a/charts/bbbatscale-support-notify/templates/_helpers.tpl b/helm/bbbatscale-support-notify/templates/_helpers.tpl
similarity index 82%
rename from charts/bbbatscale-support-notify/templates/_helpers.tpl
rename to helm/bbbatscale-support-notify/templates/_helpers.tpl
index 9aef01043bc2870eb80aa2f9c2972f51c6c4f597..94958947ecccc12c153ab2ba07bf76c16dccc829 100644
--- a/charts/bbbatscale-support-notify/templates/_helpers.tpl
+++ b/helm/bbbatscale-support-notify/templates/_helpers.tpl
@@ -61,14 +61,10 @@ Create the name of the service account to use
 {{- end }}
 {{- end }}
 
-{{- define "bbbatscale-support-notify.dockerConfig" -}}
-{{- $mappedAuths := dict }}
-{{- $auths := .auths }}
-
-{{- range (keys $auths) }}
-{{- $auth := get $auths . }}
-{{- $_ := set $mappedAuths . (dict "username" $auth.username "password" $auth.password "auth" (printf "%s:%s" $auth.username $auth.password | b64enc) "email" (get $auth "email")) }}
+{{/*
+Create the name of the image pull secret to use
+*/}}
+{{- define "bbbatscale-support-notify.imagePullSecretName" -}}
+{{- $suffix := "-image-pull-secret" }}
+{{- printf "%s%s" (include "bbbatscale-support-notify.fullname" . | trunc (sub 63 (len $suffix) | int) | trimSuffix "-") $suffix }}
 {{- end }}
-
-{{- dict "auths" $mappedAuths | mustToJson }}
-{{- end -}}
diff --git a/charts/bbbatscale-support-notify/templates/deployment.yaml b/helm/bbbatscale-support-notify/templates/deployment.yaml
similarity index 70%
rename from charts/bbbatscale-support-notify/templates/deployment.yaml
rename to helm/bbbatscale-support-notify/templates/deployment.yaml
index e1fff945e3da56f512996e02a73cb946eb9bdad8..2317562b0139f76daeffa7cd798abfd34c7433f1 100644
--- a/charts/bbbatscale-support-notify/templates/deployment.yaml
+++ b/helm/bbbatscale-support-notify/templates/deployment.yaml
@@ -1,17 +1,11 @@
 apiVersion: apps/v1
 kind: Deployment
 metadata:
-  name: {{ include "bbbatscale-support-notify.fullname" . }}
-  annotations:
-    {{- if .Values.imageStream.enabled }}
-    image.openshift.io/triggers: '[{"from":{"kind":"ImageStreamTag","name":"{{ include "bbbatscale-support-notify.fullname" . }}:latest"},"fieldPath":"spec.template.spec.containers[?(@.name==\"{{ .Chart.Name }}\")].image"}]'
-    {{- end }}
+  name: {{ include "bbbatscale-support-notify.fullname" . | quote }}
   labels:
     {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
 spec:
-  {{- if not .Values.autoscaling.enabled }}
   replicas: {{ .Values.replicaCount }}
-  {{- end }}
   selector:
     matchLabels:
       {{- include "bbbatscale-support-notify.selectorLabels" . | nindent 6 }}
@@ -24,22 +18,27 @@ spec:
       labels:
         {{- include "bbbatscale-support-notify.selectorLabels" . | nindent 8 }}
     spec:
-      {{- with .Values.imagePullSecrets }}
+      {{- if or .Values.imagePullConfig .Values.imagePullSecrets }}
       imagePullSecrets:
+      {{- if .Values.imagePullConfig }}
+        - name: {{ include "bbbatscale-support-notify.imagePullSecretName" . | quote }}
+      {{- end }}
+      {{- with .Values.imagePullSecrets }}
         {{- toYaml . | nindent 8 }}
       {{- end }}
-      serviceAccountName: {{ include "bbbatscale-support-notify.serviceAccountName" . }}
+      {{- end }}
+      serviceAccountName: {{ include "bbbatscale-support-notify.serviceAccountName" . | quote }}
       securityContext:
         {{- toYaml .Values.podSecurityContext | nindent 8 }}
       containers:
-        - name: {{ .Chart.Name }}
+        - name: {{ quote .Chart.Name }}
           securityContext:
             {{- toYaml .Values.securityContext | nindent 12 }}
-          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
-          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          image: {{ printf "%s:%s" .Values.image.repository (.Values.image.tag | default .Chart.AppVersion) | quote }}
+          imagePullPolicy: {{ quote .Values.image.pullPolicy }}
           envFrom:
             - secretRef:
-                name: {{ include "bbbatscale-support-notify.fullname" . }}
+                name: {{ include "bbbatscale-support-notify.fullname" . | quote }}
           ports:
             - name: http
               containerPort: 8080
diff --git a/charts/bbbatscale-support-notify/templates/image-pull-secret.yaml b/helm/bbbatscale-support-notify/templates/image-pull-secret.yaml
similarity index 52%
rename from charts/bbbatscale-support-notify/templates/image-pull-secret.yaml
rename to helm/bbbatscale-support-notify/templates/image-pull-secret.yaml
index 8cfe6fa4ead4316f5728c5089078b615d485141a..c9feedde42f4bf9b2c133c70990c3b7b8f7e925c 100644
--- a/charts/bbbatscale-support-notify/templates/image-pull-secret.yaml
+++ b/helm/bbbatscale-support-notify/templates/image-pull-secret.yaml
@@ -2,10 +2,10 @@
 apiVersion: v1
 kind: Secret
 metadata:
-  name: {{ include "bbbatscale-support-notify.fullname" . }}-pullsecret
+  name: {{ include "bbbatscale-support-notify.imagePullSecretName" . | quote }}
   labels:
     {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
 type: kubernetes.io/dockerconfigjson
 data:
-  .dockerconfigjson: {{ include "bbbatscale-support-notify.dockerConfig" .Values.imagePullConfig | b64enc }}
+  .dockerconfigjson: {{ include "its-infra-utils.dockerConfig" .Values.imagePullConfig | b64enc }}
 {{- end }}
diff --git a/helm/bbbatscale-support-notify/templates/secret.yaml b/helm/bbbatscale-support-notify/templates/secret.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c7d5fbbb6d447ca2c4246068767b05ef34b34af1
--- /dev/null
+++ b/helm/bbbatscale-support-notify/templates/secret.yaml
@@ -0,0 +1,13 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ include "bbbatscale-support-notify.fullname" . | quote }}
+  labels:
+    {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
+type: Opaque
+data:
+  MATRIX_HOMESERVER: {{ b64enc .Values.matrix.homeserver }}
+  MATRIX_ROOMID: {{ b64enc .Values.matrix.roomId }}
+  MATRIX_TOKEN: {{ b64enc .Values.matrix.token }}
+  MATRIX_USERID: {{ b64enc .Values.matrix.userId }}
+  NOTIFY_HOOKSECRET: {{ b64enc .Values.hookSecret }}
diff --git a/charts/bbbatscale-support-notify/templates/serviceaccount.yaml b/helm/bbbatscale-support-notify/templates/service-account.yaml
similarity index 77%
rename from charts/bbbatscale-support-notify/templates/serviceaccount.yaml
rename to helm/bbbatscale-support-notify/templates/service-account.yaml
index 715d62a18532f1d009d67bdf3feac726edcbce35..1f3cc1e8088d98c77704a52fe7137e03451844c5 100644
--- a/charts/bbbatscale-support-notify/templates/serviceaccount.yaml
+++ b/helm/bbbatscale-support-notify/templates/service-account.yaml
@@ -2,13 +2,11 @@
 apiVersion: v1
 kind: ServiceAccount
 metadata:
-  name: {{ include "bbbatscale-support-notify.serviceAccountName" . }}
+  name: {{ include "bbbatscale-support-notify.serviceAccountName" . | quote }}
   labels:
     {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
   {{- with .Values.serviceAccount.annotations }}
   annotations:
     {{- toYaml . | nindent 4 }}
   {{- end }}
-imagePullSecrets:
-  - name: {{ include "bbbatscale-support-notify.fullname" . }}-pullsecret
 {{- end }}
diff --git a/charts/bbbatscale-support-notify/templates/service.yaml b/helm/bbbatscale-support-notify/templates/service.yaml
similarity index 61%
rename from charts/bbbatscale-support-notify/templates/service.yaml
rename to helm/bbbatscale-support-notify/templates/service.yaml
index 9a5a3ab55019b634925d1d729e9d579fbb147138..fcc2613ea046293f0e307f8f8ac096c6d4f86a87 100644
--- a/charts/bbbatscale-support-notify/templates/service.yaml
+++ b/helm/bbbatscale-support-notify/templates/service.yaml
@@ -1,15 +1,15 @@
 apiVersion: v1
 kind: Service
 metadata:
-  name: {{ include "bbbatscale-support-notify.fullname" . }}
+  name: {{ include "bbbatscale-support-notify.fullname" . | quote }}
   labels:
     {{- include "bbbatscale-support-notify.labels" . | nindent 4 }}
 spec:
-  type: {{ .Values.service.type }}
+  type: {{ quote .Values.service.type }}
+  selector:
+    {{- include "bbbatscale-support-notify.selectorLabels" . | nindent 4 }}
   ports:
-    - port: {{ .Values.service.port }}
+    - name: http
+      port: {{ .Values.service.port }}
       targetPort: http
       protocol: TCP
-      name: http
-  selector:
-    {{- include "bbbatscale-support-notify.selectorLabels" . | nindent 4 }}
diff --git a/helm/bbbatscale-support-notify/values.yaml b/helm/bbbatscale-support-notify/values.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d8272bd12c7eee0dc06ecfabd613706407706b0b
--- /dev/null
+++ b/helm/bbbatscale-support-notify/values.yaml
@@ -0,0 +1,64 @@
+replicaCount: 1
+
+nameOverride: ""
+fullnameOverride: ""
+
+image:
+  repository: ""
+  pullPolicy: IfNotPresent
+  # Overrides the image tag whose default is the chart appVersion.
+  tag: ""
+
+imagePullConfig: { }
+imagePullSecrets: [ ]
+
+matrix:
+  homeserver: ""
+  userId: ""
+  token: ""
+  roomId: ""
+
+hookSecret: ""
+
+serviceAccount:
+  # Specifies whether a service account should be created
+  create: true
+  # Annotations to add to the service account
+  annotations: { }
+  # The name of the service account to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name: ""
+
+podAnnotations: { }
+
+podSecurityContext: {
+  # fsGroup: 2000
+}
+
+securityContext: {
+  # capabilities:
+  #   drop:
+  #   - ALL
+  # readOnlyRootFilesystem: true
+  # runAsNonRoot: true
+  # runAsUser: 1000
+}
+
+service:
+  type: ClusterIP
+  port: 80
+
+resources: {
+  # limits:
+  #   cpu: 100m
+  #   memory: 128Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 128Mi
+}
+
+nodeSelector: { }
+
+tolerations: [ ]
+
+affinity: { }
diff --git a/helmfile.yaml.gotmpl b/helmfile.yaml.gotmpl
new file mode 100644
index 0000000000000000000000000000000000000000..4656e10eccde0f2884bc9fccbdb17dda9de5ef3e
--- /dev/null
+++ b/helmfile.yaml.gotmpl
@@ -0,0 +1,24 @@
+repositories:
+  - name: its-infra-utils
+    url: git+https://code.fbi.h-da.de/its/infra-utils.git@charts?ref=main
+
+missingFileHandler: Error
+
+helmDefaults:
+  wait: true
+
+releases:
+  - name: bbbatscale-support-notify
+    createNamespace: false
+    chart: helm/bbbatscale-support-notify
+    values:
+      - image:
+          repository: {{ requiredEnv "BBBATSCALE_SUPPORT_NOTIFY_IMAGE" | quote }}
+          tag: {{ requiredEnv "CI_COMMIT_SHA" | quote }}
+
+        matrix:
+          homeserver: https://matrix.fbi.h-da.de/
+          userId: @rooms-support-notify:matrix.fbi.h-da.de
+          roomId: !bHhAvflNDQVScrZdFv:matrix.fbi.h-da.de
+    secrets:
+      - secrets.yaml
diff --git a/secrets.yaml b/secrets.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3178ce5642ac1c3d37d4ac5afa4ba0f227cdab5d
--- /dev/null
+++ b/secrets.yaml
@@ -0,0 +1,48 @@
+imagePullConfig:
+    code.fbi.h-da.de:
+        username: ENC[AES256_GCM,data:QJmIR9TLWr6/,iv:nrLQXGrKY97fhgSNCTLBNR5Ep22YCEYA65IuRMGJcR0=,tag:6NHOoEtw6ikbS+ft8MgzyA==,type:str]
+        password: ENC[AES256_GCM,data:MC4HaBCJzNNdHpQsUJjX2Ji7Q98=,iv:QRprBZzw2AJ+fuPdsZiTHJuDZqPb0ei78nkPrZ3+VKg=,tag:WUc04jZ2nYJaOnmFSipPWQ==,type:str]
+    registry.code.fbi.h-da.de:
+        username: ENC[AES256_GCM,data:Ikd4m5eZyYc4,iv:psZ3CZqNJ5B5ADCQurFK+7D7VLqPutfA7ChvQypTkY4=,tag:vSUktrMr5FrD2uEufOSrwg==,type:str]
+        password: ENC[AES256_GCM,data:rXMchxSqTtYM0eBXo4T+lX0501s=,iv:hUw4ujH84c5YuaCDlRf4Rf+rMF+02roUcpP306uObSc=,tag:b4JvJC00Y1w4YEkGzE7DJA==,type:str]
+matrix:
+    token: ENC[AES256_GCM,data:U3t8gzV6QC9tSLU48svYXq09JeLQSEzIBAVfj1yzfVutNgOchBBgtWIOPnxVdWN92R47o3Vfgcl5A09SdX+HOg6ubtfP/GMnUeDwTDROJ/H4ZHEXPCCQ3cZQev1sYhcenV9H87dJiv0lm4B0j4Ypn2c2anj1e568/JIuS/IR4KR9IeKAtRJiqraRhYbxDBCxc8WJsJmCRfEVygrQ9gThh24TBTF0R192LxMvKmCq7k2t174D0YSZIyLoRHMt8MRkMGrj1iHVsyjAVbJVWuuFgEsfHdqRuHXgW+yEzElQ9k/515JEV7k8jCKzvQvIL/9rgf2dWh9ElIUfJFobgoSiVDkkrJIOCxOdPs7BXbiIJc2JnorJZywWzbZG+JMmLmrZcvFGmVWV08/0ILyL+0cGbA==,iv:eOXY9hJWLB0Y3xHWQG8Ke+e50Prg5zNiBdnaMmcEvWc=,tag:JQNGfMurKK6apDy5/FpiQA==,type:str]
+hookSecret: ENC[AES256_GCM,data:1P20PYqxERUpWdNJjKoVLQajJcD6ly3F/gEySxpTfJqIeLZq7f9YWDQPVRxfnUzf,iv:xDjH4YOO6Ny+r5rg/xwJ7MkoRWeu8lb6Ni0Ypv0BU2Y=,tag:irT+Qk3OEsm6opfF8jydAg==,type:str]
+sops:
+    kms: []
+    gcp_kms: []
+    azure_kv: []
+    hc_vault: []
+    age:
+        - recipient: age17mvdf0vkccylt7lqgjgsm2p6y9wc4gfmnwupn6waet9mq26av9zqkhrw6v
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1UDVleTY4Mkx1WGNUY0Fi
+            ajZycGZmTFNrSEVDb05YcmkwdmIvTUd4NUI4CmNWMzVJaGQrekd5U00zT2dVa28x
+            NjNQaDlPeEc2VzVaOGZWNnV1UEVEeHcKLS0tIExUUUI2OXhBYmU2dVJ3VGoyb2tw
+            WHRNQ3N4QzNGWi9HTWlkL2lZY01XZk0KyELrIb+ooI4FGQ+oq8iPETeGzi+TIhLV
+            uxqO1kN58WhPuznCBhs4PB8o9C0qb8hjjcwkK9rmmfR+aNaeBp3xag==
+            -----END AGE ENCRYPTED FILE-----
+        - recipient: age1yhpzsmm3lx2hukfkcv84ww3aky3hxm6g9fksfl53wzsavlps3awsz9te80
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFVHNxZ2h1Ym9UQS9ocTJv
+            QnRPQ0xWOHZKVmhDeURJV1pkdTM2dklOckVzCmE1K1NncHJScGpiUHFqeGx6bnZF
+            cmswNytSZHhRUkhicyszeDAvU2ljc0kKLS0tIGtzNGVSUUdrZUFkSVZiaE4zaFln
+            RHJQRThSUlgvbk9OdkxxTmJ5dGJ0am8KW4GDXIMALtgmpf0km70lC8EP6LefbGex
+            0vhCAdxBPUGownooAtoZdpXcJQwWOn3sOGPm0p5tWolhrVKmlPw4lg==
+            -----END AGE ENCRYPTED FILE-----
+        - recipient: age1nx0vlfy6w5qvz0nrl3hhdmyx77cjqy757cf9p5jglfvfp7xll95scwkl7f
+          enc: |
+            -----BEGIN AGE ENCRYPTED FILE-----
+            YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGcHp2OVF2cklpcjVIK1k3
+            ZDQxa1Z0cWl2N2lMQ2xQdHV5SEtOVjEzMVVvCmZJVmtUd1VnQ1V5Y0U2RFpQU1dC
+            Zk5OSTZMU3R6UDQ1V2kwbUUySzd2UmcKLS0tIHcrZCtWdDJLOGRZWTM3ajlsN2gw
+            UVV4RmZtTmlDdnBnZTNmUTQydktlN0UKahE7umgVd+isyr2sEFS6EhZ4Danxo6Sv
+            EfM2hLuBtNzjPiLO0iZs2x5b49AvA6cs14VhtdLJHtp8HslBho7WgA==
+            -----END AGE ENCRYPTED FILE-----
+    lastmodified: "2023-07-29T00:32:55Z"
+    mac: ENC[AES256_GCM,data:L3bb84FZPDRUZBHmjiQynTgcjhJq0Bon6lEjjUXWYrXiPDAnf5cFC3jFewSkp0tr9upIuytpcSFEYFiWuV5DA2md90V+m9TOLfIljDTrDUeZOfthL5pR8yTRsN+QVvWBPcgfi7NeqzXKT9A2Z2sfr/2X8LuEuSU7oGyfmyWI5Qc=,iv:Tx0LVrspKEPXWEbksrQkQNpsxIZnYoCYSH1PbAYv9/w=,tag:wV/Xo70VmAHtFVHn4zlLBQ==,type:str]
+    pgp: []
+    unencrypted_suffix: _unencrypted
+    version: 3.7.3
diff --git a/webhooks/webhooks.go b/webhooks/webhooks.go
index b0c2d34638985600e4226f0e3378a5b895b65e27..f7e046285f51fef38f69b4796daed55ee1af1040 100644
--- a/webhooks/webhooks.go
+++ b/webhooks/webhooks.go
@@ -8,7 +8,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"strconv"
 	"strings"
@@ -26,7 +26,7 @@ type Message struct {
 	Payload   map[string]interface{} `json:"payload"`
 }
 
-// Receive returns an http.Handler that receives webhooks from BBBAtScale and
+// Receive returns a http.Handler that receives webhooks from BBBAtScale and
 // sends their contents to the returned channel.
 func Receive(opts ...ReceiverOption) (<-chan Message, http.Handler, error) {
 	r := new(receiver)
@@ -62,15 +62,7 @@ func WithLogger(l *zap.Logger) ReceiverOption {
 	}
 }
 
-// WithChanBufferSize configures the channel returned from Receive with a
-// buffer size equal to n.
-func WithChanBufferSize(n int) ReceiverOption {
-	return func(r *receiver) {
-		r.ch = make(chan Message, n)
-	}
-}
-
-// The receiver is an http.Handler receiving web hooks from BBBatScale.
+// The receiver is a http.Handler receiving web hooks from BBBatScale.
 type receiver struct {
 	ch  chan Message
 	log *zap.Logger
@@ -103,7 +95,7 @@ func (wr *receiver) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	p, err := ioutil.ReadAll(http.MaxBytesReader(w, r.Body, maxBodySize))
+	p, err := io.ReadAll(http.MaxBytesReader(w, r.Body, maxBodySize))
 	if err != nil {
 		code := http.StatusBadRequest
 		http.Error(w, http.StatusText(code), code)
@@ -154,7 +146,10 @@ func (wr *receiver) verifyTag(header string, body []byte) bool {
 	}
 
 	var b bytes.Buffer
-	fmt.Fprintf(&b, "%d.%s", t, body)
+	_, err = fmt.Fprintf(&b, "%d.%s", t, body)
+	if err != nil {
+		return false
+	}
 
 	mac := hmac.New(sha512.New, wr.macKey)
 	mac.Write(b.Bytes())