diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..6c79fb9
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.DS_Store
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/charts
+**/docker-compose*
+**/compose.y*ml
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/secrets.dev.yaml
+**/values.dev.yaml
+/bin
+/target
+LICENSE
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..b87f06b
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,50 @@
+# DockerHub image settings
+DOCKERHUB_USERNAME=hbwei
+IMAGE_TAG=v0.0.1
+
+# Host user/group IDs — set these to match your host user (id -u / id -g)
+# so that bind-mounted files are accessible inside containers.
+HOST_UID=1000
+HOST_GID=1000
+
+# quic client-side only
+# Same-host: use Docker bridge gateway (10.64.89.1). Remote: use server host's routable IP.
+QUIC_CLIENT_REMOTE_ADDR=10.64.89.1:12345
+
+QUIC_DOCKER_CLIENT_CONFIG_PATH=./docker/config/quic_config_client_docker.yaml
+
+# quic server-side only
+QUIC_SERVER_ADDR=0.0.0.0:12345
+QUIC_SERVER_PORT=12345
+
+QUIC_DOCKER_SERVER_CONFIG_PATH=./docker/config/quic_config_gcloud_docker.yaml
+
+# python client config
+PYTHON_DOCKER_CLIENT_CONFIG_PATH=./docker/config/client_config_docker.yaml
+
+# python server config
+PYTHON_DOCKER_SERVER_CONFIG_PATH=./docker/config/server_config_gcloud_docker.yaml
+
+# both client and server-side
+DOCKER_LOG_CONFIG_PATH=./docker/config/logging_config_docker.yaml
+EXPERIMENT_OUTPUT_DIR=/path/to/experiment2-out
+
+# client-side only (required even in mock camera mode — bandwidth allocator needs eval data)
+DASHBOARD_PORT=5000
+MODEL_FULL_EVAL_DIR=/path/to/full-eval
+
+# server-side only (not needed in mock inference mode, but must point to a valid directory — an empty one is fine)
+EFFDET_MODELS_DIR=/path/to/av-models
+
+# rust env vars
+RUST_LOG=info
+RUST_BACKTRACE=full
+
+# for both client and server
+EXPERIMENT_MODEL_INFO_PATH=./experiment_model_info.csv
+MOCK_EFFDET_OUTPUT_PATH=./src/python/camera_stream/example_effdet_d4_output.npy
+MOCK_EFFDET_INPUT_PATH=./src/python/camera_stream/mock_webcam_image.jpg
+
+# Mock modes — set to any non-empty value (e.g. "true") to enable, leave empty to disable
+MOCK_CAMERA=
+MOCK_INFERENCE=
diff --git a/.gitignore b/.gitignore
index b5956bd..db26615 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,7 +36,6 @@ bandwidth_allocation/.DS_Store
**/.vscode/*
**/target/*
-**/Cargo.lock
# we don't want your SSL certs/keys
**/*.key
diff --git a/README.md b/README.md
index 7dcc8e6..9ecdd77 100644
--- a/README.md
+++ b/README.md
@@ -91,56 +91,60 @@ Running on a GPU-equipped cloud instance (e.g., H100):
For detailed architecture, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
-## Quick Start
+## Quick Start (Docker with Pre-Built Images) — Recommended
+
+The recommended way to run TURBO is with Docker using pre-built container images published on [DockerHub](https://hub.docker.com/u/hbwei). The Docker setup automatically orchestrates all processes — 2 on the server (QUIC server + model servers) and 3 on the client (client orchestrator + web dashboard + QUIC client) — handling startup ordering, ZMQ socket management, and inter-process communication for you.
+
+> **Other setup methods:** To build Docker images from source instead of using pre-built images, see [Alternative 1: Docker Building from Source](#alternative-1-docker-building-from-source). To run without Docker at all, see [Alternative 2: Manual Setup](#alternative-2-manual-setup-without-docker).
### Prerequisites
-**Client (AV) side:**
-- Python 3.10; preferably managed via [uv](https://docs.astral.sh/uv/) (alternatively, via [Anaconda](https://anaconda.org/), specifically the [`Miniconda3-py310_25.11.1-1` release version on this page](https://repo.anaconda.com/miniconda/))
-- Rust 1.70+ (for QUIC transport)
-- USB webcams (or video sources)
+- [Docker Engine](https://docs.docker.com/engine/install/) 24.0+ with the [Docker Compose V2 plugin](https://docs.docker.com/compose/install/linux/) (see notes below). **Avoid Docker 28** — it seems to be incompatible with `nf_tables` for iptables, which can cause networking issues with container-to-container communication. Docker 27 is recommended.
+- [NVIDIA GPU drivers](https://www.nvidia.com/en-us/drivers/) and [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) (**server only** — needed for GPU inference; not required on the client, or if using [mock inference mode](#mock-modes))
+- USB webcams (**client only** — or use [mock camera mode](#mock-modes) for testing without cameras)
- Linux (tested on Ubuntu 20.04+)
+- Disk space:
+ - **Client:** ~50 GB (7 GB for evaluation data; 10 GB for Docker images; ~30 GB recommended minimum for experiment output). Note: evaluation data is required even in [mock camera mode](#mock-modes) — the bandwidth allocator always needs it for utility curve computation.
+ - **Server:** ~13 GB (2 GB for model checkpoints; 10 GB for Docker images; 1 GB recommended for experiment output). With [mock inference mode](#mock-modes), model checkpoints are not needed, reducing this to ~11 GB.
-**Server (Cloud) side:**
-- Python 3.10; preferably managed via [uv](https://docs.astral.sh/uv/) (alternatively, via [Anaconda](https://anaconda.org/), specifically the [`Miniconda3-py310_25.11.1-1` release version on this page](https://repo.anaconda.com/miniconda/))
-- CUDA-capable GPU (tested on H100, A100)
-- PyTorch 2.0+
-- Rust 1.70+ (for QUIC transport)
-- Fine-tuned EfficientDet model checkpoints (see [Model Setup](#model-setup) below)
-- Needed dependencies for `OpenCV` -- (e.g. `sudo apt-get update && sudo apt-get install ffmpeg libsm6 libxext6`)
+> **No GPU?** You can run the server side without a GPU by setting `MOCK_INFERENCE=true` in your `.env` file and omitting the `-f compose.gpu.yaml` override. This skips GPU model loading and returns pre-recorded detection results. See [Mock Modes](#mock-modes) for details. If using mock inference, you can skip the NVIDIA Container Toolkit prerequisite and the model checkpoint download (step 2), but you must still set `EFFDET_MODELS_DIR` to a valid (possibly empty) directory since Docker bind-mounts it unconditionally — e.g., `mkdir -p ~/av-models`.
-### Installation
+> **Docker Compose V2:** This project requires the Docker Compose V2 *plugin* (the `docker compose` subcommand), not the legacy standalone `docker-compose` binary. If `docker compose version` shows an error or is not found, install the plugin following the [official instructions](https://docs.docker.com/compose/install/linux/). On Ubuntu: `sudo apt-get install docker-compose-plugin`.
+
+Verify your setup:
+```bash
+docker compose version # should be v2.20+
+
+# Server only — skip these if running client only or using mock inference
+nvidia-smi # should show your GPU(s)
+docker run --rm --gpus all nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi # GPU in Docker
+```
+
+### Setup
+
+> **Running on separate machines?** If you are running the client and server on different hosts, perform steps 1, 4, and 5 on **both** machines. Step 2 is server-only and step 3 is client-only.
-1. **Clone the repository and install dependencies:**
+1. **Clone the repository (both client and server):**
```bash
git clone https://github.com/NetSys/turbo.git
cd turbo
- uv sync
```
-
- Alternative: using pip
+2. **Download fine-tuned EfficientDet model checkpoints (server only):**
- ```bash
- pip install .
- ```
-
+ The system uses custom EfficientDet models (D1, D2, D4, D6, D7x) fine-tuned on the [Waymo Open Dataset](https://waymo.com/open/) for 5-class object detection (vehicle, pedestrian, cyclist, sign, unknown).
-2. **Download fine-tuned EfficientDet model checkpoints:**
-
- The system uses custom EfficientDet models (D1, D2, D4, D6, D7x) fine-tuned on the [Waymo Open Dataset](https://waymo.com/open/) for 5-class object detection (vehicle, pedestrian, cyclist, sign, unknown).
-
Our fine-tuned models can be downloaded and extracted as follows:
```bash
# Download the model archive
wget https://storage.googleapis.com/turbo-nines-2026/av-models.zip
- # Extract to a location of your choice (e.g., ~/av-models in this example)
+ # Extract to your home directory (creates ~/av-models/)
unzip av-models.zip -d ~
```
- After extraction, update the checkpoint paths in your server configuration file (`config/server_config_gcloud.yaml`) and model config (`src/python/model_server/model_config.yaml`) to point to the extracted checkpoint files. See [docs/MODELS.md](docs/MODELS.md) for detailed model information and configuration.
+ See [docs/MODELS.md](docs/MODELS.md) for detailed model information.
> **IMPORTANT — Waymo Open Dataset License Notice**
>
@@ -152,6 +156,139 @@ For detailed architecture, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
>
> These models were made using the Waymo Open Dataset, provided by Waymo LLC.
+3. **Download pre-computed evaluation data (client only):**
+
+ The client requires pre-computed full evaluation data for utility curve computation. Download and extract as follows:
+
+ ```bash
+ # Download the evaluation data archive
+ wget https://storage.googleapis.com/turbo-nines-2026/full-eval.zip
+
+ # Extract to your home directory (creates ~/full-eval/)
+ unzip full-eval.zip -d ~
+ ```
+
+4. **Configure the `.env` file (both client and server):**
+
+ ```bash
+ cp .env.example .env
+ ```
+
+ Edit `.env` and update the following values to match your host system:
+
+ | Variable | Description | Quickstart value |
+ |---|---|---|
+ | `HOST_UID` | Your host user ID (run `id -u`) | `1000` |
+ | `HOST_GID` | Your host group ID (run `id -g`) | `1000` |
+ | `EXPERIMENT_OUTPUT_DIR` | Absolute path for experiment output | `~/experiment2-out` |
+ | `EFFDET_MODELS_DIR` | Absolute path to model checkpoints (server) | `~/av-models` |
+ | `MODEL_FULL_EVAL_DIR` | Absolute path to evaluation data (client) | `~/full-eval` |
+
+ If you followed the download steps above, set `EFFDET_MODELS_DIR=~/av-models` and `MODEL_FULL_EVAL_DIR=~/full-eval`. Most other settings (networking, ports) work out of the box for same-host testing. See [docker/README.Docker.md](docker/README.Docker.md#additional-configuration) for the full configuration reference.
+
+ > **Hardware-specific YAML config changes:** Depending on your hardware, you may also need to edit the Docker YAML config files in [`docker/config/`](docker/config/):
+ > - **GPU device assignment (server):** [`docker/config/server_config_gcloud_docker.yaml`](docker/config/server_config_gcloud_docker.yaml) assigns each of the 3 model services to a separate GPU (`cuda:0`, `cuda:1`, `cuda:2`). If you have fewer than 3 GPUs, update the `device` fields to match your setup (e.g., set all to `"cuda:0"` for a single-GPU machine).
+ > - **USB camera IDs (client):** [`docker/config/client_config_docker.yaml`](docker/config/client_config_docker.yaml) maps cameras to USB device IDs (`usb_id: 0`, `4`, `8`). If using real cameras (not mock mode), update these to match your system's device IDs.
+
+5. **Create the experiment output directory (both client and server):**
+ ```bash
+ mkdir -p ~/experiment2-out
+ ```
+
+### Running
+
+**Pull the pre-built images:**
+```bash
+docker compose --profile client --profile server pull
+```
+
+**GPU setup:** If the server host has NVIDIA GPUs (required for real inference, not needed for [mock inference](#mock-modes)), include the GPU override file by adding `-f compose.gpu.yaml` to all `docker compose` commands. Non-GPU hosts can omit it.
+
+**Run both client and server on the same host (with GPU):**
+```bash
+docker compose -f compose.yaml -f compose.gpu.yaml --profile client --profile server up
+```
+
+**Run server only** (e.g., on a cloud GPU machine):
+```bash
+docker compose -f compose.yaml -f compose.gpu.yaml --profile server up
+```
+
+**Run client only** (when server is running elsewhere — update `QUIC_CLIENT_REMOTE_ADDR` in `.env` to the server's IP):
+```bash
+docker compose --profile client up
+```
+
+**Run server with mock inference (no GPU needed):**
+```bash
+docker compose --profile server up
+```
+
+Once the client is running, open the monitoring dashboard at **http://localhost:5000**.
+
+**Shut down:**
+```bash
+docker compose --profile client --profile server down -v
+```
+
+The `-v` flag removes ephemeral volumes (ZMQ sockets, health signals), giving you a clean slate for the next run.
+
+**Experiment output** will be logged to Parquet files in the configured output directory (default: `~/experiment2-out/`).
+
+For troubleshooting, architecture details, and development workflows, see [docker/README.Docker.md](docker/README.Docker.md).
+
+---
+
+## Alternative 1: Docker Building from Source
+
+If you want to build the Docker images locally instead of using the pre-built images (e.g., for development or customization), see [docker/README.Docker.md](docker/README.Docker.md#building-from-source) for full setup, build, and run instructions. The `docker/` directory contains its own `compose.yaml`, `.env.example`, and Dockerfiles.
+
+---
+
+## Alternative 2: Manual Setup (without Docker)
+
+> **This approach is discouraged.** Manual setup requires installing all dependencies (Python, Rust, system libraries) by hand on both client and server machines, carefully managing process startup order, and manually cleaning up ZMQ sockets and shared memory between runs. The Docker-based methods above handle all of this automatically. Use manual setup only if you have a specific reason to avoid Docker.
+
+
+Click to expand manual setup instructions
+
+Follow the steps below to run each process directly on your host without Docker.
+
+### Prerequisites
+
+**Client (AV) side:**
+- Python 3.10; preferably managed via [uv](https://docs.astral.sh/uv/) (alternatively, via [Anaconda](https://anaconda.org/), specifically the [`Miniconda3-py310_25.11.1-1` release version on this page](https://repo.anaconda.com/miniconda/))
+- Rust 1.70+ (for QUIC transport)
+- USB webcams (or video sources)
+- Needed dependencies for `OpenCV` -- (e.g. `sudo apt-get update && sudo apt-get install ffmpeg libsm6 libxext6`)
+
+**Server (Cloud) side:**
+- Python 3.10; preferably managed via [uv](https://docs.astral.sh/uv/) (alternatively, via [Anaconda](https://anaconda.org/), specifically the [`Miniconda3-py310_25.11.1-1` release version on this page](https://repo.anaconda.com/miniconda/))
+- CUDA-capable GPU (tested on H100, A100)
+- PyTorch 2.0+
+- Rust 1.70+ (for QUIC transport)
+- Needed dependencies for `OpenCV` -- (e.g. `sudo apt-get update && sudo apt-get install ffmpeg libsm6 libxext6`)
+
+### Installation
+
+1. **Install dependencies:**
+ ```bash
+ cd turbo
+ uv sync
+ ```
+
+
+ Alternative: using pip
+
+ ```bash
+ pip install .
+ ```
+
+
+2. **Download model checkpoints and evaluation data** — follow steps 2 and 3 from the [Quick Start](#quick-start-docker-with-pre-built-images--recommended) above.
+
+ After extraction, update the checkpoint paths in your server configuration file (`config/server_config_gcloud.yaml`) and model config (`src/python/model_server/model_config.yaml`) to point to the extracted checkpoint files. Also ensure the `full_eval_dir` path in `config/client_config.yaml` points to the extracted `~/full-eval/` directory.
+
3. **Generate SSL Keys for QUIC:**
```bash
cd src/quic
@@ -188,16 +325,16 @@ For detailed architecture, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
**On the server (cloud) side:**
0. Do the following pre-run steps:
- - If previous runs were done:
+ - If previous runs were done:
- Clear all previous zeromq socket files from any previous runs, if they exist. In this example, just remove all contents of the directory containing the zeromq files:
```bash
rm ~/experiment2-out/zmq/*
```
- - Stash the previous log outputs from any previous runs, if they exist, and make sure the directories for storing the log outputs produced by all parts of this system are empty.
+ - Stash the previous log outputs from any previous runs, if they exist, and make sure the directories for storing the log outputs produced by all parts of this system are empty.
- If this is the first run:
- - Make output directories to store each of the log outputs for your current run.
- - Make output directories to store `ZeroMQ` IPC socket files.
-
+ - Make output directories to store each of the log outputs for your current run.
+ - Make output directories to store `ZeroMQ` IPC socket files.
+
For reference, the author's output directory structure was created as follows:
```bash
@@ -207,7 +344,7 @@ For detailed architecture, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
mkdir ~/experiment2-out/server
mkdir ~/experiment2-out/quic-client-out
mkdir ~/experiment2-out/quic-server-out
- ```
+ ```
1. Start the QUIC server:
```bash
@@ -232,22 +369,22 @@ For detailed architecture, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
```bash
rm ~/experiment2-out/zmq/*
```
- - Stash the previous log outputs from any previous runs, if they exist, and make sure the directories for storing the log outputs produced by all parts of this system are empty.
+ - Stash the previous log outputs from any previous runs, if they exist, and make sure the directories for storing the log outputs produced by all parts of this system are empty.
- If this is the first run:
- Allow ping requests (our PingHandler module needs to send pings from user-land):
- ```bash
+ ```bash
sudo sysctl net.ipv4.ping_group_range='0 2147483647'
```
- - Make output directories to store each of the log outputs for your current run.
- - Make output directories to store `ZeroMQ` IPC socket files.
-
+ - Make output directories to store each of the log outputs for your current run.
+ - Make output directories to store `ZeroMQ` IPC socket files.
+
**IMPORTANT:** The ordering of the following steps matters due to a behavior in ZeroMQ socket binding. See [docs/IPC.md](docs/IPC.md) for details.
1. Start the client processes (in a separate terminal):
```bash
cd src/python
- uv run client_main.py -c ../../config/client_config.yaml
+ uv run client_main.py -c ../../config/client_config.yaml -s
```
(or `python client_main.py ...` if using a pip-installed environment)
@@ -270,6 +407,86 @@ For detailed architecture, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
**Experiment output** will be logged to Parquet files in the configured output directories (default: `~/experiment2-out/`).
+
+
+## Mock Modes
+
+TURBO supports two independent mock modes for testing and development without requiring physical cameras or GPUs:
+
+- **Mock Camera** (client-side): Replaces live USB camera capture with a static image. Note that the `full-eval` evaluation data is still required — the bandwidth allocator uses it for utility curve computation regardless of camera mode.
+- **Mock Inference** (server-side): Skips GPU model loading and returns pre-recorded detection results. Optionally simulates per-model inference latency using a CSV of benchmark timings. The `av-models` model checkpoints are **not needed** in this mode (model loading is completely bypassed), though the Docker bind mount for `EFFDET_MODELS_DIR` still requires an existing directory — an empty one is fine.
+
+### Enabling Mock Modes
+
+Follow the instructions below that correspond to the setup method you used — [Quick Start](#quick-start-docker-with-pre-built-images--recommended), [Alternative 1](#alternative-1-docker-building-from-source), or [Alternative 2](#alternative-2-manual-setup-without-docker).
+
+#### Quick Start: Docker with Pre-Built Images
+
+Set environment variables in `.env` (at the repo root):
+
+```bash
+# Set to any non-empty value (e.g. "true") to enable, leave empty to disable
+MOCK_CAMERA=true
+MOCK_INFERENCE=true
+```
+
+Then run as usual — for example, to run both client and server in full mock mode (no cameras, no GPU):
+
+```bash
+docker compose --profile client --profile server up
+```
+
+Omit `-f compose.gpu.yaml` when using mock inference, since no GPU is needed.
+
+#### Alternative 1: Docker Building from Source
+
+Set environment variables in `docker/.env`:
+
+```bash
+# Set to any non-empty value (e.g. "true") to enable, leave empty to disable
+MOCK_CAMERA=true
+MOCK_INFERENCE=true
+```
+
+Then build and run from the `docker/` directory:
+
+```bash
+cd docker
+docker compose --profile client --profile server up --build
+```
+
+See [docker/README.Docker.md](docker/README.Docker.md#mock-modes) for details.
+
+#### Alternative 2: Manual Setup (without Docker)
+
+Pass CLI flags to the Python entry points:
+
+```bash
+# Client-side: mock camera
+uv run client_main.py -c ../../config/client_config.yaml -s --mock-camera
+
+# Server-side: mock inference
+uv run server_main.py -c ../../config/server_config_gcloud.yaml --mock-inference
+```
+
+### Mock File Configuration
+
+The YAML config files specify which mock files to use; the CLI flag (or Docker env var) controls whether they are actually applied.
+
+- **Mock camera image:** Configured per camera in `camera_stream_config_list` via the `mock_camera_image_path` key. A sample mock image is included at `src/python/camera_stream/mock_webcam_image.jpg`. Without mock camera enabled, this path is ignored and real USB cameras are used.
+- **Mock inference output:** Configured per server in `server_config_list` via `mock_inference_output_path` (numpy array of detections) and `mock_model_latency_csv_path` (per-model latency benchmarks). A sample mock output is included at `src/python/camera_stream/example_effdet_d4_output.npy`. Without mock inference enabled, these paths are ignored and real GPU inference is used.
+
+### Combining Mock Modes
+
+The two mock modes are fully independent — you can use any combination:
+
+| Camera Mock | Inference Mock | Use Case | Data Required |
+|---|---|---|---|
+| Off | Off | **Production** — real cameras, real GPU inference | `full-eval` + `av-models` |
+| On | Off | Test the full pipeline without cameras (still needs GPU) | `full-eval` + `av-models` |
+| Off | On | Test camera capture and transport without GPU | `full-eval` only |
+| On | On | **Full mock** — test the entire system without cameras or GPU | `full-eval` only |
+
## Documentation
- **[Model Setup & Reference](docs/MODELS.md)** - EfficientDet model download, configuration, and inference details
@@ -298,6 +515,14 @@ An LP-based allocator runs every 500ms to select the optimal (model, compression
```
turbo/
+├── compose.yaml # Docker Compose for pre-built images (recommended)
+├── .env.example # Template for configurable paths and settings
+├── docker/ # Building Docker images from source
+│ ├── compose.yaml # Docker Compose for building from source
+│ ├── .env.example # Template for build-from-source settings
+│ ├── config/ # Docker-specific YAML configs (shared by both workflows)
+│ ├── Dockerfile_turbo_* # Multi-stage Dockerfiles
+│ └── README.Docker.md # Build-from-source docs, configuration reference, troubleshooting
├── src/
│ ├── python/
│ │ ├── client_main.py # Client-side process orchestrator
@@ -315,7 +540,7 @@ turbo/
│ ├── quic_client/ # Client binary
│ ├── quic_server/ # Server binary
│ └── quic_conn/ # Shared library (bandwidth management, logging)
-├── config/ # YAML configuration files
+├── config/ # YAML configuration files (manual setup)
└── docs/ # Detailed documentation
```
@@ -333,7 +558,7 @@ turbo/
Planned features and improvements, in addition to accepted GitHub Issues/PRs:
-- [ ] Docker deployment configuration
+- [x] Docker deployment configuration
- [x] Graceful termination of python services
- [x] Graceful handling of Ctrl-C in rust processes (to kill all zmq sockets and shm files, and avoid parquet data loss)
- [ ] Migration to full Rust implementation with Rust Python+numpy bindings;
@@ -369,4 +594,6 @@ If you use this system in your research, please cite:
```
## Contact
-For questions and feedback, open a [GitHub Issue](https://github.com/NetSys/turbo/issues).
\ No newline at end of file
+For questions and feedback, open a [GitHub Issue](https://github.com/NetSys/turbo/issues).
+
+
diff --git a/compose.gpu.yaml b/compose.gpu.yaml
new file mode 100644
index 0000000..4724c1c
--- /dev/null
+++ b/compose.gpu.yaml
@@ -0,0 +1,11 @@
+# GPU override — include this file on hosts with NVIDIA GPUs.
+# Usage: docker compose -f compose.yaml -f compose.gpu.yaml --profile server up
+services:
+ server_python_main:
+ deploy:
+ resources:
+ reservations:
+ devices:
+ - driver: nvidia
+ count: all
+ capabilities: [gpu]
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 0000000..1cd82e9
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,179 @@
+services:
+ quic_client:
+ profiles: ['client']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ user: "${HOST_UID}:${HOST_GID}"
+ image: ${DOCKERHUB_USERNAME}/turbo-quic-client:${IMAGE_TAG:-latest}
+ depends_on:
+ client_python_monitor:
+ condition: service_healthy
+ environment:
+ RUST_LOG: ${RUST_LOG}
+ RUST_BACKTRACE: ${RUST_BACKTRACE}
+ QUIC_BIND_ADDR: ${QUIC_CLIENT_REMOTE_ADDR}
+ volumes:
+ - type: bind
+ source: ${QUIC_DOCKER_CLIENT_CONFIG_PATH}
+ target: /app/quic_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_client:/app/zmq-out
+ - health_signals:/health
+ networks:
+ - quic_net
+
+ quic_server:
+ profiles: ['server']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ user: "${HOST_UID}:${HOST_GID}"
+ image: ${DOCKERHUB_USERNAME}/turbo-quic-server:${IMAGE_TAG:-latest}
+ environment:
+ RUST_LOG: ${RUST_LOG}
+ RUST_BACKTRACE: ${RUST_BACKTRACE}
+ volumes:
+ - type: bind
+ source: ${QUIC_DOCKER_SERVER_CONFIG_PATH}
+ target: /app/quic_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_server:/app/zmq-out
+ ports:
+ - "${QUIC_SERVER_PORT}:${QUIC_SERVER_PORT}/udp"
+
+ client_python_main:
+ profiles: ['client']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ user: "${HOST_UID}:${HOST_GID}"
+ image: ${DOCKERHUB_USERNAME}/turbo-python-client:${IMAGE_TAG:-latest}
+ volumes:
+ - health_signals:/health
+ - type: bind
+ source: ${PYTHON_DOCKER_CLIENT_CONFIG_PATH}
+ target: /app/python_config.yaml
+ read_only: true
+ - type: bind
+ source: ${DOCKER_LOG_CONFIG_PATH}
+ target: /app/logging_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_client:/app/zmq-out
+ - type: bind
+ source: ${MODEL_FULL_EVAL_DIR}
+ target: /app/full-eval
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_MODEL_INFO_PATH}
+ target: /app/experiment_model_info.csv
+ read_only: true
+ - type: bind
+ source: ${MOCK_EFFDET_INPUT_PATH}
+ target: /app/mock_webcam_image.jpg
+ read_only: true
+ command: >
+ bash -c 'cd src/python && exec python client_main.py -c /app/python_config.yaml -s ${QUIC_CLIENT_REMOTE_ADDR} ${MOCK_CAMERA:+--mock-camera}'
+ healthcheck:
+ test: ["CMD", "test", "-f", "/health/client_main_ready"]
+ interval: 2s
+ timeout: 3s
+ start_period: 30s
+ retries: 30
+
+ server_python_main:
+ profiles: ['server']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ user: "${HOST_UID}:${HOST_GID}"
+ image: ${DOCKERHUB_USERNAME}/turbo-python-server:${IMAGE_TAG:-latest}
+ volumes:
+ - type: bind
+ source: ${PYTHON_DOCKER_SERVER_CONFIG_PATH}
+ target: /app/python_config.yaml
+ read_only: true
+ - type: bind
+ source: ${DOCKER_LOG_CONFIG_PATH}
+ target: /app/logging_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_server:/app/zmq-out
+ - type: bind
+ source: ${EFFDET_MODELS_DIR}
+ target: /app/av-models
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_MODEL_INFO_PATH}
+ target: /app/experiment_model_info.csv
+ read_only: true
+ - type: bind
+ source: ${MOCK_EFFDET_OUTPUT_PATH}
+ target: /app/example_effdet_d4_output.npy
+ read_only: true
+ command: >
+ bash -c 'cd src/python && exec python server_main.py -c /app/python_config.yaml ${MOCK_INFERENCE:+--mock-inference}'
+
+ client_python_monitor:
+ profiles: ['client']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ user: "${HOST_UID}:${HOST_GID}"
+ image: ${DOCKERHUB_USERNAME}/turbo-python-monitor:${IMAGE_TAG:-latest}
+ depends_on:
+ client_python_main:
+ condition: service_healthy
+ volumes:
+ - health_signals:/health
+ - type: bind
+ source: ${PYTHON_DOCKER_CLIENT_CONFIG_PATH}
+ target: /app/python_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_client:/app/zmq-out
+ ports:
+ - "${DASHBOARD_PORT}:5000"
+ healthcheck:
+ test: ["CMD", "test", "-f", "/health/monitor_ready"]
+ interval: 2s
+ timeout: 3s
+ start_period: 30s
+ retries: 30
+
+networks:
+ quic_net:
+ ipam:
+ config:
+ - subnet: 10.64.89.0/24
+ gateway: 10.64.89.1
+
+volumes:
+ health_signals:
+ driver: local
+ driver_opts:
+ type: tmpfs
+ device: tmpfs
+ zmq_client:
+ driver: local
+ driver_opts:
+ type: tmpfs
+ device: tmpfs
+ zmq_server:
+ driver: local
+ driver_opts:
+ type: tmpfs
+ device: tmpfs
diff --git a/config/client_config.yaml b/config/client_config.yaml
index e0ecc8a..aa33f1c 100644
--- a/config/client_config.yaml
+++ b/config/client_config.yaml
@@ -19,9 +19,6 @@ MAX_LOG_ENTRIES: &max-log-entries 100
SLO_TIMEOUT: &slo-timeout 200 # TODO: change this
QUIC_SHM_SIZE: &quic-shm-size 50000000
-# needed for ping loop
-DST_IP: &dst-ip 136.118.43.70
-
main_client_config_list:
- service_id: 1
max_entries: *max-log-entries
@@ -87,7 +84,6 @@ bandwidth_allocator_config:
bidirectional_zmq_ping_handler_sockname: ping-handler
ping_handler_config:
- dst_ip: *dst-ip
max_entries: 100
thread_concurrency: 5
bidirectional_zmq_sockname: ping-handler
@@ -103,7 +99,7 @@ camera_stream_config_list:
shmem_buf_size: *quic-shm-size
camera_np_size: [1080, 1920, 3]
zmq_kill_switch_sockname: camera-kill-1-switch
- mock_camera_image_path: null # set to a file path (e.g. mock_webcam_image.jpg) to use a static image instead of webcam
+ mock_camera_image_path: /home/hwei/turbo/src/python/camera_stream/mock_webcam_image.jpg # only used when --mock-camera is passed
- camera_id: 2 # FRONT LEFT
usb_id: 4 # TODO: CONFIGURE THIS
max_entries: *max-log-entries
@@ -113,7 +109,7 @@ camera_stream_config_list:
shmem_buf_size: *quic-shm-size
camera_np_size: [1080, 1920, 3]
zmq_kill_switch_sockname: camera-kill-2-switch
- mock_camera_image_path: null
+ mock_camera_image_path: /home/hwei/turbo/src/python/camera_stream/mock_webcam_image.jpg # only used when --mock-camera is passed
- camera_id: 3 # FRONT RIGHT
usb_id: 8 # TODO: CONFIGURE THIS
max_entries: *max-log-entries
@@ -123,7 +119,7 @@ camera_stream_config_list:
shmem_buf_size: *quic-shm-size
camera_np_size: [1080, 1920, 3]
zmq_kill_switch_sockname: camera-kill-3-switch
- mock_camera_image_path: null
+ mock_camera_image_path: /home/hwei/turbo/src/python/camera_stream/mock_webcam_image.jpg # only used when --mock-camera is passed
main_plotter_config:
# service 4 is for junk stream
diff --git a/config/server_config_gcloud.yaml b/config/server_config_gcloud.yaml
index 9d2d1b4..cab9665 100644
--- a/config/server_config_gcloud.yaml
+++ b/config/server_config_gcloud.yaml
@@ -38,8 +38,8 @@ server_config_list:
thread_concurrency: 10
shm_filesize: *shm-filesize
zmq_kill_switch_sockname: remote-server-kill-switch-1
- mock_inference_output_path: null # set to a file path (e.g. example_effdet_d4_output.npz) to skip model loading and return mock detections
- mock_model_latency_csv_path: null # set to experiment_model_info.csv path to simulate per-model inference latency in mock mode
+ mock_inference_output_path: /home/hwei/turbo/src/python/camera_stream/example_effdet_d4_output.npy # only used when --mock-inference is passed
+ mock_model_latency_csv_path: /home/hwei/turbo/experiment_model_info.csv # only used when --mock-inference is passed
- service_id: 2
max_entries: *max-log-entries
model_metadata_list: *model-list-ref
@@ -51,8 +51,8 @@ server_config_list:
thread_concurrency: 10
shm_filesize: *shm-filesize
zmq_kill_switch_sockname: remote-server-kill-switch-2
- mock_inference_output_path: null
- mock_model_latency_csv_path: null
+ mock_inference_output_path: /home/hwei/turbo/src/python/camera_stream/example_effdet_d4_output.npy # only used when --mock-inference is passed
+ mock_model_latency_csv_path: /home/hwei/turbo/experiment_model_info.csv # only used when --mock-inference is passed
- service_id: 3
max_entries: *max-log-entries
model_metadata_list: *model-list-ref
@@ -64,5 +64,5 @@ server_config_list:
thread_concurrency: 10
shm_filesize: *shm-filesize
zmq_kill_switch_sockname: remote-server-kill-switch-3
- mock_inference_output_path: null
- mock_model_latency_csv_path: null
+ mock_inference_output_path: /home/hwei/turbo/src/python/camera_stream/example_effdet_d4_output.npy # only used when --mock-inference is passed
+ mock_model_latency_csv_path: /home/hwei/turbo/experiment_model_info.csv # only used when --mock-inference is passed
diff --git a/docker/.env.example b/docker/.env.example
new file mode 100644
index 0000000..2c61d60
--- /dev/null
+++ b/docker/.env.example
@@ -0,0 +1,50 @@
+# Host user/group IDs — set these to match your host user (id -u / id -g)
+# so that bind-mounted files are accessible inside containers.
+HOST_UID=1000
+HOST_GID=1000
+
+# quic client-side only
+# Same-host: use Docker bridge gateway (10.64.89.1). Remote: use server host's routable IP.
+QUIC_CLIENT_REMOTE_ADDR=10.64.89.1:12345
+
+QUIC_DOCKER_CLIENT_CONFIG_PATH=./config/quic_config_client_docker.yaml
+
+# quic server-side only
+QUIC_SERVER_ADDR=0.0.0.0:12345
+QUIC_SERVER_PORT=12345
+
+QUIC_DOCKER_SERVER_CONFIG_PATH=./config/quic_config_gcloud_docker.yaml
+
+# python client config
+PYTHON_DOCKER_CLIENT_CONFIG_PATH=./config/client_config_docker.yaml
+
+# python server config
+PYTHON_DOCKER_SERVER_CONFIG_PATH=./config/server_config_gcloud_docker.yaml
+
+# both client and server-side
+DOCKER_LOG_CONFIG_PATH=./config/logging_config_docker.yaml
+EXPERIMENT_OUTPUT_DIR=/path/to/experiment2-out
+
+# client-side only
+DASHBOARD_PORT=5000
+MODEL_FULL_EVAL_DIR=/path/to/full-eval
+
+# server-side only
+EFFDET_MODELS_DIR=/path/to/av-models
+
+# for quic cargo build
+SSL_KEY_PATH=./src/quic/ssl_key.pem
+SSL_CERT_PATH=./src/quic/ssl_cert.pem
+
+# rust env vars
+RUST_LOG=info
+RUST_BACKTRACE=full
+
+# for both client and server
+EXPERIMENT_MODEL_INFO_PATH=../experiment_model_info.csv
+MOCK_EFFDET_OUTPUT_PATH=../src/python/camera_stream/example_effdet_d4_output.npy
+MOCK_EFFDET_INPUT_PATH=../src/python/camera_stream/mock_webcam_image.jpg
+
+# Mock modes — set to any non-empty value (e.g. "true") to enable, leave empty to disable
+MOCK_CAMERA=
+MOCK_INFERENCE=
\ No newline at end of file
diff --git a/docker/Dockerfile_turbo_python_base b/docker/Dockerfile_turbo_python_base
new file mode 100644
index 0000000..11c43b7
--- /dev/null
+++ b/docker/Dockerfile_turbo_python_base
@@ -0,0 +1,39 @@
+# syntax=docker/dockerfile:1
+
+# doesn't build with torch for python3.10-alpine
+FROM ghcr.io/astral-sh/uv:python3.10-trixie AS python_build
+
+# Enable bytecode compilation
+ENV UV_COMPILE_BYTECODE=1
+
+# Copy from the cache instead of linking since it's a mounted volume
+ENV UV_LINK_MODE=copy
+
+# Omit development dependencies
+ENV UV_NO_DEV=1
+
+# Ensure installed tools can be executed out of the box
+ENV UV_TOOL_BIN_DIR=/usr/local/bin
+
+WORKDIR /app
+
+RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 --yes
+
+# Install the project's dependencies using the lockfile and settings
+RUN --mount=type=cache,target=/root/.cache/uv \
+ --mount=type=bind,source=uv.lock,target=uv.lock \
+ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
+ uv sync --no-install-project --locked
+
+# Then, add the rest of the project source code and install it
+# Installing separately from its dependencies allows optimal layer caching
+COPY ./src/python /app/src/python
+
+RUN --mount=type=cache,target=/root/.cache/uv \
+ --mount=type=bind,source=uv.lock,target=uv.lock \
+ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
+ --mount=type=bind,source=README.md,target=README.md \
+ uv sync --locked
+
+# Place executables in the environment at the front of the path
+ENV PATH="/app/.venv/bin:$PATH"
diff --git a/docker/Dockerfile_turbo_python_binary b/docker/Dockerfile_turbo_python_binary
new file mode 100644
index 0000000..5c9f2c9
--- /dev/null
+++ b/docker/Dockerfile_turbo_python_binary
@@ -0,0 +1,29 @@
+# syntax=docker/dockerfile:1
+
+FROM base_image AS final
+WORKDIR /app
+
+ARG EXECUTABLE_DIR
+ARG EXECUTABLE_NAME
+
+# Create a non-privileged user that the app will run under.
+# See https://docs.docker.com/go/dockerfile-user-best-practices/
+ARG GID=1000
+ARG UID=1000
+
+# Setup a non-root user
+RUN groupadd --system --gid ${GID} nonroot
+RUN useradd --system --gid ${GID} --uid ${UID} --create-home nonroot
+USER nonroot
+
+
+ENV EXECUTABLE_DIR=${EXECUTABLE_DIR}
+ENV EXECUTABLE_NAME=${EXECUTABLE_NAME}
+
+# What the container should run when it is started.
+# Signal handling is provided by `init: true` in compose.yaml (tini as PID 1).
+# `exec` is optional here — it just avoids a redundant /bin/sh parent process.
+# We call python directly instead of `uv run` because uv spawns python as a
+# child and may not forward signals. The venv is already on PATH
+# (set in Dockerfile_turbo_python_base), so `python` resolves correctly.
+CMD cd ${EXECUTABLE_DIR} && exec python ${EXECUTABLE_NAME} "-c" "/app/python_config.yaml"
diff --git a/docker/Dockerfile_turbo_quic_binary b/docker/Dockerfile_turbo_quic_binary
new file mode 100644
index 0000000..5fd7963
--- /dev/null
+++ b/docker/Dockerfile_turbo_quic_binary
@@ -0,0 +1,35 @@
+# syntax=docker/dockerfile:1
+
+FROM base_image
+ARG APP_NAME
+WORKDIR /app
+
+FROM alpine:3.18 AS final
+ARG APP_NAME
+ARG QUIC_BIND_ADDR
+
+# Create a non-privileged user that the app will run under.
+# See https://docs.docker.com/go/dockerfile-user-best-practices/
+ARG UID=1000
+ARG GID=1000
+RUN addgroup -g "${GID}" sharedgroup && \
+ adduser \
+ --disabled-password \
+ --gecos "" \
+ --home "/nonexistent" \
+ --shell "/sbin/nologin" \
+ --no-create-home \
+ --uid "${UID}" \
+ --ingroup sharedgroup \
+ appuser
+USER appuser
+
+# Copy the executable from the "build" stage.
+COPY --from=base_image /bin/$APP_NAME /bin/server
+
+ENV QUIC_BIND_ADDR=${QUIC_BIND_ADDR}
+
+# What the container should run when it is started.
+# Signal handling is provided by `init: true` in compose.yaml (tini as PID 1).
+# `exec` is optional here — it just avoids a redundant /bin/sh parent process.
+CMD exec /bin/server "/app/quic_config.yaml" "${QUIC_BIND_ADDR}"
diff --git a/docker/Dockerfile_turbo_rust_base b/docker/Dockerfile_turbo_rust_base
new file mode 100644
index 0000000..ff5531f
--- /dev/null
+++ b/docker/Dockerfile_turbo_rust_base
@@ -0,0 +1,36 @@
+# syntax=docker/dockerfile:1
+
+ARG RUST_VERSION=1.93.0
+
+FROM rust:${RUST_VERSION}-alpine AS rust_build
+ARG APP_NAME_LIST
+ARG SSL_CERT_PATH
+ARG SSL_KEY_PATH
+WORKDIR /app
+
+# Install host build dependencies.
+RUN apk add --no-cache clang lld musl-dev git
+
+# Build the application.
+# Leverage a cache mount to /usr/local/cargo/registry/
+# for downloaded dependencies, a cache mount to /usr/local/cargo/git/db
+# for git repository dependencies, and a cache mount to /app/target/ for
+# compiled dependencies which will speed up subsequent builds.
+# Leverage a bind mount to the src directory to avoid having to copy the
+# source code into the container. Once built, copy the executable to an
+# output directory before the cache mounted /app/target is unmounted.
+RUN --mount=type=bind,source=src/quic/quic_client,target=quic_client \
+ --mount=type=bind,source=src/quic/quic_conn,target=quic_conn \
+ --mount=type=bind,source=src/quic/quic_server,target=quic_server \
+ --mount=type=bind,source=src/quic/Cargo.toml,target=Cargo.toml \
+ --mount=type=bind,source=src/quic/Cargo.lock,target=Cargo.lock \
+ --mount=type=bind,source=${SSL_CERT_PATH},target=ssl_cert.pem \
+ --mount=type=bind,source=${SSL_KEY_PATH},target=ssl_key.pem \
+ --mount=type=cache,target=/app/target/ \
+ --mount=type=cache,target=/usr/local/cargo/git/db \
+ --mount=type=cache,target=/usr/local/cargo/registry/ \
+ cargo build --locked --release && \
+ IFS=',' && \
+ for app_name in $APP_NAME_LIST; do \
+ echo $app_name && echo $(ls ./target/release) && cp ./target/release/$app_name /bin/$app_name; \
+ done
diff --git a/docker/README.Docker.md b/docker/README.Docker.md
new file mode 100644
index 0000000..a5cd3db
--- /dev/null
+++ b/docker/README.Docker.md
@@ -0,0 +1,382 @@
+# Docker: Building from Source & Reference
+
+This directory contains everything needed to **build Docker images from source** — Dockerfiles, a build-oriented `compose.yaml`, and shared configuration files. It is intended for development and customization, **not** for running pre-built images.
+
+- **To run TURBO using pre-built images** (recommended), use the root-level `compose.yaml` and `.env.example`. See the [Quick Start (Docker with Pre-Built Images)](../README.md#quick-start-docker-with-pre-built-images--recommended) section in the main README.
+- **To build images from source**, use the `compose.yaml` and `.env.example` in this directory. See [Building from Source](#building-from-source) below.
+
+The `docker/config/` directory contains Docker-specific YAML configs shared by both workflows.
+
+## Building from Source
+
+This section corresponds to [Alternative 1: Docker Building from Source](../README.md#alternative-1-docker-building-from-source) in the main README. If you want to build the Docker images locally instead of using the pre-built images (e.g., for development or customization), follow these steps.
+
+### Prerequisites
+
+- [Docker Engine](https://docs.docker.com/engine/install/) 24.0+ with [Docker Compose V2](https://docs.docker.com/compose/install/)
+- [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) (**server only** — needed for GPU inference; not required on the client, or if using [mock inference mode](../README.md#mock-modes))
+- USB webcams (**client only** — or use [mock camera mode](../README.md#mock-modes) for testing without cameras)
+- Linux (tested on Ubuntu 20.04+)
+
+See the main README's [Prerequisites](../README.md#prerequisites) section for Docker version warnings, disk space requirements, and setup verification commands.
+
+### Setup
+
+> **Running on separate machines?** If you are running the client and server on different hosts, perform all steps on **both** machines. Within step 1, the model download is server-only and the eval data download is client-only.
+
+1. **Complete shared setup steps:**
+
+ Follow **steps 1–3** from the [Quick Start Setup](../README.md#setup) in the main README to clone the repository, download model checkpoints (server only), and download evaluation data (client only).
+
+2. **Generate SSL keys for QUIC (both client and server):**
+
+ The QUIC binaries embed SSL certificates at compile time via Rust's `include_str!()` macro (see [SSL Certificates](#ssl-certificates)). You must generate them before building:
+
+ ```bash
+ cd src/quic
+ pip install cryptography # if not already installed
+ python generate_cert.py
+ cd ../..
+ ```
+
+3. **Configure the `.env` file (both client and server):**
+
+ ```bash
+ cp docker/.env.example docker/.env
+ ```
+
+ Edit `docker/.env` and update the following values to match your host system:
+
+ | Variable | Description | Default |
+ |---|---|---|
+ | `HOST_UID` | Your host user ID (run `id -u`) | `1000` |
+ | `HOST_GID` | Your host group ID (run `id -g`) | `1000` |
+ | `EXPERIMENT_OUTPUT_DIR` | Absolute path for experiment output | (must set) |
+ | `EFFDET_MODELS_DIR` | Absolute path to model checkpoints (server) | (must set) |
+ | `MODEL_FULL_EVAL_DIR` | Absolute path to evaluation data (client) | (must set) |
+
+ Most other settings (networking, ports, SSL paths) work out of the box for same-host testing. See [Additional Configuration](#additional-configuration) below for the full reference.
+
+4. **Create the experiment output directory (both client and server):**
+ ```bash
+ mkdir -p ~/experiment2-out
+ ```
+
+### Build and Run
+
+All Docker commands should be run from the `docker/` directory:
+```bash
+cd docker
+```
+
+**GPU setup:** If the server host has NVIDIA GPUs (required for real inference, not needed for [mock inference](../README.md#mock-modes)), include the GPU override file by adding `-f compose.gpu.yaml` to all `docker compose` commands. Non-GPU hosts can omit it.
+
+**Build and run both client and server on the same host (with GPU):**
+```bash
+docker compose -f compose.yaml -f compose.gpu.yaml --profile client --profile server up --build
+```
+
+**Build and run server only** (e.g., on a cloud GPU machine):
+```bash
+docker compose -f compose.yaml -f compose.gpu.yaml --profile server up --build
+```
+
+**Build and run client only** (when server is running elsewhere — update `QUIC_CLIENT_REMOTE_ADDR` in `.env` to the server's IP):
+```bash
+docker compose --profile client up --build
+```
+
+**Build and run server with mock inference (no GPU needed):**
+```bash
+docker compose --profile server up --build
+```
+
+Once the client is running, open the monitoring dashboard at **http://localhost:5000**.
+
+**Shut down:**
+```bash
+docker compose --profile client --profile server down -v
+```
+
+The `-v` flag removes ephemeral volumes (ZMQ sockets, health signals), giving you a clean slate for the next run.
+
+**Experiment output** will be logged to Parquet files in the configured output directory (default: `~/experiment2-out/`).
+
+## Services Overview
+
+The Compose file defines seven services organized into two profiles (`client` and `server`):
+
+| Service | Profile | Description |
+|---|---|---|
+| `rust_base` | client, server | Builds QUIC client/server Rust binaries (base image, not run directly) |
+| `python_base` | client, server | Builds Python environment with dependencies (base image, not run directly) |
+| `quic_client` | client | QUIC transport client (Rust) |
+| `client_python_main` | client | Client orchestrator — camera streams, bandwidth allocator, LP solver |
+| `client_python_monitor` | client | Web dashboard for real-time monitoring (Flask) |
+| `quic_server` | server | QUIC transport server (Rust) |
+| `server_python_main` | server | Model servers — runs EfficientDet inference on GPU |
+
+Services start in dependency order via health checks: `client_python_main` must be healthy before `client_python_monitor` starts, and `client_python_monitor` must be healthy before `quic_client` starts.
+
+## Additional Configuration
+
+The [Setup](#setup) section above covers the required `.env` variables (`HOST_UID`, `HOST_GID`, `EXPERIMENT_OUTPUT_DIR`, `EFFDET_MODELS_DIR`, `MODEL_FULL_EVAL_DIR`). The following additional variables are also available:
+
+**SSL (usually no changes needed):**
+
+| Variable | Description | Default |
+|---|---|---|
+| `SSL_KEY_PATH` | Path to QUIC SSL key (relative to repo root) | `./src/quic/ssl_key.pem` |
+| `SSL_CERT_PATH` | Path to QUIC SSL cert (relative to repo root) | `./src/quic/ssl_cert.pem` |
+
+**Networking (usually no changes needed for same-host testing):**
+
+| Variable | Description | Default |
+|---|---|---|
+| `QUIC_CLIENT_REMOTE_ADDR` | Address the QUIC client connects to | `10.64.89.1:12345` (Docker bridge gateway) |
+| `QUIC_SERVER_ADDR` | Address the QUIC server binds to | `0.0.0.0:12345` |
+| `QUIC_SERVER_PORT` | UDP port exposed for QUIC | `12345` |
+| `DASHBOARD_PORT` | Host port for the web dashboard | `5000` |
+
+When running client and server on the **same host**, the default `QUIC_CLIENT_REMOTE_ADDR` of `10.64.89.1:12345` routes through the Docker bridge gateway to reach the server container. When running on **separate hosts**, set `QUIC_CLIENT_REMOTE_ADDR` to the server machine's routable IP and port.
+
+### Docker-specific config files
+
+The `docker/config/` directory contains YAML config files that mirror the main `config/` files but with container-internal paths (e.g. `/app/experiment2-out` instead of `~/experiment2-out`). These are bind-mounted into each container at runtime.
+
+Most settings work out of the box, but the following **hardware-specific values** may need to be updated to match your setup:
+
+| File | Setting | Default | When to change |
+|---|---|---|---|
+| `server_config_gcloud_docker.yaml` | `device` per service | `cuda:0`, `cuda:1`, `cuda:2` | If you have fewer than 3 GPUs — e.g., set all to `"cuda:0"` for a single-GPU machine |
+| `client_config_docker.yaml` | `usb_id` per camera | `0`, `4`, `8` | If using real cameras (not mock mode) — update to match your system's USB device IDs |
+
+For other changes (e.g. number of cameras, model variants, SLO timeouts), edit the files in `docker/config/` — not the ones in the repo root `config/` directory.
+
+### Mock Modes
+
+TURBO supports mock camera and mock inference modes for testing without physical cameras or GPUs. See the [Mock Modes](../README.md#mock-modes) section in the main README for full details.
+
+In Docker, mock modes are toggled via environment variables in `.env`:
+
+| Variable | Effect when non-empty | Default |
+|---|---|---|
+| `MOCK_CAMERA` | Passes `--mock-camera` to `client_main.py` — uses static images instead of USB cameras | (empty — disabled) |
+| `MOCK_INFERENCE` | Passes `--mock-inference` to `server_main.py` — returns pre-recorded detections instead of GPU inference | (empty — disabled) |
+
+The mock data files (`mock_webcam_image.jpg`, `example_effdet_d4_output.npy`) are bundled into the container at `/app/` and their paths are pre-configured in `docker/config/`.
+
+### Additional running commands
+
+Beyond the commands in [Build and Run](#build-and-run), these are also useful:
+
+**Build all images without starting** (useful after Dockerfile changes):
+```bash
+docker compose --profile client --profile server build
+```
+
+**Shut down without removing volumes** (keeps ZMQ sockets and health signals):
+```bash
+docker compose --profile client --profile server down
+```
+
+**Accessing the web dashboard:**
+
+Once the client profile is running and all health checks pass, open the monitoring dashboard at `http://localhost:5000` (or whatever port you set for `DASHBOARD_PORT` in `.env`).
+
+## Development Workflow
+
+The Compose file supports [Docker Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) for hot-reload during development:
+
+```bash
+docker compose --profile client --profile server watch
+```
+
+- **Python source changes** (`src/python/`): synced into running containers without rebuild.
+- **Rust source changes** (`src/quic/`): triggers a full rebuild of the Rust base image.
+- **Dependency changes** (`uv.lock`, `pyproject.toml`, `Cargo.toml`): triggers a rebuild.
+- **Docker config changes** (`docker/`): triggers a rebuild.
+
+## Architecture Notes
+
+- **IPC mode: host** — All services use `ipc: host` so that ZeroMQ IPC sockets and POSIX shared memory segments are accessible across containers. This is required for the system's inter-process communication to work.
+- **GPU access** — The Python services (`client_python_main`, `server_python_main`) request all available NVIDIA GPUs via `deploy.resources`. The server config assigns specific services to specific GPU devices (e.g. `cuda:0`, `cuda:1`).
+- **tmpfs volumes** — ZeroMQ socket directories and health signal files use tmpfs-backed volumes for fast, ephemeral storage.
+- **Signal handling** — All services use `init: true` (tini) as PID 1 for proper signal forwarding and graceful shutdown. A 30-second grace period (`stop_grace_period`) is configured for each service. When you press Ctrl+C on `docker compose up`, Docker Compose sends **SIGTERM** (not SIGINT) to each container. Without `init: true`, the application would be PID 1, and the Linux kernel silently drops signals with default handlers for PID 1 — causing the process to ignore SIGTERM and get SIGKILL'd after the grace period. The Python orchestrators (`client_main.py`, `server_main.py`) explicitly handle both SIGTERM and SIGINT to trigger graceful shutdown (ZMQ kill-switch broadcast, shared memory unlink, Parquet flush).
+- **`exec` and `python` in Dockerfiles** — The Python Dockerfile uses `exec python` directly instead of `uv run`. `uv run` spawns Python as a child process and may not forward signals, which would prevent graceful shutdown. The venv is already on `PATH` (set in `Dockerfile_turbo_python_base`), so calling `python` directly works. The `exec` replaces the shell with the actual process, avoiding a redundant `/bin/sh` parent (optional with `init: true`, but good practice).
+- **Custom network** — A bridge network (`quic_net`, subnet `10.64.89.0/24`, gateway `10.64.89.1`) is used for QUIC communication. On same-host deployments, the QUIC client reaches the server through the bridge gateway (which routes to the host, where the server's UDP port is published). On separate-host deployments, `QUIC_CLIENT_REMOTE_ADDR` is set to the server machine's routable IP instead.
+- **QUIC uses UDP** — The server's port is published with the `/udp` protocol. Firewalls on the server host must allow inbound UDP on this port.
+- **Rust QUIC client requires IP addresses** — The Rust QUIC client parses addresses with `SocketAddr` and cannot resolve DNS hostnames. `QUIC_CLIENT_REMOTE_ADDR` must always be an `ip:port` pair (e.g. `10.64.89.1:12345`), not a hostname.
+- **Health signal synchronization** — `client_python_main` writes its health signal (`/health/client_main_ready`) only after all Client subprocesses have bound their `quic_rcv_zmq_socket`. This ensures the Rust QUIC client (which depends on this health signal via `client_python_monitor`) does not start until the Python ZMQ sockets are ready to accept connections. A `multiprocessing.Manager().Queue()` is used for this cross-process synchronization.
+
+## Path Relativity Rules
+
+Docker Compose uses different base directories for different path types, which can be confusing:
+
+| Path type | Relative to | Example |
+|---|---|---|
+| Volume `source` | **Compose file location** (`docker/`) | `./config/client_config_docker.yaml` → `docker/config/client_config_docker.yaml` |
+| Build `context` | **Compose file location** (`docker/`) | `..` → repo root |
+| Build `dockerfile` | **Build context** | `./docker/Dockerfile_turbo_python_binary` → (repo root)/docker/Dockerfile_turbo_python_binary |
+| Build `args` (paths like `EXECUTABLE_DIR`) | N/A (baked into image) | `./src/python` → resolved inside the container |
+| Watch `path` | **Build context** | `./uv.lock` → (repo root)/uv.lock |
+
+The `.env` file paths for volume mounts (e.g. `PYTHON_DOCKER_CLIENT_CONFIG_PATH`) must be relative to the compose file location (`docker/`), **not** the repo root.
+
+## SSL Certificates
+
+The QUIC binaries embed the SSL certificate and key at **compile time** via Rust's `include_str!()` macro. The `.pem` files are baked into the executable during `cargo build` and are not needed at runtime. This means the QUIC container images are self-contained — no SSL volume mounts are required.
+
+## Troubleshooting
+
+**"permission denied" on bind-mounted files:**
+Make sure `HOST_UID` and `HOST_GID` in `.env` match your host user (`id -u` and `id -g`).
+
+**GPU not available inside containers:**
+Verify the NVIDIA Container Toolkit is installed and the Docker daemon is configured to use the `nvidia` runtime. Test with:
+```bash
+docker run --rm --gpus all nvidia/cuda:12.0.0-base-ubuntu22.04 nvidia-smi
+```
+
+**Services failing health checks:**
+Check logs for a specific service to diagnose startup issues:
+```bash
+docker compose --profile client logs client_python_main
+```
+
+**QUIC connection failures between client and server on separate hosts:**
+Make sure `QUIC_CLIENT_REMOTE_ADDR` in `.env` is set to the server host's routable IP (not the Docker gateway), and that the `QUIC_SERVER_PORT` UDP port is open on the server host's firewall.
+
+**Bind mount shows "IsADirectoryError" or creates an unexpected directory:**
+When Docker bind-mounts a file but the source path doesn't exist on the host, Docker silently creates a **directory** at the target path instead of failing. This causes confusing errors like `IsADirectoryError: [Errno 21] Is a directory: '/app/python_config.yaml'`. Double-check that the source path in `.env` is correct and that the file exists. Remember that volume source paths are relative to the compose file location — see [Path Relativity Rules](#path-relativity-rules). After fixing the path, you must rebuild with `--build` since the stale directory may be cached in the image layer:
+```bash
+docker compose --profile server up --build --force-recreate
+```
+
+**`--force-recreate` vs `--build`:**
+`--force-recreate` recreates containers but does **not** rebuild images. If a problem was baked into an image during a previous build (e.g. a directory created by a bad bind mount), you need `--build` to rebuild the image. Use both when in doubt:
+```bash
+docker compose --profile server up --build --force-recreate
+```
+
+**Do not use `docker compose restart`:**
+`docker compose restart` stops and restarts containers but does **not** re-evaluate `depends_on` health checks. All containers restart simultaneously, bypassing the startup ordering. Always use `docker compose down && docker compose up` to ensure proper sequencing.
+
+**iptables errors ("Chain 'DOCKER-ISOLATION-STAGE-2' does not exist"):**
+This is a known issue with **Docker 28.x** on newer Linux kernels where iptables uses the `nf_tables` backend. Docker 28 changed its network isolation chain setup in a way that is incompatible with `nf_tables`. **Docker 27.5 does not have this issue.** Fixes to try in order:
+1. **Downgrade to Docker 27.5** — this is the most reliable fix.
+2. Restart Docker: `sudo systemctl restart docker`
+3. Switch to the legacy iptables backend:
+ ```bash
+ sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
+ sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
+ sudo systemctl restart docker
+ ```
+
+**Stale containers after changing network config:**
+If you add or modify Docker networks in `compose.yaml`, existing containers won't pick up the changes. You'll see errors like `container is not connected to the network`. Fix by recreating:
+```bash
+docker compose --profile client --profile server down
+docker compose --profile client --profile server up
+```
+
+**Network subnet overlap ("Pool overlaps with other one on this address space"):**
+Both `compose.yaml` (pre-built) and `docker/compose.yaml` (build-from-source) define `quic_net` with the same `10.64.89.0/24` subnet. If you previously ran one workflow and then switch to the other without cleaning up, the old network still exists under a different project name (e.g. `docker_quic_net` vs `turbo_quic_net`), and Docker refuses to create a second network with the same subnet. Remove the stale network:
+```bash
+# List networks to find the conflicting one
+docker network ls
+
+# Remove the stale network (e.g. from a previous build-from-source run)
+docker network rm docker_quic_net docker_default
+
+# Or from a previous pre-built run
+docker network rm turbo_quic_net turbo_default
+```
+
+**Stale POSIX shared memory after ungraceful shutdown:**
+Because all services use `ipc: host`, POSIX shared memory segments live in the host's `/dev/shm` and survive container restarts. If a container is force-killed (SIGKILL, OOM, Docker timeout, power loss) before cleanup runs, stale segments remain and cause `FileExistsError: [Errno 17] File exists` on the next startup. To clean them up:
+```bash
+# Check for stale segments
+ls /dev/shm/*-shm
+
+# Remove them (server-side example)
+rm /dev/shm/server-service*-shm
+
+# Remove them (client-side example)
+rm /dev/shm/client-service*-shm
+```
+Under normal graceful shutdown (Ctrl+C), the Python processes unlink their shared memory segments automatically.
+
+**Stale ZeroMQ sockets from a previous run:**
+ZMQ socket directories use tmpfs volumes that start empty on every `docker compose up`, so stale sockets are not normally an issue. If you see ZMQ-related errors, shut down with `-v` to remove all tmpfs volumes:
+```bash
+docker compose --profile client --profile server down -v
+```
+
+**QUIC handshake timeout (`MaxHandshakeDurationExceeded`) on same-host deployment:**
+If the QUIC client fails with `MaxHandshakeDurationExceeded` when running client and server on the same host, the most likely cause is **orphaned Docker bridge interfaces** that conflict with the `quic_net` subnet (`10.64.89.0/24`). This happens when Docker fails to clean up Linux bridge interfaces after removing networks — for example, after a `docker compose down`, a Docker daemon restart, or a system reboot. The orphaned bridge retains the same IP (`10.64.89.1/24`) as the new `quic_net` bridge, creating duplicate routes in the kernel. Packets from the QUIC client to the gateway (`10.64.89.1`) get routed to the dead orphan bridge instead of the active one, so they never reach the host and the server never sees the connection attempt.
+
+To diagnose, check if containers on `quic_net` can reach the gateway at all:
+```bash
+docker run --rm --network _quic_net alpine:3.18 ping -c2 -W2 10.64.89.1
+```
+If this shows 100% packet loss, check for orphaned bridge interfaces:
+```bash
+# List all bridge interfaces on the system
+ip -br link show type bridge
+
+# Compare against active Docker networks
+docker network ls
+
+# Bridge names use the format br-.
+# Any bridge that doesn't match an active Docker network ID is orphaned.
+```
+
+To fix, remove the orphaned bridges and restart the compose stack:
+```bash
+# Remove each orphaned bridge (example — use the actual interface names from the step above)
+sudo ip link delete br-XXXXXXXXXXXX
+
+# Restart
+docker compose down
+docker compose --profile client --profile server up
+```
+
+To prevent this, always shut down cleanly with `docker compose down` before restarting Docker or the host. If orphaned bridges recur, a Docker daemon restart (`sudo systemctl restart docker`) followed by removing any remaining orphans is the most reliable fix.
+
+**QUIC handshake timeout (`MaxHandshakeDurationExceeded`) on separate-host deployment — stale nftables rules:**
+If the QUIC client container can't reach an external server IP (but the host can), the cause is likely **stale nftables/iptables rules** referencing old Docker bridge interfaces. Docker generates firewall rules (FORWARD, MASQUERADE, etc.) that reference bridge interfaces by name (e.g. `br-a1b2c3d4e5f6`). When Docker networks are recreated (e.g. after `docker compose down` + `up`, or a Docker restart), the new network may get a different bridge name, but the old nftables rules persist — referencing bridges that no longer exist. Since the FORWARD chain has `policy drop`, traffic from the new bridge is silently dropped and never reaches the external network.
+
+To diagnose, confirm the packet is being dropped inside Docker networking:
+```bash
+# Send a UDP packet from inside a container on quic_net
+docker run --rm --network _quic_net alpine:3.18 \
+ sh -c 'echo test | nc -u -w1 '
+
+# Meanwhile, on the server host, check if the packet arrives
+sudo tcpdump -i any udp port
+```
+If the packet never arrives at the server (but a similar test from the host works), check the nftables rules:
+```bash
+sudo nft list ruleset
+```
+Look at the `DOCKER-FORWARD` chain in `table ip filter` — the `iifname` accept rules and `MASQUERADE` rules should reference bridge interfaces that actually exist (`ip link show type bridge`). If they reference bridges that don't exist, the rules are stale.
+
+To fix, flush the stale nftables rules and let Docker regenerate them:
+```bash
+# Stop all compose projects
+docker compose --profile client --profile server down
+
+# Remove unused networks
+docker network prune -f
+
+# Flush ALL nftables rules (Docker will regenerate its own on restart)
+sudo nft flush ruleset
+
+# Restart Docker to regenerate clean rules
+sudo systemctl restart docker
+
+# Bring the stack back up
+docker compose --profile client up -d
+```
+The key step is `nft flush ruleset` — without it, `systemctl restart docker` may not replace the stale rules. After the restart, verify that `sudo nft list chain ip filter DOCKER-FORWARD` references bridges matching the output of `ip link show type bridge`.
diff --git a/docker/compose.gpu.yaml b/docker/compose.gpu.yaml
new file mode 100644
index 0000000..d82cdd0
--- /dev/null
+++ b/docker/compose.gpu.yaml
@@ -0,0 +1,11 @@
+# GPU override — include this file on hosts with NVIDIA GPUs.
+# Usage (from docker/ directory): docker compose -f compose.yaml -f compose.gpu.yaml --profile server up --build
+services:
+ server_python_main:
+ deploy:
+ resources:
+ reservations:
+ devices:
+ - driver: nvidia
+ count: all
+ capabilities: [gpu]
diff --git a/docker/compose.yaml b/docker/compose.yaml
new file mode 100644
index 0000000..fb075f9
--- /dev/null
+++ b/docker/compose.yaml
@@ -0,0 +1,272 @@
+services:
+ rust_base:
+ profiles: ['client', 'server']
+ build:
+ context: ..
+ dockerfile: ./docker/Dockerfile_turbo_rust_base
+ args:
+ APP_NAME_LIST: "client,server"
+ SSL_CERT_PATH: ${SSL_CERT_PATH}
+ SSL_KEY_PATH: ${SSL_KEY_PATH}
+ develop:
+ watch:
+ - action: rebuild
+ path: ./src/quic/quic_conn
+ - action: rebuild
+ path: ./src/quic/quic_server
+ - action: rebuild
+ path: ./src/quic/quic_client
+ - action: rebuild
+ path: ./src/quic/Cargo.toml
+ - action: rebuild
+ path: ./src/quic/Cargo.lock
+ - action: rebuild
+ path: ${SSL_KEY_PATH}
+ - action: rebuild
+ path: ${SSL_CERT_PATH}
+ - action: rebuild
+ path: ./docker
+
+ python_base:
+ profiles: ['client', 'server']
+ build:
+ context: ..
+ dockerfile: ./docker/Dockerfile_turbo_python_base
+ develop:
+ watch:
+ - action: rebuild
+ path: ./uv.lock
+ - action: rebuild
+ path: ./pyproject.toml
+ - action: sync
+ path: ./src/python
+ target: /app/src/python
+ ignore:
+ - .venv/
+ - action: rebuild
+ path: ./docker
+
+ quic_client:
+ profiles: ['client']
+ # Use tini as PID 1 so Docker's SIGTERM is forwarded to the actual process.
+ # Without init, our process would be PID 1, and the Linux kernel silently
+ # drops signals with default handlers for PID 1 — causing the process to
+ # ignore SIGTERM and get SIGKILL'd after the grace period.
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ depends_on:
+ client_python_monitor:
+ condition: service_healthy
+ build:
+ context: ..
+ target: final
+ dockerfile: ./docker/Dockerfile_turbo_quic_binary
+ args:
+ APP_NAME: "client"
+ QUIC_BIND_ADDR: ${QUIC_CLIENT_REMOTE_ADDR}
+ GID: ${HOST_GID}
+ UID: ${HOST_UID}
+ additional_contexts:
+ base_image: "service:rust_base"
+ environment:
+ RUST_LOG: ${RUST_LOG}
+ RUST_BACKTRACE: ${RUST_BACKTRACE}
+ volumes:
+ - type: bind
+ source: ${QUIC_DOCKER_CLIENT_CONFIG_PATH}
+ target: /app/quic_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_client:/app/zmq-out
+ - health_signals:/health
+ networks:
+ - quic_net
+
+ quic_server:
+ profiles: ['server']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ build:
+ context: ..
+ target: final
+ dockerfile: ./docker/Dockerfile_turbo_quic_binary
+ args:
+ APP_NAME: "server"
+ QUIC_BIND_ADDR: ${QUIC_SERVER_ADDR}
+ GID: ${HOST_GID}
+ UID: ${HOST_UID}
+ additional_contexts:
+ base_image: "service:rust_base"
+ environment:
+ RUST_LOG: ${RUST_LOG}
+ RUST_BACKTRACE: ${RUST_BACKTRACE}
+ volumes:
+ - type: bind
+ source: ${QUIC_DOCKER_SERVER_CONFIG_PATH}
+ target: /app/quic_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_server:/app/zmq-out
+ ports:
+ - "${QUIC_SERVER_PORT}:${QUIC_SERVER_PORT}/udp"
+
+ client_python_main:
+ profiles: ['client']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ build:
+ context: ..
+ target: final
+ dockerfile: ./docker/Dockerfile_turbo_python_binary
+ additional_contexts:
+ base_image: "service:python_base"
+ args:
+ EXECUTABLE_DIR: ./src/python
+ EXECUTABLE_NAME: client_main.py
+ GID: ${HOST_GID}
+ UID: ${HOST_UID}
+ volumes:
+ - health_signals:/health
+ - type: bind
+ source: ${PYTHON_DOCKER_CLIENT_CONFIG_PATH}
+ target: /app/python_config.yaml
+ read_only: true
+ - type: bind
+ source: ${DOCKER_LOG_CONFIG_PATH}
+ target: /app/logging_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_client:/app/zmq-out
+ - type: bind
+ source: ${MODEL_FULL_EVAL_DIR}
+ target: /app/full-eval
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_MODEL_INFO_PATH}
+ target: /app/experiment_model_info.csv
+ read_only: true
+ - type: bind
+ source: ${MOCK_EFFDET_INPUT_PATH}
+ target: /app/mock_webcam_image.jpg
+ read_only: true
+ command: >
+ bash -c 'cd src/python && exec python client_main.py -c /app/python_config.yaml -s ${QUIC_CLIENT_REMOTE_ADDR} ${MOCK_CAMERA:+--mock-camera}'
+ healthcheck:
+ test: ["CMD", "test", "-f", "/health/client_main_ready"]
+ interval: 2s
+ timeout: 3s
+ start_period: 30s
+ retries: 30
+ server_python_main:
+ profiles: ['server']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ build:
+ context: ..
+ target: final
+ dockerfile: ./docker/Dockerfile_turbo_python_binary
+ additional_contexts:
+ base_image: "service:python_base"
+ args:
+ EXECUTABLE_DIR: ./src/python
+ EXECUTABLE_NAME: server_main.py
+ GID: ${HOST_GID}
+ UID: ${HOST_UID}
+ volumes:
+ - type: bind
+ source: ${PYTHON_DOCKER_SERVER_CONFIG_PATH}
+ target: /app/python_config.yaml
+ read_only: true
+ - type: bind
+ source: ${DOCKER_LOG_CONFIG_PATH}
+ target: /app/logging_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_server:/app/zmq-out
+ - type: bind
+ source: ${EFFDET_MODELS_DIR}
+ target: /app/av-models
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_MODEL_INFO_PATH}
+ target: /app/experiment_model_info.csv
+ read_only: true
+ - type: bind
+ source: ${MOCK_EFFDET_OUTPUT_PATH}
+ target: /app/example_effdet_d4_output.npy
+ read_only: true
+ command: >
+ bash -c 'cd src/python && exec python server_main.py -c /app/python_config.yaml ${MOCK_INFERENCE:+--mock-inference}'
+ client_python_monitor:
+ profiles: ['client']
+ init: true
+ ipc: host
+ stop_grace_period: 30s
+ depends_on:
+ client_python_main:
+ condition: service_healthy
+ build:
+ context: ..
+ target: final
+ dockerfile: ./docker/Dockerfile_turbo_python_binary
+ additional_contexts:
+ base_image: "service:python_base"
+ args:
+ EXECUTABLE_DIR: ./src/python/web_frontend
+ EXECUTABLE_NAME: start_web_dashboard.py
+ GID: ${HOST_GID}
+ UID: ${HOST_UID}
+ volumes:
+ - health_signals:/health
+ - type: bind
+ source: ${PYTHON_DOCKER_CLIENT_CONFIG_PATH}
+ target: /app/python_config.yaml
+ read_only: true
+ - type: bind
+ source: ${EXPERIMENT_OUTPUT_DIR}
+ target: /app/experiment2-out
+ - zmq_client:/app/zmq-out
+ ports:
+ - "${DASHBOARD_PORT}:5000"
+ healthcheck:
+ test: ["CMD", "test", "-f", "/health/monitor_ready"]
+ interval: 2s
+ timeout: 3s
+ start_period: 30s
+ retries: 30
+networks:
+ quic_net:
+ ipam:
+ config:
+ - subnet: 10.64.89.0/24
+ gateway: 10.64.89.1
+volumes:
+ health_signals:
+ driver: local
+ driver_opts:
+ type: tmpfs
+ device: tmpfs
+ zmq_client:
+ driver: local
+ driver_opts:
+ type: tmpfs
+ device: tmpfs
+ zmq_server:
+ driver: local
+ driver_opts:
+ type: tmpfs
+ device: tmpfs
+
+
diff --git a/docker/config/client_config_docker.yaml b/docker/config/client_config_docker.yaml
new file mode 100644
index 0000000..4ad680a
--- /dev/null
+++ b/docker/config/client_config_docker.yaml
@@ -0,0 +1,219 @@
+logging_config_filepath: /app/logging_config.yaml
+
+experiment_output_dir: /app/experiment2-out
+client_subdir: client
+zmq_dir: /app/zmq-out
+
+web_dashboard_config:
+ refresh_rate_seconds: 6
+ plotting_loop_sleep_seconds: 2
+
+model_image_size_map: &model_imagesizes
+ tf_efficientdet_d1: [640, 640]
+ tf_efficientdet_d2: [768, 768]
+ tf_efficientdet_d4: [1024, 1024]
+ tf_efficientdet_d6: [1280, 1280]
+ tf_efficientdet_d7x: [1536, 1536]
+
+MAX_LOG_ENTRIES: &max-log-entries 100
+SLO_TIMEOUT: &slo-timeout 200 # TODO: change this
+QUIC_SHM_SIZE: &quic-shm-size 50000000
+
+main_client_config_list:
+ - service_id: 1
+ max_entries: *max-log-entries
+ thread_concurrency: 10
+ camera_bidirectional_zmq_sockname: service1-camera-socket
+ camera_stream_shmem_filename: service1-camera-shmem
+ bandwidth_allocation_incoming_zmq_sockname: main-client-1-bw-subscriber
+ quic_rcv_zmq_sockname: car-server-outgoing-1
+ quic_snd_zmq_sockname: car-server-incoming-1
+ outgoing_zmq_diagnostic_sockname: car-client-diagnostics
+ camera_np_size: [1080, 1920, 3]
+ model_name_imagesize_map: *model_imagesizes
+ zmq_kill_switch_sockname: client-kill-1-switch
+ quic_snd_shm_filename: client-service1-incoming-shm
+ quic_rcv_shm_filename: client-service1-outgoing-shm
+ quic_shm_size: *quic-shm-size
+ SLO_TIMEOUT_MS: *slo-timeout
+ - service_id: 2
+ max_entries: *max-log-entries
+ thread_concurrency: 10
+ camera_bidirectional_zmq_sockname: service2-camera-socket
+ camera_stream_shmem_filename: service2-camera-shmem
+ bandwidth_allocation_incoming_zmq_sockname: main-client-2-bw-subscriber
+ quic_rcv_zmq_sockname: car-server-outgoing-2
+ quic_snd_zmq_sockname: car-server-incoming-2
+ outgoing_zmq_diagnostic_sockname: car-client-diagnostics
+ camera_np_size: [1080, 1920, 3]
+ model_name_imagesize_map: *model_imagesizes
+ zmq_kill_switch_sockname: client-kill-2-switch
+ quic_snd_shm_filename: client-service2-incoming-shm
+ quic_rcv_shm_filename: client-service2-outgoing-shm
+ quic_shm_size: *quic-shm-size
+ SLO_TIMEOUT_MS: *slo-timeout
+ - service_id: 3
+ max_entries: *max-log-entries
+ thread_concurrency: 10
+ camera_bidirectional_zmq_sockname: service3-camera-socket
+ camera_stream_shmem_filename: service3-camera-shmem
+ bandwidth_allocation_incoming_zmq_sockname: main-client-3-bw-subscriber
+ quic_rcv_zmq_sockname: car-server-outgoing-3
+ quic_snd_zmq_sockname: car-server-incoming-3
+ outgoing_zmq_diagnostic_sockname: car-client-diagnostics
+ camera_np_size: [1080, 1920, 3]
+ model_name_imagesize_map: *model_imagesizes
+ zmq_kill_switch_sockname: client-kill-3-switch
+ quic_snd_shm_filename: client-service3-incoming-shm
+ quic_rcv_shm_filename: client-service3-outgoing-shm
+ quic_shm_size: *quic-shm-size
+ SLO_TIMEOUT_MS: *slo-timeout
+
+bandwidth_allocator_config:
+ service_id_list: [1, 2, 3]
+ t_SLO: 150 #TODO: change this
+ parquet_eval_dir: /app/full-eval
+ model_info_csv_path: /app/experiment_model_info.csv
+ outgoing_zmq_diagnostic_sockname: car-client-diagnostics
+ outgoing_zmq_client_socknames:
+ - main-client-1-bw-subscriber
+ - main-client-2-bw-subscriber
+ - main-client-3-bw-subscriber
+ bidirectional_zmq_quic_sockname: car-server-bw-service
+ zmq_kill_switch_sockname: bandwidth-allocator-kill-switch
+ bidirectional_zmq_ping_handler_sockname: ping-handler
+
+ping_handler_config:
+ max_entries: 100
+ thread_concurrency: 5
+ bidirectional_zmq_sockname: ping-handler
+ zmq_kill_switch_sockname: ping-handler-kill-switch
+
+camera_stream_config_list:
+ - camera_id: 1 # FRONT
+ usb_id: 0
+ max_entries: *max-log-entries
+ thread_concurrency: 10
+ bidirectional_zmq_sockname: service1-camera-socket
+ camera_stream_shmem_filename: service1-camera-shmem
+ shmem_buf_size: *quic-shm-size
+ camera_np_size: [1080, 1920, 3]
+ zmq_kill_switch_sockname: camera-kill-1-switch
+ mock_camera_image_path: /app/mock_webcam_image.jpg # only used when --mock-camera is passed
+ - camera_id: 2 # FRONT LEFT
+ usb_id: 4 # TODO: CONFIGURE THIS
+ max_entries: *max-log-entries
+ thread_concurrency: 10
+ bidirectional_zmq_sockname: service2-camera-socket
+ camera_stream_shmem_filename: service2-camera-shmem
+ shmem_buf_size: *quic-shm-size
+ camera_np_size: [1080, 1920, 3]
+ zmq_kill_switch_sockname: camera-kill-2-switch
+ mock_camera_image_path: /app/mock_webcam_image.jpg # only used when --mock-camera is passed
+ - camera_id: 3 # FRONT RIGHT
+ usb_id: 8 # TODO: CONFIGURE THIS
+ max_entries: *max-log-entries
+ thread_concurrency: 10
+ bidirectional_zmq_sockname: service3-camera-socket
+ camera_stream_shmem_filename: service3-camera-shmem
+ shmem_buf_size: *quic-shm-size
+ camera_np_size: [1080, 1920, 3]
+ zmq_kill_switch_sockname: camera-kill-3-switch
+ mock_camera_image_path: /app/mock_webcam_image.jpg # only used when --mock-camera is passed
+
+main_plotter_config:
+# service 4 is for junk stream
+ plotting_loop_sleep_seconds: 2
+ zmq_incoming_diagnostic_name: car-client-diagnostics
+ bandwidth_allocation_plot_config:
+ service_id_list: [1, 2, 3]
+ window_size_x: 40
+ bw_min_y: -10
+ bw_max_y: null
+ utility_min_y: -.1
+ utility_max_y: 1.1
+ bw_x_major_loc: 15
+ bw_x_minor_loc: 5
+ bw_y_major_loc: 100
+ bw_y_minor_loc: 20
+ utility_x_major_loc: 15
+ utility_x_minor_loc: 5
+ utility_y_major_loc: .2
+ utility_y_minor_loc: .1
+
+ service_status_plot_config:
+ - service_id: 1
+ window_size_x: 40
+ cnt_min_y: -3
+ cnt_max_y: null
+ rate_min_y: -.5
+ rate_max_y: 1.05
+ cnt_x_major_loc: 15
+ cnt_x_minor_loc: 5
+ cnt_y_major_loc: 30
+ cnt_y_minor_loc: 10
+ rate_x_major_loc: 15
+ rate_x_minor_loc: 5
+ rate_y_major_loc: .2
+ rate_y_minor_loc: .05
+ - service_id: 2
+ window_size_x: 40
+ cnt_min_y: -3
+ cnt_max_y: null
+ rate_min_y: -.5
+ rate_max_y: 1.05
+ cnt_x_major_loc: 15
+ cnt_x_minor_loc: 5
+ cnt_y_major_loc: 30
+ cnt_y_minor_loc: 10
+ rate_x_major_loc: 15
+ rate_x_minor_loc: 5
+ rate_y_major_loc: .2
+ rate_y_minor_loc: .05
+ - service_id: 3
+ window_size_x: 40
+ cnt_min_y: -3
+ cnt_max_y: null
+ rate_min_y: -.5
+ rate_max_y: 1.05
+ cnt_x_major_loc: 15
+ cnt_x_minor_loc: 5
+ cnt_y_major_loc: 30
+ cnt_y_minor_loc: 10
+ rate_x_major_loc: 15
+ rate_x_minor_loc: 5
+ rate_y_major_loc: .2
+ rate_y_minor_loc: .05
+ service_utilization_plot_config:
+ - service_id: 1
+ window_size_x: 40
+ min_y: -10
+ max_y: null
+ x_major_loc: 15
+ x_minor_loc: 5
+ y_major_loc: 100
+ y_minor_loc: 20
+ - service_id: 2
+ window_size_x: 40
+ min_y: -10
+ max_y: null
+ x_major_loc: 15
+ x_minor_loc: 5
+ y_major_loc: 100
+ y_minor_loc: 20
+ - service_id: 3
+ window_size_x: 40
+ min_y: -10
+ max_y: null
+ x_major_loc: 15
+ x_minor_loc: 5
+ y_major_loc: 100
+ y_minor_loc: 20
+ - service_id: 4
+ window_size_x: 40
+ min_y: -10
+ max_y: null
+ x_major_loc: 15
+ x_minor_loc: 5
+ y_major_loc: 100
+ y_minor_loc: 20
diff --git a/docker/config/logging_config_docker.yaml b/docker/config/logging_config_docker.yaml
new file mode 100644
index 0000000..968f08c
--- /dev/null
+++ b/docker/config/logging_config_docker.yaml
@@ -0,0 +1,66 @@
+version: 1
+formatters:
+ verbose_singlethread:
+ format: '%(levelname)s %(asctime)s %(name)s at %(filename)s:%(funcName)s:%(lineno)d: %(message)s'
+ verbose_multithread:
+ format: '%(levelname)s %(asctime)s %(name)s, process %(process)d:%(thread)d at %(filename)s:%(funcName)s:%(lineno)d: %(message)s'
+ simple_singlethread:
+ format: '%(levelname)s %(asctime)s %(name)s: %(message)s'
+ simple_multithread:
+ format: '%(levelname)s %(asctime)s %(name)s, process %(process)d:%(thread)d: %(message)s'
+
+handlers:
+ console:
+ class: logging.StreamHandler
+ level: INFO
+ formatter: verbose_multithread
+ stream: ext://sys.stdout
+
+loggers:
+ bandwidth_allocator:
+ level: INFO
+ handlers: [console] #todo: add any of the above file handlers as needed for each of the below loggers
+ propagate: no
+ bandwidth_visualizer:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ client_main:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ client:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ server_main:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ plotter_main:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ model_server:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ camera_data_stream:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ main_plotter:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ effdet_inference:
+ level: INFO
+ handlers: [console]
+ propagate: no
+ ping_handler:
+ level: INFO
+ handlers: [console]
+ propagate: no
+root:
+ level: NOTSET
+ handlers: [console]
\ No newline at end of file
diff --git a/docker/config/quic_config_client_docker.yaml b/docker/config/quic_config_client_docker.yaml
new file mode 100644
index 0000000..66eb1d3
--- /dev/null
+++ b/docker/config/quic_config_client_docker.yaml
@@ -0,0 +1,35 @@
+experiment_output_dir: /app/experiment2-out
+zmq_dir: /app/zmq-out
+quic_client_log_subdir: quic-client-out
+quic_server_log_subdir: quic-server-out
+
+slo_timeout_ms: 100
+junk_tx_loop_interval_ms: 100
+logging_interval_ms: 500
+init_allocation: 100000000.0
+bw_update_interval_ms: 500
+bw_polling_interval_ms: 200
+
+max_junk_payload_Mb: 5 # .5 Mb/s, this is the bandwidth not the payload size per send loop.
+junk_restart_interval_ms: 500
+
+services: [1, 2, 3, 4]
+
+client_enable_bw_stat_log: True
+client_enable_allocation_stat_log: True
+client_enable_network_stat_log: True
+client_enable_incoming_image_context_log: True
+client_enable_outgoing_image_context_log: True
+
+server_enable_bw_stat_log: True
+server_enable_allocation_stat_log: True
+server_enable_network_stat_log: True
+server_enable_incoming_image_context_log: True
+server_enable_outgoing_image_context_log: True
+
+image_context_log_capacity: 100
+bw_stat_log_capacity: 100
+allocation_stat_log_capacity: 100
+network_stat_log_capacity: 100
+
+enable_junk_service: True #if True, the last service on the list is junk
diff --git a/docker/config/quic_config_gcloud_docker.yaml b/docker/config/quic_config_gcloud_docker.yaml
new file mode 100644
index 0000000..440f301
--- /dev/null
+++ b/docker/config/quic_config_gcloud_docker.yaml
@@ -0,0 +1,34 @@
+experiment_output_dir: /app/experiment2-out
+zmq_dir: /app/zmq-out
+quic_client_log_subdir: quic-client-out
+quic_server_log_subdir: quic-server-out
+
+slo_timeout_ms: 100
+junk_tx_loop_interval_ms: 7
+logging_interval_ms: 500
+init_allocation: 100000000.0
+bw_update_interval_ms: 500
+bw_polling_interval_ms: 200
+max_junk_payload_Mb: .5 # .5 Mb/s, this is the bandwidth not the payload size per send loop.
+junk_restart_interval_ms: 500
+
+services: [1, 2, 3, 4]
+
+client_enable_bw_stat_log: True
+client_enable_allocation_stat_log: True
+client_enable_network_stat_log: True
+client_enable_incoming_image_context_log: True
+client_enable_outgoing_image_context_log: True
+
+server_enable_bw_stat_log: True
+server_enable_allocation_stat_log: True
+server_enable_network_stat_log: True
+server_enable_incoming_image_context_log: True
+server_enable_outgoing_image_context_log: True
+
+image_context_log_capacity: 100
+bw_stat_log_capacity: 100
+allocation_stat_log_capacity: 100
+network_stat_log_capacity: 100
+
+enable_junk_service: True #if True, the last service on the list is junk
diff --git a/docker/config/server_config_gcloud_docker.yaml b/docker/config/server_config_gcloud_docker.yaml
new file mode 100644
index 0000000..0c14dde
--- /dev/null
+++ b/docker/config/server_config_gcloud_docker.yaml
@@ -0,0 +1,68 @@
+logging_config_filepath: /app/logging_config.yaml
+
+experiment_output_dir: /app/experiment2-out
+server_subdir: server
+zmq_dir: /app/zmq-out
+
+MAX_LOG_ENTRIES: &max-log-entries 100
+SHM_FILESIZE: &shm-filesize 50000000
+
+server_model_list: &model-list-ref
+ - checkpoint_path: /app/av-models/tf_efficientdet_d2-waymo-open-dataset/version_2/checkpoints/epoch=9-step=419700.ckpt
+ num_classes: 5
+ image_size: [768, 768]
+ base_model: "tf_efficientdet_d2"
+ - checkpoint_path: /app/av-models/tf_efficientdet_d4-waymo-open-dataset/version_0/checkpoints/epoch=9-step=839400.ckpt
+ num_classes: 5
+ image_size: [1024, 1024]
+ base_model: "tf_efficientdet_d4"
+ - checkpoint_path: /app/av-models/tf_efficientdet_d6-waymo-open-dataset/version_2/checkpoints/epoch=9-step=3357600.ckpt
+ num_classes: 5
+ image_size: [1280, 1280]
+ base_model: "tf_efficientdet_d6"
+ - checkpoint_path: /app/av-models/tf_efficientdet_d7x-waymo-open-dataset/version_1/checkpoints/epoch=8-step=1477071.ckpt
+ num_classes: 5
+ image_size: [1536, 1536]
+ base_model: "tf_efficientdet_d7x"
+
+
+server_config_list:
+ - service_id: 1
+ max_entries: *max-log-entries
+ model_metadata_list: *model-list-ref
+ device: "cuda:0"
+ incoming_zmq_sockname: remote-server-outgoing-1
+ incoming_shm_filename: server-service1-outgoing-shm
+ outgoing_zmq_sockname: remote-server-incoming-1
+ outgoing_shm_filename: server-service1-incoming-shm
+ thread_concurrency: 10
+ shm_filesize: *shm-filesize
+ zmq_kill_switch_sockname: remote-server-kill-switch-1
+ mock_inference_output_path: /app/example_effdet_d4_output.npy # only used when --mock-inference is passed
+ mock_model_latency_csv_path: /app/experiment_model_info.csv # only used when --mock-inference is passed
+ - service_id: 2
+ max_entries: *max-log-entries
+ model_metadata_list: *model-list-ref
+ device: "cuda:1"
+ incoming_zmq_sockname: remote-server-outgoing-2
+ incoming_shm_filename: server-service2-outgoing-shm
+ outgoing_zmq_sockname: remote-server-incoming-2
+ outgoing_shm_filename: server-service2-incoming-shm
+ thread_concurrency: 10
+ shm_filesize: *shm-filesize
+ zmq_kill_switch_sockname: remote-server-kill-switch-2
+ mock_inference_output_path: /app/example_effdet_d4_output.npy # only used when --mock-inference is passed
+ mock_model_latency_csv_path: /app/experiment_model_info.csv # only used when --mock-inference is passed
+ - service_id: 3
+ max_entries: *max-log-entries
+ model_metadata_list: *model-list-ref
+ device: "cuda:2"
+ incoming_zmq_sockname: remote-server-outgoing-3
+ incoming_shm_filename: server-service3-outgoing-shm
+ outgoing_zmq_sockname: remote-server-incoming-3
+ outgoing_shm_filename: server-service3-incoming-shm
+ thread_concurrency: 10
+ shm_filesize: *shm-filesize
+ zmq_kill_switch_sockname: remote-server-kill-switch-3
+ mock_inference_output_path: /app/example_effdet_d4_output.npy # only used when --mock-inference is passed
+ mock_model_latency_csv_path: /app/experiment_model_info.csv # only used when --mock-inference is passed
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
index a2e33c1..10fcddb 100644
--- a/docs/CONFIGURATION.md
+++ b/docs/CONFIGURATION.md
@@ -42,7 +42,6 @@ experiment_output_dir: /path/to/experiment-out
client_subdir: client
zmq_dir: /path/to/experiment-out/zmq
-DST_IP: &dst-ip # Cloud server IP for ICMP pings
SLO_TIMEOUT: &slo-timeout 200 # Client-side SLO timeout (ms), shared via anchor
QUIC_SHM_SIZE: &quic-shm-size 50000000 # Shared memory region size (bytes, ~50 MB)
MAX_LOG_ENTRIES: &max-log-entries 100 # Max records in memory before spilling to Parquet
@@ -54,7 +53,6 @@ MAX_LOG_ENTRIES: &max-log-entries 100 # Max records in memory before spilling
| `experiment_output_dir` | **Yes** | Base output directory. `client_main.py` creates a timestamped subdirectory here (e.g., `experiment-out/client_main_2024-01-15_10-30-00/`) for each run |
| `client_subdir` | No | Subdirectory name within the timestamped run directory for client Parquet logs. Default `client` |
| `zmq_dir` | **Yes** | Directory for ZMQ IPC socket files. Must match across client, server, and QUIC configs. Created automatically if it doesn't exist |
-| `DST_IP` | **Yes** | Public IP of your cloud server. Used by PingHandler for RTT measurement |
| `SLO_TIMEOUT` | Maybe | Service-level objective timeout in ms. Frames exceeding this are dropped. Default `200` is suitable for most setups |
| `QUIC_SHM_SIZE` | No | Size of each POSIX shared memory region in bytes. `50000000` (50 MB) is sufficient for HD frames |
| `MAX_LOG_ENTRIES` | No | Number of log records buffered in memory before flushing to Parquet. Higher = fewer I/O flushes, more memory |
@@ -199,7 +197,6 @@ camera_stream_config_list:
```yaml
ping_handler_config:
- dst_ip: *dst-ip
max_entries: 100
thread_concurrency: 5
bidirectional_zmq_sockname: ping-handler
@@ -210,7 +207,7 @@ ping_handler_config:
| Parameter | Must customize? | Description |
|-----------|:-:|---|
-| `dst_ip` | **Yes** | IP address to ping for RTT measurement. Should be your cloud server's public IP (uses global `DST_IP` anchor) |
+| `dst_ip` | N/A | Set automatically by `client_main.py` from the `--server_address` CLI argument (port stripped) |
| `max_entries` | No | Log buffer size |
| `thread_concurrency` | No | Thread pool size |
| `bidirectional_zmq_sockname` | No | Bare ZMQ socket name. Must match the bandwidth allocator's `bidirectional_zmq_ping_handler_sockname` |
@@ -480,7 +477,6 @@ mkdir -p ~/experiment-out
- [ ] `logging_config_filepath` points to `config/logging_config.yaml` (absolute path)
- [ ] `experiment_output_dir` is set to your chosen output directory
- [ ] `zmq_dir` is set (e.g., `~/experiment-out/zmq`) and matches all other configs
-- [ ] `DST_IP` is set to your cloud server's public IP address
- [ ] `parquet_eval_dir` points to the directory containing pre-computed utility curve Parquet files
- [ ] `model_info_csv_path` points to the `experiment_model_info.csv` file
- [ ] `camera_stream_config_list[].usb_id` matches your USB camera device IDs (run `ls /dev/video*`)
diff --git a/docs/IPC.md b/docs/IPC.md
index 1a6765b..0ee9c85 100644
--- a/docs/IPC.md
+++ b/docs/IPC.md
@@ -131,7 +131,7 @@ This section documents every IPC channel between components. All connections use
| Peer | Transport | Socket Name | Pattern | Direction | Purpose |
|------|-----------|-------------|---------|-----------|---------|
-| BandwidthAllocator | ZMQ REP (bind) | `ping-handler` | REQ/REP | BandwidthAllocator → PingHandler | Responds to RTT queries with the latest ICMP ping measurement to `DST_IP` (the cloud server) |
+| BandwidthAllocator | ZMQ REP (bind) | `ping-handler` | REQ/REP | BandwidthAllocator → PingHandler | Responds to RTT queries with the latest ICMP ping measurement to the server (IP from `--server_address` CLI arg) |
| client_main | ZMQ SUB (bind) | `ping-handler-kill-switch` | PUB/SUB | client_main → PingHandler | Receive "ABORT" signal for graceful shutdown |
**Config**: `ping_handler_config` in `config/client_config.yaml`
diff --git a/src/python/client.py b/src/python/client.py
index d57151e..885f598 100644
--- a/src/python/client.py
+++ b/src/python/client.py
@@ -304,7 +304,7 @@ class Client:
- Detailed latency logging to Parquet files
"""
- def __init__(self, config: ClientConfig) -> None:
+ def __init__(self, config: ClientConfig, ready_queue=None) -> None:
self.service_id = config.service_id
self._is_cleaned_up = False
self.spillable_store = ClientSpillableStore(
@@ -344,6 +344,9 @@ def __init__(self, config: ClientConfig) -> None:
self.quic_rcv_zmq_socket = self.context.socket(zmq.REP)
self.quic_rcv_zmq_socket.bind(config.quic_rcv_zmq_sockname)
+ if ready_queue is not None:
+ ready_queue.put(self.service_id)
+
# Create kill switch early so we can check it during the handshake wait
self.quic_kill_switch = self.context.socket(zmq.SUB)
self.quic_kill_switch.setsockopt_string(zmq.SUBSCRIBE, "")
diff --git a/src/python/client_main.py b/src/python/client_main.py
index 3d66a42..3cfe34b 100644
--- a/src/python/client_main.py
+++ b/src/python/client_main.py
@@ -37,7 +37,7 @@
import signal
import traceback
import yaml
-from multiprocessing import Pool, Queue, Pipe
+from multiprocessing import Manager, Pool, Pipe
import time
import os
import argparse
@@ -62,6 +62,18 @@
required=True,
help="path to server config file",
)
+ parser.add_argument(
+ "-s",
+ "--server_address",
+ type=str,
+ required=True,
+ help="server address (host or host:port) — IP is extracted for ping handler",
+ )
+ parser.add_argument(
+ "--mock-camera",
+ action="store_true",
+ help="enable mock camera mode — use static images from paths in config instead of USB cameras",
+ )
args = parser.parse_args()
@@ -120,6 +132,7 @@ def resolve_zmq(name: str) -> str:
ping_doc = config_doc["ping_handler_config"]
ping_doc["ping_savedir"] = str(client_dir)
+ ping_doc["dst_ip"] = args.server_address.split(":")[0]
for key in ["bidirectional_zmq_sockname", "zmq_kill_switch_sockname"]:
ping_doc[key] = resolve_zmq(ping_doc[key])
@@ -127,6 +140,20 @@ def resolve_zmq(name: str) -> str:
doc["camera_savedir"] = str(client_dir)
for key in ["bidirectional_zmq_sockname", "zmq_kill_switch_sockname"]:
doc[key] = resolve_zmq(doc[key])
+ if not args.mock_camera:
+ doc["mock_camera_image_path"] = None
+
+ if args.mock_camera:
+ logger.warning(
+ "\n"
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
+ "@ WARNING: MOCK CAMERA MODE IS ENABLED! @\n"
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
+ "All camera streams will serve a static image from disk\n"
+ "instead of capturing from USB cameras.\n"
+ "This is intended for testing only.\n"
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+ )
plotter_doc = config_doc["main_plotter_config"]
plotter_doc["zmq_incoming_diagnostic_name"] = resolve_zmq(
@@ -152,10 +179,10 @@ def run_bandwidth_allocator(bw_config):
except GracefulShutdown:
return None
- def run_main_client(client_config):
+ def run_main_client(client_config, ready_queue):
signal.signal(signal.SIGINT, signal.SIG_IGN)
try:
- return Client(client_config).main_loop()
+ return Client(client_config, ready_queue=ready_queue).main_loop()
except GracefulShutdown:
return None
@@ -197,8 +224,9 @@ def run_ping_handler(ping_config):
zmq_context = zmq.Context()
- # track the original signint_handler to restore it later.
+ # track the original signal handlers to restore them later.
original_sigint_handler = signal.getsignal(signal.SIGINT)
+ original_sigterm_handler = signal.getsignal(signal.SIGTERM)
with Pool(processes=total_processes) as pool:
early_abort = False
@@ -220,6 +248,7 @@ def signal_handler(sig, frame):
signal_exit = True
signal.signal(signal.SIGINT, signal_handler)
+ signal.signal(signal.SIGTERM, signal_handler)
# Phase 1: Create all configs and kill switches before starting any processes.
# This ensures err_callback can send ABORT to all processes if one fails early.
@@ -279,16 +308,32 @@ def signal_handler(sig, frame):
# Start all client processes
logger.info("Starting %d client processes", num_client_processes)
+ manager = Manager()
+ client_ready_queue = manager.Queue()
for client_config in client_configs:
logger.debug("Launching client process for service_id=%d", client_config.service_id)
proc_name = f"Client-{client_config.service_id}"
proc_results.append((
proc_name,
pool.apply_async(
- run_main_client, [client_config], error_callback=err_callback
+ run_main_client, [client_config, client_ready_queue], error_callback=err_callback
)
))
+ # Wait for all client processes to bind quic_rcv_zmq_socket before
+ # signaling readiness. Rust QUIC clients (in a separate Docker container)
+ # depend on this health signal to start, so we must ensure the Python
+ # sockets are bound and ready to accept connections first.
+ logger.info("Waiting for %d client processes to bind quic_rcv_zmq_socket...", num_client_processes)
+ for _ in range(num_client_processes):
+ service_id = client_ready_queue.get()
+ logger.info("Client %d: quic_rcv_zmq_socket bound and ready", service_id)
+
+ health_signal = Path("/health/client_main_ready")
+ if health_signal.parent.exists():
+ health_signal.touch()
+ logger.info("Health signal written: %s", health_signal)
+
# Start bandwidth allocator process
logger.info("Starting bandwidth allocator process")
proc_results.append((
@@ -354,6 +399,7 @@ def signal_handler(sig, frame):
logger.info("Pool will exit. If needed, press Ctrl-C again to terminate the program finally.")
signal.signal(signal.SIGINT, original_sigint_handler)
+ signal.signal(signal.SIGTERM, original_sigterm_handler)
logger.info("Exiting kill switches...")
for ks in kill_switches:
diff --git a/src/python/server_main.py b/src/python/server_main.py
index 2ad591e..3db40ab 100644
--- a/src/python/server_main.py
+++ b/src/python/server_main.py
@@ -54,6 +54,11 @@
required=True,
help="path to server config file",
)
+ parser.add_argument(
+ "--mock-inference",
+ action="store_true",
+ help="enable mock inference mode — skip GPU model loading and return pre-recorded detections from paths in config",
+ )
args = parser.parse_args()
@@ -87,6 +92,21 @@
doc["server_log_savedir"] = str(server_dir)
for key in ["incoming_zmq_sockname", "outgoing_zmq_sockname", "zmq_kill_switch_sockname"]:
doc[key] = f"ipc://{zmq_dir / doc[key]}"
+ if not args.mock_inference:
+ doc["mock_inference_output_path"] = None
+ doc["mock_model_latency_csv_path"] = None
+
+ if args.mock_inference:
+ logger.warning(
+ "\n"
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
+ "@ WARNING: MOCK INFERENCE MODE IS ENABLED! @\n"
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"
+ "All model servers will skip GPU model loading and return\n"
+ "pre-recorded detection results instead of running inference.\n"
+ "This is intended for testing only.\n"
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+ )
logger.info("experiment starting.")
@@ -103,8 +123,9 @@ def run_server(server_config):
num_server_processes = len(config_doc["server_config_list"])
logger.info("Initializing process pool with %d ModelServer processes", num_server_processes)
- # track the original signint_handler to restore it later.
+ # track the original signal handlers to restore them later.
original_sigint_handler = signal.getsignal(signal.SIGINT)
+ original_sigterm_handler = signal.getsignal(signal.SIGTERM)
zmq_context = zmq.Context()
with Pool(processes=num_server_processes) as pool:
@@ -127,6 +148,7 @@ def signal_handler(sig, frame):
signal_exit = True
signal.signal(signal.SIGINT, signal_handler)
+ signal.signal(signal.SIGTERM, signal_handler)
# Phase 1: Create all configs and kill switches before starting any processes.
# This ensures err_callback can send ABORT to all processes if one fails early.
@@ -205,6 +227,7 @@ def signal_handler(sig, frame):
logger.info("Pool will exit. If needed, press Ctrl-C again to terminate the program finally.")
signal.signal(signal.SIGINT, original_sigint_handler)
+ signal.signal(signal.SIGTERM, original_sigterm_handler)
for ks in kill_switches:
ks.close(linger=0)
diff --git a/src/python/web_frontend/start_web_dashboard.py b/src/python/web_frontend/start_web_dashboard.py
index c325a84..e728e5d 100755
--- a/src/python/web_frontend/start_web_dashboard.py
+++ b/src/python/web_frontend/start_web_dashboard.py
@@ -28,7 +28,7 @@ def parse_args():
help="Host to bind the server to (default: 0.0.0.0)",
)
parser.add_argument(
- "--config", type=str, help="Path to configuration file (optional)"
+ "-c", "--config", type=str, help="Path to configuration file (optional)"
)
parser.add_argument(
"--refresh-rate",
diff --git a/src/python/web_frontend/web_frontend.py b/src/python/web_frontend/web_frontend.py
index 0d08663..4a7e6df 100644
--- a/src/python/web_frontend/web_frontend.py
+++ b/src/python/web_frontend/web_frontend.py
@@ -353,12 +353,19 @@ def main(config: str):
web_plotter = WebPlotterAdapter(config, web_config.plotting_loop_sleep_seconds)
+ # Signal readiness for Docker healthcheck (used by depends_on sequencing)
+ from pathlib import Path
+ health_signal = Path("/health/monitor_ready")
+ if health_signal.parent.exists():
+ health_signal.touch()
+ LOGGER.info("Health signal written: %s", health_signal)
+
# Start the plotting loop
web_plotter.run_plotting_loop()
LOGGER.info("Starting web server on http://localhost:5000")
LOGGER.info("Press Ctrl+C to stop")
- socketio.run(app, host="0.0.0.0", port=5000, debug=False)
+ socketio.run(app, host="0.0.0.0", port=5000, debug=False, allow_unsafe_werkzeug=True)
except KeyboardInterrupt:
LOGGER.info("Shutting down...")
diff --git a/src/quic/Cargo.lock b/src/quic/Cargo.lock
new file mode 100644
index 0000000..6079877
--- /dev/null
+++ b/src/quic/Cargo.lock
@@ -0,0 +1,4936 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "argminmax"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70f13d10a41ac8d2ec79ee34178d61e6f47a29c2edfe7ef1721c7383b0359e65"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "array-init-cursor"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed51fe0f224d1d4ea768be38c51f9f831dee9d05c163c11fba0b8c44387b1fc3"
+
+[[package]]
+name = "arraydeque"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
+
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.88"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "asynchronous-codec"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233"
+dependencies = [
+ "bytes",
+ "futures-sink",
+ "futures-util",
+ "memchr",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "atoi_simd"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a49e05797ca52e312a0c658938b7d00693ef037799ef7187678f212d7684cf"
+dependencies = [
+ "debug_unsafe",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "atomic_float"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a"
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "aws-lc-rs"
+version = "1.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
+dependencies = [
+ "aws-lc-sys",
+ "untrusted 0.7.1",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
+dependencies = [
+ "bindgen",
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.69.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "itertools 0.12.1",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash 1.1.0",
+ "shlex",
+ "syn",
+ "which",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "blake3"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "brotli"
+version = "7.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+
+[[package]]
+name = "bytemuck"
+version = "1.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "castaway"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
+dependencies = [
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "chrono"
+version = "0.4.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "chrono-tz"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3"
+dependencies = [
+ "chrono",
+ "phf",
+]
+
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
+
+[[package]]
+name = "cmake"
+version = "0.1.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
+name = "comfy-table"
+version = "7.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a"
+dependencies = [
+ "crossterm",
+ "unicode-segmentation",
+ "unicode-width",
+]
+
+[[package]]
+name = "compact_str"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
+dependencies = [
+ "castaway",
+ "cfg-if",
+ "itoa",
+ "rustversion",
+ "ryu",
+ "serde",
+ "static_assertions",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "config"
+version = "0.15.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b1eb4fb07bc7f012422df02766c7bd5971effb894f573865642f06fa3265440"
+dependencies = [
+ "async-trait",
+ "convert_case",
+ "json5",
+ "pathdiff",
+ "ron",
+ "rust-ini",
+ "serde",
+ "serde_json",
+ "toml",
+ "winnow",
+ "yaml-rust2",
+]
+
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom 0.2.16",
+ "once_cell",
+ "tiny-keccak",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
+
+[[package]]
+name = "convert_case"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crossterm"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "parking_lot",
+ "rustix 0.38.44",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "crunchy"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "ctrlc"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "881c5d0a13b2f1498e2306e82cbada78390e152d4b1378fb28a84f4dcd0dc4f3"
+dependencies = [
+ "dispatch",
+ "nix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "cuckoofilter"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b810a8449931679f64cd7eef1bbd0fa315801b6d5d9cdc1ace2804d6529eee18"
+dependencies = [
+ "byteorder",
+ "fnv",
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
+dependencies = [
+ "cfg-if",
+ "hashbrown 0.14.5",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "debug_unsafe"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85d3cef41d236720ed453e102153a53e4cc3d2fde848c0078a50cf249e8e3e5b"
+
+[[package]]
+name = "deranged"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dlv-list"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
+dependencies = [
+ "const-random",
+]
+
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "enum_dispatch"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
+dependencies = [
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "env_filter"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.11.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "env_filter",
+ "jiff",
+ "log",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
+dependencies = [
+ "libc",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "ethnum"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b"
+
+[[package]]
+name = "event-listener"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "fast-float2"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55"
+
+[[package]]
+name = "flate2"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
+dependencies = [
+ "crc32fast",
+ "libz-rs-sys",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fs4"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4"
+dependencies = [
+ "rustix 1.0.8",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "glob"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+
+[[package]]
+name = "h2"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hash_hasher"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b4b9ebce26001bad2e6366295f64e381c1e9c479109202149b9e15e154973e9"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+ "rayon",
+ "serde",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+ "rayon",
+ "serde",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
+dependencies = [
+ "hashbrown 0.15.4",
+]
+
+[[package]]
+name = "hdrhistogram"
+version = "7.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
+dependencies = [
+ "byteorder",
+ "num-traits",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hex-literal"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
+
+[[package]]
+name = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "http"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "humantime"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
+
+[[package]]
+name = "hyper"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2 0.6.0",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core 0.61.2",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+
+[[package]]
+name = "icu_properties"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "potential_utf",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+
+[[package]]
+name = "icu_provider"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.15.4",
+ "serde",
+]
+
+[[package]]
+name = "intrusive-collections"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86"
+dependencies = [
+ "memoffset",
+]
+
+[[package]]
+name = "io-uring"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+
+[[package]]
+name = "iri-string"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jiff"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
+dependencies = [
+ "jiff-static",
+ "log",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde",
+]
+
+[[package]]
+name = "jiff-static"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
+dependencies = [
+ "getrandom 0.3.3",
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "json5"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
+dependencies = [
+ "pest",
+ "pest_derive",
+ "serde",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "libc"
+version = "0.2.174"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
+
+[[package]]
+name = "libloading"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.53.2",
+]
+
+[[package]]
+name = "libm"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
+
+[[package]]
+name = "libz-rs-sys"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221"
+dependencies = [
+ "zlib-rs",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
+
+[[package]]
+name = "litemap"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+
+[[package]]
+name = "lock_api"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[package]]
+name = "lz4"
+version = "1.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4"
+dependencies = [
+ "lz4-sys",
+]
+
+[[package]]
+name = "lz4-sys"
+version = "1.11.1+lz4-1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+
+[[package]]
+name = "memmap2"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+dependencies = [
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "nix"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "now"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d89e9874397a1f0a52fc1f197a8effd9735223cb2390e9dcc83ac6cd02923d0"
+dependencies = [
+ "chrono",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "object_store"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efc4f07659e11cd45a341cd24d71e683e3be65d9ff1f8150061678fe60437496"
+dependencies = [
+ "async-trait",
+ "base64 0.22.1",
+ "bytes",
+ "chrono",
+ "form_urlencoded",
+ "futures",
+ "http",
+ "http-body-util",
+ "humantime",
+ "hyper",
+ "itertools 0.14.0",
+ "parking_lot",
+ "percent-encoding",
+ "quick-xml",
+ "rand 0.9.2",
+ "reqwest",
+ "ring",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+ "url",
+ "walkdir",
+ "wasm-bindgen-futures",
+ "web-time",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
+
+[[package]]
+name = "ordered-multimap"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
+dependencies = [
+ "dlv-list",
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+
+[[package]]
+name = "pem"
+version = "3.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3"
+dependencies = [
+ "base64 0.22.1",
+ "serde",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pest"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
+dependencies = [
+ "memchr",
+ "thiserror 2.0.12",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5"
+dependencies = [
+ "pest",
+ "sha2",
+]
+
+[[package]]
+name = "phf"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "planus"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1691dd09e82f428ce8d6310bd6d5da2557c82ff17694d2a32cad7242aea89f"
+dependencies = [
+ "array-init-cursor",
+]
+
+[[package]]
+name = "polars"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0c10a02dc15223de108e0625bf152efb8dc3b181c5916b5ee335e40dcda735a"
+dependencies = [
+ "getrandom 0.2.16",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-io",
+ "polars-lazy",
+ "polars-ops",
+ "polars-parquet",
+ "polars-sql",
+ "polars-time",
+ "polars-utils",
+ "version_check",
+]
+
+[[package]]
+name = "polars-arrow"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef8c08875db45de6f71660ef15a686e459ab0dddae302b2870d66625fd3ba65"
+dependencies = [
+ "atoi_simd",
+ "bitflags",
+ "bytemuck",
+ "chrono",
+ "chrono-tz",
+ "dyn-clone",
+ "either",
+ "ethnum",
+ "getrandom 0.2.16",
+ "hashbrown 0.15.4",
+ "itoa",
+ "lz4",
+ "num-traits",
+ "polars-arrow-format",
+ "polars-error",
+ "polars-schema",
+ "polars-utils",
+ "serde",
+ "simdutf8",
+ "streaming-iterator",
+ "strum_macros",
+ "version_check",
+ "zstd",
+]
+
+[[package]]
+name = "polars-arrow-format"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b0ef2474af9396b19025b189d96e992311e6a47f90c53cd998b36c4c64b84c"
+dependencies = [
+ "planus",
+ "serde",
+]
+
+[[package]]
+name = "polars-compute"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b54171a366ec0bd3431ce184568e731de14060c20e19f306706cb073e7a98dc1"
+dependencies = [
+ "atoi_simd",
+ "bytemuck",
+ "chrono",
+ "either",
+ "fast-float2",
+ "hashbrown 0.15.4",
+ "itoa",
+ "num-traits",
+ "polars-arrow",
+ "polars-error",
+ "polars-utils",
+ "rand 0.8.5",
+ "ryu",
+ "serde",
+ "skiplist",
+ "strength_reduce",
+ "strum_macros",
+ "version_check",
+]
+
+[[package]]
+name = "polars-core"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d13c50b27ed6df2f6a9d156e9a4960e9373bf65fc763e0be2738efa5457d915"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "chrono",
+ "chrono-tz",
+ "comfy-table",
+ "either",
+ "hashbrown 0.14.5",
+ "hashbrown 0.15.4",
+ "indexmap",
+ "itoa",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-error",
+ "polars-row",
+ "polars-schema",
+ "polars-utils",
+ "rand 0.8.5",
+ "rand_distr",
+ "rayon",
+ "regex",
+ "serde",
+ "serde_json",
+ "strum_macros",
+ "version_check",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "polars-error"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa800b7240c7326e54d6b95df8376e3ee760e4cb4170fe38de1f42d14d719ffc"
+dependencies = [
+ "object_store",
+ "parking_lot",
+ "polars-arrow-format",
+ "regex",
+ "signal-hook",
+ "simdutf8",
+]
+
+[[package]]
+name = "polars-expr"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0f1673599bee079c94d7d7e7e0a31818c7c88060dcf6ec82691a99e1eb5cf9"
+dependencies = [
+ "bitflags",
+ "hashbrown 0.15.4",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-plan",
+ "polars-row",
+ "polars-time",
+ "polars-utils",
+ "rand 0.8.5",
+ "rayon",
+ "recursive",
+]
+
+[[package]]
+name = "polars-io"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1db23d90c17ab03ccded685e5a88aa8ee43fd851376463a9533ed56ba66d1538"
+dependencies = [
+ "async-trait",
+ "atoi_simd",
+ "blake3",
+ "bytes",
+ "chrono",
+ "fast-float2",
+ "fs4",
+ "futures",
+ "glob",
+ "hashbrown 0.15.4",
+ "home",
+ "itoa",
+ "memchr",
+ "memmap2",
+ "num-traits",
+ "object_store",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-parquet",
+ "polars-schema",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "reqwest",
+ "ryu",
+ "serde",
+ "serde_json",
+ "simdutf8",
+ "tokio",
+ "tokio-util",
+ "url",
+]
+
+[[package]]
+name = "polars-lazy"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03708e73a20fd7ca9fb0003e620daff5c488c9e2b287640289de78c5b135ead7"
+dependencies = [
+ "bitflags",
+ "chrono",
+ "either",
+ "memchr",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-expr",
+ "polars-io",
+ "polars-mem-engine",
+ "polars-ops",
+ "polars-pipe",
+ "polars-plan",
+ "polars-stream",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "version_check",
+]
+
+[[package]]
+name = "polars-mem-engine"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52126424d9612132b0b8b4b995ea25a42d79559d111063ef705a370dfa742d46"
+dependencies = [
+ "futures",
+ "memmap2",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-expr",
+ "polars-io",
+ "polars-ops",
+ "polars-plan",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "recursive",
+ "tokio",
+]
+
+[[package]]
+name = "polars-ops"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e308800c11d5c6d0ddef16186ca026691aedeeb2210d458cdea997520907917"
+dependencies = [
+ "argminmax",
+ "base64 0.22.1",
+ "bytemuck",
+ "chrono",
+ "chrono-tz",
+ "either",
+ "hashbrown 0.15.4",
+ "hex",
+ "indexmap",
+ "libm",
+ "memchr",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-error",
+ "polars-schema",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "regex-syntax 0.8.5",
+ "strum_macros",
+ "unicode-normalization",
+ "unicode-reverse",
+ "version_check",
+]
+
+[[package]]
+name = "polars-parquet"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e186177e24e217ce5b9bf441028417faeeef01b7e793fa815e1d26a53c38400"
+dependencies = [
+ "async-stream",
+ "base64 0.22.1",
+ "brotli",
+ "bytemuck",
+ "ethnum",
+ "flate2",
+ "futures",
+ "hashbrown 0.15.4",
+ "lz4",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-error",
+ "polars-parquet-format",
+ "polars-utils",
+ "serde",
+ "simdutf8",
+ "snap",
+ "streaming-decompression",
+ "zstd",
+]
+
+[[package]]
+name = "polars-parquet-format"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c025243dcfe8dbc57e94d9f82eb3bef10b565ab180d5b99bed87fd8aea319ce1"
+dependencies = [
+ "async-trait",
+ "futures",
+]
+
+[[package]]
+name = "polars-pipe"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59780346591510f17e26a2cb7a5f812167e9487163baf6570a53a4fab3e004f3"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-queue",
+ "enum_dispatch",
+ "futures",
+ "hashbrown 0.15.4",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-expr",
+ "polars-io",
+ "polars-ops",
+ "polars-plan",
+ "polars-row",
+ "polars-utils",
+ "rayon",
+ "uuid",
+ "version_check",
+]
+
+[[package]]
+name = "polars-plan"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb0432eed610f25f436b519c007d4c6e2607c645ff27324aaaafda34ef51bf"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "bytes",
+ "chrono",
+ "chrono-tz",
+ "either",
+ "futures",
+ "hashbrown 0.15.4",
+ "memmap2",
+ "num-traits",
+ "percent-encoding",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-io",
+ "polars-ops",
+ "polars-parquet",
+ "polars-time",
+ "polars-utils",
+ "rayon",
+ "recursive",
+ "regex",
+ "strum_macros",
+ "version_check",
+]
+
+[[package]]
+name = "polars-row"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0be6a32d19ccae4dad60ef0394972419c771b393d31e1b141a89c69de5b33990"
+dependencies = [
+ "bitflags",
+ "bytemuck",
+ "polars-arrow",
+ "polars-compute",
+ "polars-error",
+ "polars-utils",
+]
+
+[[package]]
+name = "polars-schema"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12b56ea2026a869b9bae7d8bd861b420d72e7bca4cf4b757368874577ac666b6"
+dependencies = [
+ "indexmap",
+ "polars-error",
+ "polars-utils",
+ "serde",
+ "version_check",
+]
+
+[[package]]
+name = "polars-sql"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ada7336bd78c67d896f9a13082b02e4a159c6c6fba777dcc152c0b1f126453b"
+dependencies = [
+ "bitflags",
+ "hex",
+ "polars-core",
+ "polars-error",
+ "polars-lazy",
+ "polars-ops",
+ "polars-plan",
+ "polars-time",
+ "polars-utils",
+ "rand 0.8.5",
+ "regex",
+ "serde",
+ "sqlparser",
+]
+
+[[package]]
+name = "polars-stream"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d81638ef3da43d632da0250386bf4f042fe514005f2a6d59d9e6118194888eb7"
+dependencies = [
+ "async-channel",
+ "async-trait",
+ "atomic-waker",
+ "bitflags",
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-queue",
+ "crossbeam-utils",
+ "futures",
+ "memmap2",
+ "parking_lot",
+ "percent-encoding",
+ "pin-project-lite",
+ "polars-arrow",
+ "polars-core",
+ "polars-error",
+ "polars-expr",
+ "polars-io",
+ "polars-mem-engine",
+ "polars-ops",
+ "polars-parquet",
+ "polars-plan",
+ "polars-utils",
+ "rand 0.8.5",
+ "rayon",
+ "recursive",
+ "slotmap",
+ "tokio",
+ "version_check",
+]
+
+[[package]]
+name = "polars-time"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "706ea1e67d5bfcfd9e5ca59070fb5ad591d42b9729d4a60af1b320e645ea158e"
+dependencies = [
+ "atoi_simd",
+ "bytemuck",
+ "chrono",
+ "chrono-tz",
+ "now",
+ "num-traits",
+ "polars-arrow",
+ "polars-compute",
+ "polars-core",
+ "polars-error",
+ "polars-ops",
+ "polars-utils",
+ "rayon",
+ "regex",
+ "strum_macros",
+]
+
+[[package]]
+name = "polars-utils"
+version = "0.48.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c50cd0dac46936771793eb22cb7aeeef97d6aaa4f832f4209637e73178d39aa"
+dependencies = [
+ "bincode",
+ "bytemuck",
+ "bytes",
+ "compact_str",
+ "flate2",
+ "foldhash",
+ "hashbrown 0.15.4",
+ "indexmap",
+ "libc",
+ "memmap2",
+ "num-traits",
+ "polars-error",
+ "rand 0.8.5",
+ "raw-cpuid",
+ "rayon",
+ "regex",
+ "rmp-serde",
+ "serde",
+ "serde_ignored",
+ "serde_json",
+ "slotmap",
+ "stacker",
+ "sysinfo",
+ "version_check",
+]
+
+[[package]]
+name = "portable-atomic"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
+dependencies = [
+ "portable-atomic",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.2.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "psm"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "quic_client"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "atomic_float",
+ "bytes",
+ "chrono",
+ "clap",
+ "config",
+ "ctrlc",
+ "env_logger",
+ "hdrhistogram",
+ "libc",
+ "log",
+ "polars",
+ "quic_conn",
+ "rcgen",
+ "rustls",
+ "s2n-quic",
+ "serde",
+ "serde_json",
+ "tokio",
+ "tokio-context",
+ "tracing",
+ "tracing-subscriber",
+ "zeromq",
+]
+
+[[package]]
+name = "quic_conn"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "atomic_float",
+ "bytes",
+ "clap",
+ "config",
+ "ctrlc",
+ "env_logger",
+ "hdrhistogram",
+ "libc",
+ "log",
+ "polars",
+ "rcgen",
+ "rustls",
+ "s2n-quic",
+ "serde",
+ "serde_json",
+ "tokio",
+ "tokio-context",
+ "tracing",
+ "tracing-subscriber",
+ "zeromq",
+]
+
+[[package]]
+name = "quic_server"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "atomic_float",
+ "bytes",
+ "chrono",
+ "clap",
+ "config",
+ "ctrlc",
+ "env_logger",
+ "hdrhistogram",
+ "libc",
+ "log",
+ "polars",
+ "quic_conn",
+ "rcgen",
+ "rustls",
+ "s2n-quic",
+ "serde",
+ "serde_json",
+ "tokio",
+ "tokio-context",
+ "tracing",
+ "tracing-subscriber",
+ "zeromq",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.38.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash 2.1.1",
+ "rustls",
+ "socket2 0.5.10",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
+dependencies = [
+ "bytes",
+ "getrandom 0.3.3",
+ "lru-slab",
+ "rand 0.9.2",
+ "ring",
+ "rustc-hash 2.1.1",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.12",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2 0.5.10",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.3",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.3",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.3",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "raw-cpuid"
+version = "11.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "rcgen"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2"
+dependencies = [
+ "pem",
+ "ring",
+ "rustls-pki-types",
+ "time",
+ "yasna",
+]
+
+[[package]]
+name = "recursive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e"
+dependencies = [
+ "recursive-proc-macro-impl",
+ "stacker",
+]
+
+[[package]]
+name = "recursive-proc-macro-impl"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7251471db004e509f4e75a62cca9435365b5ec7bcdff530d612ac7c87c44a792"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "reqwest"
+version = "0.12.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tokio-util",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.16",
+ "libc",
+ "untrusted 0.9.0",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rmp"
+version = "0.8.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4"
+dependencies = [
+ "byteorder",
+ "num-traits",
+ "paste",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db"
+dependencies = [
+ "byteorder",
+ "rmp",
+ "serde",
+]
+
+[[package]]
+name = "ron"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
+dependencies = [
+ "base64 0.21.7",
+ "bitflags",
+ "serde",
+ "serde_derive",
+]
+
+[[package]]
+name = "rust-ini"
+version = "0.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7295b7ce3bf4806b419dc3420745998b447178b7005e2011947b38fc5aa6791"
+dependencies = [
+ "cfg-if",
+ "ordered-multimap",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.9.4",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "069a8df149a16b1a12dcc31497c3396a173844be3cac4bd40c9e7671fef96671"
+dependencies = [
+ "aws-lc-rs",
+ "log",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3"
+dependencies = [
+ "openssl-probe",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
+dependencies = [
+ "aws-lc-rs",
+ "ring",
+ "rustls-pki-types",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "s2n-codec"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb01109923c706ce362d9652fb31b40afcbfc2a2a7c724f4586657f41e29e2bb"
+dependencies = [
+ "byteorder",
+ "bytes",
+ "zerocopy",
+]
+
+[[package]]
+name = "s2n-quic"
+version = "1.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a2652ec7ca318cc15fc042ae57fcbb74320ecb8a8c5b56787faca87d160bbc"
+dependencies = [
+ "bytes",
+ "cfg-if",
+ "cuckoofilter",
+ "futures",
+ "hash_hasher",
+ "rand 0.9.2",
+ "rand_chacha 0.9.0",
+ "s2n-codec",
+ "s2n-quic-core",
+ "s2n-quic-crypto",
+ "s2n-quic-platform",
+ "s2n-quic-tls-default",
+ "s2n-quic-transport",
+ "tokio",
+ "zerocopy",
+ "zeroize",
+]
+
+[[package]]
+name = "s2n-quic-core"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a849cbf43c5083bf413fdd434403819dff9899b8951b8bd6610f61e0f3499c"
+dependencies = [
+ "atomic-waker",
+ "byteorder",
+ "bytes",
+ "cfg-if",
+ "crossbeam-utils",
+ "hex-literal",
+ "num-rational",
+ "num-traits",
+ "once_cell",
+ "pin-project-lite",
+ "s2n-codec",
+ "subtle",
+ "tracing",
+ "zerocopy",
+]
+
+[[package]]
+name = "s2n-quic-crypto"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3abdc40e0f1d5add749020bf0537a8fb9439a2181e291eba7b07323c70d98816"
+dependencies = [
+ "aws-lc-rs",
+ "cfg-if",
+ "lazy_static",
+ "s2n-codec",
+ "s2n-quic-core",
+ "zeroize",
+]
+
+[[package]]
+name = "s2n-quic-platform"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7b23ae94ac4993d016882cb2f9c5a6b5dd15d0aa7f0dd95a36b3bed8b900aa3"
+dependencies = [
+ "cfg-if",
+ "futures",
+ "lazy_static",
+ "libc",
+ "s2n-quic-core",
+ "socket2 0.6.0",
+ "tokio",
+]
+
+[[package]]
+name = "s2n-quic-rustls"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d96dff62cd1bb3066bff552326b890fc0acd0ff4d1e5077e199bd484d86cda"
+dependencies = [
+ "bytes",
+ "rustls",
+ "rustls-pemfile",
+ "s2n-codec",
+ "s2n-quic-core",
+ "s2n-quic-crypto",
+]
+
+[[package]]
+name = "s2n-quic-tls"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25517d8be812ed2698054511bc7911cc4b04b7a46743608dbc5c77c3a8f07f31"
+dependencies = [
+ "bytes",
+ "errno",
+ "libc",
+ "s2n-codec",
+ "s2n-quic-core",
+ "s2n-quic-crypto",
+ "s2n-tls",
+]
+
+[[package]]
+name = "s2n-quic-tls-default"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e0c1d0f8eda3a545d958aec8b109fb288184fd7a5b3f1dbd6aaaee1fb989909"
+dependencies = [
+ "s2n-quic-rustls",
+ "s2n-quic-tls",
+]
+
+[[package]]
+name = "s2n-quic-transport"
+version = "0.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f141ee894b67b9cea4428dda7608b3e2da66e0a183067441c60f080a8734b91d"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "hashbrown 0.15.4",
+ "intrusive-collections",
+ "once_cell",
+ "s2n-codec",
+ "s2n-quic-core",
+ "siphasher",
+ "smallvec",
+]
+
+[[package]]
+name = "s2n-tls"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7795b2643472ca0c92abd9649fbbc96ce6b90ce88f8a75412abe49c580db89f8"
+dependencies = [
+ "errno",
+ "hex",
+ "libc",
+ "pin-project-lite",
+ "s2n-tls-sys",
+]
+
+[[package]]
+name = "s2n-tls-sys"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a1f57616b8f5b24e38f5506a1ed22133088b5d307565953d3fded6e42714639"
+dependencies = [
+ "aws-lc-rs",
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "security-framework"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_ignored"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b516445dac1e3535b6d658a7b528d771153dfb272ed4180ca4617a20550365ff"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.141"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
+
+[[package]]
+name = "siphasher"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
+
+[[package]]
+name = "skiplist"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eec25f46463fcdc5e02f388c2780b1b58e01be81a8378e62ec60931beccc3f6"
+dependencies = [
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "snap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b"
+
+[[package]]
+name = "socket2"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "socket2"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "sqlparser"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05a528114c392209b3264855ad491fcce534b94a38771b0a0b97a79379275ce8"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "stacker"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "libc",
+ "psm",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "streaming-decompression"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf6cc3b19bfb128a8ad11026086e31d3ce9ad23f8ea37354b31383a187c44cf3"
+dependencies = [
+ "fallible-streaming-iterator",
+]
+
+[[package]]
+name = "streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
+
+[[package]]
+name = "strength_reduce"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "strum_macros"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.33.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+ "memchr",
+ "ntapi",
+ "windows",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl 2.0.12",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "time"
+version = "0.3.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
+dependencies = [
+ "deranged",
+ "itoa",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
+
+[[package]]
+name = "time-macros"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tiny-keccak"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
+dependencies = [
+ "crunchy",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.47.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "io-uring",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "slab",
+ "socket2 0.6.0",
+ "tokio-macros",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "tokio-context"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf0b8394dd5ca9a1b726c629390154c19222dfd7467a4b56f1ced90adee3958"
+dependencies = [
+ "tokio",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "time",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "typenum"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-reverse"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b6f4888ebc23094adfb574fdca9fdc891826287a6397d2cd28802ffd6f20c76"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "uuid"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
+dependencies = [
+ "getrandom 0.3.3",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wasm-streams"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix 0.38.44",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
+dependencies = [
+ "windows-core 0.57.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
+dependencies = [
+ "windows-implement 0.57.0",
+ "windows-interface 0.57.0",
+ "windows-result 0.1.2",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
+dependencies = [
+ "windows-implement 0.60.0",
+ "windows-interface 0.59.1",
+ "windows-link 0.1.3",
+ "windows-result 0.3.4",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-sys"
+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.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+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.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "windows-targets"
+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.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
+dependencies = [
+ "windows_aarch64_gnullvm 0.53.0",
+ "windows_aarch64_msvc 0.53.0",
+ "windows_i686_gnu 0.53.0",
+ "windows_i686_gnullvm 0.53.0",
+ "windows_i686_msvc 0.53.0",
+ "windows_x86_64_gnu 0.53.0",
+ "windows_x86_64_gnullvm 0.53.0",
+ "windows_x86_64_msvc 0.53.0",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
+
+[[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
+
+[[package]]
+name = "winnow"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+
+[[package]]
+name = "xxhash-rust"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
+
+[[package]]
+name = "yaml-rust2"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7"
+dependencies = [
+ "arraydeque",
+ "encoding_rs",
+ "hashlink",
+]
+
+[[package]]
+name = "yasna"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
+dependencies = [
+ "time",
+]
+
+[[package]]
+name = "yoke"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zeromq"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a4528179201f6eecf211961a7d3276faa61554c82651ecc66387f68fc3004bd"
+dependencies = [
+ "async-trait",
+ "asynchronous-codec",
+ "bytes",
+ "crossbeam-queue",
+ "dashmap",
+ "futures-channel",
+ "futures-io",
+ "futures-task",
+ "futures-util",
+ "log",
+ "num-traits",
+ "once_cell",
+ "parking_lot",
+ "rand 0.8.5",
+ "regex",
+ "thiserror 1.0.69",
+ "tokio",
+ "tokio-util",
+ "uuid",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zlib-rs"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a"
+
+[[package]]
+name = "zstd"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.15+zstd.1.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
+dependencies = [
+ "cc",
+ "pkg-config",
+]