feat(matrix): deploy matrix-bridge build.1791062345 (REN-587) #1123

Merged
Ghost merged 2 commits from atlas/ren-587-matrix-bridge into master 2026-10-04 21:46:22 +00:00
4 changed files with 244 additions and 4 deletions
+219
View File
@@ -0,0 +1,219 @@
# matrix-bridge (REN-569, REN-587): Matrix appservice between Element and the
# Paperclip agents. One room per agent with the owner; owner messages become
# comments on the agent's chat card, the agent's comments come back to the
# room. Source: gitea.renner.uno/konairius/matrix-bridge.
#
# No volume: the bridge keeps its state in the bot's account data on Synapse.
# No ingress: only Synapse talks to it (appservice transactions on :8080).
#
# Secrets:
# matrix-bridge-tokens as_token (bridge -> Synapse) and hs_token (Synapse
# -> bridge), generated once in-cluster by ESO, plus
# the appservice registration Synapse loads from
# app_service_config_files (synapse.yaml). Both sides
# read the same Secret, so the tokens cannot drift.
# Deleting the Secret regenerates both; Reloader
# restarts Synapse and the bridge.
# matrix-bridge paperclip_api_key from OpenBao
# homelab/apps/matrix-bridge: the Paperclip board key
# "matrix-bridge (board)" (REN-582).
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
metadata:
name: matrix-bridge-tokens
namespace: matrix
spec:
length: 64
digits: 16
symbols: 0
allowRepeat: true
noUpper: false
secretKeys:
- as_token
- hs_token
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: matrix-bridge-tokens
namespace: matrix
spec:
# Generate once; Periodic would rotate the tokens every refresh.
refreshPolicy: CreatedOnce
target:
name: matrix-bridge-tokens
template:
engineVersion: v2
data:
as_token: "{{ .as_token }}"
hs_token: "{{ .hs_token }}"
# Agent users are @agent_<urlKey>:matrix.renner.uno; the bot is
# @paperclip:matrix.renner.uno.
registration.yaml: |
id: matrix-bridge
url: http://matrix-bridge:8080
as_token: "{{ .as_token }}"
hs_token: "{{ .hs_token }}"
sender_localpart: paperclip
rate_limited: false
namespaces:
users:
- exclusive: true
regex: "@agent_.*:matrix\\.renner\\.uno"
aliases: []
rooms: []
dataFrom:
- sourceRef:
generatorRef:
apiVersion: generators.external-secrets.io/v1alpha1
kind: Password
name: matrix-bridge-tokens
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: matrix-bridge
namespace: matrix
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: openbao
target:
name: matrix-bridge
data:
- secretKey: paperclip_api_key
remoteRef:
key: apps/matrix-bridge
property: paperclip_api_key
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: matrix-bridge
namespace: matrix
annotations:
secret.reloader.stakater.com/reload: matrix-bridge,matrix-bridge-tokens
labels:
app.kubernetes.io/name: matrix-bridge
app.kubernetes.io/part-of: matrix
spec:
replicas: 1
# One poller only: two pods would bridge every comment twice.
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: matrix-bridge
template:
metadata:
labels:
app.kubernetes.io/name: matrix-bridge
app.kubernetes.io/part-of: matrix
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
# `node` in node:24-alpine.
runAsUser: 1000
runAsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: matrix-bridge
image: gitea.renner.uno/konairius/matrix-bridge:build.1791062345
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
env:
- name: MATRIX_HOMESERVER_URL
value: http://synapse:8008
- name: MATRIX_SERVER_NAME
value: matrix.renner.uno
- name: MATRIX_OWNER
value: "@k.renner:matrix.renner.uno"
- name: MATRIX_AS_TOKEN
valueFrom:
secretKeyRef:
name: matrix-bridge-tokens
key: as_token
- name: MATRIX_HS_TOKEN
valueFrom:
secretKeyRef:
name: matrix-bridge-tokens
key: hs_token
- name: PAPERCLIP_API_URL
value: http://paperclip.paperclip.svc.cluster.local:3100
- name: PAPERCLIP_API_KEY
valueFrom:
secretKeyRef:
name: matrix-bridge
key: paperclip_api_key
- name: PAPERCLIP_COMPANY_ID
value: 72f7b75a-10b0-4084-ad5e-a0cdf31c7caf
- name: PAPERCLIP_PROJECT_ID
value: 67143c0b-e9a1-44c1-b012-0f8fa25625aa
ports:
- name: http
containerPort: 8080
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
memory: 256Mi
# /health is 503 when the Paperclip comment poll fails, so it gates
# readiness only; a Paperclip outage must not restart the bridge.
readinessProbe:
httpGet:
path: /health
port: http
periodSeconds: 10
livenessProbe:
tcpSocket:
port: http
periodSeconds: 30
failureThreshold: 4
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: matrix-bridge
namespace: matrix
spec:
selector:
app.kubernetes.io/name: matrix-bridge
ports:
- name: http
port: 8080
targetPort: http
---
# The bridge calls the Paperclip API in-cluster. Synapse <-> bridge and DNS
# are covered by matrix-app-egress (part-of: matrix); the Paperclip side of
# this flow is in apps/paperclip/network-policy.yaml.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: matrix-bridge-paperclip-egress
namespace: matrix
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: matrix-bridge
egress:
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": paperclip
app.kubernetes.io/name: paperclip
toPorts:
- ports:
- port: "3100"
protocol: TCP
@@ -5,6 +5,7 @@ resources:
- synapse.yaml
- mas.yaml
- element.yaml
- bridge.yaml
- ingress.yaml
# mx-pg CNPG Postgres + offsite Barman backup. The media store and signing
# key are on synapse-data, backed up by Velero (matrix-daily).
+14 -4
View File
@@ -90,9 +90,10 @@ data:
push:
include_content: false
# Appservice registrations (bridges) go here, one file each, mounted
# from a Secret. Empty until REN-565 Part 2 is decided.
app_service_config_files: []
# Appservice registrations (bridges), one file each, mounted from a
# Secret. matrix-bridge: bridge.yaml (REN-587).
app_service_config_files:
- /appservices/matrix-bridge/registration.yaml
---
# Media store and the server signing key. Not reconstructable: lost media
# is gone, and a lost signing key invalidates the server's past signatures.
@@ -119,7 +120,7 @@ metadata:
namespace: matrix
annotations:
configmap.reloader.stakater.com/reload: synapse-config,mas-config
secret.reloader.stakater.com/reload: synapse-secrets
secret.reloader.stakater.com/reload: synapse-secrets,matrix-bridge-tokens
labels:
app.kubernetes.io/name: synapse
app.kubernetes.io/part-of: matrix
@@ -267,6 +268,9 @@ spec:
mountPath: /data
- name: tmp
mountPath: /tmp
- name: appservice-matrix-bridge
mountPath: /appservices/matrix-bridge
readOnly: true
volumes:
- name: config
configMap:
@@ -299,6 +303,12 @@ spec:
claimName: synapse-data
- name: tmp
emptyDir: {}
- name: appservice-matrix-bridge
secret:
secretName: matrix-bridge-tokens
items:
- key: registration.yaml
path: registration.yaml
---
apiVersion: v1
kind: Service
@@ -25,6 +25,16 @@ spec:
- ports:
- port: "3100"
protocol: TCP
# matrix-bridge (REN-587), board-key API calls; egress side in
# apps/matrix/bridge.yaml.
- fromEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": matrix
app.kubernetes.io/name: matrix-bridge
toPorts:
- ports:
- port: "3100"
protocol: TCP
---
# Covers the server and every agent it spawns (same pod).
apiVersion: cilium.io/v2