This is an automated email from the ASF dual-hosted git repository.

avantgardnerio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-ballista.git


The following commit(s) were added to refs/heads/main by this push:
     new 847ee7c45 feat: add BALLISTA_PROTOCOL_VERSION + k8s health probes 
(#2088)
847ee7c45 is described below

commit 847ee7c455f62bdb44878d2bf572e9d8e4b1275e
Author: Brent Gardner <[email protected]>
AuthorDate: Sat Jul 18 22:11:48 2026 -0600

    feat: add BALLISTA_PROTOCOL_VERSION + k8s health probes (#2088)
    
    * feat: add BALLISTA_PROTOCOL_VERSION + k8s health probes
    
    Introduces a compile-time `BALLISTA_PROTOCOL_VERSION` that gates
    executor↔scheduler communication and adds `/healthz` and `/readyz`
    endpoints so live-upgrade rollouts have something for Kubernetes to
    observe.
    
    Scheduler rejects executors whose protocol version does not match with
    `FailedPrecondition`, an `info!` log, and a
    `ballista_scheduler_rejected_by_protocol_version_total` counter.
    Missing registration metadata on heartbeats is treated as a mismatch.
    
    Executor flips /readyz off on RPC failure and self-terminates after 5
    consecutive heartbeat failures, so a mismatched pod crash-loops until
    its image is bumped rather than silently sitting idle.
    
    Health probes are always mounted regardless of the `rest-api` feature:
    scheduler on the existing axum router, executor on a new
    `--bind-health-port` (default 50053). Livez is deliberately dumb —
    scheduler flapping must not restart executors in a cascade.
    
    Fixes #2071.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    * refactor: move executor health probe HTTP server into the binary
    
    The library was spinning up its own axum HTTP server on
    `--bind-health-port` from inside `start_executor_process`, adding a hard
    axum dep and a second health surface that library embedders had no way
    to disable short of forking the code.
    
    Health probes are a deployment concern, so push them to where the
    deployment lives: `ballista/executor/src/bin/main.rs`. The library keeps
    `ExecutorHealth` — a cheap `Arc<AtomicBool>` that the heartbeat loops
    flip on every RPC outcome — and exposes it on `ExecutorProcessConfig` so
    callers can observe it however they like. The axum server and its
    handlers are gated behind `#[cfg(feature = "build-binary")]`, and the
    `axum` dep moves from unconditional to `optional = true` under
    `build-binary`. With `--no-default-features`, `ballista-executor` no
    longer has a direct edge to axum in `cargo tree`.
    
    Verified end-to-end against the local native cluster: scheduler
    `/readyz` reports "ready: 2 executors registered" and both executors'
    `/healthz` and `/readyz` return 200, so the handle is threaded through
    bin -> config -> heartbeat loop -> probe correctly.
    
    Addresses 
https://github.com/apache/datafusion-ballista/pull/2088#discussion_r3606748576
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    * fix: address CI failures on the protocol-version branch
    
    - examples/mtls-cluster.rs: add ballista_protocol_version to
      ExecutorRegistration and pass ExecutorHealth to poll_loop so the
      example compiles under `--all-targets` clippy.
    - docs/.../kubernetes.md: prettier reformatting — _emphasis_ instead of
      *emphasis*, and pad the rolling-upgrade table columns to match the
      longest row.
    - python/Cargo.lock: `cd python && cargo update` to resync the lockfile
      with the workspace manifests, so `cargo metadata --locked` inside
      `python/` succeeds again.
    
    The TPC-DS SF1 CI failure was infrastructure (`No space left on device`
    on the runner), not a code issue — re-running the job should clear it.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    * feat: expose SchedulerServer::is_ready() for embedders
    
    Phillip's fork wraps the Ballista scheduler in a larger process whose
    health check is a superset of ours — his handler already reports
    app-level state and would like to AND the scheduler's readiness in as
    one factor.
    
    Add a public `SchedulerServer::is_ready() -> bool` that returns true
    when at least `min_ready_executors` executors have live heartbeats. The
    built-in `/readyz` handler now delegates to it (single source of truth),
    and embedders can call the same method from whatever health surface
    they expose. This is orthogonal to the `/healthz` and `/readyz` routes
    mounted on the axum server — those stay unconditional, so operators
    who don't embed still get the k8s probes for free.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    * feat: expose ExecutorHealth::is_ready() for embedders
    
    Mirror what SchedulerServer::is_ready() offers on the executor side.
    The heartbeat loops already flip the internal atomic on every RPC
    outcome, and the built-in `/readyz` axum handler reads it via this
    method under `build-binary`. Making the method `pub` and dropping the
    cfg gate lets an embedder observe the same signal directly and fold it
    into their own health surface, without linking axum.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
 Cargo.lock                                      |   1 +
 ballista/core/proto/ballista.proto              |   5 +
 ballista/core/src/lib.rs                        |  12 +
 ballista/core/src/serde/generated/ballista.rs   |   6 +
 ballista/executor/Cargo.toml                    |   3 +-
 ballista/executor/src/bin/main.rs               |  27 +-
 ballista/executor/src/config.rs                 |   8 +
 ballista/executor/src/execution_loop.rs         |   3 +
 ballista/executor/src/executor.rs               |  18 +-
 ballista/executor/src/executor_process.rs       |  19 +-
 ballista/executor/src/executor_server.rs        |  44 +-
 ballista/executor/src/health.rs                 | 143 +++++
 ballista/executor/src/lib.rs                    |   2 +
 ballista/executor/src/standalone.rs             |  10 +-
 ballista/scheduler/src/api/health.rs            | 163 ++++++
 ballista/scheduler/src/api/mod.rs               |   2 +
 ballista/scheduler/src/config.rs                |  11 +
 ballista/scheduler/src/metrics/mod.rs           |  22 +
 ballista/scheduler/src/scheduler_process.rs     |  11 +-
 ballista/scheduler/src/scheduler_server/grpc.rs | 175 +++++-
 ballista/scheduler/src/scheduler_server/mod.rs  |   9 +
 docs/source/user-guide/deployment/kubernetes.md |  98 ++++
 examples/examples/mtls-cluster.rs               |   5 +-
 python/Cargo.lock                               | 709 +++++++++---------------
 24 files changed, 1014 insertions(+), 492 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index f48650210..6922e2d9c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1149,6 +1149,7 @@ dependencies = [
  "arrow",
  "arrow-flight",
  "async-trait",
+ "axum",
  "ballista-core",
  "bytesize",
  "clap 4.6.2",
diff --git a/ballista/core/proto/ballista.proto 
b/ballista/core/proto/ballista.proto
index 59d844abc..ee10b2fc5 100644
--- a/ballista/core/proto/ballista.proto
+++ b/ballista/core/proto/ballista.proto
@@ -381,6 +381,11 @@ message ExecutorRegistration {
   uint32 grpc_port = 4;
   ExecutorSpecification specification = 5;
   ExecutorOperatingSystemSpecification os_info = 6;
+  // Wire-protocol version this executor was built against. The scheduler
+  // rejects heartbeats/registrations whose version does not match its own
+  // compiled-in BALLISTA_PROTOCOL_VERSION. Old executors that predate this
+  // field default to 0, which is never a valid scheduler version.
+  uint32 ballista_protocol_version = 7;
 }
 
 message ExecutorHeartbeat {
diff --git a/ballista/core/src/lib.rs b/ballista/core/src/lib.rs
index 08cbf09dc..e8364ada3 100644
--- a/ballista/core/src/lib.rs
+++ b/ballista/core/src/lib.rs
@@ -29,6 +29,18 @@ pub use crate::ids::JobId;
 /// The current version of Ballista, derived from the Cargo package version.
 pub const BALLISTA_VERSION: &str = env!("CARGO_PKG_VERSION");
 
+/// Wire-protocol version negotiated between scheduler and executor.
+///
+/// The scheduler compares this against 
`ExecutorRegistration.ballista_protocol_version`
+/// on every registration and heartbeat; mismatched executors are rejected with
+/// `Status::failed_precondition` and never receive work. Bump this on any 
release
+/// that changes the executor↔scheduler wire format (proto shape, task/plan
+/// encoding, launch/heartbeat semantics). Most releases will not bump it.
+///
+/// Zero is reserved as the proto-default "unset" value, produced by executors
+/// that predate this field — it never matches a real scheduler version.
+pub const BALLISTA_PROTOCOL_VERSION: u32 = 1;
+
 /// Prints the current Ballista version to stdout.
 pub fn print_version() {
     println!("Ballista version: {BALLISTA_VERSION}")
diff --git a/ballista/core/src/serde/generated/ballista.rs 
b/ballista/core/src/serde/generated/ballista.rs
index 7d08dca42..64080e1a2 100644
--- a/ballista/core/src/serde/generated/ballista.rs
+++ b/ballista/core/src/serde/generated/ballista.rs
@@ -575,6 +575,12 @@ pub struct ExecutorRegistration {
     pub specification: ::core::option::Option<ExecutorSpecification>,
     #[prost(message, optional, tag = "6")]
     pub os_info: ::core::option::Option<ExecutorOperatingSystemSpecification>,
+    /// Wire-protocol version this executor was built against. The scheduler
+    /// rejects heartbeats/registrations whose version does not match its own
+    /// compiled-in BALLISTA_PROTOCOL_VERSION. Old executors that predate this
+    /// field default to 0, which is never a valid scheduler version.
+    #[prost(uint32, tag = "7")]
+    pub ballista_protocol_version: u32,
 }
 #[derive(Clone, PartialEq, ::prost::Message)]
 pub struct ExecutorHeartbeat {
diff --git a/ballista/executor/Cargo.toml b/ballista/executor/Cargo.toml
index caae3b426..44740aaa6 100644
--- a/ballista/executor/Cargo.toml
+++ b/ballista/executor/Cargo.toml
@@ -34,7 +34,7 @@ required-features = ["build-binary"]
 
 [features]
 arrow-ipc-optimizations = []
-build-binary = ["clap", "tracing-subscriber", "tracing-appender", "tracing", 
"ballista-core/build-binary", "mimalloc"]
+build-binary = ["clap", "tracing-subscriber", "tracing-appender", "tracing", 
"ballista-core/build-binary", "mimalloc", "dep:axum"]
 default = ["arrow-ipc-optimizations", "build-binary"]
 spark-compat = ["ballista-core/spark-compat"]
 
@@ -42,6 +42,7 @@ spark-compat = ["ballista-core/spark-compat"]
 arrow = { workspace = true }
 arrow-flight = { workspace = true }
 async-trait = { workspace = true }
+axum = { version = "0.8.9", optional = true }
 ballista-core = { path = "../core", version = "54.0.0" }
 bytesize = "2"
 clap = { workspace = true, optional = true }
diff --git a/ballista/executor/src/bin/main.rs 
b/ballista/executor/src/bin/main.rs
index cbeb93b89..14f77349e 100644
--- a/ballista/executor/src/bin/main.rs
+++ b/ballista/executor/src/bin/main.rs
@@ -18,6 +18,7 @@
 //! Ballista Rust executor binary.
 
 use ballista_core::config::LogRotationPolicy;
+use ballista_core::error::BallistaError;
 use ballista_core::object_store::{
     runtime_env_with_s3_support, session_config_with_s3_support,
 };
@@ -25,8 +26,10 @@ use ballista_executor::config::Config;
 use ballista_executor::executor_process::{
     ExecutorProcessConfig, start_executor_process,
 };
+use ballista_executor::health::spawn_health_server;
 use clap::Parser;
 use std::env;
+use std::net::SocketAddr;
 use std::sync::Arc;
 use tracing_subscriber::EnvFilter;
 
@@ -38,6 +41,8 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
 async fn main() -> ballista_core::error::Result<()> {
     // parse command-line arguments
     let opt = Config::parse();
+    let bind_host = opt.bind_host.clone();
+    let bind_health_port = opt.bind_health_port;
 
     let mut config: ExecutorProcessConfig = opt.try_into()?;
     config.override_config_producer = 
Some(Arc::new(session_config_with_s3_support));
@@ -76,5 +81,25 @@ async fn main() -> ballista_core::error::Result<()> {
         tracing.init();
     }
 
-    start_executor_process(Arc::new(config)).await
+    // Kubernetes-style health probes are a concern of the standalone binary,
+    // not of the library, so wire them here on top of the config's shared
+    // ExecutorHealth handle. `/healthz` is process-liveness, `/readyz`
+    // reflects heartbeat state (see `ballista_executor::health`).
+    let health = config.health.clone();
+    let health_addr: SocketAddr = format!("{bind_host}:{bind_health_port}")
+        .parse()
+        .map_err(|e| {
+            BallistaError::General(format!("invalid --bind-health-port 
address: {e}"))
+        })?;
+    let (health_shutdown_tx, health_shutdown_rx) = 
tokio::sync::oneshot::channel();
+    let health_handle = spawn_health_server(health_addr, health, 
health_shutdown_rx);
+
+    let result = start_executor_process(Arc::new(config)).await;
+
+    // Ask the health server to shut down so the process can exit cleanly.
+    let _ = health_shutdown_tx.send(());
+    if let Err(e) = health_handle.await {
+        log::warn!("health server task join error: {e}");
+    }
+    result
 }
diff --git a/ballista/executor/src/config.rs b/ballista/executor/src/config.rs
index 85bbfb49c..4a7b638d3 100644
--- a/ballista/executor/src/config.rs
+++ b/ballista/executor/src/config.rs
@@ -71,6 +71,13 @@ pub struct Config {
     /// Port for the executor's gRPC service (used for task management).
     #[arg(long, default_value_t = 50052, help = "Grpc service bind port.")]
     pub bind_grpc_port: u16,
+    /// Port for the executor's HTTP health server (/healthz and /readyz).
+    #[arg(
+        long,
+        default_value_t = 50053,
+        help = "HTTP port for the executor's Kubernetes health probes 
(/healthz and /readyz)."
+    )]
+    pub bind_health_port: u16,
     /// Timeout in seconds for establishing connection to scheduler (0 = fail 
immediately).
     #[arg(
         long,
@@ -250,6 +257,7 @@ impl TryFrom<Config> for ExecutorProcessConfig {
             override_arrow_flight_service: None,
             override_create_grpc_client_endpoint: None,
             client_ttl: opt.client_ttl,
+            health: crate::health::ExecutorHealth::new(),
         })
     }
 }
diff --git a/ballista/executor/src/execution_loop.rs 
b/ballista/executor/src/execution_loop.rs
index ace8bf648..b06aa6e01 100644
--- a/ballista/executor/src/execution_loop.rs
+++ b/ballista/executor/src/execution_loop.rs
@@ -61,6 +61,7 @@ pub async fn poll_loop<T: 'static + AsLogicalPlan, U: 'static 
+ AsExecutionPlan,
     mut scheduler: SchedulerGrpcClient<C>,
     executor: Arc<Executor>,
     codec: BallistaCodec<T, U>,
+    health: crate::health::ExecutorHealth,
 ) -> Result<(), BallistaError>
 where
     C: tonic::client::GrpcService<tonic::body::Body>,
@@ -108,6 +109,7 @@ where
 
         match poll_work_result {
             Ok(result) => {
+                health.mark_heartbeat_ok();
                 let PollWorkResult {
                     tasks,
                     jobs_to_clean,
@@ -198,6 +200,7 @@ where
                 }
             }
             Err(error) => {
+                health.mark_heartbeat_failed();
                 warn!(
                     "Executor poll work loop failed. If this continues to 
happen the Scheduler might be marked as dead. Error: {error}"
                 );
diff --git a/ballista/executor/src/executor.rs 
b/ballista/executor/src/executor.rs
index c5d9c13c4..5778f65e8 100644
--- a/ballista/executor/src/executor.rs
+++ b/ballista/executor/src/executor.rs
@@ -436,11 +436,7 @@ mod test {
 
         let executor_registration = ExecutorRegistration {
             id: "executor".to_string(),
-            port: 0,
-            grpc_port: 0,
-            specification: None,
-            host: None,
-            os_info: None,
+            ..Default::default()
         };
         let config_producer = Arc::new(default_config_producer);
         let ctx = SessionContext::new();
@@ -501,11 +497,7 @@ mod test {
     fn produce_runtime_for_session_shares_read_side_state() {
         let executor_registration = ExecutorRegistration {
             id: "executor".to_string(),
-            port: 0,
-            grpc_port: 0,
-            specification: None,
-            host: None,
-            os_info: None,
+            ..Default::default()
         };
         let config_producer = Arc::new(default_config_producer);
 
@@ -540,11 +532,7 @@ mod test {
     fn produce_runtime_for_session_falls_back_without_cache() {
         let executor_registration = ExecutorRegistration {
             id: "executor".to_string(),
-            port: 0,
-            grpc_port: 0,
-            specification: None,
-            host: None,
-            os_info: None,
+            ..Default::default()
         };
         let config_producer = Arc::new(default_config_producer);
         let base_producer: RuntimeProducer =
diff --git a/ballista/executor/src/executor_process.rs 
b/ballista/executor/src/executor_process.rs
index d7fbe9013..ce7708896 100644
--- a/ballista/executor/src/executor_process.rs
+++ b/ballista/executor/src/executor_process.rs
@@ -61,7 +61,9 @@ use ballista_core::utils::{
     GrpcClientConfig, GrpcServerConfig, create_grpc_client_endpoint, 
create_grpc_server,
     default_config_producer, get_time_before,
 };
-use ballista_core::{BALLISTA_VERSION, ConfigProducer, JobId, RuntimeProducer};
+use ballista_core::{
+    BALLISTA_PROTOCOL_VERSION, BALLISTA_VERSION, ConfigProducer, JobId, 
RuntimeProducer,
+};
 
 use crate::client_pool::DefaultBallistaClientPool;
 use crate::execution_engine::{DefaultExecutionEngine, ExecutionEngine};
@@ -192,6 +194,11 @@ pub struct ExecutorProcessConfig {
     /// connection and a shuffle-heavy query can exhaust the host's ephemeral
     /// ports.
     pub client_ttl: u64,
+    /// Shared readiness state that the heartbeat loops flip on every RPC
+    /// outcome. Embedders leave this as `Default::default()` and never
+    /// observe it; the standalone binary passes a handle here and also spawns
+    /// an HTTP server on it (see `bin/main.rs`).
+    pub health: crate::health::ExecutorHealth,
 }
 
 impl ExecutorProcessConfig {
@@ -242,6 +249,7 @@ impl Default for ExecutorProcessConfig {
             override_arrow_flight_service: None,
             override_create_grpc_client_endpoint: None,
             client_ttl: 30,
+            health: crate::health::ExecutorHealth::new(),
         }
     }
 }
@@ -519,6 +527,12 @@ pub async fn start_executor_process(
     // Channels used to receive stop requests from Executor grpc service.
     let (stop_send, mut stop_recv) = mpsc::channel::<bool>(10);
 
+    // Shared readiness state, flipped by the heartbeat/poll_work loop. When
+    // the caller is the standalone binary, an HTTP probe server observes
+    // this same handle (see `bin/main.rs`); library embedders leave it
+    // unobserved.
+    let health = opt.health.clone();
+
     // Starting main executor process based on the TaskSchedulingPolicy
     //
     // PushStaged => starting new executor_server that waits for tasks from 
the schedule
@@ -534,6 +548,7 @@ pub async fn start_executor_process(
                     default_codec,
                     stop_send,
                     &shutdown_notification,
+                    health,
                 )
                 .await?,
             );
@@ -543,6 +558,7 @@ pub async fn start_executor_process(
                 scheduler.clone(),
                 executor.clone(),
                 default_codec,
+                health,
             )));
         }
     };
@@ -939,6 +955,7 @@ pub fn structure_executor_metadata(
             total_available_disk_space,
             open_files_limit,
         }),
+        ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
     }
 }
 
diff --git a/ballista/executor/src/executor_server.rs 
b/ballista/executor/src/executor_server.rs
index 3ca3d8f81..36718fde3 100644
--- a/ballista/executor/src/executor_server.rs
+++ b/ballista/executor/src/executor_server.rs
@@ -64,10 +64,18 @@ use tokio::task::JoinHandle;
 use crate::cpu_bound_executor::DedicatedExecutor;
 use crate::executor::Executor;
 use crate::executor_process::{ExecutorProcessConfig, remove_job_data};
+use crate::health::ExecutorHealth;
 use crate::metrics::ExecutorMetricCollectionPolicy;
 use crate::shutdown::ShutdownNotifier;
 use crate::{TaskExecutionTimes, as_task_status};
 
+/// Number of consecutive heartbeat failures after which the executor
+/// initiates its own shutdown, letting k8s (or the operator) restart the
+/// pod. The typical trigger is a `FailedPrecondition` from a newer
+/// scheduler on a bumped `BALLISTA_PROTOCOL_VERSION`; a restart will keep
+/// crash-looping until the executor image is bumped to match.
+const HEARTBEAT_FAILURE_TERMINATION_THRESHOLD: u32 = 5;
+
 type ServerHandle = JoinHandle<Result<(), BallistaError>>;
 type SchedulerClients = Arc<DashMap<String, SchedulerGrpcClient<Channel>>>;
 
@@ -101,6 +109,7 @@ pub async fn startup<T: 'static + AsLogicalPlan, U: 'static 
+ AsExecutionPlan>(
     codec: BallistaCodec<T, U>,
     stop_send: mpsc::Sender<bool>,
     shutdown_noti: &ShutdownNotifier,
+    health: ExecutorHealth,
 ) -> Result<ServerHandle, BallistaError> {
     let channel_buf_size = executor.vcores * 50;
     let (tx_task, rx_task) = 
mpsc::channel::<CuratorTaskDefinition>(channel_buf_size);
@@ -120,6 +129,7 @@ pub async fn startup<T: 'static + AsLogicalPlan, U: 'static 
+ AsExecutionPlan>(
         config.grpc_max_decoding_message_size as usize,
         config.override_create_grpc_client_endpoint.clone(),
         config.metric_collection_policy,
+        health,
     );
 
     // 1. Start executor grpc service
@@ -222,6 +232,8 @@ pub struct ExecutorServer<T: 'static + AsLogicalPlan, U: 
'static + AsExecutionPl
     /// Metric collection policy for this specifix executor
     metric_collection_policy: ExecutorMetricCollectionPolicy,
     override_create_grpc_client_endpoint: Option<EndpointOverrideFn>,
+    /// Shared readiness signal reflected by the /readyz probe.
+    health: ExecutorHealth,
 }
 
 #[derive(Clone)]
@@ -251,6 +263,7 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
         grpc_max_decoding_message_size: usize,
         override_create_grpc_client_endpoint: Option<EndpointOverrideFn>,
         metric_collection_policy: ExecutorMetricCollectionPolicy,
+        health: ExecutorHealth,
     ) -> Self {
         Self {
             _start_time: SystemTime::now()
@@ -266,6 +279,7 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
             grpc_max_decoding_message_size,
             override_create_grpc_client_endpoint,
             metric_collection_policy,
+            health,
         }
     }
 
