From 8b0ed63cc95e203a49adf2ad7f6c7dcc26906b2d Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 11:26:40 +0100 Subject: [PATCH 01/34] fix(security): upgrade Synapse to v1.148.0 and migrate CI to GitHub Actions - Upgrade Synapse base image from v1.98.0 to v1.148.0 (resolves 5 CVEs) - Update Python paths from 3.11 to 3.13 to match upstream image - Harden Dockerfile (--no-install-recommends, cleanup apt lists, pip --no-cache-dir) - Replace GitLab CI with GitHub Actions: lint, test, publish to GHCR - Remove old .gitlab-ci.yml --- .github/workflows/build-and-publish.yml | 163 ++++++++++++++++++++++++ .gitlab-ci.yml | 144 --------------------- docker/Dockerfile | 19 ++- 3 files changed, 172 insertions(+), 154 deletions(-) create mode 100644 .github/workflows/build-and-publish.yml delete mode 100644 .gitlab-ci.yml diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml new file mode 100644 index 0000000..4d9af43 --- /dev/null +++ b/.github/workflows/build-and-publish.yml @@ -0,0 +1,163 @@ +name: Build and Publish Docker Image + +on: + push: + branches: [master, development] + tags: ["v*"] + pull_request: + branches: [master] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + TEST_TAG: beacon-node:test + +jobs: + lint: + name: Lint Dockerfile + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run Hadolint + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: docker/Dockerfile + failure-threshold: error + + test: + name: Build & Validate Image + runs-on: ubuntu-latest + needs: lint + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image for testing + uses: docker/build-push-action@v6 + with: + context: ./docker/ + load: true + tags: ${{ env.TEST_TAG }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Verify custom modules are installed + run: | + echo "--- Checking crypto_auth_provider.py ---" + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + python -c "import crypto_auth_provider; print('crypto_auth_provider OK')" + + echo "--- Checking beacon_info_module.py ---" + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + python -c "import beacon_info_module; print('beacon_info_module OK')" + + - name: Verify constants.py patch (max size 1048576) + run: | + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + python -c "from synapse.api.constants import EventContentFields; print('synapse.api.constants imported OK')" + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + grep -q '1048576' /usr/local/lib/python3.13/site-packages/synapse/api/constants.py \ + && echo "constants.py patch OK" \ + || (echo "FAIL: constants.py patch not applied" && exit 1) + + - name: Verify entrypoint script exists and is executable + run: | + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + test -x /usr/local/bin/synctl_entrypoint.sh \ + && echo "entrypoint OK" + + - name: Verify worker configs are present + run: | + for w in main_process worker1 worker2 worker3 worker4; do + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + test -f /config/workers/${w}.yaml \ + && echo "${w}.yaml OK" \ + || (echo "FAIL: ${w}.yaml missing" && exit 1) + done + + - name: Verify pip dependencies + run: | + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + python -c "import psycopg2; print('psycopg2 OK')" + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + python -c "import pysodium; print('pysodium OK')" + + - name: Verify config files are present + run: | + for f in homeserver.yaml synapse.log.config shared_config.yaml; do + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + test -f /config/${f} \ + && echo "/config/${f} OK" \ + || (echo "FAIL: /config/${f} missing" && exit 1) + done + + - name: Verify systemd units are present + run: | + for f in synapse_master.service synapse_worker@.service matrix_synapse.target; do + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + test -f /etc/systemd/system/${f} \ + && echo "${f} OK" \ + || (echo "FAIL: ${f} missing" && exit 1) + done + + - name: Verify wait-for.sh is executable + run: | + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + test -x /usr/local/bin/wait-for.sh \ + && echo "wait-for.sh OK" + + - name: Verify /keys directory exists + run: | + docker run --rm --entrypoint="" ${{ env.TEST_TAG }} \ + test -d /keys \ + && echo "/keys OK" + + publish: + name: Publish to GHCR + runs-on: ubuntu-latest + needs: test + if: github.event_name != 'pull_request' + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=sha + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: ./docker/ + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 8b01360..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,144 +0,0 @@ -include: - - project: 'papers/papers-internal/internal' - file: '/.base-gitlab-ci.yml' - -variables: - GIT_SUBMODULE_STRATEGY: recursive - DOCKERHUB_TAG: airgapdocker/beacon-node:$CI_COMMIT_SHA - DOCKERHUB_TAG_LATEST: airgapdocker/beacon-node:latest - -stages: - - build - - publish - - deploy - - provision - -build: - stage: build - script: - - docker build -t $DOCKERHUB_TAG ./docker/ - -.publish-base: - stage: publish - before_script: - - docker login -u "$DOCKERHUB_USER" -p "$DOCKERHUB_PASSWORD" - script: - - docker tag $DOCKERHUB_TAG $SECOND_TAG - - docker push $DOCKERHUB_TAG - - docker push $SECOND_TAG - -publish: - extends: .publish-base - variables: - SECOND_TAG: $DOCKERHUB_TAG_LATEST - only: - - master - - development - -publish-tag: - extends: .publish-base - variables: - SECOND_TAG: airgapdocker/beacon-node:$CI_COMMIT_TAG - only: - - tags - - -k8s-deploy-production: - stage: deploy - only: - - master - when: manual - image: google/cloud-sdk - before_script: - - echo $GCLOUD_GOOGLE_KEY > key.json - - gcloud auth activate-service-account $GCLOUD_ACCOUNT --key-file key.json - - gcloud config set account $GCLOUD_ACCOUNT - - gcloud config set project $GCLOUD_PROJECT - - gcloud config set compute/zone $GCLOUD_ZONE - - gcloud container clusters get-credentials papers-cluster-production - script: - - sed -i "s|_TO_BE_REPLACED_BY_IMAGE_TAG_|"$DOCKERHUB_TAG"|g" k8s/common/synapse/deployment.yaml - - sed -i "s|_TO_BE_REPLACED_BY_PROD_DB_HOST_|"$PROD_DB_HOST"|g" k8s/production/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_PROD_DB_PASS_|"$PROD_DB_PASS"|g" k8s/production/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_PROD_DB_NAME_|"$PROD_DB_NAME"|g" k8s/production/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_PROD_DB_USER_|"$PROD_DB_USER"|g" k8s/production/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_PROD_SIGNING_KEY_|$PROD_SIGNING_KEY|g" k8s/production/secret.yaml - - - kubectl apply -f ./k8s/common/namespace.yaml - - kubectl apply -f ./k8s/common/ --recursive - - kubectl apply -f ./k8s/production/ --recursive - - tags: - - docker - -k8s-deploy-development: - stage: deploy - only: - - master - when: manual - image: google/cloud-sdk - before_script: - - echo $GCLOUD_GOOGLE_KEY > key.json - - gcloud auth activate-service-account $GCLOUD_ACCOUNT --key-file key.json - - gcloud config set account $GCLOUD_ACCOUNT - - gcloud config set project $GCLOUD_PROJECT - - gcloud config set compute/zone $GCLOUD_ZONE_DEVELOPMENT - - gcloud container clusters get-credentials papers-cluster-development - script: - - sed -i "s|_TO_BE_REPLACED_BY_IMAGE_TAG_|"$DOCKERHUB_TAG"|g" k8s/common/synapse/deployment.yaml - - sed -i "s|_TO_BE_REPLACED_BY_DEV_DB_HOST_|"$DEV_DB_HOST"|g" k8s/development/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_DEV_DB_PASS_|"$DEV_DB_PASS"|g" k8s/development/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_DEV_DB_NAME_|"$DEV_DB_NAME"|g" k8s/development/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_DEV_DB_USER_|"$DEV_DB_USER"|g" k8s/development/secret.yaml - - sed -i "s|_TO_BE_REPLACED_BY_DEV_SIGNING_KEY_|$DEV_SIGNING_KEY|g" k8s/development/secret.yaml - - - kubectl apply -f ./k8s/common/namespace.yaml - - kubectl apply -f ./k8s/common/ --recursive - - kubectl apply -f ./k8s/development/ --recursive - - tags: - - docker - -provision-db-development: - stage: provision - only: - - develop - - master - when: manual - image: google/cloud-sdk - before_script: - - echo $GCLOUD_GOOGLE_KEY > key.json - - gcloud auth activate-service-account $GCLOUD_ACCOUNT --key-file key.json - - gcloud config set account $GCLOUD_ACCOUNT - - gcloud config set project $GCLOUD_PROJECT - - gcloud config set compute/zone $GCLOUD_ZONE_DEVELOPMENT - - gcloud container clusters get-credentials papers-cluster-development - - export STOLON_PROXY=$(kubectl get pods --all-namespaces | grep stolon-proxy | awk 'NR==1{print $2}') - script: - - kubectl exec $STOLON_PROXY --namespace="development-postgresql" -- bash -c "export PGPASSWORD=$PG_DEV_MASTER_PASSWORD && psql --host=localhost --username=$PG_DEV_MASTER_USERNAME postgres -c \"CREATE DATABASE $DB_NAME\"" || true - - kubectl exec $STOLON_PROXY --namespace="development-postgresql" -- bash -c "export PGPASSWORD=$PG_DEV_MASTER_PASSWORD && psql --host=localhost --username=$PG_DEV_MASTER_USERNAME postgres -c \"CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS'\"" - - kubectl exec $STOLON_PROXY --namespace="development-postgresql" -- bash -c "export PGPASSWORD=$PG_DEV_MASTER_PASSWORD && psql --host=localhost --username=$PG_DEV_MASTER_USERNAME postgres -c \"GRANT ALL PRIVILEGES ON DATABASE $DB_NAME to $DB_USER\"" - tags: - - docker - -provision-db-production: - stage: provision - only: - - develop - - master - when: manual - image: google/cloud-sdk - before_script: - - echo $GCLOUD_GOOGLE_KEY > key.json - - gcloud auth activate-service-account $GCLOUD_ACCOUNT --key-file key.json - - gcloud config set account $GCLOUD_ACCOUNT - - gcloud config set project $GCLOUD_PROJECT - - gcloud config set compute/zone $GCLOUD_ZONE - - gcloud container clusters get-credentials papers-cluster-production - - export STOLON_PROXY=$(kubectl get pods --all-namespaces | grep stolon-proxy | awk 'NR==1{print $2}') - script: - - kubectl exec $STOLON_PROXY -- bash -c "export PGPASSWORD=$PG_PROD_MASTER_PASSWORD && psql --host=localhost --username=$PG_PROD_MASTER_USERNAME postgres -c \"CREATE DATABASE $PROD_DB_NAME\"" || true - - kubectl exec $STOLON_PROXY -- bash -c "export PGPASSWORD=$PG_PROD_MASTER_PASSWORD && psql --host=localhost --username=$PG_PROD_MASTER_USERNAME postgres -c \"CREATE USER $PROD_DB_USER WITH ENCRYPTED PASSWORD '$PROD_DB_PASS'\"" || true - - kubectl exec $STOLON_PROXY -- bash -c "export PGPASSWORD=$PG_PROD_MASTER_PASSWORD && psql --host=localhost --username=$PG_PROD_MASTER_USERNAME postgres -c \"GRANT ALL PRIVILEGES ON DATABASE $PROD_DB_NAME to $PROD_DB_USER\"" || true - tags: - - docker diff --git a/docker/Dockerfile b/docker/Dockerfile index eae853d..b601587 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,14 +1,13 @@ -FROM matrixdotorg/synapse:v1.98.0 +FROM matrixdotorg/synapse:v1.148.0 LABEL maintainer="AirGap Team " -# RUN apk add libsodium-dev gcc -RUN apt-get update && apt-get install -y libsodium-dev gcc +RUN apt-get update && \ + apt-get install -y --no-install-recommends libsodium-dev gcc && \ + rm -rf /var/lib/apt/lists/* && \ + pip install --no-cache-dir psycopg2 pysodium && \ + mkdir -p /keys -RUN pip install psycopg2 pysodium - -RUN mkdir -p /keys - -COPY crypto_auth_provider.py /usr/local/lib/python3.11/site-packages/ +COPY crypto_auth_provider.py /usr/local/lib/python3.13/site-packages/ COPY homeserver.yaml /config/ COPY synapse.log.config /config/ COPY synapse_master.service /etc/systemd/system/ @@ -16,10 +15,10 @@ COPY synapse_worker@.service /etc/systemd/system/ COPY matrix_synapse.target /etc/systemd/system/ COPY workers /config/workers COPY shared_config.yaml /config/ -COPY beacon_info_module.py /usr/local/lib/python3.11/site-packages/ +COPY beacon_info_module.py /usr/local/lib/python3.13/site-packages/ # run change on max size -RUN sed -i 's/65536/1048576/' /usr/local/lib/python3.11/site-packages/synapse/api/constants.py +RUN sed -i 's/65536/1048576/' /usr/local/lib/python3.13/site-packages/synapse/api/constants.py COPY wait-for.sh /usr/local/bin/ COPY synctl_entrypoint.sh /usr/local/bin/ From a62449abd5f95c6ebbb81b42f8beb2d7a3d27522 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 11:41:10 +0100 Subject: [PATCH 02/34] feat: add multi-arch build (amd64/arm64) and Renovate config - Add QEMU + multi-platform build (linux/amd64, linux/arm64) - Add Renovate config to auto-update: - Synapse base image (auto-merge patches) - GitHub Actions (auto-merge minor/patch) --- .github/workflows/build-and-publish.yml | 4 ++++ renovate.json | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 renovate.json diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 4d9af43..e4a2e33 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -130,6 +130,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -156,6 +159,7 @@ jobs: uses: docker/build-push-action@v6 with: context: ./docker/ + platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..e79053c --- /dev/null +++ b/renovate.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "packageRules": [ + { + "description": "Auto-merge minor/patch updates for GitHub Actions", + "matchManagers": ["github-actions"], + "automerge": true, + "automergeType": "pr" + }, + { + "description": "Auto-merge patch updates for Synapse base image", + "matchDatasources": ["docker"], + "matchPackageNames": ["matrixdotorg/synapse"], + "automerge": true, + "automergeType": "pr", + "matchUpdateTypes": ["patch"] + } + ] +} From 4d7c8efc06296761d3fc77be5c1e07b5c13d1005 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 12:22:18 +0100 Subject: [PATCH 03/34] chore: add AGPL-3.0 license Beacon-node is a derivative work of Synapse (element-hq/synapse) which is licensed under AGPL-3.0. --- LICENSE | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..be3f7b2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. From d3540502b2a26c1ac2e3fd4256e2f50944cc406b Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 13:28:42 +0100 Subject: [PATCH 04/34] chore: enable renovate on fork + remove unused k8s manifests Enable forkProcessing so Renovate scans this forked repo. Remove legacy k8s/ directory (now managed via Helm chart in iac repo). --- k8s/common/limitrange.yaml | 14 -------------- k8s/common/namespace.yaml | 4 ---- k8s/common/synapse/deployment.yaml | 31 ------------------------------ k8s/common/synapse/service.yaml | 14 -------------- k8s/development/config-map.yaml | 7 ------- k8s/development/ingress.yaml | 27 -------------------------- k8s/development/secret.yaml | 13 ------------- k8s/production/config-map.yaml | 7 ------- k8s/production/ingress.yaml | 20 ------------------- k8s/production/secret.yaml | 13 ------------- renovate.json | 1 + 11 files changed, 1 insertion(+), 150 deletions(-) delete mode 100644 k8s/common/limitrange.yaml delete mode 100644 k8s/common/namespace.yaml delete mode 100644 k8s/common/synapse/deployment.yaml delete mode 100644 k8s/common/synapse/service.yaml delete mode 100644 k8s/development/config-map.yaml delete mode 100644 k8s/development/ingress.yaml delete mode 100644 k8s/development/secret.yaml delete mode 100644 k8s/production/config-map.yaml delete mode 100644 k8s/production/ingress.yaml delete mode 100644 k8s/production/secret.yaml diff --git a/k8s/common/limitrange.yaml b/k8s/common/limitrange.yaml deleted file mode 100644 index 83dd6d4..0000000 --- a/k8s/common/limitrange.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: LimitRange -metadata: - name: beacon-node - namespace: beacon-node -spec: - limits: - - default: - cpu: "0.5" - memory: "1000Mi" - defaultRequest: - cpu: "0.05" - memory: 150Mi - type: Container diff --git a/k8s/common/namespace.yaml b/k8s/common/namespace.yaml deleted file mode 100644 index b8a1148..0000000 --- a/k8s/common/namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: beacon-node diff --git a/k8s/common/synapse/deployment.yaml b/k8s/common/synapse/deployment.yaml deleted file mode 100644 index d57fa8a..0000000 --- a/k8s/common/synapse/deployment.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: synapse - namespace: beacon-node - labels: - app: synapse -spec: - selector: - matchLabels: - app: synapse - strategy: - type: Recreate - template: - metadata: - labels: - app: synapse - spec: - containers: - - image: _TO_BE_REPLACED_BY_IMAGE_TAG_ - imagePullPolicy: IfNotPresent - name: synapse - envFrom: - - configMapRef: - name: synapse-config-map - - secretRef: - name: synapse-secret - ports: - - containerPort: 8008 - restartPolicy: Always -status: {} diff --git a/k8s/common/synapse/service.yaml b/k8s/common/synapse/service.yaml deleted file mode 100644 index b58e8c6..0000000 --- a/k8s/common/synapse/service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: synapse-service - namespace: beacon-node -spec: - ports: - - name: client - port: 8008 - targetPort: 8008 - protocol: TCP - type: NodePort - selector: - app: synapse diff --git a/k8s/development/config-map.yaml b/k8s/development/config-map.yaml deleted file mode 100644 index 2b52e0b..0000000 --- a/k8s/development/config-map.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: synapse-config-map - namespace: beacon-node -data: - SERVER_NAME: "matrix-dev.papers.tech" diff --git a/k8s/development/ingress.yaml b/k8s/development/ingress.yaml deleted file mode 100644 index f0c71fd..0000000 --- a/k8s/development/ingress.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: synapse - namespace: beacon-node - annotations: - kubernetes.io/ingress.class: "nginx" - cert-manager.io/cluster-issuer: "letsencrypt-prod" -spec: - tls: - - hosts: - - matrix-dev.papers.tech - - matrix.dev.gke.papers.tech - secretName: matrix-synapse-prod-tls - rules: - - host: matrix-dev.papers.tech - http: - paths: - - backend: - serviceName: synapse-service - servicePort: 8008 - - host: matrix.dev.gke.papers.tech - http: - paths: - - backend: - serviceName: synapse-service - servicePort: 8008 diff --git a/k8s/development/secret.yaml b/k8s/development/secret.yaml deleted file mode 100644 index 7c16639..0000000 --- a/k8s/development/secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# this is just a dummy sample, make sure to create the secret manually and never check it in. -apiVersion: v1 -kind: Secret -metadata: - name: synapse-secret - namespace: beacon-node -type: Opaque -stringData: - DB_HOST: _TO_BE_REPLACED_BY_DEV_DB_HOST_ - DB_NAME: _TO_BE_REPLACED_BY_DEV_DB_NAME_ - DB_PASS: _TO_BE_REPLACED_BY_DEV_DB_PASS_ - DB_USER: _TO_BE_REPLACED_BY_DEV_DB_USER_ - SIGNING_KEY: _TO_BE_REPLACED_BY_DEV_SIGNING_KEY_ diff --git a/k8s/production/config-map.yaml b/k8s/production/config-map.yaml deleted file mode 100644 index a27ec2a..0000000 --- a/k8s/production/config-map.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: synapse-config-map - namespace: beacon-node -data: - SERVER_NAME: "matrix.papers.tech" diff --git a/k8s/production/ingress.yaml b/k8s/production/ingress.yaml deleted file mode 100644 index a1c854c..0000000 --- a/k8s/production/ingress.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: networking.k8s.io/v1beta1 -kind: Ingress -metadata: - name: synapse - namespace: beacon-node - annotations: - kubernetes.io/ingress.class: "nginx" - cert-manager.io/cluster-issuer: "letsencrypt-prod" -spec: - tls: - - hosts: - - matrix.prod.gke.papers.tech - secretName: matrix-synapse-prod-tls - rules: - - host: matrix.prod.gke.papers.tech - http: - paths: - - backend: - serviceName: synapse-service - servicePort: 8008 diff --git a/k8s/production/secret.yaml b/k8s/production/secret.yaml deleted file mode 100644 index 95ee4d5..0000000 --- a/k8s/production/secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# this is just a dummy sample, make sure to create the secret manually and never check it in. -apiVersion: v1 -kind: Secret -metadata: - name: synapse-secret - namespace: beacon-node -type: Opaque -stringData: - DB_HOST: _TO_BE_REPLACED_BY_PROD_DB_HOST_ - DB_NAME: _TO_BE_REPLACED_BY_PROD_DB_NAME_ - DB_PASS: _TO_BE_REPLACED_BY_PROD_DB_PASS_ - DB_USER: _TO_BE_REPLACED_BY_PROD_DB_USER_ - SIGNING_KEY: _TO_BE_REPLACED_BY_PROD_SIGNING_KEY_ diff --git a/renovate.json b/renovate.json index e79053c..7839e6d 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,7 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], + "forkProcessing": "enabled", "packageRules": [ { "description": "Auto-merge minor/patch updates for GitHub Actions", From 58ea9e1e28c9163f2f57170e71182c97bfd44f30 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 13:32:27 +0100 Subject: [PATCH 05/34] feat: add Trivy security scan workflow Scans Docker image for CRITICAL and HIGH CVEs. Runs on docker/ changes, PRs to master, and weekly schedule. Results uploaded to GitHub Security tab (SARIF). --- .github/workflows/security-scan.yml | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/security-scan.yml diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml new file mode 100644 index 0000000..17e7c66 --- /dev/null +++ b/.github/workflows/security-scan.yml @@ -0,0 +1,61 @@ +name: Security Scan + +on: + push: + branches: [master] + paths: + - "docker/**" + - ".github/workflows/security-scan.yml" + pull_request: + branches: [master] + paths: + - "docker/**" + - ".github/workflows/security-scan.yml" + schedule: + - cron: "0 6 * * 1" # Weekly Monday 06:00 UTC + +env: + TEST_TAG: beacon-node:scan + +jobs: + trivy: + name: Trivy Image Scan + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: ./docker/ + load: true + tags: ${{ env.TEST_TAG }} + cache-from: type=gha + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: ${{ env.TEST_TAG }} + format: "sarif" + output: "trivy-results.sarif" + severity: "CRITICAL,HIGH" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: "trivy-results.sarif" + + - name: Trivy summary (table) + uses: aquasecurity/trivy-action@0.28.0 + if: always() + with: + image-ref: ${{ env.TEST_TAG }} + format: "table" + severity: "CRITICAL,HIGH" From b34310690b9ed7f2f815c42d7fb62faa26558fb7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:34:34 +0000 Subject: [PATCH 06/34] chore(deps): update redis docker tag to v6.2.6 --- samples/docker-compose-optimized.yml | 2 +- samples/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/docker-compose-optimized.yml b/samples/docker-compose-optimized.yml index 9974b4f..adac7cd 100644 --- a/samples/docker-compose-optimized.yml +++ b/samples/docker-compose-optimized.yml @@ -40,7 +40,7 @@ services: max-file: "3" redis: - image: redis:6.2.4-buster + image: redis:6.2.6-buster restart: always # Conservative Redis configuration - only setting memory limit command: > diff --git a/samples/docker-compose.yml b/samples/docker-compose.yml index 60628b8..3880e52 100644 --- a/samples/docker-compose.yml +++ b/samples/docker-compose.yml @@ -17,7 +17,7 @@ services: volumes: - ./postgres_data:/var/lib/postgresql/data redis: - image: redis:6.2.4-buster + image: redis:6.2.6-buster expose: - "6379" tezos-synapse: From f07b0cfc49d19c5bca118baaf17143d972b5367e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:34:38 +0000 Subject: [PATCH 07/34] chore(deps): update hadolint/hadolint-action action to v3.3.0 --- .github/workflows/build-and-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index e4a2e33..b41e025 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v4 - name: Run Hadolint - uses: hadolint/hadolint-action@v3.1.0 + uses: hadolint/hadolint-action@v3.3.0 with: dockerfile: docker/Dockerfile failure-threshold: error From 11def73be31e0c439a44811352c575bc4ea8288a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:34:45 +0000 Subject: [PATCH 08/34] chore(deps): update actions/checkout action to v6 --- .github/workflows/build-and-publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index e4a2e33..b1a73e5 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Run Hadolint uses: hadolint/hadolint-action@v3.1.0 @@ -32,7 +32,7 @@ jobs: needs: lint steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -128,7 +128,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up QEMU uses: docker/setup-qemu-action@v3 From 34f9799cf9b7966f99d4ff1264d325af1b36f61f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:34:49 +0000 Subject: [PATCH 09/34] chore(deps): update postgres docker tag to v18 --- samples/docker-compose-optimized.yml | 2 +- samples/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/docker-compose-optimized.yml b/samples/docker-compose-optimized.yml index 9974b4f..d61ac56 100644 --- a/samples/docker-compose-optimized.yml +++ b/samples/docker-compose-optimized.yml @@ -1,7 +1,7 @@ version: "3.1" services: postgres: - image: postgres:12.0 + image: postgres:18.2 restart: always # IMPORTANT: shm_size must be larger than shared_buffers to prevent Docker shared memory errors shm_size: '2gb' diff --git a/samples/docker-compose.yml b/samples/docker-compose.yml index 60628b8..9501938 100644 --- a/samples/docker-compose.yml +++ b/samples/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.5" services: postgres: - image: postgres:12.0 + image: postgres:18.2 environment: POSTGRES_USER: synapse POSTGRES_PASSWORD: synapsepassword From 532a9193c170b25ac47f5e8ae4a310dc8b388c4e Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 13:41:47 +0100 Subject: [PATCH 10/34] fix: multi-stage build to reduce CVE surface Use multi-stage build to compile psycopg2/pysodium in builder stage. Final image only has runtime libs (libsodium23, libpq5) without gcc, *-dev packages, and build toolchain that carry most CVEs. --- docker/Dockerfile | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index b601587..c7a9d65 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,12 +1,20 @@ -FROM matrixdotorg/synapse:v1.148.0 -LABEL maintainer="AirGap Team " +FROM matrixdotorg/synapse:v1.148.0 AS builder RUN apt-get update && \ apt-get install -y --no-install-recommends libsodium-dev gcc && \ - rm -rf /var/lib/apt/lists/* && \ pip install --no-cache-dir psycopg2 pysodium && \ + rm -rf /var/lib/apt/lists/* + +FROM matrixdotorg/synapse:v1.148.0 + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libsodium23 libpq5 && \ + rm -rf /var/lib/apt/lists/* && \ + apt-get purge -y --auto-remove && \ mkdir -p /keys +COPY --from=builder /usr/local/lib/python3.13/site-packages/ /usr/local/lib/python3.13/site-packages/ + COPY crypto_auth_provider.py /usr/local/lib/python3.13/site-packages/ COPY homeserver.yaml /config/ COPY synapse.log.config /config/ From 0ce7468d06e4e6039953ffbdcc522a78d5e927e1 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 14:12:49 +0100 Subject: [PATCH 11/34] chore: switch license from AGPL-3.0 to MIT --- LICENSE | 682 ++------------------------------------------------------ 1 file changed, 21 insertions(+), 661 deletions(-) diff --git a/LICENSE b/LICENSE index be3f7b2..90ffe8c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,661 +1,21 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. +MIT License + +Copyright (c) 2026 Anthony Pham + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 09366f24c8f9bad7101265d4c0ebc263123c154c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 13:13:56 +0000 Subject: [PATCH 12/34] chore(deps): update aquasecurity/trivy-action action to v0.34.1 --- .github/workflows/security-scan.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 17e7c66..7ed8d1e 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -39,7 +39,7 @@ jobs: cache-from: type=gha - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@0.34.1 with: image-ref: ${{ env.TEST_TAG }} format: "sarif" @@ -53,7 +53,7 @@ jobs: sarif_file: "trivy-results.sarif" - name: Trivy summary (table) - uses: aquasecurity/trivy-action@0.28.0 + uses: aquasecurity/trivy-action@0.34.1 if: always() with: image-ref: ${{ env.TEST_TAG }} From 72e5627400e706b7561980b45f07355c4c0bd803 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 13:14:01 +0000 Subject: [PATCH 13/34] chore(deps): update actions/checkout action to v6 --- .github/workflows/security-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 17e7c66..35a7d2a 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -25,7 +25,7 @@ jobs: security-events: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 From 2bb7d43ac3af22b69929932fe0a45a66df7ea364 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 13:14:05 +0000 Subject: [PATCH 14/34] chore(deps): update github/codeql-action action to v4 --- .github/workflows/security-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 17e7c66..02456af 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -47,7 +47,7 @@ jobs: severity: "CRITICAL,HIGH" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 if: always() with: sarif_file: "trivy-results.sarif" From 472da391e09899797da582f3cdf99f14ef2340f2 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:00:26 +0100 Subject: [PATCH 15/34] feat: enable automerge for all Renovate updates --- renovate.json | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/renovate.json b/renovate.json index 7839e6d..19a787a 100644 --- a/renovate.json +++ b/renovate.json @@ -2,20 +2,14 @@ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "forkProcessing": "enabled", + "automerge": true, + "automergeType": "pr", "packageRules": [ { - "description": "Auto-merge minor/patch updates for GitHub Actions", - "matchManagers": ["github-actions"], + "description": "Auto-merge all updates (CI must pass via branch protection)", + "matchManagers": ["github-actions", "dockerfile"], "automerge": true, "automergeType": "pr" - }, - { - "description": "Auto-merge patch updates for Synapse base image", - "matchDatasources": ["docker"], - "matchPackageNames": ["matrixdotorg/synapse"], - "automerge": true, - "automergeType": "pr", - "matchUpdateTypes": ["patch"] } ] } From 39f1b27a6af2c098ed97ae7a9f1ec5383905e9d1 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:02:52 +0100 Subject: [PATCH 16/34] chore: remove .gitlab issue templates --- .gitlab/issue_templates/.gitkeep | 0 .gitlab/issue_templates/Bug.md | 39 --------------------------- .gitlab/issue_templates/Epic.md | 30 --------------------- .gitlab/issue_templates/Feature.md | 43 ------------------------------ .gitlab/issue_templates/Task.md | 39 --------------------------- 5 files changed, 151 deletions(-) delete mode 100644 .gitlab/issue_templates/.gitkeep delete mode 100644 .gitlab/issue_templates/Bug.md delete mode 100644 .gitlab/issue_templates/Epic.md delete mode 100644 .gitlab/issue_templates/Feature.md delete mode 100644 .gitlab/issue_templates/Task.md diff --git a/.gitlab/issue_templates/.gitkeep b/.gitlab/issue_templates/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md deleted file mode 100644 index 7f6f091..0000000 --- a/.gitlab/issue_templates/Bug.md +++ /dev/null @@ -1,39 +0,0 @@ - - -## Summary - - - -## Steps to reproduce - - -## What is the current _bug_ behavior? - - -## What is the expected _correct_ behavior? - - -## Relevant logs, screenshots and/or links - - -## Possible fixes / approach - - -## Additional information - - ----- - -/estimate - - -/label ~"type::bug" ~"proj::beacon" - - diff --git a/.gitlab/issue_templates/Epic.md b/.gitlab/issue_templates/Epic.md deleted file mode 100644 index 67831b9..0000000 --- a/.gitlab/issue_templates/Epic.md +++ /dev/null @@ -1,30 +0,0 @@ - - -## Summary - - - - -## Which issues need to be completed - - - - - - - -/label ~2141 ~"proj::beacon" - - - - diff --git a/.gitlab/issue_templates/Feature.md b/.gitlab/issue_templates/Feature.md deleted file mode 100644 index 70b8732..0000000 --- a/.gitlab/issue_templates/Feature.md +++ /dev/null @@ -1,43 +0,0 @@ - - -## Summary - - - - -## What is the expected behavior? - - - - -## Relevant Mockups, Screenshots and/or links - - - - -## Possible fixes / approach - - - - -## Additional information - - - - - - - -/estimate - - - - -/label ~"type::feature" ~"proj::beacon" - diff --git a/.gitlab/issue_templates/Task.md b/.gitlab/issue_templates/Task.md deleted file mode 100644 index 8952ffe..0000000 --- a/.gitlab/issue_templates/Task.md +++ /dev/null @@ -1,39 +0,0 @@ - - -## Summary - - - - -## What steps need to be done? - - - - -## Relevant Mockups, Screenshots and/or links - - - - -## Additional information - - - - - - - -/estimate - - - - -/label ~"type::task" ~"proj::beacon" - - From 812fe3f6ce671bd7f20ca9fed112fb08be5f188a Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:15:14 +0100 Subject: [PATCH 17/34] fix: update docker-compose images and fix issues - postgres:18.2 (doesn't exist) -> postgres:17 - redis:6.2.6-buster (EOL) -> redis:7-alpine - Remove deprecated 'version' key - Add missing redis dependency in basic compose - Update beacon-node image to ghcr.io/apham0001/beacon-node:latest --- samples/docker-compose-optimized.yml | 60 +++++++++++++--------------- samples/docker-compose.yml | 18 ++++----- 2 files changed, 35 insertions(+), 43 deletions(-) diff --git a/samples/docker-compose-optimized.yml b/samples/docker-compose-optimized.yml index 2843fb0..3754f3d 100644 --- a/samples/docker-compose-optimized.yml +++ b/samples/docker-compose-optimized.yml @@ -1,7 +1,6 @@ -version: "3.1" services: postgres: - image: postgres:18.2 + image: postgres:17 restart: always # IMPORTANT: shm_size must be larger than shared_buffers to prevent Docker shared memory errors shm_size: '2gb' @@ -14,24 +13,24 @@ services: LC_ALL: 'C' LC_COLLATE: 'C' LC_CTYPE: 'C' - ENCODING: "UTF8" + ENCODING: "UTF8" POSTGRES_INITDB_ARGS: "-E UTF8" # Performance-optimized PostgreSQL settings for 16GB system with 8GB available command: > postgres - -c shared_buffers=1GB # Main cache for frequently accessed data (default: 128MB) - -c effective_cache_size=4GB # Hint to query planner about OS cache, not actual allocation (default: 4GB) - -c work_mem=64MB # Memory per sort/hash operation - critical for 73KB+ messages (default: 4MB) - -c maintenance_work_mem=512MB # Memory for VACUUM, CREATE INDEX - prevents vacuum issues (default: 64MB) - -c max_connections=300 # Supports 80 Synapse connections + overhead (default: 100) - -c random_page_cost=1.1 # Optimized for SSD, lower = favor index scans (default: 4.0 for HDD) - -c checkpoint_completion_target=0.9 # Spread checkpoint writes over 90% of interval, reduces I/O spikes (default: 0.5) - -c autovacuum_vacuum_scale_factor=0.05 # Vacuum when 5% of table changes, prevents bloat (default: 0.2 = 20%) - -c min_wal_size=2GB # Minimum WAL size - reduces frequent WAL recycling (default: 80MB) - -c max_wal_size=4GB # Maximum WAL size before checkpoint forced (default: 1GB) - -c wal_buffers=32MB # WAL write buffer - improves write performance (default: -1 = 1/32 of shared_buffers) - -c autovacuum_max_workers=4 # Parallel vacuum workers - faster cleanup (default: 3) - -c autovacuum_analyze_scale_factor=0.02 # Analyze when 2% changes, keeps statistics fresh (default: 0.1 = 10%) + -c shared_buffers=1GB + -c effective_cache_size=4GB + -c work_mem=64MB + -c maintenance_work_mem=512MB + -c max_connections=300 + -c random_page_cost=1.1 + -c checkpoint_completion_target=0.9 + -c autovacuum_vacuum_scale_factor=0.05 + -c min_wal_size=2GB + -c max_wal_size=4GB + -c wal_buffers=32MB + -c autovacuum_max_workers=4 + -c autovacuum_analyze_scale_factor=0.02 volumes: - ./postgres_data:/var/lib/postgresql/data logging: @@ -40,14 +39,13 @@ services: max-file: "3" redis: - image: redis:6.2.6-buster + image: redis:7-alpine restart: always - # Conservative Redis configuration - only setting memory limit command: > - redis-server - --maxmemory 1gb # Limit Redis memory usage to prevent OOM - --maxmemory-policy allkeys-lru # When limit hit, evict least recently used keys - expose: + redis-server + --maxmemory 1gb + --maxmemory-policy allkeys-lru + expose: - "6379" volumes: - ./redis_data:/data @@ -57,18 +55,17 @@ services: max-file: "3" tezos-synapse: - # Update this to your new image version after building with updated homeserver.yaml - image: airgapdocker/beacon-node:optimized + image: ghcr.io/apham0001/beacon-node:latest restart: always depends_on: - postgres - redis ports: - - "8008:8008" # Main client/federation port - - "8083:8083" # Worker 1 - - "8084:8084" # Worker 2 - - "8085:8085" # Worker 3 - - "8086:8086" # Worker 4 + - "8008:8008" + - "8083:8083" + - "8084:8084" + - "8085:8085" + - "8086:8086" environment: DB_HOST: postgres DB_USER: synapse @@ -78,8 +75,6 @@ services: SIGNING_KEY: "YOUR_SIGNING_KEY" REGISTRATION_SHARED_SECRET: "CHANGE_ME_RANDOM_SECRET" SERVER_REGION: "your-region" - # Optional: Sentry error tracking (NOTE: Requires additional setup - see docs/PERFORMANCE_IMPROVEMENTS.md) - # SENTRY_DSN: "https://fc05bbc75d345906ed15de7d8ef76fc7@reporting.papers.tech/17" volumes: - ./synapse_data:/data logging: @@ -87,10 +82,9 @@ services: max-size: "10m" max-file: "3" -# Optional: Add networks for better isolation networks: default: driver: bridge ipam: config: - - subnet: 172.20.0.0/16 \ No newline at end of file + - subnet: 172.20.0.0/16 diff --git a/samples/docker-compose.yml b/samples/docker-compose.yml index 0b89935..03601e0 100644 --- a/samples/docker-compose.yml +++ b/samples/docker-compose.yml @@ -1,8 +1,6 @@ -version: "3.5" - services: postgres: - image: postgres:18.2 + image: postgres:17 environment: POSTGRES_USER: synapse POSTGRES_PASSWORD: synapsepassword @@ -17,16 +15,17 @@ services: volumes: - ./postgres_data:/var/lib/postgresql/data redis: - image: redis:6.2.6-buster - expose: + image: redis:7-alpine + expose: - "6379" tezos-synapse: - image: beacon-node + image: ghcr.io/apham0001/beacon-node:latest depends_on: - postgres + - redis ports: - - "8008:8008" # 8008 is exposed. Route all traffic from port 443 and 8448 to this port - - "8083:8083" # exposing the workers for loadbalancing + - "8008:8008" + - "8083:8083" - "8084:8084" - "8085:8085" - "8086:8086" @@ -38,6 +37,5 @@ services: SERVER_NAME: matrix.papers.tech SIGNING_KEY: "dummy signing key" SERVER_REGION: EU - volumes: - - ./data/synapse-config:/data \ No newline at end of file + - ./data/synapse-config:/data From e67541021cc8f58650cdeb815164486fe06d3057 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:18:13 +0100 Subject: [PATCH 18/34] fix: update docker-compose images, remove optimized variant - postgres:18.2 (doesn't exist) -> postgres:16-bookworm - redis:6.2.6-buster (EOL) -> redis:8-bookworm - Remove deprecated 'version' key - Add missing redis dependency - Update beacon-node image to ghcr.io - Remove docker-compose-optimized.yml --- samples/docker-compose-optimized.yml | 90 ---------------------------- samples/docker-compose.yml | 8 +-- 2 files changed, 4 insertions(+), 94 deletions(-) delete mode 100644 samples/docker-compose-optimized.yml diff --git a/samples/docker-compose-optimized.yml b/samples/docker-compose-optimized.yml deleted file mode 100644 index 3754f3d..0000000 --- a/samples/docker-compose-optimized.yml +++ /dev/null @@ -1,90 +0,0 @@ -services: - postgres: - image: postgres:17 - restart: always - # IMPORTANT: shm_size must be larger than shared_buffers to prevent Docker shared memory errors - shm_size: '2gb' - environment: - POSTGRES_USER: synapse - POSTGRES_PASSWORD: "CHANGE_ME_STRONG_PASSWORD" - POSTGRES_DB: synapse - LANG: 'C' - LANGUAGE: 'C' - LC_ALL: 'C' - LC_COLLATE: 'C' - LC_CTYPE: 'C' - ENCODING: "UTF8" - POSTGRES_INITDB_ARGS: "-E UTF8" - # Performance-optimized PostgreSQL settings for 16GB system with 8GB available - command: > - postgres - -c shared_buffers=1GB - -c effective_cache_size=4GB - -c work_mem=64MB - -c maintenance_work_mem=512MB - -c max_connections=300 - -c random_page_cost=1.1 - -c checkpoint_completion_target=0.9 - -c autovacuum_vacuum_scale_factor=0.05 - -c min_wal_size=2GB - -c max_wal_size=4GB - -c wal_buffers=32MB - -c autovacuum_max_workers=4 - -c autovacuum_analyze_scale_factor=0.02 - volumes: - - ./postgres_data:/var/lib/postgresql/data - logging: - options: - max-size: "10m" - max-file: "3" - - redis: - image: redis:7-alpine - restart: always - command: > - redis-server - --maxmemory 1gb - --maxmemory-policy allkeys-lru - expose: - - "6379" - volumes: - - ./redis_data:/data - logging: - options: - max-size: "10m" - max-file: "3" - - tezos-synapse: - image: ghcr.io/apham0001/beacon-node:latest - restart: always - depends_on: - - postgres - - redis - ports: - - "8008:8008" - - "8083:8083" - - "8084:8084" - - "8085:8085" - - "8086:8086" - environment: - DB_HOST: postgres - DB_USER: synapse - DB_PASS: "CHANGE_ME_STRONG_PASSWORD" - DB_NAME: synapse - SERVER_NAME: "YOUR_SERVER_NAME.example.com" - SIGNING_KEY: "YOUR_SIGNING_KEY" - REGISTRATION_SHARED_SECRET: "CHANGE_ME_RANDOM_SECRET" - SERVER_REGION: "your-region" - volumes: - - ./synapse_data:/data - logging: - options: - max-size: "10m" - max-file: "3" - -networks: - default: - driver: bridge - ipam: - config: - - subnet: 172.20.0.0/16 diff --git a/samples/docker-compose.yml b/samples/docker-compose.yml index 03601e0..83ea448 100644 --- a/samples/docker-compose.yml +++ b/samples/docker-compose.yml @@ -1,6 +1,6 @@ services: postgres: - image: postgres:17 + image: postgres:16-bookworm environment: POSTGRES_USER: synapse POSTGRES_PASSWORD: synapsepassword @@ -15,7 +15,7 @@ services: volumes: - ./postgres_data:/var/lib/postgresql/data redis: - image: redis:7-alpine + image: redis:8-bookworm expose: - "6379" tezos-synapse: @@ -24,8 +24,8 @@ services: - postgres - redis ports: - - "8008:8008" - - "8083:8083" + - "8008:8008" # 8008 is exposed. Route all traffic from port 443 and 8448 to this port + - "8083:8083" # exposing the workers for loadbalancing - "8084:8084" - "8085:8085" - "8086:8086" From 78d40fde85492eaa1cf6bf543c3fc5e8f4e436cc Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:19:39 +0100 Subject: [PATCH 19/34] fix: add signing key example and missing env vars --- samples/docker-compose.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/samples/docker-compose.yml b/samples/docker-compose.yml index 83ea448..b7aaf5f 100644 --- a/samples/docker-compose.yml +++ b/samples/docker-compose.yml @@ -34,8 +34,12 @@ services: DB_USER: synapse DB_PASS: synapsepassword DB_NAME: synapse - SERVER_NAME: matrix.papers.tech - SIGNING_KEY: "dummy signing key" + SERVER_NAME: localhost SERVER_REGION: EU + # Generate a real signing key with: + # docker run --rm --entrypoint="" ghcr.io/apham0001/beacon-node:latest \ + # python -m synapse.app.homeserver --generate-keys --config-path /dev/null 2>/dev/null && cat /config/signing.key + # Or use: python -c "import signedjson.key; import sys; key=signedjson.key.generate_signing_key('0'); signedjson.key.write_signing_keys(sys.stdout, [key])" + SIGNING_KEY: "ed25519 a_fake AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" volumes: - ./data/synapse-config:/data From 2603a4f011315debb1d58f7b0c2c648022ecdf33 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:24:48 +0000 Subject: [PATCH 20/34] chore(deps): update postgres docker tag to v18 --- samples/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/docker-compose.yml b/samples/docker-compose.yml index b7aaf5f..e39383c 100644 --- a/samples/docker-compose.yml +++ b/samples/docker-compose.yml @@ -1,6 +1,6 @@ services: postgres: - image: postgres:16-bookworm + image: postgres:18-bookworm environment: POSTGRES_USER: synapse POSTGRES_PASSWORD: synapsepassword From 632c4e631829c04a8842225e47dc290fb18cfadf Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:24:57 +0100 Subject: [PATCH 21/34] chore: rename samples/ to docker-compose/ --- {samples => docker-compose}/docker-compose.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {samples => docker-compose}/docker-compose.yml (100%) diff --git a/samples/docker-compose.yml b/docker-compose/docker-compose.yml similarity index 100% rename from samples/docker-compose.yml rename to docker-compose/docker-compose.yml From b3e831cfd18c6f6a7b366e7922ce5e4a173703ff Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:33:33 +0100 Subject: [PATCH 22/34] chore: update README with current project state --- readme.md | 152 +++++++++++++++++++----------------------------------- 1 file changed, 53 insertions(+), 99 deletions(-) diff --git a/readme.md b/readme.md index 357d040..5c7f0e5 100644 --- a/readme.md +++ b/readme.md @@ -1,128 +1,82 @@ -[![Docker Pulls](https://img.shields.io/docker/pulls/airgapdocker/beacon-node)](https://hub.docker.com/r/airgapdocker/beacon-node) +# Beacon Node -# Beacon Node Docker +A custom [Synapse](https://github.com/element-hq/synapse) Docker image with cryptographic authentication, worker support, and the beacon info module. -This Docker image will run Synapse as a single process and is a fork of the matrixdotorg/synapse docker image. +## What's different from upstream Synapse -The big difference this image has is that it include pysodium and an auth provider that is compatible with cryptographic signatures: `crypto_auth_provider.py` +- **Crypto auth provider** (`crypto_auth_provider.py`) — authentication via cryptographic signatures +- **Beacon info module** (`beacon_info_module.py`) — exposes `/_synapse/client/beacon/info` +- **pysodium** and **psycopg2** pre-installed +- **Worker mode** enabled by default (1 main process + 4 workers) +- **Max event size** increased from 64KB to 1MB +- Media repo disabled (pure communication transport layer) -By default it uses a postgres database; and is hence suited for production use. +## Quick start with Docker Compose -The image also does _not_ provide a TURN server. - -## Volumes - -By default, the image expects a single volume, located at `/data`, that will hold: - -- keys; -- temporary data; +```bash +git clone https://github.com/apham0001/beacon-node.git +cd beacon-node/docker-compose +# Edit docker-compose.yml: set SERVER_NAME and SIGNING_KEY +docker compose up -d +``` -## Dependencies +## Environment variables -We require a postgres database connection. The environment variable `SERVER_NAME` controls how your matrix node will be called and reached, this _needs_ to be a fqdn which will be forwarding requests on port 8080 and 8448 to this container. +| Variable | Required | Description | +|----------|----------|-------------| +| `SERVER_NAME` | Yes | Matrix server FQDN (e.g. `beacon-node-1.octez.io`) | +| `DB_HOST` | Yes | PostgreSQL hostname | +| `DB_USER` | Yes | PostgreSQL username | +| `DB_PASS` | Yes | PostgreSQL password | +| `DB_NAME` | Yes | PostgreSQL database name | +| `SIGNING_KEY` | Yes | Ed25519 signing key (see below) | +| `SERVER_REGION` | Yes | Geographic region for beacon info | -## Service Ports +### Generating a signing key -Port 8008 is required for the actual matrix service and will be your endpoint. +```bash +docker run --rm --entrypoint="" ghcr.io/apham0001/beacon-node:latest \ + python -c "import signedjson.key, sys; signedjson.key.write_signing_keys(sys.stdout, [signedjson.key.generate_signing_key('0')])" +``` -Note: in order for federation to work you will need: +## Ports -1. SSL/TLS enabled for your domain -2. have that SSL/TLS setup for port 8448 and 443 -3. forward SSL/TLS traffic from 8448 to 8008 of this container -4. forward SSL/TLS traffic from 443 to 8008 of this container +| Port | Service | +|------|---------| +| 8008 | Main Synapse (client + federation) | +| 8083 | Worker 1 | +| 8084 | Worker 2 | +| 8085 | Worker 3 | +| 8086 | Worker 4 | -## Running beacon-node using docker +Route all external traffic (443, 8448) to port 8008 with TLS termination. -You can start beacon-node as follows (currently we support only postgres setups and expect that the domain given in "SERVER_NAME" is also where this container will be reachable on port 8080 for the letsencrypt request): +## Running with Docker -``` +```bash docker run -d --name beacon-node \ - --mount type=volume,src=synapse-data,dst=/data \ - -p 8080:8080 \ -p 8008:8008 \ -e SERVER_NAME=matrix.example.com \ -e DB_HOST=postgres \ -e DB_USER=synapse \ -e DB_NAME=synapse \ -e DB_PASS=password \ - airgapdocker/beacon-node:latest + -e SIGNING_KEY="ed25519 a_0 " \ + -e SERVER_REGION=EU \ + ghcr.io/apham0001/beacon-node:latest ``` -## Running beacon-node using docker-compose - -You can start beacon-node as follows (currently we support only postgres setups and expect that the domain given in "SERVER_NAME" is also where this container will be reachable): - -``` -git clone -cd beacon-node/samples -vim docker-compose.yml # edit according to your likings: SERVER_NAME must be changed! -docker-compose up -d -``` +## Building the image -## Sample Nginx configuration - -This is a sample configuration of `nginx` that will route all the traffic to the correct port. The certificates were added by Certbot and provided by letsencrypt. - -```nginx - -upstream matrix_workers { - server localhost:8083; - server localhost:8084; - server localhost:8085; - server localhost:8086; - } - -server { - listen 8448 ssl; - listen [::]:8448 ssl; - - server_name MY_SERVER_DOMAIN; - - location ~* ^(\/_matrix\/client\/(v2_alpha|r0)\/sync) { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://matrix_workers; - client_max_body_size 50M; - } - - location ~* ^(\/_matrix\/client\/(api/v1|r0|unstable)\/rooms\/.*\/(join|invite|leave|ban|unban|kick)) { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://matrix_workers; - client_max_body_size 50M; - } - - - location ~* ^(\/_matrix\/client\/(api/v1|r0|unstable)\/login) { - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://matrix_workers; - client_max_body_size 50M; - } - - location / { - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $http_host; - proxy_pass http://localhost:8008; - } - - listen [::]:443 ssl ipv6only=on; # managed by Certbot - listen 443 ssl; # managed by Certbot - ssl_certificate /etc/letsencrypt/live/MY_SERVER_DOMAIN/fullchain.pem; # managed by Certbot - ssl_certificate_key /etc/letsencrypt/live/MY_SERVER_DOMAIN/privkey.pem; # managed by Certbot - include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot -} +```bash +cd docker +docker build -t beacon-node . ``` -## Running beacon-node using kubernetes +## Helm chart -See the k8s folder in this project for a production ready k8s setup. +A Helm chart is available for Kubernetes deployments. See [tezos-infra/iac/charts/beacon-node](https://gitlab.com/tezos-infra/iac/charts/-/tree/main/beacon-node). -## Running beacon-node directly +## License -Our requirements to any beacon-node installation are minimal. Check `docker/homeserver.yaml` for the configuration and make sure to place `docker/crypto_auth_provider.py` to a place where it can be picked up by beacon-node (the Dockerfile is quite straight forward and the best documentation). +[MIT](LICENSE) From a26306dfe65ccbfb473a2c618277b4c3ef146e26 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:34:40 +0100 Subject: [PATCH 23/34] chore: remove helm chart reference --- readme.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/readme.md b/readme.md index 5c7f0e5..4a905c1 100644 --- a/readme.md +++ b/readme.md @@ -73,10 +73,6 @@ cd docker docker build -t beacon-node . ``` -## Helm chart - -A Helm chart is available for Kubernetes deployments. See [tezos-infra/iac/charts/beacon-node](https://gitlab.com/tezos-infra/iac/charts/-/tree/main/beacon-node). - ## License [MIT](LICENSE) From e1cf237afb0077a3306044d4b074156dafcf3e0e Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 15:53:25 +0100 Subject: [PATCH 24/34] chore: switch base image to ghcr.io/element-hq/synapse --- .hadolint.yaml | 3 +++ docker/Dockerfile | 4 ++-- renovate.json | 8 ++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 .hadolint.yaml diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..d4918af --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,3 @@ +ignored: + - DL3008 # apt pin versions - managed by base image + - DL3013 # pip pin versions - managed by base image diff --git a/docker/Dockerfile b/docker/Dockerfile index c7a9d65..98d0a6f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,11 +1,11 @@ -FROM matrixdotorg/synapse:v1.148.0 AS builder +FROM ghcr.io/element-hq/synapse:v1.148.0 AS builder RUN apt-get update && \ apt-get install -y --no-install-recommends libsodium-dev gcc && \ pip install --no-cache-dir psycopg2 pysodium && \ rm -rf /var/lib/apt/lists/* -FROM matrixdotorg/synapse:v1.148.0 +FROM ghcr.io/element-hq/synapse:v1.148.0 RUN apt-get update && \ apt-get install -y --no-install-recommends libsodium23 libpq5 && \ diff --git a/renovate.json b/renovate.json index 19a787a..08a926e 100644 --- a/renovate.json +++ b/renovate.json @@ -10,6 +10,14 @@ "matchManagers": ["github-actions", "dockerfile"], "automerge": true, "automergeType": "pr" + }, + { + "description": "Auto-merge patch updates for Synapse base image", + "matchDatasources": ["docker"], + "matchPackageNames": ["ghcr.io/element-hq/synapse"], + "automerge": true, + "automergeType": "pr", + "matchUpdateTypes": ["patch"] } ] } From 0761c6c93ed54ba45a821928a68d923ebe54989f Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 19:53:58 +0100 Subject: [PATCH 25/34] fix: update known_servers from papers.tech to octez.io --- docker/homeserver.yaml | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/docker/homeserver.yaml b/docker/homeserver.yaml index 41ec4cb..06a7673 100644 --- a/docker/homeserver.yaml +++ b/docker/homeserver.yaml @@ -155,18 +155,14 @@ modules: - module: beacon_info_module.BeaconInfoModule config: known_servers: - - "beacon-node-1.diamond.papers.tech" - - "beacon-node-1.sky.papers.tech" - - "beacon-node-2.sky.papers.tech" - - "beacon-node-1.hope.papers.tech" - - "beacon-node-1.hope-2.papers.tech" - - "beacon-node-1.hope-3.papers.tech" - - "beacon-node-1.hope-4.papers.tech" - - "beacon-node-1.hope-5.papers.tech" - - "beacon-node-1.beacon-server-1.papers.tech" - - "beacon-node-1.beacon-server-2.papers.tech" - - "beacon-node-1.beacon-server-3.papers.tech" - - "beacon-node-1.beacon-server-4.papers.tech" + - "beacon-node-1.octez.io" + - "beacon-node-2.octez.io" + - "beacon-node-3.octez.io" + - "beacon-node-4.octez.io" + - "beacon-node-5.octez.io" + - "beacon-node-6.octez.io" + - "beacon-node-7.octez.io" + - "beacon-node-8.octez.io" instance_map: main: From c26a3e39e496c93dcdf137691883f621e3829d0c Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 21:29:21 +0100 Subject: [PATCH 26/34] feat: serve well-known Matrix endpoints from Synapse module Add /.well-known/matrix/server and /.well-known/matrix/client endpoints directly in beacon_info_module.py instead of relying on nginx ingress server-snippet annotations (deprecated since ingress-nginx 1.9). --- docker/beacon_info_module.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docker/beacon_info_module.py b/docker/beacon_info_module.py index 6ac16a0..529c094 100644 --- a/docker/beacon_info_module.py +++ b/docker/beacon_info_module.py @@ -6,6 +6,22 @@ logger = logging.getLogger(__name__) + +class WellKnownServerResource(Resource): + def render_GET(self, request): + server_name = os.environ.get("SERVER_NAME", "localhost") + request.setHeader(b"content-type", b"application/json") + return json.dumps({"m.server": f"{server_name}:443"}).encode("utf-8") + + +class WellKnownClientResource(Resource): + def render_GET(self, request): + server_name = os.environ.get("SERVER_NAME", "localhost") + request.setHeader(b"content-type", b"application/json") + request.setHeader(b"Access-Control-Allow-Origin", b"*") + return json.dumps({"m.homeserver": {"base_url": f"https://{server_name}"}}).encode("utf-8") + + class BeaconInfoModule(Resource): def __init__(self, config, api): super().__init__() @@ -13,12 +29,14 @@ def __init__(self, config, api): self.config = config self.api = api self.api.register_web_resource(path="/_synapse/client/beacon/info", resource=self) + self.api.register_web_resource(path="/.well-known/matrix/server", resource=WellKnownServerResource()) + self.api.register_web_resource(path="/.well-known/matrix/client", resource=WellKnownClientResource()) def render(self, request): request.setHeader(b"content-type", b"application/json; charset=utf-8") - request.setHeader(b"Access-Control-Allow-Origin",b"*") + request.setHeader(b"Access-Control-Allow-Origin", b"*") return json.dumps({ - "region":os.environ.get("SERVER_REGION", "region not set"), - "known_servers":self.config.get("known_servers",[]), + "region": os.environ.get("SERVER_REGION", "region not set"), + "known_servers": self.config.get("known_servers", []), "timestamp": time.time() }).encode("utf-8") From e83ec15f5dab54b2f599442bd431909daea86919 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 21:47:38 +0100 Subject: [PATCH 27/34] fix: use parent Resource for well-known endpoints The previous approach registered two separate resources at /.well-known/matrix/server and /.well-known/matrix/client, which caused a KeyError in Twisted's resource tree when both paths share the /.well-known/matrix prefix. Fix: use a single WellKnownMatrixResource parent with putChild for server and client sub-resources. Also fix docker-compose PG 18 mount point (/var/lib/postgresql instead of /var/lib/postgresql/data) and update readme. --- docker-compose/docker-compose.yml | 2 +- docker/beacon_info_module.py | 16 ++++++++++++++-- readme.md | 10 +++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index e39383c..1b45b9f 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -13,7 +13,7 @@ services: ENCODING: "UTF8" POSTGRES_INITDB_ARGS: "-E UTF8" volumes: - - ./postgres_data:/var/lib/postgresql/data + - ./postgres_data:/var/lib/postgresql redis: image: redis:8-bookworm expose: diff --git a/docker/beacon_info_module.py b/docker/beacon_info_module.py index 529c094..46fee77 100644 --- a/docker/beacon_info_module.py +++ b/docker/beacon_info_module.py @@ -8,6 +8,8 @@ class WellKnownServerResource(Resource): + isLeaf = True + def render_GET(self, request): server_name = os.environ.get("SERVER_NAME", "localhost") request.setHeader(b"content-type", b"application/json") @@ -15,6 +17,8 @@ def render_GET(self, request): class WellKnownClientResource(Resource): + isLeaf = True + def render_GET(self, request): server_name = os.environ.get("SERVER_NAME", "localhost") request.setHeader(b"content-type", b"application/json") @@ -22,6 +26,15 @@ def render_GET(self, request): return json.dumps({"m.homeserver": {"base_url": f"https://{server_name}"}}).encode("utf-8") +class WellKnownMatrixResource(Resource): + """Parent resource for /.well-known/matrix/* endpoints.""" + + def __init__(self): + super().__init__() + self.putChild(b"server", WellKnownServerResource()) + self.putChild(b"client", WellKnownClientResource()) + + class BeaconInfoModule(Resource): def __init__(self, config, api): super().__init__() @@ -29,8 +42,7 @@ def __init__(self, config, api): self.config = config self.api = api self.api.register_web_resource(path="/_synapse/client/beacon/info", resource=self) - self.api.register_web_resource(path="/.well-known/matrix/server", resource=WellKnownServerResource()) - self.api.register_web_resource(path="/.well-known/matrix/client", resource=WellKnownClientResource()) + self.api.register_web_resource(path="/.well-known/matrix", resource=WellKnownMatrixResource()) def render(self, request): request.setHeader(b"content-type", b"application/json; charset=utf-8") diff --git a/readme.md b/readme.md index 4a905c1..9532464 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ A custom [Synapse](https://github.com/element-hq/synapse) Docker image with cryp ## What's different from upstream Synapse - **Crypto auth provider** (`crypto_auth_provider.py`) — authentication via cryptographic signatures -- **Beacon info module** (`beacon_info_module.py`) — exposes `/_synapse/client/beacon/info` +- **Beacon info module** (`beacon_info_module.py`) — exposes `/_synapse/client/beacon/info` and `/.well-known/matrix/*` endpoints - **pysodium** and **psycopg2** pre-installed - **Worker mode** enabled by default (1 main process + 4 workers) - **Max event size** increased from 64KB to 1MB @@ -51,6 +51,14 @@ docker run --rm --entrypoint="" ghcr.io/apham0001/beacon-node:latest \ Route all external traffic (443, 8448) to port 8008 with TLS termination. +## Federation endpoints + +The beacon info module serves Matrix well-known endpoints directly from Synapse (no nginx snippet needed): + +- `/.well-known/matrix/server` — returns `{"m.server": ":443"}` +- `/.well-known/matrix/client` — returns `{"m.homeserver": {"base_url": "https://"}}` +- `/_synapse/client/beacon/info` — returns region, known servers list, and timestamp + ## Running with Docker ```bash From 5ed4926fbd88b098962969a7fc8794b9f9cb5494 Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 21:48:26 +0100 Subject: [PATCH 28/34] docs: add Redis, PostgreSQL and known servers documentation --- readme.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/readme.md b/readme.md index 9532464..ae3d0f7 100644 --- a/readme.md +++ b/readme.md @@ -39,6 +39,29 @@ docker run --rm --entrypoint="" ghcr.io/apham0001/beacon-node:latest \ python -c "import signedjson.key, sys; signedjson.key.write_signing_keys(sys.stdout, [signedjson.key.generate_signing_key('0')])" ``` +## Dependencies + +### PostgreSQL + +Required for all Synapse data (rooms, messages, events, users, keys). Synapse requires C locale and UTF8 encoding. + +The image expects the database to be reachable at `DB_HOST` on port 5432. The entrypoint waits for PG to be ready before starting Synapse. + +### Redis + +Required for inter-process communication between the main process and the 4 workers. The image has `host: redis` **hardcoded** in `shared_config.yaml`. In Docker Compose the service name `redis` resolves directly. In Kubernetes, an ExternalName service aliases `redis` to the actual Redis service. + +Redis does not require authentication (Synapse default). + +## Known servers + +The list of known beacon servers is hardcoded in `docker/homeserver.yaml` and served via the `/_synapse/client/beacon/info` endpoint. Clients use this list for server discovery and failover. + +Current servers: +- `beacon-node-1.octez.io` through `beacon-node-8.octez.io` + +To update the list, edit `docker/homeserver.yaml` under `modules > beacon_info_module > config > known_servers` and rebuild the image. + ## Ports | Port | Service | From b72ace2bb8558906ff1edb0684011ff49c539dcc Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 26 Feb 2026 21:50:54 +0100 Subject: [PATCH 29/34] feat: make Redis host configurable via REDIS_HOST env var Add {{REDIS_HOST}} placeholder in shared_config.yaml and sed in entrypoint. Defaults to 'redis' if not set (backwards compatible). This removes the need for the ExternalName service hack in k8s. --- docker-compose/docker-compose.yml | 1 + docker/shared_config.yaml | 2 +- docker/synctl_entrypoint.sh | 1 + readme.md | 3 ++- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 1b45b9f..efae9df 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -36,6 +36,7 @@ services: DB_NAME: synapse SERVER_NAME: localhost SERVER_REGION: EU + REDIS_HOST: redis # Generate a real signing key with: # docker run --rm --entrypoint="" ghcr.io/apham0001/beacon-node:latest \ # python -m synapse.app.homeserver --generate-keys --config-path /dev/null 2>/dev/null && cat /config/signing.key diff --git a/docker/shared_config.yaml b/docker/shared_config.yaml index e3a8dc4..353b5e1 100644 --- a/docker/shared_config.yaml +++ b/docker/shared_config.yaml @@ -17,7 +17,7 @@ listeners: redis: enabled: true - host: redis + host: {{REDIS_HOST}} report_stats: false diff --git a/docker/synctl_entrypoint.sh b/docker/synctl_entrypoint.sh index 38f5dcb..a321bab 100755 --- a/docker/synctl_entrypoint.sh +++ b/docker/synctl_entrypoint.sh @@ -5,6 +5,7 @@ sed -i "s/{{DB_HOST}}/$DB_HOST/g" /config/homeserver.yaml sed -i "s/{{DB_USER}}/$DB_USER/g" /config/homeserver.yaml sed -i "s/{{DB_PASS}}/$DB_PASS/g" /config/homeserver.yaml sed -i "s/{{DB_NAME}}/$DB_NAME/g" /config/homeserver.yaml +sed -i "s/{{REDIS_HOST}}/${REDIS_HOST:-redis}/g" /config/shared_config.yaml echo "${SIGNING_KEY}" > /config/signing.key /usr/local/bin/wait-for.sh $DB_HOST:5432 diff --git a/readme.md b/readme.md index ae3d0f7..1b9cd9b 100644 --- a/readme.md +++ b/readme.md @@ -31,6 +31,7 @@ docker compose up -d | `DB_NAME` | Yes | PostgreSQL database name | | `SIGNING_KEY` | Yes | Ed25519 signing key (see below) | | `SERVER_REGION` | Yes | Geographic region for beacon info | +| `REDIS_HOST` | No | Redis hostname (default: `redis`) | ### Generating a signing key @@ -49,7 +50,7 @@ The image expects the database to be reachable at `DB_HOST` on port 5432. The en ### Redis -Required for inter-process communication between the main process and the 4 workers. The image has `host: redis` **hardcoded** in `shared_config.yaml`. In Docker Compose the service name `redis` resolves directly. In Kubernetes, an ExternalName service aliases `redis` to the actual Redis service. +Required for inter-process communication between the main process and the 4 workers. Configurable via the `REDIS_HOST` env var (defaults to `redis` if not set). Redis does not require authentication (Synapse default). From 82fea2165541dc5749415b9b0041b47805030244 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 27 Feb 2026 08:10:57 +0100 Subject: [PATCH 30/34] fix: use REDIS_HOST env var in homeserver.yaml too The homeserver.yaml had redis host hardcoded to 'redis' while shared_config.yaml used the REDIS_HOST placeholder. Both files now use {{REDIS_HOST}} with fallback to 'redis' if not set. --- docker/homeserver.yaml | 2 +- docker/synctl_entrypoint.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/homeserver.yaml b/docker/homeserver.yaml index 06a7673..afd8e84 100644 --- a/docker/homeserver.yaml +++ b/docker/homeserver.yaml @@ -134,7 +134,7 @@ url_preview_enabled: false redis: enabled: true - host: redis + host: {{REDIS_HOST}} ## Caching - Performance optimizations caches: diff --git a/docker/synctl_entrypoint.sh b/docker/synctl_entrypoint.sh index a321bab..cd32275 100755 --- a/docker/synctl_entrypoint.sh +++ b/docker/synctl_entrypoint.sh @@ -6,6 +6,7 @@ sed -i "s/{{DB_USER}}/$DB_USER/g" /config/homeserver.yaml sed -i "s/{{DB_PASS}}/$DB_PASS/g" /config/homeserver.yaml sed -i "s/{{DB_NAME}}/$DB_NAME/g" /config/homeserver.yaml sed -i "s/{{REDIS_HOST}}/${REDIS_HOST:-redis}/g" /config/shared_config.yaml +sed -i "s/{{REDIS_HOST}}/${REDIS_HOST:-redis}/g" /config/homeserver.yaml echo "${SIGNING_KEY}" > /config/signing.key /usr/local/bin/wait-for.sh $DB_HOST:5432 From 1d986a4dec4b11f1302b94b97ae6c3d102a75ebd Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 27 Feb 2026 14:59:39 +0100 Subject: [PATCH 31/34] feat: add helm chart and harden PDU size patch - Add beacon-node Helm chart (v0.4.1) with kubelauncher subcharts - Harden MAX_PDU_SIZE sed patch with strict regex and build-time verification to fail fast if upstream changes the constant --- .gitignore | 2 + charts/Chart.yaml | 27 +++ charts/README.md | 103 ++++++++++ charts/templates/NOTES.txt | 74 +++++++ charts/templates/_helpers.tpl | 135 +++++++++++++ charts/templates/configmap.yaml | 13 ++ charts/templates/deployment.yaml | 118 +++++++++++ charts/templates/ingress.yaml | 41 ++++ charts/templates/pvc.yaml | 19 ++ charts/templates/secret.yaml | 19 ++ charts/templates/service.yaml | 33 ++++ charts/templates/serviceaccount.yaml | 12 ++ charts/templates/servicemonitor.yaml | 21 ++ charts/values.yaml | 281 +++++++++++++++++++++++++++ docker/Dockerfile | 7 +- 15 files changed, 903 insertions(+), 2 deletions(-) create mode 100644 charts/Chart.yaml create mode 100644 charts/README.md create mode 100644 charts/templates/NOTES.txt create mode 100644 charts/templates/_helpers.tpl create mode 100644 charts/templates/configmap.yaml create mode 100644 charts/templates/deployment.yaml create mode 100644 charts/templates/ingress.yaml create mode 100644 charts/templates/pvc.yaml create mode 100644 charts/templates/secret.yaml create mode 100644 charts/templates/service.yaml create mode 100644 charts/templates/serviceaccount.yaml create mode 100644 charts/templates/servicemonitor.yaml create mode 100644 charts/values.yaml diff --git a/.gitignore b/.gitignore index 4058889..559d3c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ data .vscode +charts/charts/ +charts/Chart.lock diff --git a/charts/Chart.yaml b/charts/Chart.yaml new file mode 100644 index 0000000..e752449 --- /dev/null +++ b/charts/Chart.yaml @@ -0,0 +1,27 @@ +apiVersion: v2 +name: beacon-node +description: A Helm chart for deploying Beacon Node (Matrix Synapse fork with crypto authentication) +type: application +version: 0.4.1 +appVersion: "1.148.0" + +keywords: + - matrix + - synapse + - beacon + - airgap + - crypto-auth + +home: https://github.com/apham0001/beacon-node +sources: + - https://github.com/apham0001/beacon-node + +dependencies: + - name: postgresql + version: "0.2.8" + repository: https://kubelauncher.github.io/charts + condition: postgresql.enabled + - name: redis + version: "0.2.15" + repository: https://kubelauncher.github.io/charts + condition: redis.enabled diff --git a/charts/README.md b/charts/README.md new file mode 100644 index 0000000..c9487d8 --- /dev/null +++ b/charts/README.md @@ -0,0 +1,103 @@ +# beacon-node + +![Version: 0.4.1](https://img.shields.io/badge/Version-0.4.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.148.0](https://img.shields.io/badge/AppVersion-1.148.0-informational?style=flat-square) + +A Helm chart for deploying Beacon Node (Matrix Synapse fork with crypto authentication) + +**Homepage:** + +## Source Code + +* + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| https://kubelauncher.github.io/charts | postgresql | 0.2.8 | +| https://kubelauncher.github.io/charts | redis | 0.2.15 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Affinity rules for pod scheduling | +| existingSigningKeySecret | object | `{"key":"SIGNING_KEY","name":""}` | Use an existing secret for the signing key instead of creating one If set, signingKey value is ignored | +| existingSigningKeySecret.key | string | `"SIGNING_KEY"` | Key in the secret that contains the signing key value | +| existingSigningKeySecret.name | string | `""` | Name of the existing secret containing the signing key | +| externalDatabase.database | string | `"synapse"` | External PostgreSQL database name | +| externalDatabase.host | string | `""` | External PostgreSQL host | +| externalDatabase.password | string | `""` | External PostgreSQL password | +| externalDatabase.port | int | `5432` | External PostgreSQL port | +| externalDatabase.username | string | `"synapse"` | External PostgreSQL username | +| fullnameOverride | string | `""` | Override the full release name | +| image.digest | string | `""` | Image digest (optional, for pinning exact image version) Example: "sha256:abc123..." | +| image.pullPolicy | string | `"Always"` | Image pull policy | +| image.repository | string | `"ghcr.io/apham0001/beacon-node"` | Container image repository | +| image.tag | string | `"latest"` | Image tag | +| imagePullSecrets | list | `[]` | Image pull secrets for private registries | +| ingress.annotations | object | `{"cert-manager.io/cluster-issuer":"letsencrypt-prod","nginx.ingress.kubernetes.io/proxy-connect-timeout":"60","nginx.ingress.kubernetes.io/proxy-read-timeout":"300","nginx.ingress.kubernetes.io/proxy-send-timeout":"300"}` | Ingress annotations | +| ingress.className | string | `"nginx"` | Ingress class name | +| ingress.enabled | bool | `false` | Enable ingress | +| ingress.hosts | list | `[{"host":"matrix.example.com","paths":[{"path":"/","pathType":"Prefix"}]}]` | Ingress hosts configuration | +| ingress.tls | list | `[{"hosts":["matrix.example.com"],"secretName":"beacon-node-tls"}]` | Ingress TLS configuration | +| livenessProbe.failureThreshold | int | `3` | | +| livenessProbe.httpGet.path | string | `"/health"` | | +| livenessProbe.httpGet.port | string | `"http"` | | +| livenessProbe.initialDelaySeconds | int | `30` | | +| livenessProbe.periodSeconds | int | `10` | | +| livenessProbe.timeoutSeconds | int | `5` | | +| monitoring.enabled | bool | `false` | Enable ServiceMonitor for Prometheus scraping | +| monitoring.interval | string | `"30s"` | Scrape interval | +| monitoring.path | string | `"/_synapse/metrics"` | Metrics endpoint path | +| monitoring.prometheusRelease | string | `"prometheus-stack"` | Prometheus release label for service discovery | +| monitoring.scrapeTimeout | string | `"10s"` | Scrape timeout | +| nameOverride | string | `""` | Override the chart name | +| nodeSelector | object | `{}` | Node selector for pod scheduling | +| persistence.accessModes | list | `["ReadWriteOnce"]` | Access modes | +| persistence.enabled | bool | `true` | Enable persistence for Synapse data | +| persistence.size | string | `"5Gi"` | Storage size | +| persistence.storageClass | string | `""` | Storage class (leave empty for default) | +| podAnnotations | object | `{}` | Pod annotations | +| podSecurityContext | object | `{}` | Pod security context | +| postgresql.auth.database | string | `"synapse"` | PostgreSQL database name | +| postgresql.auth.password | string | `""` | PostgreSQL password (REQUIRED if postgresql.enabled=true) | +| postgresql.auth.username | string | `"synapse"` | PostgreSQL username | +| postgresql.enabled | bool | `true` | Deploy PostgreSQL as a subchart | +| postgresql.primary.configuration | string | `"max_connections = 300\n"` | PostgreSQL configuration Synapse workers use cp_min=20, cp_max=80 connections per process With 4 workers + 1 main: 5 * 80 + buffer = 300 | +| postgresql.primary.initdb | object | `{"args":"--encoding=UTF8 --lc-collate=C --lc-ctype=C"}` | initdb configuration (IMPORTANT: Synapse requires C locale) | +| postgresql.primary.persistence.enabled | bool | `true` | Enable persistence for PostgreSQL | +| postgresql.primary.persistence.size | string | `"10Gi"` | Storage size for PostgreSQL | +| postgresql.primary.resources | object | `{"limits":{"cpu":"500m","memory":"1Gi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | PostgreSQL resource limits | +| readinessProbe.failureThreshold | int | `3` | | +| readinessProbe.httpGet.path | string | `"/health"` | | +| readinessProbe.httpGet.port | string | `"http"` | | +| readinessProbe.initialDelaySeconds | int | `10` | | +| readinessProbe.periodSeconds | int | `5` | | +| readinessProbe.timeoutSeconds | int | `3` | | +| redis.architecture | string | `"standalone"` | Redis architecture (standalone or replication) | +| redis.auth.enabled | bool | `false` | Disable Redis authentication (Synapse default) | +| redis.enabled | bool | `true` | Deploy Redis as a subchart (required for workers) | +| redis.master.persistence.enabled | bool | `true` | Enable persistence for Redis | +| redis.master.persistence.size | string | `"1Gi"` | Storage size for Redis | +| redis.master.resources | object | `{"limits":{"cpu":"200m","memory":"256Mi"},"requests":{"cpu":"50m","memory":"64Mi"}}` | Redis resource limits | +| registrationSharedSecret | string | `""` | Shared secret for user registration (optional) | +| replicaCount | int | `1` | Number of replicas (only 1 supported due to Synapse architecture) | +| resources.limits.cpu | string | `"1000m"` | | +| resources.limits.memory | string | `"2Gi"` | | +| resources.requests.cpu | string | `"250m"` | | +| resources.requests.memory | string | `"512Mi"` | | +| securityContext | object | `{}` | Container security context | +| serverName | string | `"matrix.example.com"` | Matrix server name (FQDN). This MUST match your domain. Example: matrix.example.com | +| serverRegion | string | `"EU"` | Geographic region for beacon info endpoint | +| service.port | int | `8008` | Main client/federation port | +| service.type | string | `"ClusterIP"` | Service type (ClusterIP, NodePort, LoadBalancer) | +| serviceAccount.annotations | object | `{}` | Service account annotations | +| serviceAccount.create | bool | `true` | Create a service account | +| serviceAccount.name | string | `""` | Service account name (generated if not set) | +| signingKey | string | `""` | Ed25519 signing key for Matrix federation Format: "ed25519 a_XXXX " IMPORTANT: The base64-encoded seed MUST decode to exactly 32 bytes! Generate with: python3 -c "import base64,os; print(f'ed25519 a_{os.urandom(2).hex()} {base64.b64encode(os.urandom(32)).decode()}')" If not provided, a deterministic key is auto-generated based on release name. WARNING: Auto-generated keys are NOT secure for production - always provide your own key! | +| tolerations | list | `[]` | Tolerations for pod scheduling | +| workers.enabled | bool | `true` | Enable Synapse workers (4 generic workers on ports 8083-8086) Worker count is fixed at 4 in the Docker image (hardcoded worker configs) | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/templates/NOTES.txt b/charts/templates/NOTES.txt new file mode 100644 index 0000000..eab6913 --- /dev/null +++ b/charts/templates/NOTES.txt @@ -0,0 +1,74 @@ +=============================================================================== + Beacon Node has been deployed! +=============================================================================== + +Server Name: {{ .Values.serverName }} +Server Region: {{ .Values.serverRegion }} + +{{- if .Values.ingress.enabled }} + +Your beacon node is accessible at: +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + https://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} + +{{- else }} + +To access your beacon node, run: + + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "beacon-node.fullname" . }} 8008:{{ .Values.service.port }} + +Then open: http://localhost:8008 + +{{- end }} + +To verify the deployment, check the beacon info endpoint: + +{{- if .Values.ingress.enabled }} + curl https://{{ (index .Values.ingress.hosts 0).host }}/_synapse/client/beacon/info +{{- else }} + # After port-forwarding: + curl http://localhost:8008/_synapse/client/beacon/info +{{- end }} + +Expected response: + {"region": "{{ .Values.serverRegion }}", "known_servers": [...], "timestamp": ...} + +=============================================================================== + Important Notes +=============================================================================== + +{{- if not .Values.signingKey }} + +WARNING: No signing key was provided! +The deployment may fail without a valid Ed25519 signing key. + +Generate one with: + docker run --rm matrixdotorg/synapse generate + +Then upgrade the release: + helm upgrade {{ .Release.Name }} beacon-node --set signingKey="ed25519 a_XXXX " + +{{- end }} + +{{- if .Values.postgresql.enabled }} +{{- if not .Values.postgresql.auth.password }} + +WARNING: No PostgreSQL password was provided! +Please set postgresql.auth.password for production deployments. + +{{- end }} +{{- end }} + +Database: {{ include "beacon-node.postgresql.host" . }}:5432/{{ include "beacon-node.postgresql.database" . }} +Redis: {{ include "beacon-node.redis.host" . }}:6379 + +{{- if .Values.workers.enabled }} + +Workers: 4 generic workers enabled (ports 8083-8086) + +{{- end }} + +For more information, visit: https://github.com/apham0001/beacon-node diff --git a/charts/templates/_helpers.tpl b/charts/templates/_helpers.tpl new file mode 100644 index 0000000..01628b8 --- /dev/null +++ b/charts/templates/_helpers.tpl @@ -0,0 +1,135 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "beacon-node.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "beacon-node.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "beacon-node.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "beacon-node.labels" -}} +helm.sh/chart: {{ include "beacon-node.chart" . }} +{{ include "beacon-node.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "beacon-node.selectorLabels" -}} +app.kubernetes.io/name: {{ include "beacon-node.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "beacon-node.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "beacon-node.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Get the PostgreSQL host +*/}} +{{- define "beacon-node.postgresql.host" -}} +{{- if .Values.postgresql.enabled }} +{{- printf "%s-postgresql" .Release.Name }} +{{- else }} +{{- .Values.externalDatabase.host }} +{{- end }} +{{- end }} + +{{/* +Get the PostgreSQL port +*/}} +{{- define "beacon-node.postgresql.port" -}} +{{- if .Values.postgresql.enabled }} +{{- printf "5432" }} +{{- else }} +{{- .Values.externalDatabase.port | default 5432 }} +{{- end }} +{{- end }} + +{{/* +Get the PostgreSQL username +*/}} +{{- define "beacon-node.postgresql.username" -}} +{{- if .Values.postgresql.enabled }} +{{- .Values.postgresql.auth.username }} +{{- else }} +{{- .Values.externalDatabase.username }} +{{- end }} +{{- end }} + +{{/* +Get the PostgreSQL password secret name +*/}} +{{- define "beacon-node.postgresql.secretName" -}} +{{- if .Values.postgresql.enabled }} +{{- printf "%s-postgresql" .Release.Name }} +{{- else }} +{{- include "beacon-node.fullname" . }} +{{- end }} +{{- end }} + +{{/* +Get the PostgreSQL password secret key +*/}} +{{- define "beacon-node.postgresql.secretKey" -}} +{{- if .Values.postgresql.enabled }} +{{- printf "password" }} +{{- else }} +{{- printf "db-password" }} +{{- end }} +{{- end }} + +{{/* +Get the PostgreSQL database name +*/}} +{{- define "beacon-node.postgresql.database" -}} +{{- if .Values.postgresql.enabled }} +{{- .Values.postgresql.auth.database }} +{{- else }} +{{- .Values.externalDatabase.database }} +{{- end }} +{{- end }} + +{{/* +Get the Redis host +*/}} +{{- define "beacon-node.redis.host" -}} +{{- printf "%s-redis" .Release.Name }} +{{- end }} diff --git a/charts/templates/configmap.yaml b/charts/templates/configmap.yaml new file mode 100644 index 0000000..e545e7f --- /dev/null +++ b/charts/templates/configmap.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "beacon-node.fullname" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} +data: + SERVER_NAME: {{ .Values.serverName | quote }} + SERVER_REGION: {{ .Values.serverRegion | quote }} + DB_HOST: {{ include "beacon-node.postgresql.host" . | quote }} + DB_NAME: {{ include "beacon-node.postgresql.database" . | quote }} + DB_USER: {{ include "beacon-node.postgresql.username" . | quote }} + REDIS_HOST: {{ include "beacon-node.redis.host" . | quote }} diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml new file mode 100644 index 0000000..07a1f44 --- /dev/null +++ b/charts/templates/deployment.yaml @@ -0,0 +1,118 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "beacon-node.fullname" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + {{- include "beacon-node.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "beacon-node.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "beacon-node.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.image.digest }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}@{{ .Values.image.digest }}" + {{- else }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + envFrom: + - configMapRef: + name: {{ include "beacon-node.fullname" . }} + - secretRef: + name: {{ include "beacon-node.fullname" . }} + env: + {{- if and .Values.existingSigningKeySecret .Values.existingSigningKeySecret.name }} + # Signing key from existing secret (e.g., ExternalSecret) + - name: SIGNING_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.existingSigningKeySecret.name }} + key: {{ .Values.existingSigningKeySecret.key | default "SIGNING_KEY" }} + {{- end }} + # Database password from appropriate secret + - name: DB_PASS + valueFrom: + secretKeyRef: + name: {{ include "beacon-node.postgresql.secretName" . }} + key: {{ include "beacon-node.postgresql.secretKey" . }} + ports: + - name: http + containerPort: 8008 + protocol: TCP + {{- if .Values.workers.enabled }} + - name: worker-1 + containerPort: 8083 + protocol: TCP + - name: worker-2 + containerPort: 8084 + protocol: TCP + - name: worker-3 + containerPort: 8085 + protocol: TCP + - name: worker-4 + containerPort: 8086 + protocol: TCP + {{- end }} + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "beacon-node.fullname" . }} + {{- else }} + emptyDir: {} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/templates/ingress.yaml b/charts/templates/ingress.yaml new file mode 100644 index 0000000..818769e --- /dev/null +++ b/charts/templates/ingress.yaml @@ -0,0 +1,41 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "beacon-node.fullname" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ include "beacon-node.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/templates/pvc.yaml b/charts/templates/pvc.yaml new file mode 100644 index 0000000..2acccf6 --- /dev/null +++ b/charts/templates/pvc.yaml @@ -0,0 +1,19 @@ +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "beacon-node.fullname" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} +spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- end }} diff --git a/charts/templates/secret.yaml b/charts/templates/secret.yaml new file mode 100644 index 0000000..01da1ec --- /dev/null +++ b/charts/templates/secret.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "beacon-node.fullname" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} +type: Opaque +stringData: + {{- if or .Values.signingKey (not (and .Values.existingSigningKeySecret .Values.existingSigningKeySecret.name)) }} + # Signing key is required for Matrix federation + # If not provided, a deterministic key is generated based on release name (NOT SECURE FOR PRODUCTION) + SIGNING_KEY: {{ .Values.signingKey | default (printf "ed25519 a_%s %s" (substr 0 4 (sha256sum .Release.Name)) (b64enc (substr 0 32 (sha256sum (printf "%s-signing-key" .Release.Name))))) | quote }} + {{- end }} + {{- if .Values.registrationSharedSecret }} + REGISTRATION_SHARED_SECRET: {{ .Values.registrationSharedSecret | quote }} + {{- end }} + {{- if not .Values.postgresql.enabled }} + db-password: {{ .Values.externalDatabase.password | quote }} + {{- end }} diff --git a/charts/templates/service.yaml b/charts/templates/service.yaml new file mode 100644 index 0000000..7942456 --- /dev/null +++ b/charts/templates/service.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "beacon-node.fullname" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + {{- if .Values.workers.enabled }} + - port: 8083 + targetPort: 8083 + protocol: TCP + name: worker-1 + - port: 8084 + targetPort: 8084 + protocol: TCP + name: worker-2 + - port: 8085 + targetPort: 8085 + protocol: TCP + name: worker-3 + - port: 8086 + targetPort: 8086 + protocol: TCP + name: worker-4 + {{- end }} + selector: + {{- include "beacon-node.selectorLabels" . | nindent 4 }} diff --git a/charts/templates/serviceaccount.yaml b/charts/templates/serviceaccount.yaml new file mode 100644 index 0000000..b54d2f3 --- /dev/null +++ b/charts/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "beacon-node.serviceAccountName" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/templates/servicemonitor.yaml b/charts/templates/servicemonitor.yaml new file mode 100644 index 0000000..a4bbcb6 --- /dev/null +++ b/charts/templates/servicemonitor.yaml @@ -0,0 +1,21 @@ +{{- if .Values.monitoring.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "beacon-node.fullname" . }} + labels: + {{- include "beacon-node.labels" . | nindent 4 }} + {{- if .Values.monitoring.prometheusRelease }} + release: {{ .Values.monitoring.prometheusRelease }} + {{- end }} +spec: + jobLabel: app.kubernetes.io/name + selector: + matchLabels: + {{- include "beacon-node.selectorLabels" . | nindent 6 }} + endpoints: + - port: http + path: {{ .Values.monitoring.path }} + interval: {{ .Values.monitoring.interval }} + scrapeTimeout: {{ .Values.monitoring.scrapeTimeout }} +{{- end }} diff --git a/charts/values.yaml b/charts/values.yaml new file mode 100644 index 0000000..3b9eedd --- /dev/null +++ b/charts/values.yaml @@ -0,0 +1,281 @@ +# Beacon Node Helm Chart Values +# ============================== + +# -- Number of replicas (only 1 supported due to Synapse architecture) +replicaCount: 1 + +image: + # -- Container image repository + repository: ghcr.io/apham0001/beacon-node + # -- Image pull policy + pullPolicy: Always + # -- Image tag + tag: "latest" + # -- Image digest (optional, for pinning exact image version) + # Example: "sha256:abc123..." + digest: "" + +# -- Image pull secrets for private registries +imagePullSecrets: [] + +# -- Override the chart name +nameOverride: "" +# -- Override the full release name +fullnameOverride: "" + +# ============================================================================= +# Beacon Node Configuration +# ============================================================================= + +# -- Matrix server name (FQDN). This MUST match your domain. +# Example: matrix.example.com +serverName: "matrix.example.com" + +# -- Geographic region for beacon info endpoint +serverRegion: "EU" + +# -- Ed25519 signing key for Matrix federation +# Format: "ed25519 a_XXXX " +# IMPORTANT: The base64-encoded seed MUST decode to exactly 32 bytes! +# Generate with: +# python3 -c "import base64,os; print(f'ed25519 a_{os.urandom(2).hex()} {base64.b64encode(os.urandom(32)).decode()}')" +# If not provided, a deterministic key is auto-generated based on release name. +# WARNING: Auto-generated keys are NOT secure for production - always provide your own key! +signingKey: "" + +# -- Use an existing secret for the signing key instead of creating one +# If set, signingKey value is ignored +existingSigningKeySecret: + # -- Name of the existing secret containing the signing key + name: "" + # -- Key in the secret that contains the signing key value + key: "SIGNING_KEY" + +# -- Shared secret for user registration (optional) +registrationSharedSecret: "" + +# ============================================================================= +# Workers Configuration +# ============================================================================= + +workers: + # -- Enable Synapse workers (4 generic workers on ports 8083-8086) + # Worker count is fixed at 4 in the Docker image (hardcoded worker configs) + enabled: true + +# ============================================================================= +# PostgreSQL Configuration (kubelauncher subchart) +# ============================================================================= + +postgresql: + # -- Deploy PostgreSQL as a subchart + enabled: true + auth: + # -- PostgreSQL username + username: synapse + # -- PostgreSQL password (REQUIRED if postgresql.enabled=true) + password: "" + # -- PostgreSQL database name + database: synapse + primary: + # -- initdb configuration (IMPORTANT: Synapse requires C locale) + initdb: + args: "--encoding=UTF8 --lc-collate=C --lc-ctype=C" + # -- PostgreSQL configuration + # Synapse workers use cp_min=20, cp_max=80 connections per process + # With 4 workers + 1 main: 5 * 80 + buffer = 300 + configuration: | + max_connections = 300 + persistence: + # -- Enable persistence for PostgreSQL + enabled: true + # -- Storage size for PostgreSQL + size: 10Gi + # -- PostgreSQL resource limits + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 1Gi + cpu: 500m + +# ============================================================================= +# External Database Configuration (when postgresql.enabled=false) +# ============================================================================= + +externalDatabase: + # -- External PostgreSQL host + host: "" + # -- External PostgreSQL port + port: 5432 + # -- External PostgreSQL username + username: synapse + # -- External PostgreSQL password + password: "" + # -- External PostgreSQL database name + database: synapse + +# ============================================================================= +# Redis Configuration (kubelauncher subchart) +# ============================================================================= + +redis: + # -- Deploy Redis as a subchart (required for workers) + enabled: true + # -- Redis architecture (standalone or replication) + architecture: standalone + auth: + # -- Disable Redis authentication (Synapse default) + enabled: false + master: + persistence: + # -- Enable persistence for Redis + enabled: true + # -- Storage size for Redis + size: 1Gi + # -- Redis resource limits + resources: + requests: + memory: 64Mi + cpu: 50m + limits: + memory: 256Mi + cpu: 200m + +# ============================================================================= +# Monitoring Configuration +# ============================================================================= + +monitoring: + # -- Enable ServiceMonitor for Prometheus scraping + enabled: false + # -- Prometheus release label for service discovery + prometheusRelease: prometheus-stack + # -- Metrics endpoint path + path: /_synapse/metrics + # -- Scrape interval + interval: 30s + # -- Scrape timeout + scrapeTimeout: 10s + +# ============================================================================= +# Service Configuration +# ============================================================================= + +service: + # -- Service type (ClusterIP, NodePort, LoadBalancer) + type: ClusterIP + # -- Main client/federation port + port: 8008 + +# ============================================================================= +# Ingress Configuration +# ============================================================================= + +ingress: + # -- Enable ingress + enabled: false + # -- Ingress class name + className: "nginx" + # -- Ingress annotations + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + # Federation requests can take longer, increase timeouts + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/proxy-send-timeout: "300" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" + # -- Ingress hosts configuration + hosts: + - host: matrix.example.com + paths: + - path: / + pathType: Prefix + # -- Ingress TLS configuration + tls: + - secretName: beacon-node-tls + hosts: + - matrix.example.com + +# ============================================================================= +# Resources Configuration +# ============================================================================= + +resources: + requests: + memory: 512Mi + cpu: 250m + limits: + memory: 2Gi + cpu: 1000m + +# ============================================================================= +# Pod Configuration +# ============================================================================= + +# -- Node selector for pod scheduling +nodeSelector: {} + +# -- Tolerations for pod scheduling +tolerations: [] + +# -- Affinity rules for pod scheduling +affinity: {} + +# -- Pod annotations +podAnnotations: {} + +# -- Pod security context +podSecurityContext: {} + +# -- Container security context +securityContext: {} + +# ============================================================================= +# Probes Configuration +# ============================================================================= + +livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + +# ============================================================================= +# Persistence Configuration +# ============================================================================= + +persistence: + # -- Enable persistence for Synapse data + enabled: true + # -- Storage class (leave empty for default) + storageClass: "" + # -- Access modes + accessModes: + - ReadWriteOnce + # -- Storage size + size: 5Gi + +# ============================================================================= +# Service Account Configuration +# ============================================================================= + +serviceAccount: + # -- Create a service account + create: true + # -- Service account annotations + annotations: {} + # -- Service account name (generated if not set) + name: "" diff --git a/docker/Dockerfile b/docker/Dockerfile index 98d0a6f..8399d77 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -25,8 +25,11 @@ COPY workers /config/workers COPY shared_config.yaml /config/ COPY beacon_info_module.py /usr/local/lib/python3.13/site-packages/ -# run change on max size -RUN sed -i 's/65536/1048576/' /usr/local/lib/python3.13/site-packages/synapse/api/constants.py +# Increase max event size (1MB instead of default 64KB). +# Beacon messages can exceed the default Matrix PDU size limit. +RUN sed -i 's/^MAX_PDU_SIZE = 65536$/MAX_PDU_SIZE = 1048576/' /usr/local/lib/python3.13/site-packages/synapse/api/constants.py && \ + grep -q '^MAX_PDU_SIZE = 1048576$' /usr/local/lib/python3.13/site-packages/synapse/api/constants.py || \ + (echo "FATAL: PDU size patch failed - 'MAX_PDU_SIZE = 65536' not found in constants.py. Upstream may have changed." >&2 && exit 1) COPY wait-for.sh /usr/local/bin/ COPY synctl_entrypoint.sh /usr/local/bin/ From a6675f52ba07a2d1437805bb26bdb709e293a2cb Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 27 Feb 2026 15:04:01 +0100 Subject: [PATCH 32/34] feat: add helm lint and KIND smoke test CI workflow Runs on chart or docker changes: - Helm dependency build + lint - KIND cluster deploy with smoke tests - Verifies pods healthy, no FATAL errors, well-known endpoints --- .github/workflows/helm-smoke-test.yml | 136 ++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 .github/workflows/helm-smoke-test.yml diff --git a/.github/workflows/helm-smoke-test.yml b/.github/workflows/helm-smoke-test.yml new file mode 100644 index 0000000..8c98476 --- /dev/null +++ b/.github/workflows/helm-smoke-test.yml @@ -0,0 +1,136 @@ +name: Helm Lint & Smoke Test + +on: + push: + branches: [master] + paths: + - "charts/**" + - "docker/**" + - ".github/workflows/helm-smoke-test.yml" + pull_request: + branches: [master] + paths: + - "charts/**" + - "docker/**" + - ".github/workflows/helm-smoke-test.yml" + +env: + TEST_TAG: beacon-node:test + +jobs: + helm-lint: + name: Helm Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Helm + uses: azure/setup-helm@v4 + + - name: Add subchart repos + run: helm repo add kubelauncher https://kubelauncher.github.io/charts + + - name: Build dependencies + run: helm dependency build charts/ + + - name: Lint chart + run: helm lint charts/ + + smoke-test: + name: KIND Smoke Test + runs-on: ubuntu-latest + needs: helm-lint + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: ./docker/ + load: true + tags: ${{ env.TEST_TAG }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Set up Helm + uses: azure/setup-helm@v4 + + - name: Create KIND cluster + uses: helm/kind-action@v1 + + - name: Load image into KIND + run: kind load docker-image ${{ env.TEST_TAG }} + + - name: Add subchart repos & build deps + run: | + helm repo add kubelauncher https://kubelauncher.github.io/charts + helm dependency build charts/ + + - name: Install chart + run: | + helm install beacon-node charts/ \ + --set image.repository=beacon-node \ + --set image.tag=test \ + --set image.digest="" \ + --set image.pullPolicy=Never \ + --set serverName=beacon-node-test.local \ + --set signingKey="ed25519 a_test $(openssl rand -base64 32)" \ + --set postgresql.enabled=true \ + --set postgresql.auth.password=testpass \ + --set postgresql.auth.postgresPassword=testpass \ + --set redis.enabled=true \ + --set ingress.enabled=false \ + --set workers.enabled=true \ + --wait \ + --timeout 180s + + - name: Verify pods are running + run: | + kubectl get pods + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=beacon-node --timeout=120s + echo "All beacon-node pods are ready" + + - name: Check beacon-node logs for errors + run: | + POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}') + kubectl logs "$POD" --tail=50 + # Fail if FATAL errors (exclude expected startup warnings) + if kubectl logs "$POD" | grep -i "FATAL" | grep -v "reserved for"; then + echo "FATAL errors found in logs" + exit 1 + fi + echo "No FATAL errors in logs" + + - name: Smoke test well-known endpoints + run: | + POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}') + echo "--- Testing /.well-known/matrix/server ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/server) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "m.server" || (echo "FAIL: well-known/server missing m.server" && exit 1) + + echo "--- Testing /.well-known/matrix/client ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/client) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "m.homeserver" || (echo "FAIL: well-known/client missing m.homeserver" && exit 1) + + echo "Smoke tests passed" + + - name: Debug on failure + if: failure() + run: | + echo "=== Pod status ===" + kubectl get pods -o wide + echo "=== Events ===" + kubectl get events --sort-by='.lastTimestamp' + echo "=== Beacon node logs ===" + POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) + [ -n "$POD" ] && kubectl logs "$POD" --tail=100 + echo "=== PostgreSQL logs ===" + PG_POD=$(kubectl get pod -l app.kubernetes.io/name=postgresql -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) + [ -n "$PG_POD" ] && kubectl logs "$PG_POD" --tail=50 From 238a56a3fd5b04ed13f68b6b92de98c757594006 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 27 Feb 2026 15:07:32 +0100 Subject: [PATCH 33/34] fix: improve smoke test CI - cluster name, endpoints, timeout - Fix kind load cluster name (chart-testing) - Add Synapse API endpoint tests (versions, federation, login) - Increase helm install timeout to 300s - Generate signing key properly --- .github/workflows/helm-smoke-test.yml | 44 +++++++++++++++++++++------ 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/.github/workflows/helm-smoke-test.yml b/.github/workflows/helm-smoke-test.yml index 8c98476..70581ca 100644 --- a/.github/workflows/helm-smoke-test.yml +++ b/.github/workflows/helm-smoke-test.yml @@ -64,13 +64,18 @@ jobs: uses: helm/kind-action@v1 - name: Load image into KIND - run: kind load docker-image ${{ env.TEST_TAG }} + run: kind load docker-image ${{ env.TEST_TAG }} --name chart-testing - name: Add subchart repos & build deps run: | helm repo add kubelauncher https://kubelauncher.github.io/charts helm dependency build charts/ + - name: Generate signing key + run: | + SIGNING_KEY="ed25519 a_test $(openssl rand -base64 32)" + echo "SIGNING_KEY=${SIGNING_KEY}" >> "$GITHUB_ENV" + - name: Install chart run: | helm install beacon-node charts/ \ @@ -79,7 +84,7 @@ jobs: --set image.digest="" \ --set image.pullPolicy=Never \ --set serverName=beacon-node-test.local \ - --set signingKey="ed25519 a_test $(openssl rand -base64 32)" \ + --set "signingKey=${SIGNING_KEY}" \ --set postgresql.enabled=true \ --set postgresql.auth.password=testpass \ --set postgresql.auth.postgresPassword=testpass \ @@ -87,7 +92,12 @@ jobs: --set ingress.enabled=false \ --set workers.enabled=true \ --wait \ - --timeout 180s + --timeout 300s + + - name: Verify all pods + run: | + kubectl get pods + kubectl get pods -o wide - name: Verify pods are running run: | @@ -106,20 +116,36 @@ jobs: fi echo "No FATAL errors in logs" - - name: Smoke test well-known endpoints + - name: Smoke test Synapse endpoints run: | POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}') - echo "--- Testing /.well-known/matrix/server ---" + + echo "--- /.well-known/matrix/server ---" RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/server) echo "$RESPONSE" - echo "$RESPONSE" | grep -q "m.server" || (echo "FAIL: well-known/server missing m.server" && exit 1) + echo "$RESPONSE" | grep -q "m.server" || (echo "FAIL: missing m.server" && exit 1) - echo "--- Testing /.well-known/matrix/client ---" + echo "--- /.well-known/matrix/client ---" RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/client) echo "$RESPONSE" - echo "$RESPONSE" | grep -q "m.homeserver" || (echo "FAIL: well-known/client missing m.homeserver" && exit 1) + echo "$RESPONSE" | grep -q "m.homeserver" || (echo "FAIL: missing m.homeserver" && exit 1) + + echo "--- /_matrix/client/versions ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/client/versions) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "versions" || (echo "FAIL: missing versions" && exit 1) + + echo "--- /_matrix/federation/v1/version ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/federation/v1/version) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "1.148.0" || (echo "FAIL: unexpected Synapse version" && exit 1) + + echo "--- /_matrix/client/v3/login ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/client/v3/login) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "m.login.password" || (echo "FAIL: login flow missing" && exit 1) - echo "Smoke tests passed" + echo "All smoke tests passed" - name: Debug on failure if: failure() From 140e29aed073f23bf1f5f0f8628555197f746a84 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 27 Feb 2026 15:10:33 +0100 Subject: [PATCH 34/34] feat: move smoke test into build-and-publish workflow - Add KIND smoke test job to build-and-publish.yml - Runs on every PR (including Renovate Dockerfile bumps) - Publish now requires both test + smoke-test to pass - Remove separate helm-smoke-test.yml to avoid duplication --- .github/workflows/build-and-publish.yml | 122 +++++++++++++++++- .github/workflows/helm-smoke-test.yml | 162 ------------------------ 2 files changed, 121 insertions(+), 163 deletions(-) delete mode 100644 .github/workflows/helm-smoke-test.yml diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 0d44481..742866b 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -117,10 +117,130 @@ jobs: test -d /keys \ && echo "/keys OK" + smoke-test: + name: KIND Smoke Test + runs-on: ubuntu-latest + needs: test + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: ./docker/ + load: true + tags: ${{ env.TEST_TAG }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Set up Helm + uses: azure/setup-helm@v4 + + - name: Create KIND cluster + uses: helm/kind-action@v1 + + - name: Load image into KIND + run: kind load docker-image ${{ env.TEST_TAG }} --name chart-testing + + - name: Add subchart repos & build deps + run: | + helm repo add kubelauncher https://kubelauncher.github.io/charts + helm dependency build charts/ + + - name: Lint chart + run: helm lint charts/ + + - name: Generate signing key + run: | + SIGNING_KEY="ed25519 a_test $(openssl rand -base64 32)" + echo "SIGNING_KEY=${SIGNING_KEY}" >> "$GITHUB_ENV" + + - name: Install chart + run: | + helm install beacon-node charts/ \ + --set image.repository=beacon-node \ + --set image.tag=test \ + --set image.digest="" \ + --set image.pullPolicy=Never \ + --set serverName=beacon-node-test.local \ + --set "signingKey=${SIGNING_KEY}" \ + --set postgresql.enabled=true \ + --set postgresql.auth.password=testpass \ + --set postgresql.auth.postgresPassword=testpass \ + --set redis.enabled=true \ + --set ingress.enabled=false \ + --set workers.enabled=true \ + --wait \ + --timeout 300s + + - name: Verify pods are running + run: | + kubectl get pods -o wide + kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=beacon-node --timeout=120s + + - name: Check beacon-node logs for errors + run: | + POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}') + kubectl logs "$POD" --tail=50 + if kubectl logs "$POD" | grep -i "FATAL" | grep -v "reserved for"; then + echo "FATAL errors found in logs" + exit 1 + fi + echo "No FATAL errors in logs" + + - name: Smoke test Synapse endpoints + run: | + POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}') + + echo "--- /.well-known/matrix/server ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/server) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "m.server" || (echo "FAIL: missing m.server" && exit 1) + + echo "--- /.well-known/matrix/client ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/client) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "m.homeserver" || (echo "FAIL: missing m.homeserver" && exit 1) + + echo "--- /_matrix/client/versions ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/client/versions) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "versions" || (echo "FAIL: missing versions" && exit 1) + + echo "--- /_matrix/federation/v1/version ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/federation/v1/version) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "Synapse" || (echo "FAIL: unexpected version response" && exit 1) + + echo "--- /_matrix/client/v3/login ---" + RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/client/v3/login) + echo "$RESPONSE" + echo "$RESPONSE" | grep -q "m.login.password" || (echo "FAIL: login flow missing" && exit 1) + + echo "All smoke tests passed" + + - name: Debug on failure + if: failure() + run: | + echo "=== Pod status ===" + kubectl get pods -o wide + echo "=== Events ===" + kubectl get events --sort-by='.lastTimestamp' + echo "=== Beacon node logs ===" + POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) + [ -n "$POD" ] && kubectl logs "$POD" --tail=100 + echo "=== PostgreSQL logs ===" + PG_POD=$(kubectl get pod -l app.kubernetes.io/name=postgresql -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) + [ -n "$PG_POD" ] && kubectl logs "$PG_POD" --tail=50 + publish: name: Publish to GHCR runs-on: ubuntu-latest - needs: test + needs: [test, smoke-test] if: github.event_name != 'pull_request' permissions: contents: read diff --git a/.github/workflows/helm-smoke-test.yml b/.github/workflows/helm-smoke-test.yml deleted file mode 100644 index 70581ca..0000000 --- a/.github/workflows/helm-smoke-test.yml +++ /dev/null @@ -1,162 +0,0 @@ -name: Helm Lint & Smoke Test - -on: - push: - branches: [master] - paths: - - "charts/**" - - "docker/**" - - ".github/workflows/helm-smoke-test.yml" - pull_request: - branches: [master] - paths: - - "charts/**" - - "docker/**" - - ".github/workflows/helm-smoke-test.yml" - -env: - TEST_TAG: beacon-node:test - -jobs: - helm-lint: - name: Helm Lint - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Set up Helm - uses: azure/setup-helm@v4 - - - name: Add subchart repos - run: helm repo add kubelauncher https://kubelauncher.github.io/charts - - - name: Build dependencies - run: helm dependency build charts/ - - - name: Lint chart - run: helm lint charts/ - - smoke-test: - name: KIND Smoke Test - runs-on: ubuntu-latest - needs: helm-lint - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build image - uses: docker/build-push-action@v6 - with: - context: ./docker/ - load: true - tags: ${{ env.TEST_TAG }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Set up Helm - uses: azure/setup-helm@v4 - - - name: Create KIND cluster - uses: helm/kind-action@v1 - - - name: Load image into KIND - run: kind load docker-image ${{ env.TEST_TAG }} --name chart-testing - - - name: Add subchart repos & build deps - run: | - helm repo add kubelauncher https://kubelauncher.github.io/charts - helm dependency build charts/ - - - name: Generate signing key - run: | - SIGNING_KEY="ed25519 a_test $(openssl rand -base64 32)" - echo "SIGNING_KEY=${SIGNING_KEY}" >> "$GITHUB_ENV" - - - name: Install chart - run: | - helm install beacon-node charts/ \ - --set image.repository=beacon-node \ - --set image.tag=test \ - --set image.digest="" \ - --set image.pullPolicy=Never \ - --set serverName=beacon-node-test.local \ - --set "signingKey=${SIGNING_KEY}" \ - --set postgresql.enabled=true \ - --set postgresql.auth.password=testpass \ - --set postgresql.auth.postgresPassword=testpass \ - --set redis.enabled=true \ - --set ingress.enabled=false \ - --set workers.enabled=true \ - --wait \ - --timeout 300s - - - name: Verify all pods - run: | - kubectl get pods - kubectl get pods -o wide - - - name: Verify pods are running - run: | - kubectl get pods - kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=beacon-node --timeout=120s - echo "All beacon-node pods are ready" - - - name: Check beacon-node logs for errors - run: | - POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}') - kubectl logs "$POD" --tail=50 - # Fail if FATAL errors (exclude expected startup warnings) - if kubectl logs "$POD" | grep -i "FATAL" | grep -v "reserved for"; then - echo "FATAL errors found in logs" - exit 1 - fi - echo "No FATAL errors in logs" - - - name: Smoke test Synapse endpoints - run: | - POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}') - - echo "--- /.well-known/matrix/server ---" - RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/server) - echo "$RESPONSE" - echo "$RESPONSE" | grep -q "m.server" || (echo "FAIL: missing m.server" && exit 1) - - echo "--- /.well-known/matrix/client ---" - RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/.well-known/matrix/client) - echo "$RESPONSE" - echo "$RESPONSE" | grep -q "m.homeserver" || (echo "FAIL: missing m.homeserver" && exit 1) - - echo "--- /_matrix/client/versions ---" - RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/client/versions) - echo "$RESPONSE" - echo "$RESPONSE" | grep -q "versions" || (echo "FAIL: missing versions" && exit 1) - - echo "--- /_matrix/federation/v1/version ---" - RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/federation/v1/version) - echo "$RESPONSE" - echo "$RESPONSE" | grep -q "1.148.0" || (echo "FAIL: unexpected Synapse version" && exit 1) - - echo "--- /_matrix/client/v3/login ---" - RESPONSE=$(kubectl exec "$POD" -- curl -sf http://localhost:8008/_matrix/client/v3/login) - echo "$RESPONSE" - echo "$RESPONSE" | grep -q "m.login.password" || (echo "FAIL: login flow missing" && exit 1) - - echo "All smoke tests passed" - - - name: Debug on failure - if: failure() - run: | - echo "=== Pod status ===" - kubectl get pods -o wide - echo "=== Events ===" - kubectl get events --sort-by='.lastTimestamp' - echo "=== Beacon node logs ===" - POD=$(kubectl get pod -l app.kubernetes.io/name=beacon-node -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) - [ -n "$POD" ] && kubectl logs "$POD" --tail=100 - echo "=== PostgreSQL logs ===" - PG_POD=$(kubectl get pod -l app.kubernetes.io/name=postgresql -o jsonpath='{.items[0].metadata.name}' 2>/dev/null) - [ -n "$PG_POD" ] && kubectl logs "$PG_POD" --tail=50