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

What

Deploys matrix-bridge build.1791062345 (REN-587, parent REN-569) next to Synapse in matrix:

  • apps/matrix/bridge.yaml (new): ESO Password generator + ExternalSecret matrix-bridge-tokens (CreatedOnce: as_token, hs_token and the rendered registration), ExternalSecret matrix-bridge (paperclip_api_key from OpenBao homelab/apps/matrix-bridge), Deployment (1 replica, Recreate, uid 1000, read-only root FS, no volume), Service matrix-bridge:8080, CNP egress bridge → paperclip:3100.
  • apps/matrix/synapse.yaml: app_service_config_files now loads /appservices/matrix-bridge/registration.yaml, mounted from matrix-bridge-tokens. Reloader also watches it.
  • apps/paperclip/network-policy.yaml: lets the bridge pod in to paperclip:3100.

The tokens are generated in-cluster by ESO, so no one seeds them by hand; they never exist outside the cluster. Synapse and the bridge read the same Secret, so they cannot drift. Deleting the Secret regenerates both and Reloader restarts both. The user regex is @agent_.*:matrix\.renner\.uno (server_name is matrix.renner.uno, not renner.uno). MATRIX_OWNER=@k.renner:matrix.renner.uno. No ingress.

Why

REN-565: the owner chats with each agent in Element.

Merge gate

Merge after OpenBao homelab/apps/matrix-bridge has paperclip_api_key (metadata 404 at time of writing; Hermes REN-649 sets up the route, no owner step). Synapse no longer depends on it; without it only the bridge pod stays unready and the matrix KS reports not-ready.

Impact

  • Synapse restarts once (Recreate, about 1 min chat outage). Not Home Assistant, not cluster-wide: the policy changes are additive allow rules in matrix and paperclip. No window needed.
  • Synapse with MAS accepts appservice /register only with inhibit_login: true; the bridge sends it (src/matrix.ts).

Verified

Lint only, nothing on the cluster yet: kustomize build + kubeconform -strict pass for apps/matrix (25 resources) and apps/paperclip (37). The rendered registration parses; the regex matches @agent_atlas:matrix.renner.uno and rejects @agent_atlas:matrixXrenner.uno. Image tag resolves in the registry (anonymous pull 200).

After merge (Atlas): ExternalSecret Ready, Synapse and bridge Ready, GET /health 200, owner sees one room per agent.

Revert

Revert this commit. Synapse restarts without the registration; the bridge objects are pruned. The bridge's Matrix users and rooms stay on Synapse (harmless).

🤖 Generated with Claude Code

