From 556d62a0deee94cc55b2f9cfb29e29dd7926aa1c Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Wed, 24 Jun 2026 16:53:46 -0700 Subject: [PATCH 1/3] build: Align xrpld RPM packaging with DEB package (#7529) --- .github/workflows/reusable-package.yml | 20 +--- cmake/XrplPackaging.cmake | 1 - cspell.config.yaml | 1 + package/README.md | 159 ++++++++++++++++--------- package/build_pkg.sh | 148 ++++++++++++----------- package/rpm/xrpld.spec | 22 +++- package/shared/50-xrpld.preset | 2 - 7 files changed, 203 insertions(+), 150 deletions(-) delete mode 100644 package/shared/50-xrpld.preset diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index eed4bfc4a3..249e807592 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -39,23 +39,8 @@ jobs: working-directory: .github/scripts/strategy-matrix run: ./generate.py --packaging >>"${GITHUB_OUTPUT}" - generate-version: - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - sparse-checkout: | - .github/actions/generate-version - src/libxrpl/protocol/BuildInfo.cpp - - name: Generate version - id: version - uses: ./.github/actions/generate-version - package: - needs: [generate-matrix, generate-version] + needs: [generate-matrix] if: ${{ github.event.repository.visibility == 'public' }} strategy: fail-fast: false @@ -82,14 +67,13 @@ jobs: - name: Build package env: - PKG_VERSION: ${{ needs.generate-version.outputs.version }} PKG_RELEASE: ${{ inputs.pkg_release }} run: ./package/build_pkg.sh - name: Upload package artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }} + name: ${{ matrix.artifact_name }}-pkg path: | ${{ env.BUILD_DIR }}/debbuild/*.deb ${{ env.BUILD_DIR }}/debbuild/*.ddeb diff --git a/cmake/XrplPackaging.cmake b/cmake/XrplPackaging.cmake index fe885c200c..8e3861925d 100644 --- a/cmake/XrplPackaging.cmake +++ b/cmake/XrplPackaging.cmake @@ -28,7 +28,6 @@ endif() set(package_env SRC_DIR=${CMAKE_SOURCE_DIR} BUILD_DIR=${CMAKE_BINARY_DIR} - PKG_VERSION=${xrpld_version} PKG_RELEASE=${pkg_release} ) diff --git a/cspell.config.yaml b/cspell.config.yaml index 0d38c4be7b..8273df6c98 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -301,6 +301,7 @@ words: - txs - ubsan - UBSAN + - ufdio - umant - unacquired - unambiguity diff --git a/package/README.md b/package/README.md index 63c2ab88fc..4b78106c4c 100644 --- a/package/README.md +++ b/package/README.md @@ -6,10 +6,10 @@ This directory contains all files needed to build RPM and Debian packages for `x ``` package/ - build_pkg.sh Staging and build script (called by CMake targets and CI) + build_pkg.sh Staging and build script (called by the CMake `package` target and CI) rpm/ - xrpld.spec RPM spec (xrpld_version/pkg_release passed via rpmbuild --define) - debian/ Debian control files (control, rules, install, links, conffiles, ...) + xrpld.spec RPM spec + debian/ Debian control files (control, rules, copyright, xrpld.docs, xrpld.links, source/format) shared/ xrpld.service systemd unit file (used by both RPM and DEB) xrpld.sysusers sysusers.d config (used by both RPM and DEB) @@ -21,21 +21,19 @@ package/ Packaging targets and their container images are declared in [`.github/scripts/strategy-matrix/linux.json`](../.github/scripts/strategy-matrix/linux.json) -inside `package_configs` configurations. Today only -`linux/amd64` is emitted. The package format -(deb or rpm) is inferred at build time from the container's package manager -(`apt-get` -> deb, `dnf`/`yum` -> rpm). The image tag is composed as -`ghcr.io/xrplf/xrpld/packaging-:sha-` — -the same scheme used by `reusable-build-test.yml`. Bump `image_sha` in -`linux.json` and both CI and local builds pick up the new image with no -workflow edits. +under `package_configs`, one entry per distro. Today only `linux/amd64` is +emitted. Each entry pins its full container image in an `image` field; to move +to a new image, edit that field and both CI and local builds pick it up. The +package format (deb or rpm) is inferred at build time from the container's +package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). -| Package type | Image (derived from `linux.json`) | Tool required | -| ------------ | ---------------------------------------------------- | --------------------------------------------------------------- | -| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-` | `rpmbuild` | -| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-` | `dpkg-buildpackage`, `debhelper (>= 13)`, `dh-sequence-systemd` | +| Package type | Image (`package_configs.[].image` in `linux.json`) | Tools required | +| ------------ | ---------------------------------------------------------- | --------------------------------------------------- | +| RPM | `ghcr.io/xrplf/xrpld/packaging-rhel:sha-` | `rpmbuild` | +| DEB | `ghcr.io/xrplf/xrpld/packaging-debian:sha-` | `dpkg-buildpackage`, debhelper with compat level 13 | -To print the exact image tags for the current `linux.json`: +To print the full packaging matrix (artifact names and images) for the current +`linux.json`: ```bash ./.github/scripts/strategy-matrix/generate.py --packaging @@ -46,12 +44,13 @@ To print the exact image tags for the current `linux.json`: ### Via CI Caller workflows (`on-pr.yml`, `on-tag.yml`, `on-trigger.yml`) call -`reusable-strategy-matrix.yml` with `mode: packaging` to generate the matrix of -`{artifact_name, os}` entries, then fan out to -`reusable-package.yml` per entry. That workflow downloads the pre-built `xrpld` -binary artifact, detects the package format from the container, and calls -`build_pkg.sh` directly — no CMake configure or build step is needed inside -the packaging job. +`reusable-package.yml`. That workflow generates its own packaging matrix from +`package_configs` in `linux.json` (via `generate.py --packaging`) and fans out +one job per distro. Each job downloads the pre-built `xrpld` binary artifact and +runs in that distro's container, so the package format follows from the +container's package manager. The packaging script derives the package version +from the downloaded binary's `xrpld --version` output; no CMake configure or +build step is needed inside the packaging job. ### Locally (mirrors CI) @@ -60,22 +59,19 @@ inside the same container CI uses. The image tag is derived from `linux.json` so you don't need to hardcode a SHA. ```bash -# From the repo root. Pick any image flagged with `"package": true` in -# linux.json; the package format is inferred from the container's package -# manager. Example for the rpm-producing image: -IMAGE=$(jq -r ' - .os | map(select(.package == true))[0] | - "ghcr.io/xrplf/ci/\(.distro_name)-\(.distro_version):\(.compiler_name)-\(.compiler_version)-sha-\(.image_sha)" -' .github/scripts/strategy-matrix/linux.json) +# From the repo root. Each distro's container image is the `image` field of its +# package_configs entry in linux.json; the package format is inferred from the +# container's package manager. Example for the rpm-producing image (use +# .package_configs.debian[0].image for the deb image): +IMAGE=$(jq -r '.package_configs.rhel[0].image' .github/scripts/strategy-matrix/linux.json) -VERSION=2.4.0-local PKG_RELEASE=1 docker run --rm \ -v "$(pwd):/src" \ -w /src \ - "$IMAGE" \ - ./package/build_pkg.sh --pkg-version "$VERSION" --pkg-release "$PKG_RELEASE" + "${IMAGE}" \ + ./package/build_pkg.sh --pkg-release "${PKG_RELEASE}" # Output: # build/debbuild/*.deb (DEB + dbgsym .ddeb) @@ -91,41 +87,73 @@ needed, but the host toolchain replaces the pinned CI image: ```bash cmake \ -Dxrpld=ON \ - -Dxrpld_version=2.4.0-local \ + -Dpkg_release=1 \ -Dtests=OFF \ .. cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL ``` -The `cmake/XrplPackaging.cmake` module defines the target only if at least one -of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then infers the -package format from the host's package manager. The packaging script installs -to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of +The `cmake/XrplPackaging.cmake` module defines the `package` target only if at +least one of `rpmbuild` / `dpkg-buildpackage` is present; `build_pkg.sh` then +infers the package format from the host's package manager. The packaging script +installs to FHS-standard paths (`/usr/bin`, `/etc/xrpld`, etc.) regardless of `CMAKE_INSTALL_PREFIX`. +The package version is not a CMake input on this path: `build_pkg.sh` derives it +from the just-built `xrpld` binary's `xrpld --version` output. The package +release defaults to 1 and is overridable with `-Dpkg_release=N`. + ## How `build_pkg.sh` works -`build_pkg.sh` accepts long-form flags, each of which can also be set via an -environment variable. Flags override env vars; env vars override the built-in -defaults. Run `./package/build_pkg.sh --help` for the same table: +`build_pkg.sh` derives the `xrpld` software version from +`${BUILD_DIR}/xrpld --version` in both package formats. -| Flag | Env var | Default | Purpose | -| -------------------------- | ------------------- | ----------------------------- | ----------------------------------- | -| `--src-dir DIR` | `SRC_DIR` | `$PWD` | repo root | -| `--build-dir DIR` | `BUILD_DIR` | `$PWD/build` | directory holding pre-built `xrpld` | -| `--pkg-version STR` | `PKG_VERSION` | parsed from `xrpld --version` | version string, e.g. `3.2.0-b1` | -| `--pkg-release N` | `PKG_RELEASE` | `1` | package release number | -| `--source-date-epoch SECS` | `SOURCE_DATE_EPOCH` | latest git commit ctime | reproducibility timestamp | +The binary's version is already SemVer-validated by `BuildInfo`. +`build_pkg.sh` converts pre-release versions such as `3.2.0-b1` or +`3.2.0-rc1` from `-` to `~` for package metadata so pre-releases sort before +the final release. If that normalized package version still contains `-`, +packaging fails because RPM forbids `-` in `Version`, and Debian uses `-` as +the upstream/revision separator. + +`pkg_version` is the normalized package metadata version derived inside +`build_pkg.sh` from the binary-reported `xrpld` version (`-` pre-release +separator converted to `~`). It is not a separate user input. + +`PKG_RELEASE` is a different value: the package release iteration for that +`xrpld` version. RPM receives the normalized `pkg_version` and `PKG_RELEASE` as +the `pkg_version` and `pkg_release` macros for its `Version` and `Release` +values; DEB writes them as `${pkg_version}-${PKG_RELEASE}` in +`debian/changelog`. + +With `PKG_RELEASE=1`, the package metadata becomes: + +| Input version | RPM version/release | Debian version | +| ------------------ | ---------------------------- | -------------------- | +| `3.2.0` | `3.2.0-1%{?dist}` | `3.2.0-1` | +| `3.2.0-b0+abc1234` | `3.2.0~b0+abc1234-1%{?dist}` | `3.2.0~b0+abc1234-1` | +| `3.2.0-b1` | `3.2.0~b1-1%{?dist}` | `3.2.0~b1-1` | +| `3.2.0-rc1` | `3.2.0~rc1-1%{?dist}` | `3.2.0~rc1-1` | + +The Debian changelog entry carries the repository component: final releases use +`stable`, `b0` builds, including `b0+metadata`, use `develop`, and `bN`/`rcN` +pre-releases use `unstable`. +Build metadata on a final release, such as `3.2.0+abc123`, is rejected. + +The RPM path intentionally uses `~` in `Version`, matching the Debian +pre-release ordering convention, so RPM filenames/NVRs begin with forms like +`xrpld-3.2.0~b1-...` and `xrpld-3.2.0~rc1-...` instead of encoding +pre-releases with an older `0..` RPM `Release` value. The package format (`deb` or `rpm`) is inferred from the host's package manager (`apt-get` -> deb, `dnf`/`yum` -> rpm). Hosts without one of those fail early. Flags are for explicit invocation; environment variables are intended for -CMake/systemd/CI integration. The CI workflow and the CMake `package` target -both invoke `build_pkg.sh` with no flags, configuring it entirely via env -(see `cmake/XrplPackaging.cmake`). +CMake/CI integration. The CI workflow and the CMake `package` target both invoke +`build_pkg.sh` with no flags; CMake supplies `SRC_DIR`, `BUILD_DIR`, and +`PKG_RELEASE` via env, while CI supplies `BUILD_DIR` and `PKG_RELEASE` via env +and lets the script use defaults for the rest. It resolves `SRC_DIR` and `BUILD_DIR` to absolute paths, then calls `stage_common()` to copy the binary, config files, and shared support files @@ -134,18 +162,32 @@ into the staging area, and invokes the platform build tool. ### RPM 1. Creates the standard `rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}` tree inside the build directory. -2. Copies `xrpld.spec` and all source files (binary, configs, service files) into `SOURCES/`. -3. Runs `rpmbuild -bb --define "xrpld_version ..." --define "pkg_release ..."`. The spec uses manual `install` commands to place files. +2. Copies `xrpld.spec` and all shared source files (binary, configs, service files) into `SOURCES/`. +3. Runs `rpmbuild -bb`, passing the normalized package metadata version as the + `pkg_version` RPM macro and `PKG_RELEASE` as the `pkg_release` RPM macro. + The spec uses manual `install` commands to place files, disables `dwz`, and + writes uncompressed RPM payloads while generating debuginfo packages. 4. Output: `rpmbuild/RPMS/x86_64/xrpld-*.rpm` +The uncompressed RPM payload setting is intentionally unconditional for +generated RPMs. It trades larger RPM artifacts for much shorter package +build/validation time, which keeps RPM package validation in the same rough time +class as Debian package validation. + +RPM upgrades intentionally do not restart a running `xrpld` service. The spec +uses `%systemd_postun`, matching Debian's `dh_installsystemd +--no-stop-on-upgrade` behavior; operators pick up the new binary on the next +service restart. + ### DEB 1. Creates a staging source tree at `debbuild/source/` inside the build directory. 2. Stages the binary, configs, `README.md`, and `LICENSE.md`. 3. Copies `package/debian/` control files into `debbuild/source/debian/`. 4. Copies shared service/sysusers/tmpfiles into `debian/` where `dh_installsystemd`, `dh_installsysusers`, and `dh_installtmpfiles` pick them up automatically. -5. Generates a minimal `debian/changelog` (pre-release versions use `~` instead of `-`). -6. Runs `dpkg-buildpackage -b --no-sign`. `debian/rules` uses manual `install` commands. +5. Generates a minimal `debian/changelog` using `${pkg_version}-${PKG_RELEASE}`, + where `pkg_version` is derived from the binary-reported `xrpld` version. +6. Runs `dpkg-buildpackage -b --no-sign -d` (`-d` skips the build-dependency check, since the binary is already built). `debian/rules` uses manual `install` commands. 7. Output: `debbuild/*.deb` and `debbuild/*.ddeb` (dbgsym package) ## Post-build verification @@ -161,11 +203,14 @@ rpm -qlp rpmbuild/RPMS/x86_64/*.rpm ## Reproducibility -The following environment variables improve build reproducibility. They are not -set automatically by `build_pkg.sh`; set them manually if needed: +`build_pkg.sh` already defaults `SOURCE_DATE_EPOCH` to the latest git commit +time, or the current time outside a git tree, and exports it (override with +`--source-date-epoch` / `SOURCE_DATE_EPOCH`); the RPM spec clamps file +modification times to it via `%build_mtime_policy`. The remaining variables +below further improve reproducibility but are _not_ set by the script — export +them yourself if needed: ```bash -export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) export TZ=UTC export LC_ALL=C.UTF-8 export GZIP=-n diff --git a/package/build_pkg.sh b/package/build_pkg.sh index e2ec8fee3d..3684fc096a 100755 --- a/package/build_pkg.sh +++ b/package/build_pkg.sh @@ -3,20 +3,18 @@ set -euo pipefail # Build an RPM or Debian package from a pre-built xrpld binary. # -# Flags override env vars; env vars override defaults. Env vars are intended -# for CMake/systemd/CI integration; flags are for explicit invocation. +# Flags override env vars; env vars override defaults. usage() { cat <<'EOF' Usage: build_pkg.sh [options] Options (each can also be set via the env var shown): - --src-dir DIR repo root [SRC_DIR; default: $PWD] - --build-dir DIR directory holding xrpld [BUILD_DIR; default: $PWD/build] - --pkg-version STR version, e.g. 3.2.0-b1 [PKG_VERSION; default: parsed from xrpld --version] - --pkg-release N package release number [PKG_RELEASE; default: 1] - --source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; default: latest git commit ctime] - -h, --help show this help and exit + --src-dir DIR repo root [SRC_DIR; default: ${PWD}] + --build-dir DIR directory holding xrpld [BUILD_DIR; default: ${PWD}/build] + --pkg-release N package release iteration [PKG_RELEASE; default: 1] + --source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; latest git ctime; fallback: current time] + -h, --help show this help and exit EOF } @@ -30,8 +28,7 @@ need_arg() { # Seed from env. CLI parsing below overrides these directly. SRC_DIR="${SRC_DIR:-}" BUILD_DIR="${BUILD_DIR:-}" -PKG_VERSION="${PKG_VERSION:-}" -PKG_RELEASE="${PKG_RELEASE:-}" +PKG_RELEASE="${PKG_RELEASE:-1}" SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}" while [[ $# -gt 0 ]]; do @@ -46,11 +43,6 @@ while [[ $# -gt 0 ]]; do BUILD_DIR="$2" shift 2 ;; - --pkg-version) - need_arg "$@" - PKG_VERSION="$2" - shift 2 - ;; --pkg-release) need_arg "$@" PKG_RELEASE="$2" @@ -74,19 +66,61 @@ while [[ $# -gt 0 ]]; do done SRC_DIR="$(cd "${SRC_DIR:-${PWD}}" && pwd)" -BUILD_DIR="$(cd "${BUILD_DIR:-${PWD}/build}" && pwd)" -PKG_RELEASE="${PKG_RELEASE:-1}" - -if [[ -z "${PKG_VERSION}" ]]; then - PKG_VERSION="$("${BUILD_DIR}/xrpld" --version | awk 'NR==1 {print $3; exit}')" +BUILD_DIR="${BUILD_DIR:-${PWD}/build}" +if [[ ! -d "${BUILD_DIR}" ]]; then + echo "build_pkg.sh: build directory not found: ${BUILD_DIR}" >&2 + echo "Build xrpld before packaging, or set BUILD_DIR to the directory containing xrpld." >&2 + exit 1 fi +BUILD_DIR="$(cd "${BUILD_DIR}" && pwd)" -if [[ -z "${PKG_VERSION}" ]]; then - echo "PKG_VERSION is empty (not provided and could not be derived)." >&2 +xrpld_binary="${BUILD_DIR}/xrpld" +if [[ ! -x "${xrpld_binary}" ]]; then + echo "build_pkg.sh: expected executable xrpld binary at ${xrpld_binary}." >&2 + echo "Build xrpld before packaging, or set BUILD_DIR to the directory containing xrpld." >&2 exit 1 fi -VERSION="${PKG_VERSION}" +xrpld_version="$("${xrpld_binary}" --version | awk 'NR == 1 { print $3 }')" + +if [[ -z "${xrpld_version}" ]]; then + echo "build_pkg.sh: unable to derive xrpld version from ${xrpld_binary} --version." >&2 + exit 1 +fi + +# The version as the package formats consume it: identical to xrpld_version +# except a pre-release uses '~' (3.2.0-b1 -> 3.2.0~b1), which also sorts before +# the final 3.2.0; a no-op for a final release. Lowercase = derived internally, +# not an input (cf. pkg_type). +pkg_version="${xrpld_version}" +pre_release="" +if [[ "${xrpld_version}" == *-* ]]; then + pre_release="${xrpld_version#*-}" + pkg_version="${xrpld_version%%-*}~${pre_release}" +fi + +# BuildInfo already SemVer-validates the binary's version. Packaging adds one +# narrower constraint: after pre-release normalization, the package version must +# not contain '-' because RPM forbids it in Version and Debian uses it as the +# upstream/revision separator. +if [[ "${pkg_version}" == *-* ]]; then + echo "build_pkg.sh: unsupported xrpld version '${xrpld_version}'." >&2 + echo "Package version '${pkg_version}' cannot contain '-'." >&2 + echo "Use a single-token pre-release like 3.2.0-b1 or 3.2.0-rc2." >&2 + exit 1 +fi + +if [[ -z "${pre_release}" && "${xrpld_version}" == *+* ]]; then + echo "build_pkg.sh: unsupported xrpld version '${xrpld_version}'." >&2 + echo "Build metadata is only supported on bN/rcN pre-releases." >&2 + exit 1 +fi + +if [[ -n "${pre_release}" && ! "${pre_release}" =~ ^(b0|b[1-9][0-9]*|rc[0-9]+)(\+.*)?$ ]]; then + echo "build_pkg.sh: unsupported xrpld pre-release '${pre_release}'." >&2 + echo "Use bN or rcN, e.g. 3.2.0-b1 or 3.2.0-rc2." >&2 + exit 1 +fi if command -v apt-get >/dev/null 2>&1; then pkg_type=deb @@ -98,32 +132,15 @@ else fi if [[ -z "${SOURCE_DATE_EPOCH}" ]]; then - if git -C "$SRC_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then - SOURCE_DATE_EPOCH="$(git -C "$SRC_DIR" log -1 --format=%ct)" + if git -C "${SRC_DIR}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + SOURCE_DATE_EPOCH="$(git -C "${SRC_DIR}" log -1 --format=%ct)" else SOURCE_DATE_EPOCH="$(date +%s)" fi fi export SOURCE_DATE_EPOCH -CHANGELOG_DATE="$(date -u -R -d "@$SOURCE_DATE_EPOCH")" - -# Split VERSION at the first '-' into base and optional pre-release suffix. -# Examples: "3.2.0" -> ("3.2.0", ""); "3.2.0-b1" -> ("3.2.0", "b1"). -VER_BASE="${VERSION%%-*}" -VER_SUFFIX="${VERSION#*-}" -[[ "${VER_SUFFIX}" == "${VERSION}" ]] && VER_SUFFIX="" - -# Reject multi-segment suffixes (e.g. "beta-1", "rc1-15-gabc123"). Neither an -# RPM Version nor a Debian upstream version may contain '-' (it's the NVR / -# version-revision separator), and the convention here is single-token -# suffixes like b1 or rc2. Fail early with a clear message rather than letting -# the package tooling blow up or silently mangle dashes. -if [[ "${VER_SUFFIX}" == *-* ]]; then - echo "build_pkg.sh: multi-segment pre-release in VERSION='${VERSION}' (suffix '${VER_SUFFIX}')." >&2 - echo "Use single-token suffixes like 3.2.0-b1 or 3.2.0-rc2." >&2 - exit 1 -fi +CHANGELOG_DATE="$(date -u -R -d "@${SOURCE_DATE_EPOCH}")" SHARED="${SRC_DIR}/package/shared" DEBIAN_DIR="${SRC_DIR}/package/debian" @@ -143,7 +160,6 @@ stage_common() { cp "${SHARED}/xrpld.sysusers" "${dest}/xrpld.sysusers" cp "${SHARED}/xrpld.tmpfiles" "${dest}/xrpld.tmpfiles" cp "${SHARED}/xrpld.logrotate" "${dest}/xrpld.logrotate" - cp "${SHARED}/50-xrpld.preset" "${dest}/50-xrpld.preset" } build_rpm() { @@ -154,18 +170,11 @@ build_rpm() { cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec" stage_common "${topdir}/SOURCES" - # Pre-releases use the modern rpm '~' convention (rpm >= 4.10): the suffix - # goes in Version (e.g. 3.2.0~b1), which rpmvercmp sorts *before* the final - # 3.2.0 — identical semantics to Debian's '~'. Release is just the package - # release number. This replaces the older "0.." Release - # hack and keeps the RPM and DEB version strings symmetric. - local rpm_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}" - set -x rpmbuild -bb \ --define "_topdir ${topdir}" \ - --define "xrpld_version ${rpm_version}" \ - --define "xrpld_release ${PKG_RELEASE}" \ + --define "pkg_version ${pkg_version}" \ + --define "pkg_release ${PKG_RELEASE}" \ "${topdir}/SPECS/xrpld.spec" } @@ -182,23 +191,26 @@ build_deb() { cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles" cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate" - # Debian '~' marks a pre-release; 3.2.0~b1 sorts before 3.2.0. - local deb_full_version="${VER_BASE}${VER_SUFFIX:+~${VER_SUFFIX}}-${PKG_RELEASE}" - - # Derive release channel from the version suffix: - # (none) -> stable (tagged release) - # b0 -> develop (develop-branch build) - # b, rc -> unstable (pre-release) - local deb_distribution - case "${VER_SUFFIX}" in - "") deb_distribution="stable" ;; - b0) deb_distribution="develop" ;; - *) deb_distribution="unstable" ;; - esac + # Choose the Debian repository component for this package. + # 3.2.0 -> stable, *-b0[+metadata] -> develop, + # bN/rcN pre-releases -> unstable. + local deb_component + if [[ -z "${pre_release}" ]]; then + deb_component="stable" + elif [[ "${pre_release}" =~ ^b0(\+.*)?$ ]]; then + deb_component="develop" + elif [[ "${pre_release}" =~ ^(b[1-9][0-9]*|rc[0-9]+)(\+.*)?$ ]]; then + deb_component="unstable" + else + echo "build_pkg.sh: unsupported xrpld pre-release '${pre_release}'." >&2 + echo "Use bN or rcN, e.g. 3.2.0-b1 or 3.2.0-rc2." >&2 + exit 1 + fi + # Debian version is [~
]-.
     cat >"${staging}/debian/changelog" <  ${CHANGELOG_DATE}
 EOF