@@ -305,7 +319,9 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
 
     /// 1. First Heartbeat to its registration scheduler, if successful then 
return; else go next.
     /// 2. Heartbeat to schedulers which has launching tasks to this executor 
until one succeeds
-    async fn heartbeat(&self) {
+    ///
+    /// Returns `true` iff any scheduler acknowledged the heartbeat.
+    async fn heartbeat(&self) -> bool {
         let status = if TERMINATING.load(Ordering::Acquire) {
             executor_status::Status::Terminating(String::default())
         } else {
@@ -326,7 +342,8 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
             .await
         {
             Ok(_) => {
-                return;
+                self.health.mark_heartbeat_ok();
+                return true;
             }
             Err(e) => {
                 warn!(
@@ -344,7 +361,8 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
                 .await
             {
                 Ok(_) => {
-                    break;
+                    self.health.mark_heartbeat_ok();
+                    return true;
                 }
                 Err(e) => {
                     warn!(
@@ -353,6 +371,8 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> ExecutorServer<T,
                 }
             }
         }
+        self.health.mark_heartbeat_failed();
+        false
     }
     /// This method should not return Err. If task fails, a failure task 
status should be sent
     /// to the channel to notify the scheduler.
@@ -634,11 +654,27 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> Heartbeater<T, U>
         let executor_server = self.executor_server.clone();
         let mut heartbeat_shutdown = shutdown_noti.subscribe_for_shutdown();
         let heartbeat_complete = shutdown_noti.shutdown_complete_tx.clone();
+        let notify_shutdown = shutdown_noti.notify_shutdown.clone();
         tokio::spawn(async move {
             info!("Starting heartbeater to send heartbeat the scheduler 
periodically");
+            let mut consecutive_failures: u32 = 0;
             // As long as the shutdown notification has not been received
             while !heartbeat_shutdown.is_shutdown() {
-                executor_server.heartbeat().await;
+                if executor_server.heartbeat().await {
+                    consecutive_failures = 0;
+                } else {
+                    consecutive_failures += 1;
+                    if consecutive_failures >= 
HEARTBEAT_FAILURE_TERMINATION_THRESHOLD {
+                        error!(
+                            "Heartbeat failed {consecutive_failures} 
consecutive times; \
+                             initiating executor shutdown. Check for scheduler 
outage \
+                             or BALLISTA_PROTOCOL_VERSION mismatch."
+                        );
+                        let _ = notify_shutdown.send(());
+                        drop(heartbeat_complete);
+                        return;
+                    }
+                }
                 tokio::select! {
                     _ = 
tokio::time::sleep(Duration::from_secs(executor_heartbeat_interval_seconds)) => 
{},
                     _ = heartbeat_shutdown.recv() => {
diff --git a/ballista/executor/src/health.rs b/ballista/executor/src/health.rs
new file mode 100644
index 000000000..74ab1b4a1
--- /dev/null
+++ b/ballista/executor/src/health.rs
@@ -0,0 +1,143 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Shared readiness state for Kubernetes-style health probes.
+//!
+//! The state itself (`ExecutorHealth`) is always compiled: the heartbeat
+//! loops in `execution_loop` and `executor_server` flip it on every RPC
+//! outcome regardless of whether anything is observing it. That keeps the
+//! library transport-neutral — callers that embed the executor as a library
+//! never have to link in axum, bind a port, or field an unwanted HTTP
+//! surface.
+//!
+//! The HTTP surface (`/healthz` and `/readyz` served over axum) is behind
+//! `#[cfg(feature = "build-binary")]` and lives here for symmetry, but is
+//! spawned from `bin/main.rs`, not from `start_executor_process`.
+//!
+//! `/healthz` is deliberately independent of scheduler connectivity: if the
+//! scheduler is flapping or on the wrong protocol version, restarting this
+//! pod will not help — that would just cascade into a fleet-wide crash
+//! loop. `/readyz` reflects the last heartbeat: startup begins not-ready, a
+//! successful heartbeat flips it ready, a failure flips it back. Failing
+//! `/readyz` removes the executor from any `Service` endpoint but does not
+//! restart the pod.
+
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+/// Shared executor health state, cheap to clone (internally arc'd).
+#[derive(Clone, Default)]
+pub struct ExecutorHealth {
+    heartbeat_ok: Arc<AtomicBool>,
+}
+
+impl ExecutorHealth {
+    /// Creates a new health handle. Starts in the not-ready state; the
+    /// executor's first successful heartbeat flips it to ready.
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Marks the last heartbeat as successful. Called from the heartbeat
+    /// loop after the scheduler acknowledges the heartbeat.
+    pub fn mark_heartbeat_ok(&self) {
+        self.heartbeat_ok.store(true, Ordering::Release);
+    }
+
+    /// Marks the last heartbeat as failed. Called from the heartbeat loop
+    /// when the scheduler returns an error, including
+    /// `FailedPrecondition` for a protocol-version mismatch.
+    pub fn mark_heartbeat_failed(&self) {
+        self.heartbeat_ok.store(false, Ordering::Release);
+    }
+
+    /// True once the executor's most recent heartbeat to the scheduler
+    /// succeeded. Embedders that host `ExecutorHealth` in their own process
+    /// can fold this into their app-wide readiness check the same way the
+    /// scheduler exposes `SchedulerServer::is_ready`.
+    pub fn is_ready(&self) -> bool {
+        self.heartbeat_ok.load(Ordering::Acquire)
+    }
+}
+
+#[cfg(feature = "build-binary")]
+mod server {
+    use super::ExecutorHealth;
+    use axum::{
+        Router,
+        extract::State,
+        http::StatusCode,
+        response::{IntoResponse, Response},
+        routing::get,
+    };
+    use ballista_core::error::BallistaError;
+    use log::{info, warn};
+    use std::net::SocketAddr;
+    use tokio::task::JoinHandle;
+
+    /// Spawns an HTTP server serving `/healthz` and `/readyz`. The task
+    /// shuts down when the receiver at `shutdown` fires.
+    pub fn spawn_health_server(
+        addr: SocketAddr,
+        health: ExecutorHealth,
+        shutdown: tokio::sync::oneshot::Receiver<()>,
+    ) -> JoinHandle<Result<(), BallistaError>> {
+        tokio::spawn(async move {
+            let router = Router::new()
+                .route("/healthz", get(healthz))
+                .route("/readyz", get(readyz))
+                .with_state(health);
+
+            let listener = 
tokio::net::TcpListener::bind(addr).await.map_err(|e| {
+                BallistaError::General(format!(
+                    "failed to bind health server on {addr}: {e}"
+                ))
+            })?;
+
+            info!("Executor health server listening on {addr}");
+
+            axum::serve(listener, router.into_make_service())
+                .with_graceful_shutdown(async move {
+                    let _ = shutdown.await;
+                })
+                .await
+                .map_err(|e| {
+                    warn!("Executor health server exited with error: {e}");
+                    BallistaError::General(format!("health server error: {e}"))
+                })
+        })
+    }
+
+    async fn healthz() -> Response {
+        (StatusCode::OK, "ok\n").into_response()
+    }
+
+    async fn readyz(State(health): State<ExecutorHealth>) -> Response {
+        if health.is_ready() {
+            (StatusCode::OK, "ready\n").into_response()
+        } else {
+            (
+                StatusCode::SERVICE_UNAVAILABLE,
+                "not ready: no successful heartbeat yet\n",
+            )
+                .into_response()
+        }
+    }
+}
+
+#[cfg(feature = "build-binary")]
+pub use server::spawn_health_server;
diff --git a/ballista/executor/src/lib.rs b/ballista/executor/src/lib.rs
index 70234ceaa..802e16f5a 100644
--- a/ballista/executor/src/lib.rs
+++ b/ballista/executor/src/lib.rs
@@ -37,6 +37,8 @@ pub mod executor_process;
 pub mod executor_server;
 /// Arrow Flight service for streaming shuffle data between executors.
 pub mod flight_service;
+/// HTTP server for Kubernetes-style /healthz and /readyz probes.
+pub mod health;
 /// Metrics collection for executor runtime statistics.
 pub mod metrics;
 /// Session-scoped cache of shared executor runtime environments.
diff --git a/ballista/executor/src/standalone.rs 
b/ballista/executor/src/standalone.rs
index 5034e53cc..d39841e8b 100644
--- a/ballista/executor/src/standalone.rs
+++ b/ballista/executor/src/standalone.rs
@@ -27,7 +27,7 @@ use ballista_core::extension::SessionConfigExt;
 use ballista_core::registry::BallistaFunctionRegistry;
 use ballista_core::utils::{GrpcServerConfig, default_config_producer};
 use ballista_core::{
-    BALLISTA_VERSION,
+    BALLISTA_PROTOCOL_VERSION, BALLISTA_VERSION,
     error::Result,
     serde::BallistaCodec,
     serde::protobuf::{ExecutorRegistration, 
scheduler_grpc_client::SchedulerGrpcClient},
@@ -110,6 +110,7 @@ pub async fn new_standalone_executor_from_builder(
                 .into(),
         ),
         os_info: Some(ExecutorOperatingSystemSpecification::default().into()),
+        ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
     };
 
     let config = config_producer();
@@ -142,7 +143,12 @@ pub async fn new_standalone_executor_from_builder(
             )),
     );
 
-    tokio::spawn(execution_loop::poll_loop(scheduler, executor, codec));
+    tokio::spawn(execution_loop::poll_loop(
+        scheduler,
+        executor,
+        codec,
+        crate::health::ExecutorHealth::new(),
+    ));
     Ok(())
 }
 
diff --git a/ballista/scheduler/src/api/health.rs 
b/ballista/scheduler/src/api/health.rs
new file mode 100644
index 000000000..d0ee4a852
--- /dev/null
+++ b/ballista/scheduler/src/api/health.rs
@@ -0,0 +1,163 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Kubernetes-style health and readiness probes.
+//!
+//! `/healthz` always returns 200 while the process is running — it reports
+//! liveness only, so a probe failure signals "the process is dead" (which is
+//! the sole condition under which k8s should kill and restart the pod).
+//!
+//! `/readyz` returns 200 only when the scheduler is ready to accept work,
+//! defined as having at least `min_ready_executors` live executors registered.
+//! A readiness failure removes the pod from Service endpoints but leaves the
+//! process alone, which is what we want during rolling upgrades: a fresh
+//! scheduler with `BALLISTA_PROTOCOL_VERSION` rejects executors from the
+//! previous release, so it will fail /readyz until new executors register.
+
+use axum::{
+    Router,
+    extract::State,
+    http::StatusCode,
+    response::{IntoResponse, Response},
+    routing::get,
+};
+use datafusion_proto::logical_plan::AsLogicalPlan;
+use datafusion_proto::physical_plan::AsExecutionPlan;
+use std::sync::Arc;
+
+use crate::scheduler_server::SchedulerServer;
+
+/// Routes for k8s liveness/readiness probes. Always mounted, regardless of
+/// the `rest-api` feature flag.
+pub(crate) fn health_routes<
+    T: AsLogicalPlan + Clone + Send + Sync + 'static,
+    U: AsExecutionPlan + Send + Sync + 'static,
+>(
+    scheduler_server: Arc<SchedulerServer<T, U>>,
+) -> Router {
+    Router::new()
+        .route("/healthz", get(healthz))
+        .route("/readyz", get(readyz::<T, U>))
+        .with_state(scheduler_server)
+}
+
+async fn healthz() -> Response {
+    (StatusCode::OK, "ok").into_response()
+}
+
+async fn readyz<
+    T: AsLogicalPlan + Clone + Send + Sync + 'static,
+    U: AsExecutionPlan + Send + Sync + 'static,
+>(
+    State(scheduler): State<Arc<SchedulerServer<T, U>>>,
+) -> Response {
+    let alive = scheduler.state.executor_manager.get_alive_executors().len();
+    let required = scheduler.state.config.min_ready_executors;
+    if scheduler.is_ready() {
+        (
+            StatusCode::OK,
+            format!("ready: {alive} executors registered\n"),
+        )
+            .into_response()
+    } else {
+        (
+            StatusCode::SERVICE_UNAVAILABLE,
+            format!("not ready: {alive}/{required} executors registered\n"),
+        )
+            .into_response()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::config::SchedulerConfig;
+    use crate::metrics::default_metrics_collector;
+    use crate::scheduler_server::SchedulerServer;
+    use crate::test_utils::test_cluster_context;
+    use ballista_core::serde::BallistaCodec;
+    use ballista_core::serde::protobuf::{
+        ExecutorHeartbeat, ExecutorStatus, executor_status,
+    };
+    use datafusion_proto::protobuf::{LogicalPlanNode, PhysicalPlanNode};
+
+    async fn scheduler_with_min_ready(
+        min_ready: usize,
+    ) -> Arc<SchedulerServer<LogicalPlanNode, PhysicalPlanNode>> {
+        let config = SchedulerConfig {
+            min_ready_executors: min_ready,
+            ..Default::default()
+        };
+        let mut server = SchedulerServer::new(
+            "localhost:50050".to_owned(),
+            test_cluster_context(),
+            BallistaCodec::default(),
+            Arc::new(config),
+            default_metrics_collector().unwrap(),
+        );
+        server.init().await.expect("scheduler init");
+        Arc::new(server)
+    }
+
+    #[tokio::test]
+    async fn healthz_is_always_ok() {
+        let response = healthz().await;
+        assert_eq!(response.status(), StatusCode::OK);
+    }
+
+    #[tokio::test]
+    async fn readyz_fails_with_no_executors() {
+        let scheduler = scheduler_with_min_ready(1).await;
+        let response = readyz(State(scheduler)).await;
+        assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
+    }
+
+    #[tokio::test]
+    async fn readyz_ok_when_min_ready_is_zero() {
+        let scheduler = scheduler_with_min_ready(0).await;
+        let response = readyz(State(scheduler)).await;
+        assert_eq!(response.status(), StatusCode::OK);
+    }
+
+    #[tokio::test]
+    async fn readyz_ok_when_active_executor_registered() {
+        let scheduler = scheduler_with_min_ready(1).await;
+
+        // Publish an active-status heartbeat so the executor counts as alive.
+        scheduler
+            .state
+            .executor_manager
+            .save_executor_heartbeat(ExecutorHeartbeat {
+                executor_id: "abc".to_owned(),
+                timestamp: std::time::SystemTime::now()
+                    .duration_since(std::time::UNIX_EPOCH)
+                    .unwrap()
+                    .as_secs(),
+                metrics: vec![],
+                status: Some(ExecutorStatus {
+                    status: 
Some(executor_status::Status::Active("".to_string())),
+                }),
+                peak_proc_physical_memory: 0,
+                peak_proc_virtual_memory: 0,
+            })
+            .await
+            .expect("save heartbeat");
+
+        let response = readyz(State(scheduler)).await;
+        assert_eq!(response.status(), StatusCode::OK);
+    }
+}
diff --git a/ballista/scheduler/src/api/mod.rs 
b/ballista/scheduler/src/api/mod.rs
index c02b47fd4..81d7177cf 100644
--- a/ballista/scheduler/src/api/mod.rs
+++ b/ballista/scheduler/src/api/mod.rs
@@ -12,8 +12,10 @@
 
 #[cfg(feature = "rest-api")]
 mod handlers;
+mod health;
 #[cfg(feature = "rest-api")]
 mod routes;
+pub(super) use health::health_routes;
 #[cfg(feature = "rest-api")]
 pub use routes::get_routes;
 
diff --git a/ballista/scheduler/src/config.rs b/ballista/scheduler/src/config.rs
index 19fad4331..940888714 100644
--- a/ballista/scheduler/src/config.rs
+++ b/ballista/scheduler/src/config.rs
@@ -173,6 +173,13 @@ pub struct Config {
         help = "Interval, in seconds, to check expired or dead executors."
     )]
     pub expire_dead_executor_interval_seconds: u64,
+    /// Minimum number of registered executors before /readyz returns 200
+    #[arg(
+        long,
+        default_value_t = 1,
+        help = "Minimum number of registered executors before the scheduler's 
/readyz probe returns 200. Set to 0 to always report ready."
+    )]
+    pub min_ready_executors: usize,
     /// Number of failures attempts before task is considered failed
     #[arg(
         long,
@@ -265,6 +272,8 @@ pub struct SchedulerConfig {
     pub override_create_grpc_client_endpoint: Option<EndpointOverrideFn>,
     /// Whether to use TLS when connecting to executors (for flight proxy)
     pub use_tls: bool,
+    /// Minimum number of registered executors before /readyz returns 200
+    pub min_ready_executors: usize,
     /// Number of failures attempts before task is considered failed
     pub task_max_failures: usize,
     /// Number of failures attempts before stage is considered failed
@@ -306,6 +315,7 @@ impl Default for SchedulerConfig {
             override_physical_codec: None,
             override_create_grpc_client_endpoint: None,
             use_tls: false,
+            min_ready_executors: 1,
             task_max_failures: 4,
             stage_max_failures: 4,
             #[cfg(feature = "rest-api")]
@@ -538,6 +548,7 @@ impl TryFrom<Config> for SchedulerConfig {
             override_session_builder: None,
             override_create_grpc_client_endpoint: None,
             use_tls: false,
+            min_ready_executors: opt.min_ready_executors,
             task_max_failures: opt.task_max_failures,
             stage_max_failures: opt.stage_max_failures,
             #[cfg(feature = "rest-api")]
diff --git a/ballista/scheduler/src/metrics/mod.rs 
b/ballista/scheduler/src/metrics/mod.rs
index 18e06c21c..8f7520d7d 100644
--- a/ballista/scheduler/src/metrics/mod.rs
+++ b/ballista/scheduler/src/metrics/mod.rs
@@ -92,3 +92,25 @@ pub fn default_metrics_collector() -> Result<Arc<dyn 
SchedulerMetricsCollector>>
 pub fn default_metrics_collector() -> Result<Arc<dyn 
SchedulerMetricsCollector>> {
     Ok(Arc::new(NoopMetricsCollector::default()))
 }
+
+/// Increments the counter of executor RPCs (register/heartbeat/poll_work)
+/// rejected because their `ballista_protocol_version` did not match the
+/// scheduler's `BALLISTA_PROTOCOL_VERSION`. No-op when the `prometheus`
+/// feature is disabled.
+#[cfg(feature = "prometheus")]
+pub fn record_protocol_mismatch() {
+    use ::prometheus::{IntCounter, register_int_counter};
+    use once_cell::sync::Lazy;
+    static COUNTER: Lazy<IntCounter> = Lazy::new(|| {
+        register_int_counter!(
+            "ballista_scheduler_rejected_by_protocol_version_total",
+            "Executor RPCs rejected because of BALLISTA_PROTOCOL_VERSION 
mismatch",
+        )
+        .expect("register 
ballista_scheduler_rejected_by_protocol_version_total")
+    });
+    COUNTER.inc();
+}
+
+/// See prometheus-featured variant.
+#[cfg(not(feature = "prometheus"))]
+pub fn record_protocol_mismatch() {}
diff --git a/ballista/scheduler/src/scheduler_process.rs 
b/ballista/scheduler/src/scheduler_process.rs
index 24bc58b9b..70a2dd09d 100644
--- a/ballista/scheduler/src/scheduler_process.rs
+++ b/ballista/scheduler/src/scheduler_process.rs
@@ -20,6 +20,7 @@ use crate::flight_proxy_service::BallistaFlightProxyService;
 
 #[cfg(feature = "rest-api")]
 use crate::api::get_routes;
+use crate::api::health_routes;
 use crate::api::route_disabled;
 use crate::cluster::BallistaCluster;
 use crate::config::SchedulerConfig;
@@ -92,7 +93,7 @@ pub async fn start_grpc_service<
     address: SocketAddr,
     scheduler: SchedulerServer<T, U>,
 ) -> ballista_core::error::Result<()> {
-    let config = &scheduler.state.config;
+    let config = scheduler.state.config.clone();
     let scheduler_grpc_server = SchedulerGrpcServer::new(scheduler.clone())
         
.max_encoding_message_size(config.grpc_server_max_encoding_message_size as 
usize)
         
.max_decoding_message_size(config.grpc_server_max_decoding_message_size as 
usize);
@@ -135,16 +136,21 @@ pub async fn start_grpc_service<
     let tonic =
         tonic.fallback(|| async { 
SchedulerErrorResponse::new(StatusCode::NOT_FOUND) });
 
+    let scheduler = Arc::new(scheduler);
+    let health = health_routes(scheduler.clone());
+
     #[cfg(feature = "rest-api")]
     let final_route = if config.disable_rest_api {
         tonic
             .merge(route_disabled(
                 "REST API has been disabled at startup".to_string(),
             ))
+            .merge(health)
             .into_make_service_with_connect_info::<SocketAddr>()
     } else {
-        let axum = get_routes(Arc::new(scheduler));
+        let axum = get_routes(scheduler);
         axum.merge(tonic)
+            .merge(health)
             .into_make_service_with_connect_info::<SocketAddr>()
     };
 
@@ -153,6 +159,7 @@ pub async fn start_grpc_service<
         .merge(route_disabled(
             "REST API has been disabled at compile time".to_string(),
         ))
+        .merge(health)
         .into_make_service_with_connect_info::<SocketAddr>();
 
     let listener = tokio::net::TcpListener::bind(&address)
diff --git a/ballista/scheduler/src/scheduler_server/grpc.rs 
b/ballista/scheduler/src/scheduler_server/grpc.rs
index b1431b1d9..81aa9acde 100644
--- a/ballista/scheduler/src/scheduler_server/grpc.rs
+++ b/ballista/scheduler/src/scheduler_server/grpc.rs
@@ -16,6 +16,7 @@
 // under the License.
 
 use axum::extract::ConnectInfo;
+use ballista_core::BALLISTA_PROTOCOL_VERSION;
 use ballista_core::config::BALLISTA_JOB_NAME;
 use ballista_core::error::{BallistaError, Result as BResult};
 use ballista_core::extension::SessionConfigHelperExt;
@@ -25,13 +26,13 @@ use ballista_core::serde::protobuf::{
     AvailableVcores, CancelJobParams, CancelJobResult, CleanJobDataParams,
     CleanJobDataResult, CreateUpdateSessionParams, CreateUpdateSessionResult,
     ExecuteQueryFailureResult, ExecuteQueryParams, ExecuteQueryResult,
-    ExecuteQuerySuccessResult, ExecutorHeartbeat, ExecutorStoppedParams,
-    ExecutorStoppedResult, GetJobMetricsParams, GetJobMetricsResult, 
GetJobStatusParams,
-    GetJobStatusResult, HeartBeatParams, HeartBeatResult, JobStatus, 
KeyValuePair,
-    PollWorkParams, PollWorkResult, RegisterExecutorParams, 
RegisterExecutorResult,
-    RemoveSessionParams, RemoveSessionResult, UpdateTaskStatusParams,
-    UpdateTaskStatusResult, execute_query_failure_result, execute_query_result,
-    executor_metric::Metric,
+    ExecuteQuerySuccessResult, ExecutorHeartbeat, ExecutorRegistration,
+    ExecutorStoppedParams, ExecutorStoppedResult, GetJobMetricsParams,
+    GetJobMetricsResult, GetJobStatusParams, GetJobStatusResult, 
HeartBeatParams,
+    HeartBeatResult, JobStatus, KeyValuePair, PollWorkParams, PollWorkResult,
+    RegisterExecutorParams, RegisterExecutorResult, RemoveSessionParams,
+    RemoveSessionResult, UpdateTaskStatusParams, UpdateTaskStatusResult,
+    execute_query_failure_result, execute_query_result, 
executor_metric::Metric,
 };
 use ballista_core::serde::scheduler::ExecutorMetadata;
 use datafusion_proto::logical_plan::AsLogicalPlan;
@@ -60,8 +61,27 @@ use std::sync::Arc;
 use std::time::{SystemTime, UNIX_EPOCH};
 use tonic::{Request, Response, Status};
 
+use crate::metrics::record_protocol_mismatch;
 use crate::scheduler_server::SchedulerServer;
 
+/// Rejects an executor RPC whose `ballista_protocol_version` does not match
+/// the scheduler's compiled-in `BALLISTA_PROTOCOL_VERSION`. See the constant
+/// in `ballista_core` for the upgrade semantics.
+fn check_protocol_version(metadata: &ExecutorRegistration) -> Result<(), 
Status> {
+    if metadata.ballista_protocol_version == BALLISTA_PROTOCOL_VERSION {
+        return Ok(());
+    }
+    record_protocol_mismatch();
+    info!(
+        "Rejecting executor {}: protocol version mismatch (scheduler={}, 
executor={})",
+        metadata.id, BALLISTA_PROTOCOL_VERSION, 
metadata.ballista_protocol_version,
+    );
+    Err(Status::failed_precondition(format!(
+        "protocol version mismatch: scheduler={}, executor={}",
+        BALLISTA_PROTOCOL_VERSION, metadata.ballista_protocol_version,
+    )))
+}
+
 #[tonic::async_trait]
 impl<T: 'static + AsLogicalPlan, U: 'static + AsExecutionPlan> SchedulerGrpc
     for SchedulerServer<T, U>
@@ -87,6 +107,7 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> SchedulerGrpc
         } = request.into_inner()
         {
             trace!("Received poll_work request for {metadata:?}");
+            check_protocol_version(&metadata)?;
             let executor_id = metadata.id.clone();
 
             // It's not necessary.
@@ -186,6 +207,7 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> SchedulerGrpc
         } = request.into_inner()
         {
             info!("Received register executor request for {metadata:?}");
+            check_protocol_version(&metadata)?;
             let metadata = ExecutorMetadata {
                 id: metadata.id,
                 host: metadata
@@ -223,6 +245,19 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> SchedulerGrpc
         } = request.into_inner();
         trace!("Received heart beat request for {:?}", executor_id);
 
+        let Some(metadata_ref) = metadata.as_ref() else {
+            info!(
+                "Rejecting heartbeat from {executor_id}: missing registration 
metadata"
+            );
+            record_protocol_mismatch();
+            return Err(Status::failed_precondition(
+                "heartbeat missing registration metadata; \
+                 executors must include ExecutorRegistration \
+                 (see BALLISTA_PROTOCOL_VERSION)",
+            ));
+        };
+        check_protocol_version(metadata_ref)?;
+
         // If not registered, do registration first before saving heart beat
         if let Err(e) = self
             .state
@@ -870,6 +905,7 @@ mod test {
 
     use crate::config::SchedulerConfig;
     use crate::metrics::default_metrics_collector;
+    use ballista_core::BALLISTA_PROTOCOL_VERSION;
     use ballista_core::error::BallistaError;
     use ballista_core::serde::BallistaCodec;
     use ballista_core::serde::protobuf::{
@@ -909,6 +945,7 @@ mod test {
             grpc_port: 0,
             specification: 
Some(ExecutorSpecification::default().with_vcores(2).into()),
             os_info: 
Some(ExecutorOperatingSystemSpecification::default().into()),
+            ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
         };
         let request: Request<PollWorkParams> = Request::new(PollWorkParams {
             metadata: Some(exec_meta.clone()),
@@ -998,6 +1035,7 @@ mod test {
             grpc_port: 0,
             specification: 
Some(ExecutorSpecification::default().with_vcores(2).into()),
             os_info: 
Some(ExecutorOperatingSystemSpecification::default().into()),
+            ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
         };
 
         let request: Request<RegisterExecutorParams> =
@@ -1084,6 +1122,7 @@ mod test {
             grpc_port: 0,
             specification: 
Some(ExecutorSpecification::default().with_vcores(2).into()),
             os_info: 
Some(ExecutorOperatingSystemSpecification::default().into()),
+            ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
         };
 
         let request: Request<HeartBeatParams> = Request::new(HeartBeatParams {
@@ -1115,6 +1154,126 @@ mod test {
         Ok(())
     }
 
+    async fn scheduler_for_protocol_test()
+    -> Result<SchedulerServer<LogicalPlanNode, PhysicalPlanNode>, 
BallistaError> {
+        let cluster = test_cluster_context();
+        let config = SchedulerConfig::default();
+        let mut scheduler: SchedulerServer<LogicalPlanNode, PhysicalPlanNode> =
+            SchedulerServer::new(
+                "localhost:50050".to_owned(),
+                cluster,
+                BallistaCodec::default(),
+                Arc::new(config),
+                default_metrics_collector().unwrap(),
+            );
+        scheduler.init().await?;
+        Ok(scheduler)
+    }
+
+    fn exec_meta_with_version(version: u32) -> ExecutorRegistration {
+        ExecutorRegistration {
+            id: "abc".to_owned(),
+            host: Some("http://localhost:8080".to_owned()),
+            port: 0,
+            grpc_port: 0,
+            specification: 
Some(ExecutorSpecification::default().with_vcores(2).into()),
+            os_info: 
Some(ExecutorOperatingSystemSpecification::default().into()),
+            ballista_protocol_version: version,
+        }
+    }
+
+    #[tokio::test]
+    async fn register_rejects_protocol_mismatch() -> Result<(), BallistaError> 
{
+        let scheduler = scheduler_for_protocol_test().await?;
+        // Version 0 is what an old executor without the field defaults to;
+        // the scheduler must reject it.
+        let request = Request::new(RegisterExecutorParams {
+            metadata: Some(exec_meta_with_version(0)),
+        });
+
+        let status = scheduler
+            .register_executor(request)
+            .await
+            .expect_err("register with mismatched version should be rejected");
+        assert_eq!(status.code(), tonic::Code::FailedPrecondition);
+        assert!(
+            status.message().contains("protocol version mismatch"),
+            "unexpected message: {}",
+            status.message()
+        );
+        Ok(())
+    }
+
+    #[tokio::test]
+    async fn heartbeat_rejects_protocol_mismatch() -> Result<(), 
BallistaError> {
+        let scheduler = scheduler_for_protocol_test().await?;
+        let meta = exec_meta_with_version(BALLISTA_PROTOCOL_VERSION + 1);
+        let request = Request::new(HeartBeatParams {
+            executor_id: meta.id.clone(),
+            metrics: vec![],
+            status: Some(ExecutorStatus {
+                status: Some(executor_status::Status::Active("".to_string())),
+            }),
+            metadata: Some(meta),
+        });
+        let status = scheduler
+            .heart_beat_from_executor(request)
+            .await
+            .expect_err("heartbeat with mismatched version should be 
rejected");
+        assert_eq!(status.code(), tonic::Code::FailedPrecondition);
+        assert!(status.message().contains("protocol version mismatch"));
+        Ok(())
+    }
+
+    #[tokio::test]
+    async fn heartbeat_rejects_missing_metadata() -> Result<(), BallistaError> 
{
+        let scheduler = scheduler_for_protocol_test().await?;
+        let request = Request::new(HeartBeatParams {
+            executor_id: "abc".to_owned(),
+            metrics: vec![],
+            status: Some(ExecutorStatus {
+                status: Some(executor_status::Status::Active("".to_string())),
+            }),
+            metadata: None,
+        });
+        let status = scheduler
+            .heart_beat_from_executor(request)
+            .await
+            .expect_err("heartbeat with no metadata should be rejected");
+        assert_eq!(status.code(), tonic::Code::FailedPrecondition);
+        assert!(status.message().contains("missing registration metadata"));
+        Ok(())
+    }
+
+    #[tokio::test]
+    async fn poll_work_rejects_protocol_mismatch() -> Result<(), 
BallistaError> {
+        let config = SchedulerConfig {
+            scheduling_policy: 
ballista_core::config::TaskSchedulingPolicy::PullStaged,
+            ..Default::default()
+        };
+        let mut scheduler: SchedulerServer<LogicalPlanNode, PhysicalPlanNode> =
+            SchedulerServer::new(
+                "localhost:50050".to_owned(),
+                test_cluster_context(),
+                BallistaCodec::default(),
+                Arc::new(config),
+                default_metrics_collector().unwrap(),
+            );
+        scheduler.init().await?;
+
+        let request = Request::new(PollWorkParams {
+            metadata: Some(exec_meta_with_version(0)),
+            num_free_vcores: 0,
+            task_status: vec![],
+        });
+        let status = scheduler
+            .poll_work(request)
+            .await
+            .expect_err("poll_work with mismatched version should be 
rejected");
+        assert_eq!(status.code(), tonic::Code::FailedPrecondition);
+        Ok(())
+    }
+
     #[tokio::test]
     #[ignore]
     async fn test_expired_executor() -> Result<(), BallistaError> {
@@ -1138,6 +1297,7 @@ mod test {
             grpc_port: 0,
             specification: 
Some(ExecutorSpecification::default().with_vcores(2).into()),
             os_info: 
Some(ExecutorOperatingSystemSpecification::default().into()),
+            ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
         };
 
         let request: Request<RegisterExecutorParams> =
@@ -1225,6 +1385,7 @@ mod test {
             grpc_port: 0,
             specification: 
Some(ExecutorSpecification::default().with_vcores(2).into()),
             os_info: 
Some(ExecutorOperatingSystemSpecification::default().into()),
+            ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
         };
 
         let request: Request<RegisterExecutorParams> =
diff --git a/ballista/scheduler/src/scheduler_server/mod.rs 
b/ballista/scheduler/src/scheduler_server/mod.rs
index 2c6a966e0..c42b9603a 100644
--- a/ballista/scheduler/src/scheduler_server/mod.rs
+++ b/ballista/scheduler/src/scheduler_server/mod.rs
@@ -182,6 +182,15 @@ impl<T: 'static + AsLogicalPlan, U: 'static + 
AsExecutionPlan> SchedulerServer<T
     pub fn running_job_number(&self) -> usize {
         self.state.task_manager.running_job_number()
     }
+
+    /// True when at least `min_ready_executors` executors currently have
+    /// live heartbeats. Embedders can call this from their own 
health/readiness
+    /// handler and AND it with whatever app-specific state they track. The
+    /// built-in `/readyz` endpoint (see `api::health`) reports the same value.
+    pub fn is_ready(&self) -> bool {
+        let alive = self.state.executor_manager.get_alive_executors().len();
+        alive >= self.state.config.min_ready_executors
+    }
     #[cfg(feature = "rest-api")]
     pub(crate) fn metrics_collector(&self) -> &dyn SchedulerMetricsCollector {
         self.query_stage_scheduler.metrics_collector()
diff --git a/docs/source/user-guide/deployment/kubernetes.md 
b/docs/source/user-guide/deployment/kubernetes.md
index a979a6c8c..9e936abba 100644
--- a/docs/source/user-guide/deployment/kubernetes.md
+++ b/docs/source/user-guide/deployment/kubernetes.md
@@ -144,6 +144,18 @@ spec:
           ports:
             - containerPort: 50050
               name: flight
+          livenessProbe:
+            httpGet:
+              path: /healthz
+              port: 50050
+            failureThreshold: 3
+            periodSeconds: 10
+          readinessProbe:
+            httpGet:
+              path: /readyz
+              port: 50050
+            failureThreshold: 3
+            periodSeconds: 5
           volumeMounts:
             - mountPath: /mnt
               name: data
@@ -177,6 +189,20 @@ spec:
           ports:
             - containerPort: 50051
               name: flight
+            - containerPort: 50053
+              name: health
+          livenessProbe:
+            httpGet:
+              path: /healthz
+              port: 50053
+            failureThreshold: 3
+            periodSeconds: 10
+          readinessProbe:
+            httpGet:
+              path: /readyz
+              port: 50053
+            failureThreshold: 3
+            periodSeconds: 5
           volumeMounts:
             - mountPath: /mnt
               name: data
@@ -217,6 +243,78 @@ INFO ballista_scheduler::scheduler_server::grpc: Received 
register_executor requ
 INFO ballista_scheduler::scheduler_server::grpc: Received register_executor 
request for ExecutorMetadata { id: "816e4502-a876-4ed8-b33f-86d243dcf63f", 
host: "10.1.23.150", port: 50051 }
 ```
 
+## Health Probes and Rolling Upgrades
+
+Both the scheduler and executor expose two Kubernetes-style HTTP probe 
endpoints:
+
+- **`/healthz`** — liveness. Always returns `200 OK` while the process is
+  running. Failing this signals only "the process is dead," which is the sole
+  case where a restart can actually recover. Do **not** make liveness reflect
+  scheduler connectivity — a flapping scheduler would then take the whole
+  executor fleet down with it in a restart cascade.
+- **`/readyz`** — readiness. Returns `200 OK` when the pod is prepared to
+  accept traffic (scheduler: at least `--min-ready-executors` executors
+  registered; executor: last heartbeat to the scheduler succeeded).
+  A failing `/readyz` removes the pod from `Service` endpoints but does
+  **not** trigger a restart, so it is safe to gate on cluster state.
+
+The scheduler serves both probes on the same port as its gRPC/REST API
+(default `50050`). The executor serves them on a dedicated HTTP port
+(`--bind-health-port`, default `50053`) since the Arrow Flight port is not
+HTTP.
+
+### Protocol version and forced upgrades
+
+Every executor sends a compile-time `BALLISTA_PROTOCOL_VERSION` on
+registration and heartbeat. The scheduler compares against its own compiled
+value and rejects mismatches with `FailedPrecondition`; the executor then
+flips `/readyz` off and, after several consecutive failures, self-terminates
+so the operator (or Deployment controller) can bring a matching version up.
+
+Bump `BALLISTA_PROTOCOL_VERSION` only on releases that change the
+executor↔scheduler wire format. Most releases will not bump it.
+
+### Rolling both Deployments together
+
+The scheduler and executor live in separate Deployments; there is no single
+"app roll." When you cut a release that bumps `BALLISTA_PROTOCOL_VERSION`,
+update **both** Deployments' image tags in the same change and apply them
+together. Kubernetes will roll each Deployment independently under its own
+rollout policy, but the fleet stays serving throughout:
+
+| scheduler ↔ executor | outcome                                               
                       |
+| -------------------- | 
---------------------------------------------------------------------------- |
+| old ↔ old            | works (old scheduler pre-dates the check)             
                       |
+| old ↔ new            | works (old scheduler ignores the unknown field)       
                       |
+| new ↔ new            | works                                                 
                       |
+| new ↔ old            | rejected — new scheduler fails `/readyz` until enough 
new executors register |
+
+Because the _old_ scheduler is permissive (it never learned to check the
+field), executors that reach it during the transition are always accepted.
+The rejection happens only when a _new_ scheduler sees an _old_ executor,
+which is the case the version check is designed to protect against —
+mismatched pairs never exchange work, so they cannot corrupt state.
+
+Give each Deployment a `maxSurge` so Kubernetes brings new pods up before
+tearing old ones down; this keeps executor capacity at roughly 100% during
+the roll:
+
+```yaml
+spec:
+  strategy:
+    type: RollingUpdate
+    rollingUpdate:
+      maxSurge: 25%
+      maxUnavailable: 0
+```
+
+The scheduler's `/readyz` gate (`--min-ready-executors`, default `1`) means
+a fresh scheduler pod does not receive `Service` traffic until at least one
+matching-version executor has registered with it, so clients keep hitting
+the old scheduler until the new one has real capacity behind it. Set
+`--min-ready-executors=0` if you want the scheduler to advertise readiness
+immediately (e.g. single-node development clusters).
+
 ## Port Forwarding
 
 If you want to run applications outside of the cluster and have them connect 
to the scheduler then it is necessary to
diff --git a/examples/examples/mtls-cluster.rs 
b/examples/examples/mtls-cluster.rs
index d5fe89748..3533019b9 100644
--- a/examples/examples/mtls-cluster.rs
+++ b/examples/examples/mtls-cluster.rs
@@ -54,6 +54,7 @@ use std::process::Command;
 use std::sync::Arc;
 
 use arrow_flight::flight_service_server::FlightServiceServer;
+use ballista_core::BALLISTA_PROTOCOL_VERSION;
 use ballista_core::ConfigProducer;
 use ballista_core::extension::{SessionConfigExt, SessionStateExt};
 use ballista_core::serde::protobuf::executor_resource::Resource;
@@ -367,6 +368,7 @@ async fn run_executor() -> Result<(), Box<dyn 
std::error::Error>> {
             }],
         }),
         os_info: None,
+        ballista_protocol_version: BALLISTA_PROTOCOL_VERSION,
     };
 
     let config_producer = create_tls_config_producer(tls.client_tls.clone());
@@ -429,8 +431,9 @@ async fn run_executor() -> Result<(), Box<dyn 
std::error::Error>> {
     // Run the pull-based execution loop
     // This registers the executor and starts polling for tasks
     info!("Starting execution poll loop...");
+    let health = ballista_executor::health::ExecutorHealth::new();
     let poll_handle = tokio::spawn(async move {
-        execution_loop::poll_loop(scheduler, executor, codec).await
+        execution_loop::poll_loop(scheduler, executor, codec, health).await
     });
 
     tokio::select! {
diff --git a/python/Cargo.lock b/python/Cargo.lock
index 0cb9fe594..a0544afc6 100644
--- a/python/Cargo.lock
+++ b/python/Cargo.lock
@@ -39,9 +39,9 @@ checksum = 
"cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
 
 [[package]]
 name = "alloc-stdlib"
-version = "0.2.2"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
 dependencies = [
  "alloc-no-stdlib",
 ]
@@ -63,24 +63,24 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.102"
+version = "1.0.104"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
 
 [[package]]
 name = "ar_archive_writer"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
+checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348"
 dependencies = [
  "object",
 ]
 
 [[package]]
 name = "arc-swap"
-version = "1.9.1"
+version = "1.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
+checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
 dependencies = [
  "rustversion",
 ]
@@ -93,9 +93,9 @@ checksum = 
"76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
 
 [[package]]
 name = "arrayvec"
-version = "0.7.6"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
 
 [[package]]
 name = "arrow"
@@ -167,7 +167,7 @@ dependencies = [
  "flate2",
  "indexmap",
  "liblzma",
- "rand 0.9.4",
+ "rand 0.9.5",
  "serde",
  "serde_json",
  "snap",
@@ -407,13 +407,13 @@ checksum = 
"f4de21c0feef7e5a556e51af767c953f0501f7f300ba785cc99c47bdc8081a50"
 
 [[package]]
 name = "async-trait"
-version = "0.1.89"
+version = "0.1.91"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
+checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.0",
 ]
 
 [[package]]
@@ -611,9 +611,9 @@ dependencies = [
 
 [[package]]
 name = "bitflags"
-version = "2.11.1"
+version = "2.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
 
 [[package]]
 name = "blake2"
@@ -649,18 +649,18 @@ dependencies = [
 
 [[package]]
 name = "block-buffer"
-version = "0.12.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
+checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
 dependencies = [
  "hybrid-array",
 ]
 
 [[package]]
 name = "brotli"
-version = "8.0.2"
+version = "8.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560"
+checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -669,9 +669,9 @@ dependencies = [
 
 [[package]]
 name = "brotli-decompressor"
-version = "5.0.0"
+version = "5.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03"
+checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
 dependencies = [
  "alloc-no-stdlib",
  "alloc-stdlib",
@@ -691,15 +691,15 @@ checksum = 
"1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "bytes"
-version = "1.11.1"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
 
 [[package]]
 name = "bytesize"
-version = "2.3.1"
+version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
+checksum = "3d7c8918969267b2932ffd5655509bbbea0833823058c378876953217f5fc50e"
 
 [[package]]
 name = "bzip2"
@@ -712,9 +712,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.62"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
+checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
 dependencies = [
  "find-msvc-tools",
  "jobserver",
@@ -730,15 +730,15 @@ checksum = 
"9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
 
 [[package]]
 name = "cfg_aliases"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
 
 [[package]]
 name = "chacha20"
-version = "0.10.0"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
+checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
 dependencies = [
  "cfg-if",
  "cpufeatures",
@@ -747,9 +747,9 @@ dependencies = [
 
 [[package]]
 name = "chrono"
-version = "0.4.44"
+version = "0.4.45"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
+checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
 dependencies = [
  "iana-time-zone",
  "num-traits",
@@ -800,9 +800,9 @@ checksum = 
"cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789"
 
 [[package]]
 name = "console"
-version = "0.16.3"
+version = "0.16.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
+checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
 dependencies = [
  "encode_unicode",
  "libc",
@@ -892,9 +892,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.21"
+version = "0.8.22"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
 
 [[package]]
 name = "crunchy"
@@ -1134,7 +1134,7 @@ dependencies = [
  "log",
  "object_store",
  "parking_lot",
- "rand 0.9.4",
+ "rand 0.9.5",
  "tokio",
  "tokio-util",
  "url",
@@ -1284,7 +1284,7 @@ dependencies = [
  "log",
  "object_store",
  "parking_lot",
- "rand 0.9.4",
+ "rand 0.9.5",
  "tempfile",
  "url",
 ]
@@ -1383,7 +1383,7 @@ dependencies = [
  "md-5 0.11.0",
  "memchr",
  "num-traits",
- "rand 0.9.4",
+ "rand 0.9.5",
  "regex",
  "sha2",
  "uuid",
@@ -1498,7 +1498,7 @@ checksum = 
"587164e03ad68732aa9e7bfe5686e3f25970d4c64fd4bd80790749840892dae5"
 dependencies = [
  "datafusion-doc",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -1763,7 +1763,7 @@ dependencies = [
  "log",
  "num-traits",
  "percent-encoding",
- "rand 0.9.4",
+ "rand 0.9.5",
  "serde_json",
  "sha1",
  "sha2",
@@ -1807,20 +1807,20 @@ version = "0.11.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
 dependencies = [
- "block-buffer 0.12.0",
+ "block-buffer 0.12.1",
  "const-oid",
  "crypto-common 0.2.2",
 ]
 
 [[package]]
 name = "displaydoc"
-version = "0.2.5"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -1919,9 +1919,9 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
+checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -1934,9 +1934,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
+checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1944,15 +1944,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
+checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -1961,38 +1961,38 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
+checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
+checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
 
 [[package]]
 name = "futures-task"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
 
 [[package]]
 name = "futures-util"
-version = "0.3.32"
+version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -2035,25 +2035,23 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
 dependencies = [
  "cfg-if",
- "js-sys",
  "libc",
  "r-efi 5.3.0",
  "wasip2",
- "wasm-bindgen",
 ]
 
 [[package]]
 name = "getrandom"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
 dependencies = [
  "cfg-if",
+ "js-sys",
  "libc",
  "r-efi 6.0.0",
  "rand_core 0.10.1",
- "wasip2",
- "wasip3",
+ "wasm-bindgen",
 ]
 
 [[package]]
@@ -2064,9 +2062,9 @@ checksum = 
"0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
 
 [[package]]
 name = "h2"
-version = "0.4.14"
+version = "0.4.15"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733"
+checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
 dependencies = [
  "atomic-waker",
  "bytes",
@@ -2133,9 +2131,9 @@ checksum = 
"7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
 [[package]]
 name = "http"
-version = "1.4.0"
+version = "1.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
+checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
 dependencies = [
  "bytes",
  "itoa",
@@ -2143,9 +2141,9 @@ dependencies = [
 
 [[package]]
 name = "http-body"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
 dependencies = [
  "bytes",
  "http",
@@ -2153,9 +2151,9 @@ dependencies = [
 
 [[package]]
 name = "http-body-util"
-version = "0.1.3"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
 dependencies = [
  "bytes",
  "futures-core",
@@ -2178,24 +2176,24 @@ checksum = 
"df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
 
 [[package]]
 name = "humantime"
-version = "2.3.0"
+version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
+checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15"
 
 [[package]]
 name = "hybrid-array"
-version = "0.4.12"
+version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
+checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
 dependencies = [
  "typenum",
 ]
 
 [[package]]
 name = "hyper"
-version = "1.9.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
+checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
 dependencies = [
  "atomic-waker",
  "bytes",
@@ -2371,12 +2369,6 @@ dependencies = [
  "zerovec",
 ]
 
-[[package]]
-name = "id-arena"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
-
 [[package]]
 name = "idna"
 version = "1.1.0"
@@ -2406,8 +2398,6 @@ checksum = 
"d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
 dependencies = [
  "equivalent",
  "hashbrown 0.17.1",
- "serde",
- "serde_core",
 ]
 
 [[package]]
@@ -2460,32 +2450,25 @@ checksum = 
"8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
 
 [[package]]
 name = "jobserver"
-version = "0.1.34"
+version = "0.1.35"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
 dependencies = [
- "getrandom 0.3.4",
+ "getrandom 0.4.3",
  "libc",
 ]
 
 [[package]]
 name = "js-sys"
-version = "0.3.99"
+version = "0.3.103"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
+checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
 dependencies = [
  "cfg-if",
  "futures-util",
- "once_cell",
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "leb128fmt"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
-
 [[package]]
 name = "lexical-core"
 version = "1.0.6"
@@ -2567,18 +2550,18 @@ dependencies = [
 
 [[package]]
 name = "liblzma"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b6033b77c21d1f56deeae8014eb9fbe7bdf1765185a6c508b5ca82eeaed7f899"
+checksum = "45aec2360b3933207e27908049d8e4df4e476b58180afb1e56b2a4fb72efe4ba"
 dependencies = [
  "liblzma-sys",
 ]
 
 [[package]]
 name = "liblzma-sys"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1a60851d15cd8c5346eca4ab8babff585be2ae4bc8097c067291d3ffe2add3b6"
+checksum = "a046c7f353ba30f810545151e04f63545833803f5b86ee3ddf1517247fe560a5"
 dependencies = [
  "cc",
  "libc",
@@ -2670,9 +2653,9 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.8.0"
+version = "2.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
 
 [[package]]
 name = "memory-stats"
@@ -2702,9 +2685,9 @@ dependencies = [
 
 [[package]]
 name = "mio"
-version = "1.2.0"
+version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
+checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
 dependencies = [
  "libc",
  "wasi",
@@ -2728,9 +2711,9 @@ dependencies = [
 
 [[package]]
 name = "num-bigint"
-version = "0.4.6"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
 dependencies = [
  "num-integer",
  "num-traits",
@@ -2971,7 +2954,7 @@ checksum = 
"c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -2988,9 +2971,9 @@ checksum = 
"19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
 
 [[package]]
 name = "portable-atomic"
-version = "1.13.1"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
 
 [[package]]
 name = "potential_utf"
@@ -3017,7 +3000,7 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
 dependencies = [
  "proc-macro2",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3050,9 +3033,9 @@ dependencies = [
 
 [[package]]
 name = "prost-build"
-version = "0.14.3"
+version = "0.14.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7"
+checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042"
 dependencies = [
  "heck",
  "itertools 0.14.0",
@@ -3065,7 +3048,7 @@ dependencies = [
  "pulldown-cmark",
  "pulldown-cmark-to-cmark",
  "regex",
- "syn",
+ "syn 2.0.119",
  "tempfile",
 ]
 
@@ -3079,7 +3062,7 @@ dependencies = [
  "itertools 0.14.0",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3218,7 +3201,7 @@ dependencies = [
  "proc-macro2",
  "pyo3-macros-backend",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3231,7 +3214,7 @@ dependencies = [
  "proc-macro2",
  "pyo3-build-config 0.28.3",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3255,9 +3238,9 @@ dependencies = [
 
 [[package]]
 name = "quinn"
-version = "0.11.9"
+version = "0.11.11"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
+checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
 dependencies = [
  "bytes",
  "cfg_aliases",
@@ -3275,14 +3258,15 @@ dependencies = [
 
 [[package]]
 name = "quinn-proto"
-version = "0.11.14"
+version = "0.11.16"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
+checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
 dependencies = [
  "bytes",
- "getrandom 0.3.4",
+ "getrandom 0.4.3",
  "lru-slab",
- "rand 0.9.4",
+ "rand 0.10.2",
+ "rand_pcg",
  "ring",
  "rustc-hash",
  "rustls",
@@ -3296,23 +3280,23 @@ dependencies = [
 
 [[package]]
 name = "quinn-udp"
-version = "0.5.14"
+version = "0.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
 dependencies = [
  "cfg_aliases",
  "libc",
  "once_cell",
  "socket2",
  "tracing",
- "windows-sys 0.60.2",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.45"
+version = "1.0.46"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
 dependencies = [
  "proc-macro2",
 ]
@@ -3331,9 +3315,9 @@ checksum = 
"f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
 
 [[package]]
 name = "rand"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
+checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
 dependencies = [
  "rand_chacha",
  "rand_core 0.9.5",
@@ -3346,7 +3330,7 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
 dependencies = [
  "chacha20",
- "getrandom 0.4.2",
+ "getrandom 0.4.3",
  "rand_core 0.10.1",
 ]
 
@@ -3375,6 +3359,15 @@ version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
 
+[[package]]
+name = "rand_pcg"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
+dependencies = [
+ "rand_core 0.10.1",
+]
+
 [[package]]
 name = "recursive"
 version = "0.1.1"
@@ -3392,7 +3385,7 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b"
 dependencies = [
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3406,9 +3399,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.12.3"
+version = "1.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -3418,9 +3411,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.14"
+version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -3429,9 +3422,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.10"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
+checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
 
 [[package]]
 name = "reqwest"
@@ -3491,9 +3484,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-hash"
-version = "2.1.2"
+version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
+checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
 
 [[package]]
 name = "rustc_version"
@@ -3519,9 +3512,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.23.40"
+version = "0.23.42"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
+checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
 dependencies = [
  "once_cell",
  "ring",
@@ -3533,9 +3526,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-native-certs"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
+checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
 dependencies = [
  "openssl-probe",
  "rustls-pki-types",
@@ -3545,9 +3538,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-pki-types"
-version = "1.14.1"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
+checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046"
 dependencies = [
  "web-time",
  "zeroize",
@@ -3566,9 +3559,9 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.22"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
 
 [[package]]
 name = "ryu"
@@ -3637,9 +3630,9 @@ checksum = 
"1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
 
 [[package]]
 name = "serde"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
 dependencies = [
  "serde_core",
  "serde_derive",
@@ -3647,22 +3640,22 @@ dependencies = [
 
 [[package]]
 name = "serde_core"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.0",
 ]
 
 [[package]]
@@ -3731,9 +3724,9 @@ checksum = 
"5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9"
 
 [[package]]
 name = "shlex"
-version = "1.3.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
 
 [[package]]
 name = "signal-hook-registry"
@@ -3747,9 +3740,9 @@ dependencies = [
 
 [[package]]
 name = "simd-adler32"
-version = "0.3.9"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
 
 [[package]]
 name = "simdutf8"
@@ -3777,21 +3770,21 @@ checksum = 
"0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
 
 [[package]]
 name = "smallvec"
-version = "1.15.1"
+version = "1.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
 
 [[package]]
 name = "snap"
-version = "1.1.1"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b"
+checksum = "199905e6153d6405f9728fe44daace35f8f837bbf830bb6e85fbd5828709a886"
 
 [[package]]
 name = "socket2"
-version = "0.6.3"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
+checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
 dependencies = [
  "libc",
  "windows-sys 0.61.2",
@@ -3816,7 +3809,7 @@ checksum = 
"a6dd45d8fc1c79299bfbb7190e42ccbbdf6a5f52e4a6ad98d92357ea965bd289"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3850,7 +3843,7 @@ dependencies = [
  "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3881,7 +3874,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3892,9 +3885,20 @@ checksum = 
"13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "syn"
-version = "2.0.117"
+version = "2.0.119"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+checksum = "f2fac314a64dc9a36e61a9eb4261a5e9bbfbc922b27e518af97bc32b926cf967"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3918,7 +3922,7 @@ checksum = 
"728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -3948,7 +3952,7 @@ source = 
"registry+https://github.com/rust-lang/crates.io-index";
 checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
 dependencies = [
  "fastrand",
- "getrandom 0.4.2",
+ "getrandom 0.4.3",
  "once_cell",
  "rustix",
  "windows-sys 0.61.2",
@@ -3956,22 +3960,22 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "2.0.18"
+version = "2.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.18"
+version = "2.0.19"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 3.0.0",
 ]
 
 [[package]]
@@ -4006,9 +4010,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.11.0"
+version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
+checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -4038,13 +4042,13 @@ dependencies = [
 
 [[package]]
 name = "tokio-macros"
-version = "2.7.0"
+version = "2.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
+checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -4093,9 +4097,9 @@ dependencies = [
 
 [[package]]
 name = "toml_edit"
-version = "0.25.12+spec-1.1.0"
+version = "0.25.13+spec-1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
+checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
 dependencies = [
  "indexmap",
  "toml_datetime",
@@ -4150,7 +4154,7 @@ dependencies = [
  "prettyplease",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -4175,7 +4179,7 @@ dependencies = [
  "prost-build",
  "prost-types",
  "quote",
- "syn",
+ "syn 2.0.119",
  "tempfile",
  "tonic-build",
 ]
@@ -4264,7 +4268,7 @@ checksum = 
"7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -4288,14 +4292,14 @@ version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c"
 dependencies = [
- "rand 0.9.4",
+ "rand 0.9.5",
 ]
 
 [[package]]
 name = "typenum"
-version = "1.20.0"
+version = "1.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
 
 [[package]]
 name = "unicase"
@@ -4311,9 +4315,9 @@ checksum = 
"e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.13.2"
+version = "1.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
+checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
 
 [[package]]
 name = "unicode-width"
@@ -4321,12 +4325,6 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
 
-[[package]]
-name = "unicode-xid"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
-
 [[package]]
 name = "untrusted"
 version = "0.9.0"
@@ -4357,7 +4355,7 @@ version = "1.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
 dependencies = [
- "getrandom 0.4.2",
+ "getrandom 0.4.3",
  "js-sys",
  "wasm-bindgen",
 ]
@@ -4395,27 +4393,18 @@ checksum = 
"ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 
 [[package]]
 name = "wasip2"
-version = "1.0.3+wasi-0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
-dependencies = [
- "wit-bindgen 0.57.1",
-]
-
-[[package]]
-name = "wasip3"
-version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+version = "1.0.4+wasi-0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
 dependencies = [
- "wit-bindgen 0.51.0",
+ "wit-bindgen",
 ]
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
+checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -4426,9 +4415,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.72"
+version = "0.4.76"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f"
+checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -4436,9 +4425,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
+checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -4446,48 +4435,26 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
+checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
 dependencies = [
  "bumpalo",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.122"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
+checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
 dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "wasm-encoder"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
-dependencies = [
- "leb128fmt",
- "wasmparser",
-]
-
-[[package]]
-name = "wasm-metadata"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
-dependencies = [
- "anyhow",
- "indexmap",
- "wasm-encoder",
- "wasmparser",
-]
-
 [[package]]
 name = "wasm-streams"
 version = "0.4.2"
@@ -4501,23 +4468,11 @@ dependencies = [
  "web-sys",
 ]
 
-[[package]]
-name = "wasmparser"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
-dependencies = [
- "bitflags",
- "hashbrown 0.15.5",
- "indexmap",
- "semver",
-]
-
 [[package]]
 name = "web-sys"
-version = "0.3.99"
+version = "0.3.103"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436"
+checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -4617,7 +4572,7 @@ checksum = 
"053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -4628,7 +4583,7 @@ checksum = 
"3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -4671,16 +4626,7 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.60.2"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
-dependencies = [
- "windows-targets 0.53.5",
+ "windows-targets",
 ]
 
 [[package]]
@@ -4698,31 +4644,14 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm 0.52.6",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.53.5"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
-dependencies = [
- "windows-link",
- "windows_aarch64_gnullvm 0.53.1",
- "windows_aarch64_msvc 0.53.1",
- "windows_i686_gnu 0.53.1",
- "windows_i686_gnullvm 0.53.1",
- "windows_i686_msvc 0.53.1",
- "windows_x86_64_gnu 0.53.1",
- "windows_x86_64_gnullvm 0.53.1",
- "windows_x86_64_msvc 0.53.1",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
 ]
 
 [[package]]
@@ -4740,199 +4669,63 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
-
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
-
 [[package]]
 name = "windows_i686_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
 
-[[package]]
-name = "windows_i686_gnu"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
-
 [[package]]
 name = "windows_i686_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
-
 [[package]]
 name = "windows_i686_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
-[[package]]
-name = "windows_i686_msvc"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
-
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
-
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
-
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.53.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
-
 [[package]]
 name = "winnow"
-version = "1.0.3"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
+checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
 dependencies = [
  "memchr",
 ]
 
-[[package]]
-name = "wit-bindgen"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
-dependencies = [
- "wit-bindgen-rust-macro",
-]
-
 [[package]]
 name = "wit-bindgen"
 version = "0.57.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
 
-[[package]]
-name = "wit-bindgen-core"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
-dependencies = [
- "anyhow",
- "heck",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-bindgen-rust"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
-dependencies = [
- "anyhow",
- "heck",
- "indexmap",
- "prettyplease",
- "syn",
- "wasm-metadata",
- "wit-bindgen-core",
- "wit-component",
-]
-
-[[package]]
-name = "wit-bindgen-rust-macro"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
-dependencies = [
- "anyhow",
- "prettyplease",
- "proc-macro2",
- "quote",
- "syn",
- "wit-bindgen-core",
- "wit-bindgen-rust",
-]
-
-[[package]]
-name = "wit-component"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
-dependencies = [
- "anyhow",
- "bitflags",
- "indexmap",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "wasm-encoder",
- "wasm-metadata",
- "wasmparser",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap",
- "log",
- "semver",
- "serde",
- "serde_derive",
- "serde_json",
- "unicode-xid",
- "wasmparser",
-]
-
 [[package]]
 name = "writeable"
 version = "0.6.3"
@@ -4941,9 +4734,9 @@ checksum = 
"1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
 
 [[package]]
 name = "yoke"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
+checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
 dependencies = [
  "stable_deref_trait",
  "yoke-derive",
@@ -4958,28 +4751,28 @@ checksum = 
"de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
  "synstructure",
 ]
 
 [[package]]
 name = "zerocopy"
-version = "0.8.48"
+version = "0.8.54"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
+checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
 dependencies = [
  "zerocopy-derive",
 ]
 
 [[package]]
 name = "zerocopy-derive"
-version = "0.8.48"
+version = "0.8.54"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
+checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
@@ -4999,15 +4792,15 @@ checksum = 
"11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
  "synstructure",
 ]
 
 [[package]]
 name = "zeroize"
-version = "1.8.2"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
 
 [[package]]
 name = "zerotrie"
@@ -5039,20 +4832,20 @@ checksum = 
"625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.119",
 ]
 
 [[package]]
 name = "zlib-rs"
-version = "0.6.3"
+version = "0.6.6"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
+checksum = "b142a20ec14a91d5bc708c1dc21b080c550113d8aa77afa29635673a65dd02c5"
 
 [[package]]
 name = "zmij"
-version = "1.0.21"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
 
 [[package]]
 name = "zstd"


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to