## What Deploys matrix-bridge `build.1791062345` (REN-587, parent REN-569) next to Synapse in `matrix`: - `apps/matrix/bridge.yaml` (new): ESO Password generator + ExternalSecret `matrix-bridge-tokens` (CreatedOnce: `as_token`, `hs_token` and the rendered registration), ExternalSecret `matrix-bridge` (`paperclip_api_key` from OpenBao `homelab/apps/matrix-bridge`), Deployment (1 replica, Recreate, uid 1000, read-only root FS, no volume), Service `matrix-bridge:8080`, CNP egress bridge → `paperclip:3100`. - `apps/matrix/synapse.yaml`: `app_service_config_files` now loads `/appservices/matrix-bridge/registration.yaml`, mounted from `matrix-bridge-tokens`. Reloader also watches it. - `apps/paperclip/network-policy.yaml`: lets the bridge pod in to `paperclip:3100`. The tokens are generated in-cluster by ESO, so no one seeds them by hand; they never exist outside the cluster. Synapse and the bridge read the same Secret, so they cannot drift. Deleting the Secret regenerates both and Reloader restarts both. The user regex is `@agent_.*:matrix\.renner\.uno` (server_name is `matrix.renner.uno`, not `renner.uno`). `MATRIX_OWNER=@k.renner:matrix.renner.uno`. No ingress. ## Why REN-565: the owner chats with each agent in Element. ## Merge gate **Merge after OpenBao `homelab/apps/matrix-bridge` has `paperclip_api_key`** (metadata 404 at time of writing; Hermes REN-649 sets up the route, no owner step). Synapse no longer depends on it; without it only the bridge pod stays unready and the `matrix` KS reports not-ready. ## Impact - Synapse restarts once (Recreate, about 1 min chat outage). Not Home Assistant, not cluster-wide: the policy changes are additive allow rules in `matrix` and `paperclip`. No window needed. - Synapse with MAS accepts appservice `/register` only with `inhibit_login: true`; the bridge sends it (`src/matrix.ts`). ## Verified Lint only, nothing on the cluster yet: `kustomize build` + `kubeconform -strict` pass for `apps/matrix` (25 resources) and `apps/paperclip` (37). The rendered registration parses; the regex matches `@agent_atlas:matrix.renner.uno` and rejects `@agent_atlas:matrixXrenner.uno`. Image tag resolves in the registry (anonymous pull 200). After merge (Atlas): ExternalSecret Ready, Synapse and bridge Ready, `GET /health` 200, owner sees one room per agent. ## Revert Revert this commit. Synapse restarts without the registration; the bridge objects are pruned. The bridge's Matrix users and rooms stay on Synapse (harmless). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Ghost added 1 commit 2026-10-04 21:41:23 +00:00
feat(matrix): deploy matrix-bridge build.1791062345 as a Synapse appservice (REN-587)
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was canceled
ci/woodpecker/pr/ci-manifests-test Pipeline was canceled
9093b52e00
Adds the matrix-bridge Deployment, Service and ExternalSecret in the
matrix namespace, loads its appservice registration in Synapse, and opens
the bridge -> Paperclip API path (:3100) in both namespaces' policies.
Tokens and the board key come from OpenBao homelab/apps/matrix-bridge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ghost approved these changes 2026-10-04 21:43:12 +00:00
Dismissed
Ghost left a comment

Approved on 9093b52e008597e853166ef5ea236d8fbe8c7073. Checked: registration template renders the regex @agent_.*:matrix\\.renner\\.uno and both tokens from the same OpenBao keys the pod gets; bridge→paperclip:3100 egress (matrix) matches the live paperclip Service selector app.kubernetes.io/name=paperclip and the new ingress rule in apps/paperclip; Synapse↔bridge are covered by default-deny-ingress intra-ns and matrix-app-egress (bridge is part-of: matrix); readiness /health, liveness TCP; non-root, RO rootfs, no SA token. Merge gate not met: OpenBao homelab/metadata/apps/matrix-bridge = 404. Merge waits for the owner seed (REN-587).

Approved on `9093b52e008597e853166ef5ea236d8fbe8c7073`. Checked: registration template renders the regex `@agent_.*:matrix\\.renner\\.uno` and both tokens from the same OpenBao keys the pod gets; bridge→paperclip:3100 egress (matrix) matches the live paperclip Service selector `app.kubernetes.io/name=paperclip` and the new ingress rule in apps/paperclip; Synapse↔bridge are covered by default-deny-ingress intra-ns and matrix-app-egress (bridge is part-of: matrix); readiness /health, liveness TCP; non-root, RO rootfs, no SA token. **Merge gate not met:** OpenBao `homelab/metadata/apps/matrix-bridge` = 404. Merge waits for the owner seed (REN-587).
Ghost added 1 commit 2026-10-04 21:45:08 +00:00
feat(matrix): generate the appservice tokens in-cluster (REN-587)
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
b052fc2576
as_token and hs_token come from an ESO Password generator (CreatedOnce)
instead of OpenBao, so no one has to seed them by hand. Synapse mounts
only the generated Secret and no longer depends on the OpenBao path;
OpenBao homelab/apps/matrix-bridge now holds only paperclip_api_key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ghost dismissed Ghost's review 2026-10-04 21:45:08 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

Ghost approved these changes 2026-10-04 21:46:21 +00:00
Ghost left a comment

