Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rqlite for openSUSE:Factory checked 
in at 2026-10-31 23:08:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rqlite (Old)
 and      /work/SRC/openSUSE:Factory/.rqlite.new.1255 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rqlite"

Sat Oct 31 23:08:55 2026 rev:61 rq:1394035 version:10.6.6

Changes:
--------
--- /work/SRC/openSUSE:Factory/rqlite/rqlite.changes    2026-10-23 
20:26:31.445081606 +0200
+++ /work/SRC/openSUSE:Factory/.rqlite.new.1255/rqlite.changes  2026-10-31 
23:11:00.495474492 +0100
@@ -1,0 +2,15 @@
+Sat Oct 31 07:26:58 UTC 2026 - Andreas Stieger <[email protected]>
+
+- Update to version 10.6.6: 
+  * Keep the local auto-backup file if the metadata rename fails
+  * Reject local auto-backup filenames reserved for internal use
+  * POST /demote and POST /promote return a JSON error body, and
+    404 when the target node is not in the cluster.
+  * DELETE /remove returns a JSON error body.
+- Includes changes from  version 10.6.5:
+  * Guard the inter-node backup stream handler directly against a
+    missing backup request
+  * POST /leader returns a JSON error body, and 404 when the target
+    node is not in the cluster
+
+-------------------------------------------------------------------

Old:
----
  rqlite-10.6.4.tar.xz

New:
----
  rqlite-10.6.6.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rqlite.spec ++++++
--- /var/tmp/diff_new_pack.H0jLOE/_old  2026-10-31 23:11:02.757568404 +0100
+++ /var/tmp/diff_new_pack.H0jLOE/_new  2026-10-31 23:11:02.759568487 +0100
@@ -18,7 +18,7 @@
 
 
 Name:           rqlite
-Version:        10.6.4
+Version:        10.6.6
 Release:        0
 Summary:        Distributed relational database built on SQLite
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.H0jLOE/_old  2026-10-31 23:11:02.861572722 +0100
+++ /var/tmp/diff_new_pack.H0jLOE/_new  2026-10-31 23:11:02.869573054 +0100
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/rqlite/rqlite.git</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v10.6.4</param>
+    <param name="revision">v10.6.6</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">enable</param>
     <param name="versionrewrite-pattern">v(.*)</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.H0jLOE/_old  2026-10-31 23:11:02.924575338 +0100
+++ /var/tmp/diff_new_pack.H0jLOE/_new  2026-10-31 23:11:02.930575587 +0100
@@ -1,7 +1,7 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">https://github.com/rqlite/rqlite.git</param>
-    <param 
name="changesrevision">64145717224d620809174cb2235ac86ec604aac5</param>
+    <param 
name="changesrevision">ce2152610ad76b7371e459931c7663e0176092d2</param>
   </service>
 </servicedata>
 (No newline at EOF)