diff --git a/package/rpm/xrpld.spec b/package/rpm/xrpld.spec
index 5595fd0d8d..61c2d61ec6 100644
--- a/package/rpm/xrpld.spec
+++ b/package/rpm/xrpld.spec
@@ -1,6 +1,14 @@
+%if "%{?pkg_version}" == ""
+%{error:pkg_version must be defined}
+%endif
+
+%if "%{?pkg_release}" == ""
+%{error:pkg_release must be defined}
+%endif
+
 Name:     xrpld
-Version:  %{xrpld_version}
-Release:  %{xrpld_release}%{?dist}
+Version:  %{pkg_version}
+Release:  %{pkg_release}%{?dist}
 Summary:  XRP Ledger daemon
 
 License:  ISC
@@ -11,6 +19,9 @@ BuildRequires: systemd-rpm-macros
 
 %undefine _debugsource_packages
 %debug_package
+# Intentionally trade larger RPM artifacts for faster package validation.
+%global _binary_payload w.ufdio
+%global _find_debuginfo_dwz_opts %{nil}
 
 %build_mtime_policy clamp_to_source_date_epoch
 
@@ -37,7 +48,10 @@ install -Dm0644 %{_sourcedir}/validators.txt       %{buildroot}%{_sysconfdir}/%{
 install -Dm0644 %{_sourcedir}/xrpld.service        %{buildroot}%{_unitdir}/xrpld.service
 install -Dm0644 %{_sourcedir}/xrpld.sysusers       %{buildroot}%{_sysusersdir}/xrpld.conf
 install -Dm0644 %{_sourcedir}/xrpld.tmpfiles       %{buildroot}%{_tmpfilesdir}/xrpld.conf
-install -Dm0644 %{_sourcedir}/50-xrpld.preset      %{buildroot}%{_presetdir}/50-xrpld.preset
+install -Dm0644 /dev/null %{buildroot}%{_presetdir}/50-xrpld.preset
+cat >%{buildroot}%{_presetdir}/50-xrpld.preset <<'EOF'
+enable xrpld.service
+EOF
 
 # Logrotate config
 install -Dm0644 %{_sourcedir}/xrpld.logrotate      %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
@@ -62,7 +76,7 @@ systemd-tmpfiles --create %{_tmpfilesdir}/xrpld.conf || :
 %systemd_preun xrpld.service
 
 %postun
-%systemd_postun_with_restart xrpld.service
+%systemd_postun xrpld.service
 
 %files
 %license %{_docdir}/%{name}/LICENSE.md
diff --git a/package/shared/50-xrpld.preset b/package/shared/50-xrpld.preset
deleted file mode 100644
index bfbcd56577..0000000000
--- a/package/shared/50-xrpld.preset
+++ /dev/null
@@ -1,2 +0,0 @@
-# /usr/lib/systemd/system-preset/50-xrpld.preset
-enable xrpld.service

From 3097c157b6ca13d368eef8b03d26fa027b277a4c Mon Sep 17 00:00:00 2001
From: Ayaz Salikhov 
Date: Thu, 25 Jun 2026 13:40:06 +0100
Subject: [PATCH 2/3] build: Switch to a new conan XRPLF remote (#7622)

---
 .github/actions/setup-conan/action.yml       |  2 +-
 .github/workflows/on-pr.yml                  |  4 +-
 .github/workflows/on-tag.yml                 |  4 +-
 .github/workflows/on-trigger.yml             |  4 +-
 .github/workflows/reusable-upload-recipe.yml | 20 ++----
 .github/workflows/upload-conan-deps.yml      |  6 +-
 BUILD.md                                     |  2 +-
 conan.lock                                   | 64 ++++++++++----------
 conan/lockfile/regenerate.sh                 |  2 +-
 conanfile.py                                 |  4 +-
 docs/build/advanced_conan.md                 |  2 +-
 11 files changed, 54 insertions(+), 60 deletions(-)

diff --git a/.github/actions/setup-conan/action.yml b/.github/actions/setup-conan/action.yml
index 0dd22f0d92..e8a548cfce 100644
--- a/.github/actions/setup-conan/action.yml
+++ b/.github/actions/setup-conan/action.yml
@@ -9,7 +9,7 @@ inputs:
   remote_url:
     description: "The URL of the Conan endpoint to use."
     required: false
-    default: https://conan.ripplex.io
+    default: https://conan.xrplf.org/repository/conan/
 
 runs:
   using: composite
diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml
index 0c9eeda712..2ad0641863 100644
--- a/.github/workflows/on-pr.yml
+++ b/.github/workflows/on-pr.yml
@@ -154,8 +154,8 @@ jobs:
     if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release') }}
     uses: ./.github/workflows/reusable-upload-recipe.yml
     secrets:
-      remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
-      remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
+      remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
+      remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
 
   notify-clio:
     needs: upload-recipe
diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml
index 42d5827cab..abedc13d69 100644
--- a/.github/workflows/on-tag.yml
+++ b/.github/workflows/on-tag.yml
@@ -20,8 +20,8 @@ jobs:
     if: ${{ github.repository == 'XRPLF/rippled' }}
     uses: ./.github/workflows/reusable-upload-recipe.yml
     secrets:
-      remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
-      remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
+      remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
+      remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
 
   build-test:
     if: ${{ github.repository == 'XRPLF/rippled' }}
diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml
index 063cdbff7f..5f018cb12c 100644
--- a/.github/workflows/on-trigger.yml
+++ b/.github/workflows/on-trigger.yml
@@ -98,8 +98,8 @@ jobs:
     if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
     uses: ./.github/workflows/reusable-upload-recipe.yml
     secrets:
-      remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
-      remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
+      remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
+      remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
 
   package:
     needs: build-test
diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml
index a18f76796a..feeee0a621 100644
--- a/.github/workflows/reusable-upload-recipe.yml
+++ b/.github/workflows/reusable-upload-recipe.yml
@@ -14,7 +14,7 @@ on:
         description: "The URL of the Conan endpoint to use."
         required: false
         type: string
-        default: https://conan.ripplex.io
+        default: https://conan.xrplf.org/repository/conan/
 
     secrets:
       remote_username:
@@ -41,6 +41,10 @@ jobs:
   upload:
     runs-on: ubuntu-latest
     container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523
+    env:
+      REMOTE_NAME: ${{ inputs.remote_name }}
+      CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
+      CONAN_PASSWORD_XRPLF: ${{ secrets.remote_password }}
     steps:
       - name: Checkout repository
         uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -56,15 +60,9 @@ jobs:
           remote_url: ${{ inputs.remote_url }}
 
       - name: Log into Conan remote
-        env:
-          REMOTE_NAME: ${{ inputs.remote_name }}
-          REMOTE_USERNAME: ${{ secrets.remote_username }}
-          REMOTE_PASSWORD: ${{ secrets.remote_password }}
-        run: conan remote login "${REMOTE_NAME}" "${REMOTE_USERNAME}" --password "${REMOTE_PASSWORD}"
+        run: conan remote login "${REMOTE_NAME}" "${CONAN_LOGIN_USERNAME_XRPLF}" --password "${CONAN_PASSWORD_XRPLF}"
 
       - name: Upload Conan recipe (version)
-        env:
-          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=${{ steps.version.outputs.version }}
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
@@ -73,8 +71,6 @@ jobs:
       # 'develop' branch, see on-trigger.yml.
       - name: Upload Conan recipe (develop)
         if: ${{ github.event_name == 'push' }}
-        env:
-          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=develop
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop
@@ -83,8 +79,6 @@ jobs:
       # one of the 'release' branches, see on-pr.yml.
       - name: Upload Conan recipe (rc)
         if: ${{ github.event_name == 'pull_request' }}
-        env:
-          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=rc
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc
@@ -93,8 +87,6 @@ jobs:
       # release, see on-tag.yml.
       - name: Upload Conan recipe (release)
         if: ${{ startsWith(github.ref, 'refs/tags/') }}
-        env:
-          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=release
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release
diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml
index 5d3712cf9e..92b72cf6a9 100644
--- a/.github/workflows/upload-conan-deps.yml
+++ b/.github/workflows/upload-conan-deps.yml
@@ -34,7 +34,7 @@ on:
 
 env:
   CONAN_REMOTE_NAME: xrplf
-  CONAN_REMOTE_URL: https://conan.ripplex.io
+  CONAN_REMOTE_URL: https://conan.xrplf.org/repository/conan/
   NPROC_SUBTRACT: 2
 
 concurrency:
@@ -108,10 +108,12 @@ jobs:
 
       - name: Log into Conan remote
         if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
-        run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}"
+        run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.NEXUS_REMOTE_USERNAME }}" --password "${{ secrets.NEXUS_REMOTE_PASSWORD }}"
 
       - name: Upload Conan packages
         if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
         env:
           FORCE_OPTION: ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}