Approved on b052fc2576fdde6e476c89524f0017a8a55238b5 (re-review of b052fc25 vs 9093b52e). Checked: Password generator generators.external-secrets.io/v1alpha1 is served in-cluster (ESO v2.11.0, namespaced); ExternalSecret v1 refreshPolicy enum includes CreatedOnce; tokens are alnum (YAML-safe in the registration); Synapse mounts only matrix-bridge-tokens (generator), so no OpenBao dependency for Synapse; bridge reads both Secrets, Reloader on both. OpenBao homelab/metadata/apps/matrix-bridge = 200 (v1, 2026-10-04T21:45:52Z). Earlier checks on policies/probes/regex still hold (unchanged).

Approved on `b052fc2576fdde6e476c89524f0017a8a55238b5` (re-review of b052fc25 vs 9093b52e). Checked: Password generator `generators.external-secrets.io/v1alpha1` is served in-cluster (ESO v2.11.0, namespaced); ExternalSecret v1 `refreshPolicy` enum includes CreatedOnce; tokens are alnum (YAML-safe in the registration); Synapse mounts only `matrix-bridge-tokens` (generator), so no OpenBao dependency for Synapse; bridge reads both Secrets, Reloader on both. OpenBao `homelab/metadata/apps/matrix-bridge` = 200 (v1, 2026-10-04T21:45:52Z). Earlier checks on policies/probes/regex still hold (unchanged).
Ghost merged commit f1e987bdf5 into master 2026-10-04 21:46:22 +00:00
review-bot left a comment
Member

🤖 Automated review — model CLI default (complexity: complex), commit b052fc2576fdde6e476c89524f0017a8a55238b5.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

Adds matrix-bridge (REN-587) next to Synapse: a Password-generator-backed ExternalSecret for the appservice as_token/hs_token and registration, an OpenBao-backed ExternalSecret for the Paperclip API key, the bridge Deployment/Service, and the matching CNP egress/ingress rules in matrix and paperclip. Namespace/label wiring, network policy, and secret-store conventions all match the rest of the repo. There is one likely functional defect in how the two tokens are generated that is worth confirming before merge.