++++++ rqlite-10.6.4.tar.xz -> rqlite-10.6.6.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/CHANGELOG.md 
new/rqlite-10.6.6/CHANGELOG.md
--- old/rqlite-10.6.4/CHANGELOG.md      2026-10-22 04:39:23.000000000 +0200
+++ new/rqlite-10.6.6/CHANGELOG.md      2026-10-28 13:41:24.000000000 +0100
@@ -1,5 +1,18 @@
+## v10.6.6 (October 28th 2026)
+### Implementation changes and bug fixes
+- [PR #2987](https://github.com/rqlite/rqlite/pull/2987): Keep the local 
auto-backup file if the metadata rename fails. Thanks @lmtlssss
+- [PR #2988](https://github.com/rqlite/rqlite/pull/2988): Reject local 
auto-backup filenames reserved for internal use. Thanks @lmtlssss
+- [PR #2989](https://github.com/rqlite/rqlite/pull/2989): `POST /demote` and 
`POST /promote` return a JSON error body, and 404 when the target node is not 
in the cluster.
+- [PR #2991](https://github.com/rqlite/rqlite/pull/2991): `DELETE /remove` 
returns a JSON error body.
+- [PR #2992](https://github.com/rqlite/rqlite/pull/2992): Upgrade dependencies 
via `go get`.
+
+## v10.6.5 (October 24th 2026)
+### Implementation changes and bug fixes
+- [PR #2982](https://github.com/rqlite/rqlite/pull/2982): Guard the inter-node 
backup stream handler directly against a missing backup request.
+- [PR #2984](https://github.com/rqlite/rqlite/pull/2984): `POST /leader` 
returns a JSON error body, and 404 when the target node is not in the cluster.
+
 ## v10.6.4 (October 21st 2026)
-This release includes [a critical security 
fix](https://github.com/rqlite/rqlite/security/advisories/GHSA-f4f3-px5g-gm4x). 
Users should upgrade.
+This release includes [a critical security 
fix](https://github.com/rqlite/rqlite/security/advisories/GHSA-f4f3-px5g-gm4x) 
related to backups. An unauthorized user could download a backup if they had 
access to the Raft port. Upgrading strongly recommended.
 
 ### Implementation changes and bug fixes
 - [PR #2977](https://github.com/rqlite/rqlite/pull/2977): `GET /leader` 
returns a JSON error body when there is no leader. Fixes issue 
[#2974](https://github.com/rqlite/rqlite/issues/2974).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/auto/file/file.go 
new/rqlite-10.6.6/auto/file/file.go
--- old/rqlite-10.6.4/auto/file/file.go 2026-10-22 04:39:23.000000000 +0200
+++ new/rqlite-10.6.6/auto/file/file.go 2026-10-28 13:41:24.000000000 +0100
@@ -13,6 +13,11 @@
        "github.com/rqlite/rqlite/v10/internal/fsutil"
 )
 
+const (
+       metadataFile = "METADATA.json"
+       touchFile    = ".touch"
+)
+
 // Config represents configuration for the file storage client.
 type Config struct {
        Dir  string `json:"dir"`
@@ -57,12 +62,15 @@
                cleanFile != name {
                return nil, fmt.Errorf("invalid file parameter: %s (must be a 
simple filename without path separators)", name)
        }
+       if equalFoldAny(name, metadataFile, metadataFile+".tmp", touchFile) {
+               return nil, fmt.Errorf("invalid file parameter: %s is reserved 
for internal use", name)
+       }
 
        // Ensure the destination directory exists and is writable
        if err := os.MkdirAll(dir, 0755); err != nil {
                return nil, fmt.Errorf("failed to create directory %s: %w", 
dir, err)
        }
-       touchPath := filepath.Join(dir, ".touch")
+       touchPath := filepath.Join(dir, touchFile)
        f, err := os.OpenFile(touchPath, os.O_CREATE|os.O_WRONLY, 0644)
        if err != nil {
                return nil, fmt.Errorf("failed to test writing to directory %s: 
%w", dir, err)
@@ -73,7 +81,7 @@
        c := &Client{
                dir:      dir,
                name:     name,
-               metaPath: filepath.Join(dir, "METADATA.json"),
+               metaPath: filepath.Join(dir, metadataFile),
        }
 
        if opt != nil {
@@ -177,8 +185,11 @@
                return fmt.Errorf("failed to rename temporary file %s to %s: 
%w", tmpPath, finalPath, err)
        }
 
+       // Do not remove finalPath if the metadata rename fails. In stable-name
+       // mode it has already replaced the previous backup, so removing it 
would
+       // leave the existing metadata pointing at a file that no longer exists.
+       // Leaving it in place means a complete backup is always present.
        if err := fsutil.Rename(tmpMetaPath, c.metaPath); err != nil {
-               fsutil.Remove(finalPath)
                return fmt.Errorf("failed to rename temporary metadata file %s 
to %s: %w", tmpMetaPath, c.metaPath, err)
        }
        return nil
@@ -196,6 +207,16 @@
        return md.ID, nil
 }
 
+// equalFoldAny returns true if s matches any of names under Unicode 
case-folding.
+func equalFoldAny(s string, names ...string) bool {
+       for _, n := range names {
+               if strings.EqualFold(s, n) {
+                       return true
+               }
+       }
+       return false
+}
+
 // timestampedPath returns a new path with the given timestamp prepended.
 // If path contains /, the timestamp is prepended to the last segment.
 func timestampedPath(path string, t time.Time) string {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/cluster/service.go 
new/rqlite-10.6.6/cluster/service.go
--- old/rqlite-10.6.4/cluster/service.go        2026-10-22 04:39:23.000000000 
+0200
+++ new/rqlite-10.6.6/cluster/service.go        2026-10-28 13:41:24.000000000 
+0100
@@ -654,30 +654,22 @@
 
                case proto.Command_COMMAND_TYPE_BACKUP_STREAM:
                        stats.Add(numBackupRequest, 1)
-                       resp := &proto.CommandBackupResponse{}
 
                        br := c.GetBackupRequest()
                        if br == nil {
-                               resp.Error = "BackupRequest is nil"
-                       }
-                       p, err = pb.Marshal(resp)
-                       if err != nil {
-                               conn.Close()
-                               return
-                       }
-                       if err := writeBytesWithLength(conn, p); err != nil {
+                               // Nothing to stream. Tell the client and end 
the connection.
+                               resp := &proto.CommandBackupResponse{Error: 
"BackupRequest is nil"}
+                               marshalAndWrite(conn, resp)
                                return
                        }
-                       if resp.Error != "" {
-                               // There is nothing to stream, so end the 
connection
-                               // so the client sees a clean end instead of 
waiting.
+
+                       // Tell the client the stream is coming, then stream 
it. Enable
+                       // compressed mode regardless of what the client asked 
for, so the
+                       // client can detect the end of the stream and to save 
space on
+                       // the wire.
+                       if err := marshalAndWrite(conn, 
&proto.CommandBackupResponse{}); err != nil {
                                return
                        }
-
-                       // Now, start streaming the backup. Enable compressed 
mode
-                       // regardless of whether the client requested it, so 
the client
-                       // can easily detect the end of the stream, as well as 
saving
-                       // space on the wire.
                        br.Compress = true
                        if _, err := s.db.Backup(context.Background(), br, 
conn); err != nil {
                                s.logger.Printf("failed to stream backup: %s", 
err.Error())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/go.mod new/rqlite-10.6.6/go.mod
--- old/rqlite-10.6.4/go.mod    2026-10-22 04:39:23.000000000 +0200
+++ new/rqlite-10.6.6/go.mod    2026-10-28 13:41:24.000000000 +0100
@@ -3,11 +3,11 @@
 go 1.27.0
 
 require (
-       github.com/aws/aws-sdk-go-v2 v1.47.3
-       github.com/aws/aws-sdk-go-v2/config v1.33.9
-       github.com/aws/aws-sdk-go-v2/credentials v1.20.9
-       github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.14
-       github.com/aws/aws-sdk-go-v2/service/s3 v1.114.3
+       github.com/aws/aws-sdk-go-v2 v1.47.5
+       github.com/aws/aws-sdk-go-v2/config v1.33.12
+       github.com/aws/aws-sdk-go-v2/credentials v1.20.12
+       github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.18
+       github.com/aws/aws-sdk-go-v2/service/s3 v1.114.6
        github.com/hashicorp/go-hclog v1.6.3
        github.com/hashicorp/raft v1.8.0
        github.com/klauspost/compress v1.20.3
@@ -25,27 +25,27 @@
        go.opentelemetry.io/otel/sdk v1.47.0
        go.opentelemetry.io/otel/sdk/metric v1.47.0
        go.opentelemetry.io/proto/otlp v1.11.1
-       golang.org/x/net v0.61.0
-       google.golang.org/grpc v1.84.0
+       golang.org/x/net v0.62.0
+       google.golang.org/grpc v1.85.0
        google.golang.org/protobuf v1.36.12
 )
 
 require (
        github.com/armon/go-metrics v0.7.0 // indirect
-       github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.22 // 
indirect
-       github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.3 // indirect
-       github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.6 // indirect
-       github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.6 // indirect
-       github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.6 // indirect
-       github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.21 
// indirect
-       github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.7 // 
indirect
-       github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.6 // 
indirect
-       github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.6 // 
indirect
-       github.com/aws/aws-sdk-go-v2/service/signin v1.10.4 // indirect
-       github.com/aws/aws-sdk-go-v2/service/sso v1.38.4 // indirect
-       github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.4 // indirect
-       github.com/aws/aws-sdk-go-v2/service/sts v1.51.4 // indirect
-       github.com/aws/smithy-go v1.28.5 // indirect
+       github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.25 // 
indirect
+       github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.5 // indirect
+       github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.8 // indirect
+       github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.8 // indirect
+       github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.8 // indirect
+       github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.23 
// indirect
+       github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.9 // 
indirect
+       github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.8 // 
indirect
+       github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.8 // 
indirect
+       github.com/aws/aws-sdk-go-v2/service/signin v1.12.0 // indirect
+       github.com/aws/aws-sdk-go-v2/service/sso v1.39.0 // indirect
+       github.com/aws/aws-sdk-go-v2/service/ssooidc v1.44.0 // indirect
+       github.com/aws/aws-sdk-go-v2/service/sts v1.51.6 // indirect
+       github.com/aws/smithy-go v1.28.7 // indirect
        github.com/cenkalti/backoff/v5 v5.0.3 // indirect
        github.com/cespare/xxhash/v2 v2.3.0 // indirect
        github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
@@ -57,8 +57,8 @@
        github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
        github.com/golang/protobuf v1.5.4 // indirect
        github.com/google/uuid v1.6.0 // indirect
-       github.com/grpc-ecosystem/grpc-gateway/v2 v2.32.0 // indirect
-       github.com/hashicorp/consul/api v1.34.5 // indirect
+       github.com/grpc-ecosystem/grpc-gateway/v2 v2.33.0 // indirect
+       github.com/hashicorp/consul/api v1.35.0 // indirect
        github.com/hashicorp/errwrap v1.1.0 // indirect
        github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
        github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
@@ -66,7 +66,7 @@
        github.com/hashicorp/go-msgpack v1.1.5 // indirect
        github.com/hashicorp/go-msgpack/v2 v2.1.5 // indirect
        github.com/hashicorp/go-multierror v1.1.1 // indirect
-       github.com/hashicorp/go-rootcerts v1.0.2 // indirect
+       github.com/hashicorp/go-rootcerts v1.1.0 // indirect
        github.com/hashicorp/golang-lru v1.0.2 // indirect
        github.com/hashicorp/serf v0.11.0 // indirect
        github.com/labstack/gommon v0.5.0 // indirect
@@ -75,9 +75,9 @@
        github.com/mattn/go-runewidth v0.0.31 // indirect
        github.com/mitchellh/go-homedir v1.1.0 // indirect
        github.com/mkideal/expr v0.1.0 // indirect
-       go.etcd.io/etcd/api/v3 v3.7.2 // indirect
-       go.etcd.io/etcd/client/pkg/v3 v3.7.2 // indirect
-       go.etcd.io/etcd/client/v3 v3.7.2 // indirect
+       go.etcd.io/etcd/api/v3 v3.7.3 // indirect
+       go.etcd.io/etcd/client/pkg/v3 v3.7.3 // indirect
+       go.etcd.io/etcd/client/v3 v3.7.3 // indirect
        go.opentelemetry.io/auto/sdk v1.2.1 // indirect
        go.opentelemetry.io/otel/log v1.47.0 // indirect
        go.opentelemetry.io/otel/metric v1.47.0 // indirect
@@ -89,8 +89,8 @@
        golang.org/x/sys v0.49.0 // indirect
        golang.org/x/term v0.47.0 // indirect
        golang.org/x/text v0.43.0 // indirect
-       google.golang.org/genproto/googleapis/api 
v0.0.0-20261012220534-4341edf3f941 // indirect
-       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20261012220534-4341edf3f941 // indirect
+       google.golang.org/genproto/googleapis/api 
v0.0.0-20261026165753-2ffab32bce7e // indirect
+       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20261026165753-2ffab32bce7e // indirect
 )
 
 replace (
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/go.sum new/rqlite-10.6.6/go.sum
--- old/rqlite-10.6.4/go.sum    2026-10-22 04:39:23.000000000 +0200
+++ new/rqlite-10.6.6/go.sum    2026-10-28 13:41:24.000000000 +0100
@@ -3,44 +3,44 @@
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod 
h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod 
h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod 
h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/aws/aws-sdk-go-v2 v1.47.3 
h1:HRoT/MkNi3bjO5xF4dCzLzvVmkvRGHj3/cLINDnJejk=
-github.com/aws/aws-sdk-go-v2 v1.47.3/go.mod 
h1:xSl+OSI5ZUw6bSMupWc218zE5Pl+65MCCu3ySWFY99A=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.22 
h1:oiEmPz/YMMeh/JvAE8xSxNCIsvWE6lpFe4ZPlyFykQI=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.22/go.mod 
h1:QWUDuRi9PFjBHIU2iH1Pkldbe7zqLfXJC4wWLbF8nuQ=
-github.com/aws/aws-sdk-go-v2/config v1.33.9 
h1:Hv6bcQUJVnmuWaKjqD6Ot8sE75NrWsWV20oGT5aWle0=
-github.com/aws/aws-sdk-go-v2/config v1.33.9/go.mod 
h1:ivowmrZNfBQLcg4QkaItgi5wae4Bzot9RzIdbY1hfTg=
-github.com/aws/aws-sdk-go-v2/credentials v1.20.9 
h1:SNO55PnWhaDafsOcv16lCA0kdoFMl/YlegWoVsU02s0=
-github.com/aws/aws-sdk-go-v2/credentials v1.20.9/go.mod 
h1:+2reOTRtdD3VSBqpRDNwrZIYy8yMCYa7oK3WKTJ/I1I=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.3 
h1:gr3b87OLB29jymWPzp/CAyXwQRJVE+sC4Wm0qBIKHsk=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.3/go.mod 
h1:BRjf4L/Yx2hY54o+ZibPbLJyMMeSwqYQXwcJfqW66a8=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.14 
h1:nX4dV32e7e2GSg60GojffG61w4tvYxVpvEGWfSlcImo=
-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.14/go.mod 
h1:dNw78YbA0ylhEVJ1h84fPdtIiWkVTaszT7oHkE/cq5E=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.6 
h1:CqL8NN3p4I0JTwwV5MEoatIl4Y2amZ2X9dRD+0hbRL0=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.6/go.mod 
h1:exdJ32hMqXl0Wyo6NwqEC5yIhtua3+PDaKE8P29m4qE=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.6 
h1:FpBq24TG6WVSObTNhQOv7Q0088rounUUlwn3zlNItkM=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.6/go.mod 
h1:ONzdPVYC95oBOu0VWNEsKEntcoemP3xVKjT+qWml1s4=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.6 
h1:Qmn60RWuk9+bd2ddvJJqwf3Cpac3VvGMOdbGemtUhX4=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.6/go.mod 
h1:h/YJhe4AVyQxEUMluph10iqV7mjajH7A9L+BHF5ppis=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.21 
h1:e6YW5uME30TkrElR+4/xcY7NB6CCj2dcv7vtmGXhfhQ=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.21/go.mod 
h1:obpHAMP693+f3/A8PkLhIqfK1C+NWWn8zXzqD3OH520=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.7 
h1:CwvFlKw0uUScfgOMflXSRZa+ayXgOf6wk7lhyMemQYU=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.7/go.mod 
h1:4SbD8BB+p+QEnG0aX6DoWwrtk9SufyYXRsUv0nve8LE=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.6 
h1:LHHdobjo5le1sjJdolitV7uVvYg3wkaeM1Ju2fDgeZQ=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.6/go.mod 
h1:CBGB0evAiaMD3Lk4wPO7LQCcCZLuhzL5Qf/wfEUHQ5w=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.6 
h1:mYViDr2azcgxOpRrObZOl631bvavqILoE/L+Kxgbwys=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.6/go.mod 
h1:OXVdAo9jNhggB+hJRGOyy7WolGTKB6Rg7IIulDeSzPM=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.114.3 
h1:NyeuyXkZL476uoAowNActm4XJO1QPKf7aassgXHpiqA=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.114.3/go.mod 
h1:GfoBeKMDJecgRUsGf/0RGdx0NJAh81ObTd922h8pQys=
-github.com/aws/aws-sdk-go-v2/service/signin v1.10.4 
h1:tvpDjuUvgKURf3Yk4hOaGbZ4QdY+070OWcA5d6LUv2A=
-github.com/aws/aws-sdk-go-v2/service/signin v1.10.4/go.mod 
h1:3ibVKg5Ng82Zby8++jELTD2nHxiPk2DJRLEeW/LKp8M=
-github.com/aws/aws-sdk-go-v2/service/sso v1.38.4 
h1:ChCtagixSFOxltv0o6E3NT8F7ksvZtZGPwUBynO5Nmk=
-github.com/aws/aws-sdk-go-v2/service/sso v1.38.4/go.mod 
h1:+FEO9jrVMpc9oN3CdAxJbtlQEu3z8UddDPSedpeM3PY=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.4 
h1:j6c+HA4AoS0EBEjArPjavFEBrucJEryfrQU0TffmACk=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.4/go.mod 
h1:RhghaXUk9q66VtOQUX1FdPddQuHnqQZzbB0fEYXmPRA=
-github.com/aws/aws-sdk-go-v2/service/sts v1.51.4 
h1:OKEfbf2tcfsF2pBk/S9QqNpo6alaAtiBs4hysCsBjPw=
-github.com/aws/aws-sdk-go-v2/service/sts v1.51.4/go.mod 
h1:mCd6qGo9b8hTtfjgsmNkFlzwjyce4owd7HDVFnCLXhM=
-github.com/aws/smithy-go v1.28.5 
h1:OqbqKtzY9NUnr2yAe9+hiTdFb20tNaK/lI0PMOGXX9k=
-github.com/aws/smithy-go v1.28.5/go.mod 
h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
+github.com/aws/aws-sdk-go-v2 v1.47.5 
h1:5yA+A+BiXbDq1ZLbYwPViwhEGDMdR0MEhL8Jh2LnLS4=
+github.com/aws/aws-sdk-go-v2 v1.47.5/go.mod 
h1:iNIkogrRvFwm+1OQIUOP2lPBtlXZboOIUWiCa+pHEMw=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.25 
h1:MW0Iql1SOTgQPj8mCxrU/eH/K/AIDWi08ItOsXaWRLU=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.25/go.mod 
h1:+ucVYmHYB3JqVueY0E1RHahdXAnKQbIHqybSbvOUbOM=
+github.com/aws/aws-sdk-go-v2/config v1.33.12 
h1:IV1YARaXahwCqUIyP8IlWsP85Lil+5ozTozDQXPumRk=
+github.com/aws/aws-sdk-go-v2/config v1.33.12/go.mod 
h1:h51R06bzMVwNBRZOX5ylHhNDnkB/azZeq/LvxM6q2pQ=
+github.com/aws/aws-sdk-go-v2/credentials v1.20.12 
h1:6P7Q7yH/GIwHg6MLKv3ySsBqaCoE1z+C0OTzYMenhEY=
+github.com/aws/aws-sdk-go-v2/credentials v1.20.12/go.mod 
h1:Kw/SaoW/wc7IqJErqXJIzKo8b7Aelxfbuc6XlbrRnsc=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.5 
h1:iXjy2SFhGYNB1jWZBhQqq8HbhsbPF23w+/zbMvPXsTg=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.5/go.mod 
h1:k0RUiBnyXU0AXRJ9tJmY2jQke6XUIX2PNVnWtvgaPtc=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.18 
h1:cNToRnziFY/DKoIUg9Vw350yx0sFfaN1FCJ+xVuF53Y=
+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.23.18/go.mod 
h1:FQWrFCsxaXWlLBYB3YQOBx1zbfDNRrrVspVnnsTZ8Z0=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.8 
h1:gzIWc3DTPfPbWKrQnmL6BV6WD7NPD1htxzgdk/bQ/Z4=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.8/go.mod 
h1:vvUA5cDAZWGo4R14FZPZfXfJCmgsafOWEq78Kg0D+ug=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.8 
h1:PWv+vG8aCLS8qd+HPdYhK7Vees90aKHWFFQ1AGYD/8E=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.8/go.mod 
h1:xxPZEpflYHJA12366ZGyq66z9dHPCoeCIu99Jck+MPw=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.8 
h1:WOp5De9iWdEHDPh2Kb0tBP3vGBm/dVAuRGh4a4tqduo=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.8/go.mod 
h1:z8W8YgfhbWl62XqcTtK+CxPrE+iHGRVUOtrrbIOp7ZY=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.23 
h1:MCIR6iZaCKO4UE0hUvoWWLI6JVjppopV64JfAJaMjng=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.23/go.mod 
h1:pphWFS+z0MmIPivj8c/6bATd1Z+Av7Ust75DPdEFsPA=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.9 
h1:GACgECI422Xt5n4Pojr3O0aocPv8lfH4PJe2Aon5Wv8=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.11.9/go.mod 
h1:lPfnRkHpn1qWZJLbVEw10rIqSuCee1pNQTyGkUjAYh8=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.8 
h1:+PMpWlJYOn2tm7sm6OZ/ZLd4jStl17T7wjNapd1Rczs=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.8/go.mod 
h1:cnG3xZLGQL4c/bt8pjdzlFtZsW7epBBuNS04a5xMjdo=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.8 
h1:RS1Wt+K0r4X0ci9Vf4JYTUfHzsQihYA2LgGJeEneKpk=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.20.8/go.mod 
h1:adMnTy5kyoUW86jhXnGSVc/mvMQI4GZd1qYWMeaS8M8=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.114.6 
h1:to7+Vr3rLkpYRwVKJ5QRriI0eE306vrVN/6TnVHgxvY=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.114.6/go.mod 
h1:Jc89ASa2k7HMAskj3bSnt8bf+k+p2IApzkoXnEjkgUw=
+github.com/aws/aws-sdk-go-v2/service/signin v1.12.0 
h1:3vu/eUqh0rjh7Zy4N4YKcpZX4Aud+zinB3VsLhhlQFw=
+github.com/aws/aws-sdk-go-v2/service/signin v1.12.0/go.mod 
h1:wxAk0D2oVBSjuXEIzvcKxTZfM90zticdAbh/VD42L0Y=
+github.com/aws/aws-sdk-go-v2/service/sso v1.39.0 
h1:kIEpD5dUry7VET3Ybl7zj7PPHRB4lb52G9iTz+a1Tqk=
+github.com/aws/aws-sdk-go-v2/service/sso v1.39.0/go.mod 
h1:UGsEYgWz4Gi+46vDQ5zkFLqUgJziyOE2SXjZjgmfmHc=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.44.0 
h1:WfFev+HgTodggcNon5HieiLxxwWwb8Br8R2+nDkixvk=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.44.0/go.mod 
h1:gJSdIY6YUzDvqQLcMtPVZgBRHrFwzHryvpE8Igzbxps=
+github.com/aws/aws-sdk-go-v2/service/sts v1.51.6 
h1:BVjKrtRUtSYKztBCCfE1oAB+d3ViKcW2cKZfserOqMQ=
+github.com/aws/aws-sdk-go-v2/service/sts v1.51.6/go.mod 
h1:eSukFLeLkPMm+1OxDR+F0joauZdyBkF47Ow7MqjZiJw=
+github.com/aws/smithy-go v1.28.7 
h1:h/n1SHrwVtaDYLz8mR1/Kq4Xh4RaFbFW7bOYckg9mfE=
+github.com/aws/smithy-go v1.28.7/go.mod 
h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod 
h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod 
h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1/go.mod 
h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -92,12 +92,12 @@
 github.com/google/gofuzz v1.0.0/go.mod 
h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.32.0 
h1:QMILzF8BA7xftE4zVORkRrlv08m12St3csK4RsyRgRs=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.32.0/go.mod 
h1:+ZQ6iUL8+ATyxGDQNuRKpNwJFdGDUi0e7E1QL9MvI7U=
-github.com/hashicorp/consul/api v1.34.5 
h1:QpMhHZyfYsOsIu5n5QA7TQTLabM4OQJEbKi3pXXnw7U=
-github.com/hashicorp/consul/api v1.34.5/go.mod 
h1:OrXEufkaxFy1pMIRHFrn3JkuircxMhA4BHHpbR8k+5U=
-github.com/hashicorp/consul/sdk v0.18.2 
h1:wMFx4OkUPg8un6kimUmzADVBsuRqUdNRtJ0KREGs7vM=
-github.com/hashicorp/consul/sdk v0.18.2/go.mod 
h1:2V4Z2YguOFZelOtkQs3UnIrkCXDQ6iL3P4B6EtSqoQY=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.33.0 
h1:5T5Ns3kMpWr+z3s5ZA0e7ljVyWNVNxK76sHPAxi5D1k=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.33.0/go.mod 
h1:oWNYw09cH8wl8fFgC5hKlBLxR4XfKy2KQpP8MJjeCcc=
+github.com/hashicorp/consul/api v1.35.0 
h1:4ULHUF0DDHcDqQE48KpdbV7PIIQDua3UaJ/bEcUiTE0=
+github.com/hashicorp/consul/api v1.35.0/go.mod 
h1:RwV43v4y1mHVnJY2V9m89rR/0mDF9QL6q7+G0Rkma/I=
+github.com/hashicorp/consul/sdk v0.19.0 
h1:zucuiWFKpggzzrx9MPHCpei74gnrLoHT/TGP2NmegHc=
+github.com/hashicorp/consul/sdk v0.19.0/go.mod 
h1:Xak7xFgsTYKR6j523cZDqCgZy0g/JvKMNs9MhX7MUM4=
 github.com/hashicorp/errwrap v1.0.0/go.mod 
h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/errwrap v1.1.0 
h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
 github.com/hashicorp/errwrap v1.1.0/go.mod 
h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -122,8 +122,8 @@
 github.com/hashicorp/go-multierror v1.1.1 
h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 github.com/hashicorp/go-multierror v1.1.1/go.mod 
h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod 
h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
-github.com/hashicorp/go-rootcerts v1.0.2 
h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
-github.com/hashicorp/go-rootcerts v1.0.2/go.mod 
h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
+github.com/hashicorp/go-rootcerts v1.1.0 
h1:c5kkPHzmvHrgKn7I1goudi6h62PsRTZt2JddV18QAtA=
+github.com/hashicorp/go-rootcerts v1.1.0/go.mod 
h1:HJQ+oBQQZMdSLaLBrdKWoQzKCc3vwdgySXfrBgUTgt8=
 github.com/hashicorp/go-sockaddr v1.0.7 
h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
 github.com/hashicorp/go-sockaddr v1.0.7/go.mod 
h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
 github.com/hashicorp/go-uuid v1.0.0/go.mod 
h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -233,12 +233,12 @@
 go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
 go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU=
 go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk=
-go.etcd.io/etcd/api/v3 v3.7.2 h1:xgt/6el1LsPWWYNLkhMAK4tZm6dF+1sCqDecpE5gdbk=
-go.etcd.io/etcd/api/v3 v3.7.2/go.mod 
h1:RoRCBRt9BfBff1pIGZLUVMiz7wu3bY+b2qLysGu1HY4=
-go.etcd.io/etcd/client/pkg/v3 v3.7.2 
h1:SVtlR7tiSVAYOQ4nWPIyFXb4RMgEcnzeAG9RQ8MoNDU=
-go.etcd.io/etcd/client/pkg/v3 v3.7.2/go.mod 
h1:HsSux/B3ahgyw/D5+d4YbZqicOi0mEbuxm6lIUdjAoI=
-go.etcd.io/etcd/client/v3 v3.7.2 
h1:Z66GqDQDI7zPDfVSsIBqGSK4mJYLtv8ESwXa4mPf+wY=
-go.etcd.io/etcd/client/v3 v3.7.2/go.mod 
h1:x03t1qMs4tGZirCDJlMuzPBJdQffXJImIyEjLhNBCsY=
+go.etcd.io/etcd/api/v3 v3.7.3 h1:2tumepXe+wi7Wab64uUO71K7ISQyUWLGC1DJbbc59sY=
+go.etcd.io/etcd/api/v3 v3.7.3/go.mod 
h1:HOR9Qn4gb/1/hVAmYprmlLpvTe6ZgfgNS9Z/+Ty65po=
+go.etcd.io/etcd/client/pkg/v3 v3.7.3 
h1:5zMwNu3ycrzCeA5NwOfQbxFRhqq974chq1DrYMua4QI=
+go.etcd.io/etcd/client/pkg/v3 v3.7.3/go.mod 
h1:/OhzqYrF0U7d1NOk55VMyUQGbYD1WoIe4bJwxb47wOk=
+go.etcd.io/etcd/client/v3 v3.7.3 
h1:UrXcetgkuSEocaBlQlulQRJlXLEnUAHuBRbHh0lue1Q=
+go.etcd.io/etcd/client/v3 v3.7.3/go.mod 
h1:DcPxpqcpV8WIaRi/2waPCx0/Uq8rQUweDJaDYZk1gEE=
 go.opentelemetry.io/auto/sdk v1.2.1 
h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
 go.opentelemetry.io/auto/sdk v1.2.1/go.mod 
h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
 go.opentelemetry.io/contrib/instrumentation/runtime v0.72.0 
h1:rv8t3kKjyC2/9D99L1MZt4tJSZO4aYUmHus7L//iKKg=
@@ -282,8 +282,8 @@
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod 
h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw=
-golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
+golang.org/x/net v0.62.0 h1:18ELalfM5+XNRwzM2nYqXsZQ9ByS1uhj2O/U7+5jqRY=
+golang.org/x/net v0.62.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -318,12 +318,12 @@
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
 gonum.org/v1/gonum v0.17.0/go.mod 
h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
-google.golang.org/genproto/googleapis/api v0.0.0-20261012220534-4341edf3f941 
h1:DS/w3N8rsfrwxwgGSj6WhMsPo7aNQGmDx74J81zd0NQ=
-google.golang.org/genproto/googleapis/api 
v0.0.0-20261012220534-4341edf3f941/go.mod 
h1:AuGAdi9KnXss7xbLxIKhCvk+Ro/SxpET/iS45DNYN+M=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941 
h1:QG6hS4zFplRZvOQedzei4t2pT/zkQc3VR1u+S9/rcAw=
-google.golang.org/genproto/googleapis/rpc 
v0.0.0-20261012220534-4341edf3f941/go.mod 
h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
-google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0=
-google.golang.org/grpc v1.84.0/go.mod 
h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0=
+google.golang.org/genproto/googleapis/api v0.0.0-20261026165753-2ffab32bce7e 
h1:e789sLZjeTdc02ZCQGTSzHOu0fR73+dwPQVKUp67Z88=
+google.golang.org/genproto/googleapis/api 
v0.0.0-20261026165753-2ffab32bce7e/go.mod 
h1:vHV3wJM9WZyfV7GyB+OQGq5dWVrFwCDxapPK4WlRD88=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e 
h1:w3fy/G8OrJHYexn+u5y7hFyc8YRRIr7DCevtIOwAcGY=
+google.golang.org/genproto/googleapis/rpc 
v0.0.0-20261026165753-2ffab32bce7e/go.mod 
h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
+google.golang.org/grpc v1.85.0 h1:BPBCfuVRQ1qvBhKDOq9uvQllMvO59rKsYhXpp7Dd8vw=
+google.golang.org/grpc v1.85.0/go.mod 
h1:0rQrOD58yjal/0EaVbGBANindixsCZNQPDV3VKtaKuQ=
 google.golang.org/protobuf v1.36.12 
h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
 google.golang.org/protobuf v1.36.12/go.mod 
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod 
h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/http/service.go 
new/rqlite-10.6.6/http/service.go
--- old/rqlite-10.6.4/http/service.go   2026-10-22 04:39:23.000000000 +0200
+++ new/rqlite-10.6.6/http/service.go   2026-10-28 13:41:24.000000000 +0100
@@ -550,7 +550,14 @@
 }
 
 // handleRemove handles cluster-remove requests.
+//
+// Unlike the other node-management endpoints, removing a node which is not
+// in the cluster is not reported as an error. Raft treats such a removal as
+// a no-op and this endpoint has always returned 200 for it, so clients and
+// the shutdown self-removal path depend on that behavior.
 func (s *Service) handleRemove(w http.ResponseWriter, r *http.Request, qp 
QueryParams) {
+       w.Header().Set("Content-Type", "application/json; charset=utf-8")
+
        if !s.CheckRequestPerm(r, auth.PermRemove) {
                w.WriteHeader(http.StatusUnauthorized)
                return
@@ -577,14 +584,17 @@
                }
                if errors.Is(err, proxy.ErrLeaderNotFound) {
                        stats.Add(numLeaderNotFound, 1)
-                       http.Error(w, proxy.ErrLeaderNotFound.Error(), 
http.StatusServiceUnavailable)
+                       w.WriteHeader(http.StatusServiceUnavailable)
+                       s.writeResponse(w, qp, &Response{Error: 
proxy.ErrLeaderNotFound.Error()})
                        return
                }
                if errors.Is(err, proxy.ErrUnauthorized) {
-                       http.Error(w, "remote remove node not authorized", 
http.StatusUnauthorized)
+                       w.WriteHeader(http.StatusUnauthorized)
+                       s.writeResponse(w, qp, &Response{Error: err.Error()})
                        return
                }
-               http.Error(w, err.Error(), http.StatusInternalServerError)
+               w.WriteHeader(http.StatusInternalServerError)
+               s.writeResponse(w, qp, &Response{Error: err.Error()})
                return
        }
        w.Header().Set(ServedByHTTPHeader, addr)
@@ -592,6 +602,8 @@
 
 // handleDemote handles requests to demote a voting node to a non-voter.
 func (s *Service) handleDemote(w http.ResponseWriter, r *http.Request, qp 
QueryParams) {
+       w.Header().Set("Content-Type", "application/json; charset=utf-8")
+
        if !s.CheckRequestPerm(r, auth.PermDemote) {
                w.WriteHeader(http.StatusUnauthorized)
                return
@@ -618,14 +630,21 @@
                }
                if errors.Is(err, proxy.ErrLeaderNotFound) {
                        stats.Add(numLeaderNotFound, 1)
-                       http.Error(w, proxy.ErrLeaderNotFound.Error(), 
http.StatusServiceUnavailable)
+                       w.WriteHeader(http.StatusServiceUnavailable)
+                       s.writeResponse(w, qp, &Response{Error: 
proxy.ErrLeaderNotFound.Error()})
                        return
                }
                if errors.Is(err, proxy.ErrUnauthorized) {
-                       http.Error(w, "remote demote node not authorized", 
http.StatusUnauthorized)
+                       w.WriteHeader(http.StatusUnauthorized)
+                       s.writeResponse(w, qp, &Response{Error: err.Error()})
                        return
                }
-               http.Error(w, err.Error(), http.StatusInternalServerError)
+               statusCode := http.StatusInternalServerError
+               if errors.Is(err, proxy.ErrNodeNotFound) {
+                       statusCode = http.StatusNotFound
+               }
+               w.WriteHeader(statusCode)
+               s.writeResponse(w, qp, &Response{Error: err.Error()})
                return
        }
        w.Header().Set(ServedByHTTPHeader, addr)
@@ -633,6 +652,8 @@
 
 // handlePromote handles requests to promote a non-voting node to a voter.
 func (s *Service) handlePromote(w http.ResponseWriter, r *http.Request, qp 
QueryParams) {
+       w.Header().Set("Content-Type", "application/json; charset=utf-8")
+
        if !s.CheckRequestPerm(r, auth.PermPromote) {
                w.WriteHeader(http.StatusUnauthorized)
                return
@@ -659,14 +680,21 @@
                }
                if errors.Is(err, proxy.ErrLeaderNotFound) {
                        stats.Add(numLeaderNotFound, 1)
-                       http.Error(w, proxy.ErrLeaderNotFound.Error(), 
http.StatusServiceUnavailable)
+                       w.WriteHeader(http.StatusServiceUnavailable)
+                       s.writeResponse(w, qp, &Response{Error: 
proxy.ErrLeaderNotFound.Error()})
                        return
                }
                if errors.Is(err, proxy.ErrUnauthorized) {
-                       http.Error(w, "remote promote node not authorized", 
http.StatusUnauthorized)
+                       w.WriteHeader(http.StatusUnauthorized)
+                       s.writeResponse(w, qp, &Response{Error: err.Error()})
                        return
                }
-               http.Error(w, err.Error(), http.StatusInternalServerError)
+               statusCode := http.StatusInternalServerError
+               if errors.Is(err, proxy.ErrNodeNotFound) {
+                       statusCode = http.StatusNotFound
+               }
+               w.WriteHeader(statusCode)
+               s.writeResponse(w, qp, &Response{Error: err.Error()})
                return
        }
        w.Header().Set(ServedByHTTPHeader, addr)
@@ -1272,19 +1300,24 @@
                        }
                        if errors.Is(err, proxy.ErrLeaderNotFound) {
                                stats.Add(numLeaderNotFound, 1)
-                               http.Error(w, proxy.ErrLeaderNotFound.Error(), 
http.StatusServiceUnavailable)
+                               w.WriteHeader(http.StatusServiceUnavailable)
+                               s.writeResponse(w, qp, &Response{Error: 
proxy.ErrLeaderNotFound.Error()})
                                return
                        }
                        if errors.Is(err, proxy.ErrUnauthorized) {
-                               http.Error(w, "remote stepdown not authorized", 
http.StatusUnauthorized)
+                               w.WriteHeader(http.StatusUnauthorized)
+                               s.writeResponse(w, qp, &Response{Error: 
err.Error()})
                                return
                        }
 
                        statusCode := http.StatusInternalServerError
-                       if err == store.ErrNotOpen {
+                       if errors.Is(err, store.ErrNotOpen) {
                                statusCode = http.StatusServiceUnavailable
+                       } else if errors.Is(err, proxy.ErrNodeNotFound) {
+                               statusCode = http.StatusNotFound
                        }
-                       http.Error(w, fmt.Sprintf("stepdown: %s", err.Error()), 
statusCode)
+                       w.WriteHeader(statusCode)
+                       s.writeResponse(w, qp, &Response{Error: err.Error()})
                        return
                }
                w.Header().Set(ServedByHTTPHeader, addr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/http/service_test.go 
new/rqlite-10.6.6/http/service_test.go
--- old/rqlite-10.6.4/http/service_test.go      2026-10-22 04:39:23.000000000 
+0200
+++ new/rqlite-10.6.6/http/service_test.go      2026-10-28 13:41:24.000000000 
+0100
@@ -1913,8 +1913,188 @@
                t.Fatalf("expected 500, got %d", rr.Code)
        }
 
-       if !strings.Contains(rr.Body.String(), "stepdown failed") {
-               t.Fatalf("expected error message to contain 'stepdown failed', 
got %s", rr.Body.String())
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != "stepdown failed" {
+               t.Fatalf("expected error 'stepdown failed', got %q", resp.Error)
+       }
+}
+
+func Test_Leader_POST_NodeNotFound(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               stepdownFn: func(wait bool, id string) error {
+                       return store.ErrNodeNotFound
+               },
+       }
+       cluster := &mockClusterService{apiAddr: "http://127.0.0.1:4001"}
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("POST", "/leader", 
strings.NewReader(`{"id": "nonexistent"}`))
+       if err != nil {
+               t.Fatalf("failed to create POST request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusNotFound {
+               t.Fatalf("expected 404, got %d", rr.Code)
+       }
+
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != store.ErrNodeNotFound.Error() {
+               t.Fatalf("expected error %q, got %q", 
store.ErrNodeNotFound.Error(), resp.Error)
+       }
+}
+
+func Test_Leader_POST_NodeNotFound_Forwarded(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               stepdownFn: func(wait bool, id string) error {
+                       return store.ErrNotLeader
+               },
+       }
+       cluster := &mockClusterService{
+               apiAddr: "http://127.0.0.1:4001";,
+               stepdownFn: func(sr *command.StepdownRequest, nodeAddr string, 
t time.Duration) error {
+                       // The cluster client rebuilds the remote error from 
its message.
+                       return errors.New(store.ErrNodeNotFound.Error())
+               },
+       }
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("POST", "/leader", 
strings.NewReader(`{"id": "nonexistent"}`))
+       if err != nil {
+               t.Fatalf("failed to create POST request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusNotFound {
+               t.Fatalf("expected 404, got %d", rr.Code)
+       }
+
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != store.ErrNodeNotFound.Error() {
+               t.Fatalf("expected error %q, got %q", 
store.ErrNodeNotFound.Error(), resp.Error)
+       }
+}
+
+func Test_Remove_DELETE(t *testing.T) {
+       removedID := ""
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               removeFn: func(rn *command.RemoveNodeRequest) error {
+                       removedID = rn.Id
+                       return nil
+               },
+       }
+       cluster := &mockClusterService{apiAddr: "http://127.0.0.1:4001"}
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("DELETE", "/remove", 
strings.NewReader(`{"id": "node1"}`))
+       if err != nil {
+               t.Fatalf("failed to create DELETE request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusOK {
+               t.Fatalf("expected 200, got %d", rr.Code)
+       }
+       if removedID != "node1" {
+               t.Fatalf("expected node1 to be removed, got %q", removedID)
+       }
+}
+
+func Test_Remove_DELETE_StoreError(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               removeFn: func(rn *command.RemoveNodeRequest) error {
+                       return errors.New("remove failed")
+               },
+       }
+       cluster := &mockClusterService{apiAddr: "http://127.0.0.1:4001"}
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("DELETE", "/remove", 
strings.NewReader(`{"id": "node1"}`))
+       if err != nil {
+               t.Fatalf("failed to create DELETE request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusInternalServerError {
+               t.Fatalf("expected 500, got %d", rr.Code)
+       }
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != "remove failed" {
+               t.Fatalf("expected error 'remove failed', got %q", resp.Error)
+       }
+}
+
+func Test_Remove_DELETE_ForwardError(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               removeFn: func(rn *command.RemoveNodeRequest) error {
+                       return store.ErrNotLeader
+               },
+       }
+       cluster := &mockClusterService{
+               apiAddr: "http://127.0.0.1:4001";,
+               removeNodeFn: func(rn *command.RemoveNodeRequest, nodeAddr 
string, t time.Duration) error {
+                       return errors.New("remove failed")
+               },
+       }
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("DELETE", "/remove", 
strings.NewReader(`{"id": "node1"}`))
+       if err != nil {
+               t.Fatalf("failed to create DELETE request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusInternalServerError {
+               t.Fatalf("expected 500, got %d", rr.Code)
+       }
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != "remove failed" {
+               t.Fatalf("expected error 'remove failed', got %q", resp.Error)
        }
 }
 
@@ -1996,8 +2176,48 @@
        if rr.Code != http.StatusInternalServerError {
                t.Fatalf("expected 500, got %d", rr.Code)
        }
-       if !strings.Contains(rr.Body.String(), "demote failed") {
-               t.Fatalf("expected error message to contain 'demote failed', 
got %s", rr.Body.String())
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != "demote failed" {
+               t.Fatalf("expected error 'demote failed', got %q", resp.Error)
+       }
+}
+
+func Test_Demote_POST_NodeNotFound(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               demoteFn: func(dn *command.DemoteNodeRequest) error {
+                       return store.ErrNodeNotFound
+               },
+       }
+       cluster := &mockClusterService{apiAddr: "http://127.0.0.1:4001"}
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("POST", "/demote", 
strings.NewReader(`{"id": "nonexistent"}`))
+       if err != nil {
+               t.Fatalf("failed to create POST request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusNotFound {
+               t.Fatalf("expected 404, got %d", rr.Code)
+       }
+
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != store.ErrNodeNotFound.Error() {
+               t.Fatalf("expected error %q, got %q", 
store.ErrNodeNotFound.Error(), resp.Error)
        }
 }
 
@@ -2067,8 +2287,54 @@
        if rr.Code != http.StatusInternalServerError {
                t.Fatalf("expected 500, got %d", rr.Code)
        }
-       if !strings.Contains(rr.Body.String(), "demote failed") {
-               t.Fatalf("expected error message to contain 'demote failed', 
got %s", rr.Body.String())
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != "demote failed" {
+               t.Fatalf("expected error 'demote failed', got %q", resp.Error)
+       }
+}
+
+func Test_Demote_POST_NodeNotFound_Forwarded(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               demoteFn: func(dn *command.DemoteNodeRequest) error {
+                       return store.ErrNotLeader
+               },
+       }
+       cluster := &mockClusterService{
+               apiAddr: "http://127.0.0.1:4001";,
+               demoteNodeFn: func(dn *command.DemoteNodeRequest, nodeAddr 
string, t time.Duration) error {
+                       // The cluster client rebuilds the remote error from 
its message.
+                       return errors.New(store.ErrNodeNotFound.Error())
+               },
+       }
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("POST", "/demote", 
strings.NewReader(`{"id": "nonexistent"}`))
+       if err != nil {
+               t.Fatalf("failed to create POST request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusNotFound {
+               t.Fatalf("expected 404, got %d", rr.Code)
+       }
+
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != store.ErrNodeNotFound.Error() {
+               t.Fatalf("expected error %q, got %q", 
store.ErrNodeNotFound.Error(), resp.Error)
        }
 }
 
@@ -2180,8 +2446,48 @@
        if rr.Code != http.StatusInternalServerError {
                t.Fatalf("expected 500, got %d", rr.Code)
        }
-       if !strings.Contains(rr.Body.String(), "promote failed") {
-               t.Fatalf("expected error message to contain 'promote failed', 
got %s", rr.Body.String())
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != "promote failed" {
+               t.Fatalf("expected error 'promote failed', got %q", resp.Error)
+       }
+}
+
+func Test_Promote_POST_NodeNotFound(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               promoteFn: func(dn *command.PromoteNodeRequest) error {
+                       return store.ErrNodeNotFound
+               },
+       }
+       cluster := &mockClusterService{apiAddr: "http://127.0.0.1:4001"}
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("POST", "/promote", 
strings.NewReader(`{"id": "nonexistent"}`))
+       if err != nil {
+               t.Fatalf("failed to create POST request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusNotFound {
+               t.Fatalf("expected 404, got %d", rr.Code)
+       }
+
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != store.ErrNodeNotFound.Error() {
+               t.Fatalf("expected error %q, got %q", 
store.ErrNodeNotFound.Error(), resp.Error)
        }
 }
 
@@ -2251,8 +2557,54 @@
        if rr.Code != http.StatusInternalServerError {
                t.Fatalf("expected 500, got %d", rr.Code)
        }
-       if !strings.Contains(rr.Body.String(), "promote failed") {
-               t.Fatalf("expected error message to contain 'promote failed', 
got %s", rr.Body.String())
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != "promote failed" {
+               t.Fatalf("expected error 'promote failed', got %q", resp.Error)
+       }
+}
+
+func Test_Promote_POST_NodeNotFound_Forwarded(t *testing.T) {
+       st := &MockStore{
+               leaderAddr: "127.0.0.1:8001",
+               promoteFn: func(dn *command.PromoteNodeRequest) error {
+                       return store.ErrNotLeader
+               },
+       }
+       cluster := &mockClusterService{
+               apiAddr: "http://127.0.0.1:4001";,
+               promoteNodeFn: func(dn *command.PromoteNodeRequest, nodeAddr 
string, t time.Duration) error {
+                       // The cluster client rebuilds the remote error from 
its message.
+                       return errors.New(store.ErrNodeNotFound.Error())
+               },
+       }
+       cred := &mockCredentialStore{HasPermOK: true}
+
+       s := New("127.0.0.1:4001", st, cluster, proxy.New(st, cluster), cred)
+
+       req, err := http.NewRequest("POST", "/promote", 
strings.NewReader(`{"id": "nonexistent"}`))
+       if err != nil {
+               t.Fatalf("failed to create POST request: %s", err.Error())
+       }
+       rr := httptest.NewRecorder()
+       s.ServeHTTP(rr, req)
+
+       if rr.Code != http.StatusNotFound {
+               t.Fatalf("expected 404, got %d", rr.Code)
+       }
+
+       var resp struct {
+               Error string `json:"error"`
+       }
+       if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+               t.Fatalf("expected JSON error body, got %q: %s", 
rr.Body.String(), err)
+       }
+       if resp.Error != store.ErrNodeNotFound.Error() {
+               t.Fatalf("expected error %q, got %q", 
store.ErrNodeNotFound.Error(), resp.Error)
        }
 }
 
@@ -2788,6 +3140,7 @@
        readFromFn  func(r io.Reader) (int64, error)
        committedFn func(timeout time.Duration) (uint64, error)
        stepdownFn  func(wait bool, id string) error
+       removeFn    func(rn *command.RemoveNodeRequest) error
        demoteFn    func(dn *command.DemoteNodeRequest) error
        promoteFn   func(pn *command.PromoteNodeRequest) error
        leaderAddr  string
@@ -2825,6 +3178,9 @@
 }
 
 func (m *MockStore) Remove(ctx context.Context, rn *command.RemoveNodeRequest) 
error {
+       if m.removeFn != nil {
+               return m.removeFn(rn)
+       }
        return nil
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/proxy/proxy.go 
new/rqlite-10.6.6/proxy/proxy.go
--- old/rqlite-10.6.4/proxy/proxy.go    2026-10-22 04:39:23.000000000 +0200
+++ new/rqlite-10.6.6/proxy/proxy.go    2026-10-28 13:41:24.000000000 +0100
@@ -26,6 +26,10 @@
        // It is an alias for store.ErrLeaderNotFound.
        ErrLeaderNotFound = store.ErrLeaderNotFound
 
+       // ErrNodeNotFound is returned when a node referenced by ID is not
+       // found in the cluster.
+       ErrNodeNotFound = store.ErrNodeNotFound
+
        // ErrUnauthorized is returned when the remote node rejects credentials.
        ErrUnauthorized = errors.New("unauthorized")
 )
@@ -269,7 +273,7 @@
                }
                err = p.cluster.DemoteNode(ctx, dn, addr, creds, timeout)
                if err != nil {
-                       return "", wrapIfUnauthorized(err)
+                       return "", wrapRemoteNodeError(err)
                }
                stats.Add(numRemoteDemoteNode, 1)
                return addr, nil
@@ -294,7 +298,7 @@
                }
                err = p.cluster.PromoteNode(ctx, pn, addr, creds, timeout)
                if err != nil {
-                       return "", wrapIfUnauthorized(err)
+                       return "", wrapRemoteNodeError(err)
                }
                stats.Add(numRemotePromoteNode, 1)
                return addr, nil
@@ -326,7 +330,7 @@
                }
                err = p.cluster.Stepdown(ctx, sr, addr, creds, timeout)
                if err != nil {
-                       return "", wrapIfUnauthorized(err)
+                       return "", wrapRemoteNodeError(err)
                }
                stats.Add(numRemoteStepdowns, 1)
                return addr, nil
@@ -347,6 +351,19 @@
        return addr, nil
 }
 
+// wrapRemoteNodeError restores the sentinel for an error returned by a
+// remote node-management operation. The remote node's error arrives as a
+// bare string, so it is matched by message.
+func wrapRemoteNodeError(err error) error {
+       if err == nil {
+               return nil
+       }
+       if err.Error() == ErrNodeNotFound.Error() {
+               return ErrNodeNotFound
+       }
+       return wrapIfUnauthorized(err)
+}
+
 // wrapIfUnauthorized wraps an error as ErrUnauthorized if the error
 // message is "unauthorized", allowing callers to use errors.Is().
 func wrapIfUnauthorized(err error) error {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/proxy/proxy_test.go 
new/rqlite-10.6.6/proxy/proxy_test.go
--- old/rqlite-10.6.4/proxy/proxy_test.go       2026-10-22 04:39:23.000000000 
+0200
+++ new/rqlite-10.6.6/proxy/proxy_test.go       2026-10-28 13:41:24.000000000 
+0100
@@ -794,6 +794,30 @@
        }
 }
 
+func Test_Demote_NotLeader_ForwardNodeNotFound(t *testing.T) {
+       t.Parallel()
+       s := &mockStore{
+               demoteFn: func(ctx context.Context, dn 
*proto.DemoteNodeRequest) error {
+                       return store.ErrNotLeader
+               },
+               leaderAddrFn: func() (string, error) {
+                       return "leader:4002", nil
+               },
+       }
+       c := &mockCluster{
+               demoteNodeFn: func(ctx context.Context, dn 
*proto.DemoteNodeRequest, nodeAddr string, creds *clstrPB.Credentials, timeout 
time.Duration) error {
+                       // The cluster client rebuilds the remote error from 
its message.
+                       return errors.New(store.ErrNodeNotFound.Error())
+               },
+       }
+       p := newTestProxy(s, c)
+
+       _, err := p.Demote(context.Background(), &proto.DemoteNodeRequest{Id: 
"nonexistent"}, nil, time.Second, false)
+       if !errors.Is(err, ErrNodeNotFound) {
+               t.Fatalf("expected ErrNodeNotFound, got %v", err)
+       }
+}
+
 func Test_Promote_LocalSuccess(t *testing.T) {
        t.Parallel()
        called := false
@@ -929,6 +953,30 @@
        }
 }
 
+func Test_Promote_NotLeader_ForwardNodeNotFound(t *testing.T) {
+       t.Parallel()
+       s := &mockStore{
+               promoteFn: func(ctx context.Context, dn 
*proto.PromoteNodeRequest) error {
+                       return store.ErrNotLeader
+               },
+               leaderAddrFn: func() (string, error) {
+                       return "leader:4002", nil
+               },
+       }
+       c := &mockCluster{
+               promoteNodeFn: func(ctx context.Context, dn 
*proto.PromoteNodeRequest, nodeAddr string, creds *clstrPB.Credentials, timeout 
time.Duration) error {
+                       // The cluster client rebuilds the remote error from 
its message.
+                       return errors.New(store.ErrNodeNotFound.Error())
+               },
+       }
+       p := newTestProxy(s, c)
+
+       _, err := p.Promote(context.Background(), &proto.PromoteNodeRequest{Id: 
"nonexistent"}, nil, time.Second, false)
+       if !errors.Is(err, ErrNodeNotFound) {
+               t.Fatalf("expected ErrNodeNotFound, got %v", err)
+       }
+}
+
 func Test_Stepdown_LocalSuccess(t *testing.T) {
        t.Parallel()
        s := &mockStore{}
@@ -1016,6 +1064,30 @@
        }
 }
 
+func Test_Stepdown_NodeNotFound_Forward(t *testing.T) {
+       t.Parallel()
+       s := &mockStore{
+               stepdownFn: func(wait bool, id string) error {
+                       return store.ErrNotLeader
+               },
+               leaderAddrFn: func() (string, error) {
+                       return "leader:4002", nil
+               },
+       }
+       c := &mockCluster{
+               stepdownFn: func(ctx context.Context, sr 
*proto.StepdownRequest, nodeAddr string, creds *clstrPB.Credentials, timeout 
time.Duration) error {
+                       // The cluster client rebuilds the remote error from 
its message.
+                       return errors.New(store.ErrNodeNotFound.Error())
+               },
+       }
+       p := newTestProxy(s, c)
+
+       _, err := p.Stepdown(context.Background(), true, "nonexistent", nil, 
time.Second, false)
+       if !errors.Is(err, ErrNodeNotFound) {
+               t.Fatalf("expected ErrNodeNotFound, got %v", err)
+       }
+}
+
 func Test_LeaderAddrError(t *testing.T) {
        t.Parallel()
        leaderErr := errors.New("raft error")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rqlite-10.6.4/store/store_cdc_test.go 
new/rqlite-10.6.6/store/store_cdc_test.go
--- old/rqlite-10.6.4/store/store_cdc_test.go   2026-10-22 04:39:23.000000000 
+0200
+++ new/rqlite-10.6.6/store/store_cdc_test.go   2026-10-28 13:41:24.000000000 
+0100
@@ -8,6 +8,7 @@
        "time"
 
        "github.com/rqlite/rqlite/v10/command/proto"
+       "github.com/rqlite/rqlite/v10/internal/fsutil"
        "github.com/rqlite/rqlite/v10/internal/random"
 )
 
@@ -342,3 +343,80 @@
        }
        return s, ly
 }
+
+// Test_StoreCDC_Events_AfterSnapshotRestore tests that CDC events are still
+// emitted after the node restores its database from a Raft snapshot. The
+// restore swaps in a new SQLite file, so the CDC hooks must survive the 
reopen.
+func Test_StoreCDC_Events_AfterSnapshotRestore(t *testing.T) {
+       s, ln := mustNewStoreCDC(t)
+       defer ln.Close()
+
+       if err := s.Open(); err != nil {
+               t.Fatalf("failed to open single-node store: %s", err.Error())
+       }
+       if err := s.Bootstrap(NewServer(s.ID(), s.Addr(), true)); err != nil {
+               t.Fatalf("failed to bootstrap single-node store: %s", 
err.Error())
+       }
+       defer s.Close(true)
+       if _, err := s.WaitForLeader(10 * time.Second); err != nil {
+               t.Fatalf("Error waiting for leader: %s", err)
+       }
+
+       er := executeRequestFromString(`CREATE TABLE foo (id INTEGER NOT NULL 
PRIMARY KEY, name TEXT)`, false, false)
+       if _, _, err := s.Execute(context.Background(), er); err != nil {
+               t.Fatalf("failed to execute CREATE TABLE on single node: %s", 
err.Error())
+       }
+       er = executeRequestFromString(`INSERT INTO foo(id, name) VALUES(101, 
"fiona")`, false, false)
+       if _, _, err := s.Execute(context.Background(), er); err != nil {
+               t.Fatalf("failed to execute INSERT on single node: %s", 
err.Error())
+       }
+       mustReceiveCDCInsert(t, s, 101)
+
+       // Snapshot, then restart without the clean-snapshot marker so the node
+       // cannot fast-restart on the existing SQLite file and must restore from
+       // the Raft snapshot.
+       if err := s.Snapshot(0); err != nil {
+               t.Fatalf("failed to take snapshot: %s", err.Error())
+       }
+       if err := s.Close(true); err != nil {
+               t.Fatalf("failed to close single-node store: %s", err.Error())
+       }
+       if err := fsutil.Remove(s.cleanSnapshotPath); err != nil {
+               t.Fatalf("failed to remove clean snapshot marker: %s", 
err.Error())
+       }
+       if err := s.Open(); err != nil {
+               t.Fatalf("failed to reopen single-node store: %s", err.Error())
+       }
+       defer s.Close(true)
+       if _, err := s.WaitForLeader(10 * time.Second); err != nil {
+               t.Fatalf("Error waiting for leader: %s", err)
+       }
+       if s.numSnapshotsStart.Load() != 1 {
+               t.Fatalf("expected snapshot start count to be 1, got %d", 
s.numSnapshotsStart.Load())
+       }
+
+       er = executeRequestFromString(`INSERT INTO foo(id, name) VALUES(102, 
"declan")`, false, false)
+       if _, _, err := s.Execute(context.Background(), er); err != nil {
+               t.Fatalf("failed to execute INSERT after restore: %s", 
err.Error())
+       }
+       mustReceiveCDCInsert(t, s, 102)
+}
+
+// mustReceiveCDCInsert waits for a single CDC INSERT event on table foo with
+// the given row ID, failing the test if it does not arrive.
+func mustReceiveCDCInsert(t *testing.T, s *Store, rowID int64) {
+       t.Helper()
+       select {
+       case events := <-s.CDCEventsC():
+               if events == nil || len(events.Events) != 1 {
+                       t.Fatalf("expected 1 CDC event, got %v", events)
+               }
+               ev := events.Events[0]
+               if ev.Table != "foo" || ev.Op != proto.CDCEvent_INSERT || 
ev.NewRowId != rowID {
+                       t.Fatalf("expected INSERT on foo with row ID %d, got %s 
on %s with row ID %d",
+                               rowID, ev.Op, ev.Table, ev.NewRowId)
+               }
+       case <-time.After(5 * time.Second):
+               t.Fatalf("timeout waiting for CDC INSERT event with row ID %d", 
rowID)
+       }
+}

++++++ vendor.tar.xz ++++++
++++ 25036 lines of diff (skipped)

Reply via email to