+          CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.NEXUS_REMOTE_USERNAME }}
+          CONAN_PASSWORD_XRPLF: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
         run: conan upload "*" --remote="${CONAN_REMOTE_NAME}" --confirm ${FORCE_OPTION}
diff --git a/BUILD.md b/BUILD.md
index 2ac24f2c5d..847cd7bc1a 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -101,7 +101,7 @@ More information on customizing Conan can be found in the [Advanced Conan config
 Run the following command to add the `xrplf` remote, which hosts some of our dependencies:
 
 ```bash
-conan remote add --index 0 --force xrplf https://conan.ripplex.io
+conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
 ```
 
 ### Set Up Ccache
diff --git a/conan.lock b/conan.lock
index d80a6d0c57..ae45a900b6 100644
--- a/conan.lock
+++ b/conan.lock
@@ -1,43 +1,43 @@
 {
     "version": "0.5",
     "requires": [
-        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
-        "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
-        "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311",
-        "soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
-        "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
-        "secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
-        "rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
-        "re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
-        "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
-        "openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f%1778071755.506",
-        "nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
-        "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
-        "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
-        "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
-        "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
+        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
+        "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1743678659.187",
+        "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149",
+        "soci/4.0.3#e726491a03468795453f7c83fc924a96%1751554127.172",
+        "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
+        "secp256k1/0.7.1#b1f450b7f78a36fff75bb6934a356f3a%1782338841.3729",
+        "rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1759820024.194",
+        "re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1772560729.95",
+        "protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1778763145.334",
+        "openssl/3.6.3#1163d4ddc603907084d08a6a0c6e580f%1782307150.583886",
+        "nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1774883011.384",
+        "lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1775037240.923",
+        "libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1774021608.288",
+        "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1722218217.276",
+        "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1776147552.838",
         "jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
-        "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
-        "grpc/1.81.0#2fb144aeb47e7f35c6ebb0e5f35bed31%1781620605.685",
-        "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
-        "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
-        "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
-        "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
-        "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282",
-        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
+        "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1755784855.585",
+        "grpc/1.81.1#5217e6ef0544c42b46f4af35d5e7f649%1782307148.845616",
+        "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
+        "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1754573467.979",
+        "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1766500685.317",
+        "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1762886692.465",
+        "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778050991.9",
+        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
     ],
     "build_requires": [
-        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
-        "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
-        "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
-        "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
+        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
+        "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1751971032.423",
+        "protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1778763145.334",
+        "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1745483323.489",
         "msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
-        "m4/1.4.19#4523e4347b55cd26ae918bd5770cab9a%1778062762.471",
-        "cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
-        "b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
+        "m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259",
+        "cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1781521538.233",
+        "b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1766594659.866",
         "automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
         "autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
-        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
+        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
     ],
     "python_requires": [],
     "overrides": {
@@ -57,7 +57,7 @@
             "boost/1.91.0"
         ],
         "lz4/[>=1.9.4 <2]": [
-            "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
+            "lz4/1.10.0#982d9b673900f665a1da109e09c17cab"
         ]
     },
     "config_requires": []
diff --git a/conan/lockfile/regenerate.sh b/conan/lockfile/regenerate.sh
index 1aa47628f0..98ee6f7c99 100755
--- a/conan/lockfile/regenerate.sh
+++ b/conan/lockfile/regenerate.sh
@@ -14,7 +14,7 @@ export CONAN_HOME="$TEMP_DIR"
 # Ensure that the xrplf remote is the first to be consulted, so any recipes we
 # patched are used. We also add it there to not created huge diff when the
 # official Conan Center Index is updated.
-conan remote add --force --index 0 xrplf https://conan.ripplex.io
+conan remote add --force --index 0 xrplf https://conan.xrplf.org/repository/conan/
 
 # Delete any existing lockfile.
 rm -f conan.lock
diff --git a/conanfile.py b/conanfile.py
index 5b78dc22e3..2733d4fc9c 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -28,10 +28,10 @@ class Xrpl(ConanFile):
 
     requires = [
         "ed25519/2015.03",
-        "grpc/1.81.0",
+        "grpc/1.81.1",
         "libarchive/3.8.7",
         "nudb/2.0.9",
-        "openssl/3.6.2",
+        "openssl/3.6.3",
         "secp256k1/0.7.1",
         "soci/4.0.3",
         "zlib/1.3.2",
diff --git a/docs/build/advanced_conan.md b/docs/build/advanced_conan.md
index aae17e385a..26b88ef186 100644
--- a/docs/build/advanced_conan.md
+++ b/docs/build/advanced_conan.md
@@ -34,7 +34,7 @@ higher index than the default Conan Center remote, so it is consulted first. You
 can do this by running:
 
 ```bash
-conan remote add --index 0 --force xrplf https://conan.ripplex.io
+conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
 ```
 
 Alternatively, you can pull our recipes from the repository and export them locally:

From 07c64f07f02cedf9eeb185fc7c28e2d6711fc113 Mon Sep 17 00:00:00 2001
From: Ayaz Salikhov 
Date: Thu, 25 Jun 2026 15:47:55 +0100
Subject: [PATCH 3/3] chore: Revert "build: Switch to a new conan XRPLF remote
 (#7622)" (#7623)

---
 .github/actions/setup-conan/action.yml       |  2 +-
 .github/workflows/on-pr.yml                  |  4 +-
 .github/workflows/on-tag.yml                 |  4 +-
 .github/workflows/on-trigger.yml             |  4 +-
 .github/workflows/reusable-upload-recipe.yml | 20 ++++--
 .github/workflows/upload-conan-deps.yml      |  6 +-
 BUILD.md                                     |  2 +-
 conan.lock                                   | 64 ++++++++++----------
 conan/lockfile/regenerate.sh                 |  2 +-
 conanfile.py                                 |  4 +-
 docs/build/advanced_conan.md                 |  2 +-
 11 files changed, 60 insertions(+), 54 deletions(-)

diff --git a/.github/actions/setup-conan/action.yml b/.github/actions/setup-conan/action.yml
index e8a548cfce..0dd22f0d92 100644
--- a/.github/actions/setup-conan/action.yml
+++ b/.github/actions/setup-conan/action.yml
@@ -9,7 +9,7 @@ inputs:
   remote_url:
     description: "The URL of the Conan endpoint to use."
     required: false
-    default: https://conan.xrplf.org/repository/conan/
+    default: https://conan.ripplex.io
 
 runs:
   using: composite
diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml
index 2ad0641863..0c9eeda712 100644
--- a/.github/workflows/on-pr.yml
+++ b/.github/workflows/on-pr.yml
@@ -154,8 +154,8 @@ jobs:
     if: ${{ github.repository == 'XRPLF/rippled' && needs.should-run.outputs.go == 'true' && github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release') }}
     uses: ./.github/workflows/reusable-upload-recipe.yml
     secrets:
-      remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
-      remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
+      remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
+      remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
 
   notify-clio:
     needs: upload-recipe
diff --git a/.github/workflows/on-tag.yml b/.github/workflows/on-tag.yml
index abedc13d69..42d5827cab 100644
--- a/.github/workflows/on-tag.yml
+++ b/.github/workflows/on-tag.yml
@@ -20,8 +20,8 @@ jobs:
     if: ${{ github.repository == 'XRPLF/rippled' }}
     uses: ./.github/workflows/reusable-upload-recipe.yml
     secrets:
-      remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
-      remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
+      remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
+      remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
 
   build-test:
     if: ${{ github.repository == 'XRPLF/rippled' }}
diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml
index 5f018cb12c..063cdbff7f 100644
--- a/.github/workflows/on-trigger.yml
+++ b/.github/workflows/on-trigger.yml
@@ -98,8 +98,8 @@ jobs:
     if: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
     uses: ./.github/workflows/reusable-upload-recipe.yml
     secrets:
-      remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
-      remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
+      remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }}
+      remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }}
 
   package:
     needs: build-test
diff --git a/.github/workflows/reusable-upload-recipe.yml b/.github/workflows/reusable-upload-recipe.yml
index feeee0a621..a18f76796a 100644
--- a/.github/workflows/reusable-upload-recipe.yml
+++ b/.github/workflows/reusable-upload-recipe.yml
@@ -14,7 +14,7 @@ on:
         description: "The URL of the Conan endpoint to use."
         required: false
         type: string
-        default: https://conan.xrplf.org/repository/conan/
+        default: https://conan.ripplex.io
 
     secrets:
       remote_username:
@@ -41,10 +41,6 @@ jobs:
   upload:
     runs-on: ubuntu-latest
     container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523
-    env:
-      REMOTE_NAME: ${{ inputs.remote_name }}
-      CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
-      CONAN_PASSWORD_XRPLF: ${{ secrets.remote_password }}
     steps:
       - name: Checkout repository
         uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -60,9 +56,15 @@ jobs:
           remote_url: ${{ inputs.remote_url }}
 
       - name: Log into Conan remote
-        run: conan remote login "${REMOTE_NAME}" "${CONAN_LOGIN_USERNAME_XRPLF}" --password "${CONAN_PASSWORD_XRPLF}"
+        env:
+          REMOTE_NAME: ${{ inputs.remote_name }}
+          REMOTE_USERNAME: ${{ secrets.remote_username }}
+          REMOTE_PASSWORD: ${{ secrets.remote_password }}
+        run: conan remote login "${REMOTE_NAME}" "${REMOTE_USERNAME}" --password "${REMOTE_PASSWORD}"
 
       - name: Upload Conan recipe (version)
+        env:
+          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=${{ steps.version.outputs.version }}
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
@@ -71,6 +73,8 @@ jobs:
       # 'develop' branch, see on-trigger.yml.
       - name: Upload Conan recipe (develop)
         if: ${{ github.event_name == 'push' }}
+        env:
+          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=develop
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/develop
@@ -79,6 +83,8 @@ jobs:
       # one of the 'release' branches, see on-pr.yml.
       - name: Upload Conan recipe (rc)
         if: ${{ github.event_name == 'pull_request' }}
+        env:
+          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=rc
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/rc
@@ -87,6 +93,8 @@ jobs:
       # release, see on-tag.yml.
       - name: Upload Conan recipe (release)
         if: ${{ startsWith(github.ref, 'refs/tags/') }}
+        env:
+          REMOTE_NAME: ${{ inputs.remote_name }}
         run: |
           conan export . --version=release
           conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release
diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml
index 92b72cf6a9..5d3712cf9e 100644
--- a/.github/workflows/upload-conan-deps.yml
+++ b/.github/workflows/upload-conan-deps.yml
@@ -34,7 +34,7 @@ on:
 
 env:
   CONAN_REMOTE_NAME: xrplf
-  CONAN_REMOTE_URL: https://conan.xrplf.org/repository/conan/
+  CONAN_REMOTE_URL: https://conan.ripplex.io
   NPROC_SUBTRACT: 2
 
 concurrency:
@@ -108,12 +108,10 @@ jobs:
 
       - name: Log into Conan remote
         if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
-        run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.NEXUS_REMOTE_USERNAME }}" --password "${{ secrets.NEXUS_REMOTE_PASSWORD }}"
+        run: conan remote login "${CONAN_REMOTE_NAME}" "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}"
 
       - name: Upload Conan packages
         if: ${{ github.repository == 'XRPLF/rippled' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') }}
         env:
           FORCE_OPTION: ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }}