🔴 Blocking

  • kubernetes/apps/matrix/bridge.yaml:27-39 + :53-76: the Password generator (generators.external-secrets.io/v1alpha1) is invoked once, and the ExternalSecret template reads {{ .as_token }} and {{ .hs_token }} from its output (bridge.yaml:54-55,61-62), with secretKeys: [as_token, hs_token] set on the generator spec (bridge.yaml:37-39). As far as I can tell from the upstream PasswordSpec (length, digits, symbols, symbolCharacters, noUpper, allowRepeat), this generator produces a single string under the key password, not a map of named keys — secretKeys is not a field I recognize on that spec. If that's right, .as_token and .hs_token would both resolve to the zero value (empty string) in the template, so matrix-bridge-tokens would end up with as_token/hs_token both empty — defeating the two-sided-trust design the comment at bridge.yaml:16-20 describes, and likely leaving the appservice registration non-functional (Synapse would reject/ignore a registration with an empty as_token/hs_token, or the bridge's own calls to Synapse would fail auth).

    I could not verify this against the actual CRD in this sandbox (no network access to pull the external-secrets v2.7+ CRD schema, and kubeconform/kustomize aren't installed here either). The check that settles it: after this ExternalSecret reconciles, run kubectl -n matrix get secret matrix-bridge-tokens -o jsonpath='{.data.as_token}' | base64 -d and the same for hs_token/registration.yaml, and confirm they're non-empty and different from each other. If they come back empty or identical, the generator spec needs fixing (e.g. two separate Password generators, one per token, each referenced by its own sourceRef).

🟡 Suggestions

  • kubernetes/apps/matrix/bridge.yaml:174-184: livenessProbe is a bare tcpSocket check on the same port the app's HTTP server listens on. The comment explains why httpGet /health is reserved for readiness (a Paperclip outage must not restart the bridge), but a tcpSocket probe will also pass if the Node event loop wedges while the listener socket stays open, so liveness won't catch an actual application hang. Not blocking — just worth knowing readiness is effectively the only real health signal here.

Tests

No tests included or expected — this is a pure Kubernetes manifest change (new Deployment/Service/ExternalSecret/NetworkPolicy), consistent with how the rest of kubernetes/apps/matrix and kubernetes/apps/paperclip are structured (no test suite in this directory tree). The PR body's own verification (kustomize build + kubeconform -strict) is the right kind of check for this change, but kubeconform typically doesn't vendor CRD schemas for generators.external-secrets.io, so it would not have caught the token-generation issue above — that needs a live-cluster check (see Blocking).

Recommendation

Request changes — 1 blocking issue (verify the Password generator actually yields two distinct, non-empty as_token/hs_token values before merging).

🤖 **Automated review** — model `CLI default` (complexity: complex), commit `b052fc2576fdde6e476c89524f0017a8a55238b5`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary Adds `matrix-bridge` (REN-587) next to Synapse: a Password-generator-backed `ExternalSecret` for the appservice `as_token`/`hs_token` and registration, an OpenBao-backed `ExternalSecret` for the Paperclip API key, the bridge Deployment/Service, and the matching CNP egress/ingress rules in `matrix` and `paperclip`. Namespace/label wiring, network policy, and secret-store conventions all match the rest of the repo. There is one likely functional defect in how the two tokens are generated that is worth confirming before merge. ## 🔴 Blocking - `kubernetes/apps/matrix/bridge.yaml:27-39` + `:53-76`: the `Password` generator (`generators.external-secrets.io/v1alpha1`) is invoked once, and the `ExternalSecret` template reads `{{ .as_token }}` and `{{ .hs_token }}` from its output (`bridge.yaml:54-55,61-62`), with `secretKeys: [as_token, hs_token]` set on the generator spec (`bridge.yaml:37-39`). As far as I can tell from the upstream `PasswordSpec` (`length`, `digits`, `symbols`, `symbolCharacters`, `noUpper`, `allowRepeat`), this generator produces a single string under the key `password`, not a map of named keys — `secretKeys` is not a field I recognize on that spec. If that's right, `.as_token` and `.hs_token` would both resolve to the zero value (empty string) in the template, so `matrix-bridge-tokens` would end up with `as_token`/`hs_token` both empty — defeating the two-sided-trust design the comment at `bridge.yaml:16-20` describes, and likely leaving the appservice registration non-functional (Synapse would reject/ignore a registration with an empty `as_token`/`hs_token`, or the bridge's own calls to Synapse would fail auth). I could not verify this against the actual CRD in this sandbox (no network access to pull the `external-secrets` v2.7+ CRD schema, and `kubeconform`/`kustomize` aren't installed here either). The check that settles it: after this `ExternalSecret` reconciles, run `kubectl -n matrix get secret matrix-bridge-tokens -o jsonpath='{.data.as_token}' | base64 -d` and the same for `hs_token`/`registration.yaml`, and confirm they're non-empty and different from each other. If they come back empty or identical, the generator spec needs fixing (e.g. two separate `Password` generators, one per token, each referenced by its own `sourceRef`). ## 🟡 Suggestions - `kubernetes/apps/matrix/bridge.yaml:174-184`: `livenessProbe` is a bare `tcpSocket` check on the same port the app's HTTP server listens on. The comment explains why `httpGet /health` is reserved for readiness (a Paperclip outage must not restart the bridge), but a `tcpSocket` probe will also pass if the Node event loop wedges while the listener socket stays open, so liveness won't catch an actual application hang. Not blocking — just worth knowing readiness is effectively the only real health signal here. ## Tests No tests included or expected — this is a pure Kubernetes manifest change (new Deployment/Service/ExternalSecret/NetworkPolicy), consistent with how the rest of `kubernetes/apps/matrix` and `kubernetes/apps/paperclip` are structured (no test suite in this directory tree). The PR body's own verification (`kustomize build` + `kubeconform -strict`) is the right kind of check for this change, but `kubeconform` typically doesn't vendor CRD schemas for `generators.external-secrets.io`, so it would not have caught the token-generation issue above — that needs a live-cluster check (see Blocking). ## Recommendation Request changes — 1 blocking issue (verify the `Password` generator actually yields two distinct, non-empty `as_token`/`hs_token` values before merging). <!-- pr-review:sha=b052fc2576fdde6e476c89524f0017a8a55238b5 -->
Sign in to join this conversation.