-          CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.NEXUS_REMOTE_USERNAME }}
-          CONAN_PASSWORD_XRPLF: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
         run: conan upload "*" --remote="${CONAN_REMOTE_NAME}" --confirm ${FORCE_OPTION}
diff --git a/BUILD.md b/BUILD.md
index 847cd7bc1a..2ac24f2c5d 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -101,7 +101,7 @@ More information on customizing Conan can be found in the [Advanced Conan config
 Run the following command to add the `xrplf` remote, which hosts some of our dependencies:
 
 ```bash
-conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
+conan remote add --index 0 --force xrplf https://conan.ripplex.io
 ```
 
 ### Set Up Ccache
diff --git a/conan.lock b/conan.lock
index ae45a900b6..d80a6d0c57 100644
--- a/conan.lock
+++ b/conan.lock
@@ -1,43 +1,43 @@
 {
     "version": "0.5",
     "requires": [
-        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
-        "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1743678659.187",
-        "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1776096494.149",
-        "soci/4.0.3#e726491a03468795453f7c83fc924a96%1751554127.172",
-        "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1782307151.633168",
-        "secp256k1/0.7.1#b1f450b7f78a36fff75bb6934a356f3a%1782338841.3729",
-        "rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1759820024.194",
-        "re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1772560729.95",
-        "protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1778763145.334",
-        "openssl/3.6.3#1163d4ddc603907084d08a6a0c6e580f%1782307150.583886",
-        "nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1774883011.384",
-        "lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1775037240.923",
-        "libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1774021608.288",
-        "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1722218217.276",
-        "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1776147552.838",
+        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
+        "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
+        "sqlite3/3.53.0#324ada52333108388a9a6108bfa96734%1778091117.311",
+        "soci/4.0.3#fe32b9ad5eb47e79ab9e45a68f363945%1774450067.231",
+        "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
+        "secp256k1/0.7.1#481881709eb0bdd0185a12b912bbe8ad%1770910500.329",
+        "rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
+        "re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1774398111.888",
+        "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
+        "openssl/3.6.2#4789bbf131b77d0515d15e094c8f697f%1778071755.506",
+        "nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1775040983.408",
+        "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
+        "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
+        "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
+        "libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1778091117.848",
         "jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
-        "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1755784855.585",
-        "grpc/1.81.1#5217e6ef0544c42b46f4af35d5e7f649%1782307148.845616",
-        "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
-        "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1754573467.979",
-        "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1766500685.317",
-        "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1762886692.465",
-        "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778050991.9",
-        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
+        "gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1768312129.152",
+        "grpc/1.81.0#2fb144aeb47e7f35c6ebb0e5f35bed31%1781620605.685",
+        "ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1765850143.772",
+        "date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
+        "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681",
+        "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
+        "boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1778091165.282",
+        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
     ],
     "build_requires": [
-        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1777558780.503",
-        "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1751971032.423",
-        "protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1778763145.334",
-        "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1745483323.489",
+        "zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1778091116.056",
+        "strawberryperl/5.32.1.1#8d114504d172cfea8ea1662d09b6333e%1774447376.964",
+        "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12",
+        "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
         "msys2/cci.latest#d22fe7b2808f5fd34d0a7923ace9c54f%1770657326.649",
-        "m4/1.4.19#34c4bbc3eeebe98ca6edf2f52d602e7d%1777282960.259",
-        "cmake/4.3.3#840cf00ea09777e05c2050a50a82c722%1781521538.233",
-        "b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1766594659.866",
+        "m4/1.4.19#4523e4347b55cd26ae918bd5770cab9a%1778062762.471",
+        "cmake/4.3.0#b939a42e98f593fb34d3a8c5cc860359%1774439249.183",
+        "b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1774439233.447",
         "automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
         "autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
-        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
+        "abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1774365460.196"
     ],
     "python_requires": [],
     "overrides": {
@@ -57,7 +57,7 @@
             "boost/1.91.0"
         ],
         "lz4/[>=1.9.4 <2]": [
-            "lz4/1.10.0#982d9b673900f665a1da109e09c17cab"
+            "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
         ]
     },
     "config_requires": []
diff --git a/conan/lockfile/regenerate.sh b/conan/lockfile/regenerate.sh
index 98ee6f7c99..1aa47628f0 100755
--- a/conan/lockfile/regenerate.sh
+++ b/conan/lockfile/regenerate.sh
@@ -14,7 +14,7 @@ export CONAN_HOME="$TEMP_DIR"
 # Ensure that the xrplf remote is the first to be consulted, so any recipes we
 # patched are used. We also add it there to not created huge diff when the
 # official Conan Center Index is updated.
-conan remote add --force --index 0 xrplf https://conan.xrplf.org/repository/conan/
+conan remote add --force --index 0 xrplf https://conan.ripplex.io
 
 # Delete any existing lockfile.
 rm -f conan.lock
diff --git a/conanfile.py b/conanfile.py
index 2733d4fc9c..5b78dc22e3 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -28,10 +28,10 @@ class Xrpl(ConanFile):
 
     requires = [
         "ed25519/2015.03",
-        "grpc/1.81.1",
+        "grpc/1.81.0",
         "libarchive/3.8.7",
         "nudb/2.0.9",
-        "openssl/3.6.3",
+        "openssl/3.6.2",
         "secp256k1/0.7.1",
         "soci/4.0.3",
         "zlib/1.3.2",
diff --git a/docs/build/advanced_conan.md b/docs/build/advanced_conan.md
index 26b88ef186..aae17e385a 100644
--- a/docs/build/advanced_conan.md
+++ b/docs/build/advanced_conan.md
@@ -34,7 +34,7 @@ higher index than the default Conan Center remote, so it is consulted first. You
 can do this by running:
 
 ```bash
-conan remote add --index 0 --force xrplf https://conan.xrplf.org/repository/conan/
+conan remote add --index 0 --force xrplf https://conan.ripplex.io
 ```
 
 Alternatively, you can pull our recipes from the repository and export them locally: