diff --git a/Cargo.lock b/Cargo.lock index 44c65cd6..f1b99856 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2886,6 +2886,7 @@ dependencies = [ "tikv-jemallocator", "time", "tokio", + "tokio-stream", "tokio-util", "tower", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 8c002d22..464663cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,6 +119,7 @@ tokio = { version = "1", default-features = false, features = [ "macros", "test-util" ] } +tokio-stream = "0.1" [[bench]] name = "validation" diff --git a/simulation/azure/.gitignore b/simulation/azure/.gitignore new file mode 100644 index 00000000..99aafb7e --- /dev/null +++ b/simulation/azure/.gitignore @@ -0,0 +1,5 @@ +/bin/ +/node_modules/ +Pulumi.dev.yaml + +flowproxy.env \ No newline at end of file diff --git a/simulation/azure/CLAUDE.md b/simulation/azure/CLAUDE.md new file mode 100644 index 00000000..1b23c9c6 --- /dev/null +++ b/simulation/azure/CLAUDE.md @@ -0,0 +1,273 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +This is a Pulumi-based Infrastructure as Code (IaC) project that provisions Azure staging infrastructure for FlowProxy, an orderflow proxy service. It deploys a multi-region setup with HAProxy load balancers and FlowProxy instances in East US and West Europe. + +## Essential Commands + +### Pulumi Operations +```bash +# Preview infrastructure changes +pulumi preview + +# Deploy infrastructure +pulumi up + +# Destroy all resources +pulumi destroy + +# View current stack outputs +pulumi stack output + +# Set configuration values +pulumi config set flowproxy-staging:adminUsername "" +pulumi config set flowproxy-staging:haProxyVersion "3.0.6@sha256:..." +pulumi config set --secret flowproxy-staging:sshPublicKey "ssh-ed25519 ..." +pulumi config set --secret flowproxy-staging:githubToken "" +pulumi config set flowproxy-staging:flowProxyArtifact "" +pulumi config set --secret flowproxy-staging:tailscaleAuthKey "" # optional +``` + +### Azure CLI +```bash +# Login to Azure +az login + +# View current subscription +az account show +``` + +### Package Management +```bash +# Install dependencies +pnpm install + +# TypeScript compilation +npx tsc +``` + +## Architecture + +### Multi-Region Deployment +The infrastructure creates a complete staging environment across two Azure regions: +- **East US** (`eastus`): Primary region with BuilderHub VM and FlowProxy instance +- **West Europe** (`westeurope`): Secondary region with FlowProxy instance + +#### East US Resources: +1. **vm-builderhub**: Internal-only VM (no public IP) running BuilderHub mock service + - Accessible via Tailscale VPN or SSH jump host through vm-eastus + - DNS: `builderhub.flowproxy.internal` + - Purpose: Credential registration endpoint for FlowProxy instances +2. **vm-eastus**: FlowProxy instance with HAProxy frontend (has public IP) + +#### West Europe Resources: +1. **vm-westeurope**: FlowProxy instance with HAProxy frontend (has public IP) + +Each FlowProxy region consists of: +1. **Resource Group**: Contains all regional resources +2. **Virtual Network (VNet)**: Isolated network (10.10.0.0/16 for East, 10.20.0.0/16 for West) +3. **Subnet**: Dedicated subnet for VMs (10.10.1.0/24 for East, 10.20.1.0/24 for West) +4. **Network Security Group (NSG)**: Firewall rules allowing SSH (22), HTTP (80), HTTPS (443), HAProxy (5544), and ICMP from peer VNet +5. **Public IP**: Static IP for external access +6. **Network Interface (NIC)**: Connected to subnet with public IP +7. **Virtual Machine**: Ubuntu 22.04 LTS (Standard_B2s: 2 vCPU, 4GB RAM) +8. **VM Extension**: CustomScript extension for automated provisioning + +### Cross-Region Connectivity +- **VNet Peering**: Bidirectional peering between East and West VNets enables private network communication +- **Private DNS Zone**: `flowproxy.internal` zone linked to both VNets provides cross-region name resolution + - `builderhub.flowproxy.internal` → BuilderHub VM private IP (internal-only) + - `vm-eastus.flowproxy.internal` → East US FlowProxy VM private IP + - `vm-westeurope.flowproxy.internal` → West Europe FlowProxy VM private IP + +### BuilderHub VM Provisioning +The BuilderHub VM (vm-builderhub) is provisioned first since FlowProxy instances depend on it for credential registration. Provisioning steps (index.ts:385-414): + +1. **Base System Setup**: + - Install Docker, curl, ca-certificates + - Enable and start Docker service + +2. **Optional Tailscale Setup**: + - Install and configure Tailscale if `tailscaleAuthKey` is provided + - Registers with hostname `vm-builderhub` and enables SSH over Tailscale + +3. **MockHub Service Deployment**: + - Run mockhub Docker container from `mempirate/mockhub:latest` + - Exposed on port 3000 + - TLS enabled via `ENABLE_TLS=true` environment variable + - Accessible at `http://builderhub.flowproxy.internal` from other VMs + +### FlowProxy VM Provisioning Flow +Each FlowProxy VM (vm-eastus, vm-westeurope) is provisioned via the CustomScript extension with the following automated steps: + +1. **Base System Setup**: + - Install Docker, curl, ca-certificates, openssl, unzip, jq + - Create HAProxy directories: `/usr/local/etc/haproxy/{certs,static}` + +2. **HAProxy Configuration**: + - Deploy `haproxy.cfg` (base64-encoded from local file) + - Generate self-signed TLS certificate if not present + - Pull and run HAProxy container on ports 80, 443, 5544, 8405 + +3. **Optional Tailscale Setup**: + - Install and configure Tailscale if `tailscaleAuthKey` is provided + - Registers VM with hostname (e.g., `vm-eastus`, `vm-westeurope`) + +4. **FlowProxy Installation**: + - Download FlowProxy binary from GitHub Actions artifact using GitHub API + - Extract to `/usr/local/bin/flowproxy` + - Deploy systemd service template `flowproxy@.service` + - Deploy environment file `flowproxy.env` to `/etc/flowproxy/` + +5. **ECDSA Key Generation**: + - Install Foundry toolchain (for `cast` command) + - Generate new ECDSA private key using `cast wallet new` + - Append private key to `/etc/flowproxy/flowproxy.env` as `FLASHBOTS_ORDERFLOW_SIGNER` + +6. **BuilderHub Registration**: + - Extract TLS certificate from HAProxy + - POST credentials (TLS cert + ECDSA public key address) to BuilderHub at `http://builderhub.flowproxy.internal/api/l1-builder/v1/register_credentials/orderflow_proxy` + - Retry up to 5 times with 10s delays + - Registration is required before FlowProxy can start serving traffic + +7. **Service Activation**: + - Start FlowProxy as systemd service: `flowproxy@.service` + - Service reads `/etc/flowproxy/flowproxy.env` for configuration + +### Extension Update Mechanism +The VM extension uses a `forceUpdateTag` (index.ts:24-28) computed from the SHA256 hash of: +- `haproxy.cfg` content +- `haProxyVersion` value +- `flowproxy.env` content +- `flowproxy@.service` template +- `flowProxyArtifact` URL + +When any of these inputs change, the hash changes, triggering a re-run of the CustomScript extension without VM recreation. This enables zero-downtime configuration updates. + +### HAProxy Configuration +HAProxy (haproxy.cfg) acts as a reverse proxy with: +- **Frontend `default`** (ports 80, 443): Routes to `user_of` backend (FlowProxy user endpoint at 127.0.0.1:5543) +- **Frontend `system`** (port 5544): Routes to `system_of` backend (FlowProxy system endpoint at 127.0.0.1:5542) +- **Frontend `prometheus`** (port 8405, localhost only): Exposes HAProxy metrics at `/metrics` +- **Frontend `public_cert`** (port 14727, localhost only): Serves certificate from `/usr/local/etc/haproxy/static/le.cer` +- Health checks on `/livez` endpoint for both backends +- Stick tables for connection tracking and rate limiting +- TLS termination with certificates from `/usr/local/etc/haproxy/certs/` + +### FlowProxy Service +FlowProxy runs as a templated systemd service `flowproxy@.service`: +- Instance name (e.g., `vm-eastus`) is passed via systemd template variable `%i` and set as `BUILDER_NAME` environment variable +- Reads configuration from `/etc/flowproxy/flowproxy.env` +- Connects to BuilderHub at `http://builderhub.flowproxy.internal` +- Sends metrics to ClickHouse (configured via environment variables) +- Listens on: + - 127.0.0.1:5543 (user orderflow endpoint) + - 127.0.0.1:5542 (system orderflow endpoint) + - 127.0.0.1:8090 (metrics endpoint) + +## Required Configuration + +Before running `pulumi up`, you must: + +1. **Copy example config**: `cp Pulumi.dev.example.yaml Pulumi.dev.yaml` + +2. **Set required Pulumi config values**: + - `adminUsername`: SSH username for VMs + - `sshPublicKey`: SSH public key for authentication (must be secret) + - `flowProxyArtifact`: GitHub Actions artifact URL (e.g., `https://github.com/ORG/REPO/actions/runs/RUN_ID/artifacts/ARTIFACT_ID`) + - `githubToken`: GitHub personal access token with `actions:read` scope (must be secret) + - `haProxyVersion`: Docker image tag for HAProxy (can include SHA256 digest) + +3. **Optional config values**: + - `tailscaleAuthKey`: Tailscale auth key for VPN access (secret) + - `allowedSshCidr`: CIDR range for SSH access (defaults to `0.0.0.0/0`) + +## Local Files Required + +The following files must exist in this directory before deployment: +- `haproxy.cfg`: HAProxy configuration file +- `flowproxy.env`: FlowProxy environment variables (contains ClickHouse credentials) +- `flowproxy@.service`: systemd service unit template + +**Note**: The mockhub service is deployed via Docker Hub (`mempirate/mockhub:latest`), so no local binary is required. + +## Key Implementation Details + +### GitHub Artifact URL Conversion +The `toApiZip` function (index.ts:392-403) converts GitHub UI artifact URLs to API endpoints: +- Input: `https://github.com/ORG/REPO/actions/runs/RUN_ID/artifacts/ARTIFACT_ID` +- Output: `https://api.github.com/repos/ORG/REPO/actions/artifacts/ARTIFACT_ID/zip` + +This enables automated download of FlowProxy binaries from GitHub Actions. + +### VM Recreation Prevention +VMs are created with `ignoreChanges: ["osProfile"]` (index.ts:227-228) to prevent recreation when SSH keys or admin usernames change in Pulumi config. This preserves VM state across deployments. + +### Pulumi Outputs +The stack exports the following outputs for operational use: +- `eastPublicIp`, `westPublicIp`: Public IPs for external access to FlowProxy VMs +- `eastPrivateIp`, `westPrivateIp`, `builderhubPrivateIp`: Private IPs within VNets +- `sshEast`, `sshWest`: Ready-to-use SSH commands (e.g., `ssh azureuser@1.2.3.4`) +- `builderhubSshNote`: Instructions for SSH access to builderhub (via jump host or Tailscale) +- `eastPrivateFqdn`, `westPrivateFqdn`, `builderhubPrivateFqdn`: Private DNS names (e.g., `builderhub.flowproxy.internal`) +- `eastNicPrivateFqdn`, `westNicPrivateFqdn`, `builderhubNicPrivateFqdn`: Azure-provided internal FQDNs + +## Development Workflow + +1. Make changes to infrastructure code in `index.ts` +2. Update configuration files (`haproxy.cfg`, `flowproxy.env`, `flowproxy@.service`) as needed +3. Run `pulumi preview` to see planned changes +4. Run `pulumi up` to apply changes +5. Monitor VM provisioning via Azure Portal or SSH to VMs +6. Check service status: `ssh @ "systemctl status flowproxy@"` +7. View logs: `ssh @ "journalctl -u flowproxy@ -f"` + +## Debugging + +### VM Extension Logs +```bash +# SSH to VM +ssh @ + +# Check extension execution logs +sudo cat /var/lib/waagent/custom-script/download/0/stderr +sudo cat /var/lib/waagent/custom-script/download/0/stdout + +# Check provisioning script +cat /tmp/provision.sh +``` + +### Service Logs +```bash +# FlowProxy service status +systemctl status flowproxy@vm-eastus # or vm-westeurope + +# FlowProxy logs +journalctl -u flowproxy@vm-eastus -f --no-pager -n 100 + +# HAProxy logs +docker logs haproxy -f +``` + +### Network Connectivity +```bash +# Test cross-region DNS resolution +nslookup vm-westeurope.flowproxy.internal # from East VM +nslookup vm-eastus.flowproxy.internal # from West VM +nslookup builderhub.flowproxy.internal # should resolve to East VM + +# Test cross-region connectivity +ping vm-westeurope.flowproxy.internal # from East VM +curl http://builderhub.flowproxy.internal # from West VM +``` + +## Security Considerations + +- SSH access is controlled via `allowedSshCidr` (default allows all IPs; restrict in production) +- Secrets (SSH keys, GitHub token, Tailscale key) must be set using `--secret` flag in Pulumi config +- VMs use SSH key authentication only (password auth disabled) +- TLS certificates are self-signed by default; production should use valid certificates +- Credentials are registered with BuilderHub before FlowProxy starts accepting traffic diff --git a/simulation/azure/Pulumi.dev.example.yaml b/simulation/azure/Pulumi.dev.example.yaml new file mode 100644 index 00000000..2768544d --- /dev/null +++ b/simulation/azure/Pulumi.dev.example.yaml @@ -0,0 +1,2 @@ +config: + azure-native:location: WestEU \ No newline at end of file diff --git a/simulation/azure/Pulumi.yaml b/simulation/azure/Pulumi.yaml new file mode 100644 index 00000000..77ea7b33 --- /dev/null +++ b/simulation/azure/Pulumi.yaml @@ -0,0 +1,10 @@ +name: flowproxy-staging +description: A minimal Azure Native TypeScript Pulumi program +runtime: + name: nodejs + options: + packagemanager: pnpm +config: + pulumi:tags: + value: + pulumi:template: azure-typescript diff --git a/simulation/azure/README.md b/simulation/azure/README.md new file mode 100644 index 00000000..8dcdf3a3 --- /dev/null +++ b/simulation/azure/README.md @@ -0,0 +1,38 @@ +# Azure Staging Environment + +## Requirements +- Azure CLI +- Pulumi CLI + +## Setup +- Log in to Azure: `az login` +- Copy `Pulumi.dev.example.yaml` to `Pulumi.dev.yaml` + +- Set config values: +```bash +pulumi config set flowproxy-staging:adminUsername "" +pulumi config set flowproxy-staging:haProxyVersion "3.0.6@sha256:0f3127e63b00982c3f12b2a9a17ecbd0595003a191ec1cb403741a692f7a39a9" +pulumi config set --secret flowproxy-staging:sshPublicKey "ssh-ed25519 ..." + +# Optional +pulumi config set --secret flowproxy-staging:tailscaleAuthKey "" +``` + +### Config Files +- [`haproxy.cfg`](haproxy.cfg): Stripped down HAProxy configuration for FlowProxy, based on [BuilderNet configuration file](https://github.com/flashbots/meta-evm/blob/main/recipes-nodes/haproxy/haproxy.cfg.mustache). + +### Local binaries +- Place the compiled binaries in this folder before running `pulumi up`: + - `simulation/azure/flowproxy` + - `simulation/azure/mockhub` +- Pulumi copies them over SSH using `~/.ssh/fiber_aws_key`. + +## Run +- `pulumi up` + +## Destroy +- `pulumi destroy` + +## Notes +- A new VM `vm-builderhub` is created in `eastus` with a public IP for provisioning and a private DNS record `builderhub.flowproxy.internal` in the VNet. +- FlowProxy instances register credentials against `http://builderhub.flowproxy.internal` before the service is enabled. diff --git a/simulation/azure/flowproxy@.service b/simulation/azure/flowproxy@.service new file mode 100644 index 00000000..a87cf3bd --- /dev/null +++ b/simulation/azure/flowproxy@.service @@ -0,0 +1,19 @@ +[Unit] +Description=FlowProxy orderflow proxy (%i) +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +EnvironmentFile=/etc/flowproxy/flowproxy.env +# Derive builder name/FQDN from the instance name +Environment=BUILDER_NAME=%i +Environment=BUILDERNET_NODE_NAME=%i +ExecStart=/usr/local/bin/flowproxy +Restart=always +RestartSec=3s +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target + diff --git a/simulation/azure/grafana/dashboards/README.md b/simulation/azure/grafana/dashboards/README.md new file mode 100644 index 00000000..93aefbf4 --- /dev/null +++ b/simulation/azure/grafana/dashboards/README.md @@ -0,0 +1,22 @@ +# Grafana Dashboards + +Place your Grafana dashboard JSON files in this directory. + +## Usage + +1. Export a dashboard from Grafana as JSON +2. Place the `.json` file in this directory +3. Run `pulumi up` to deploy the dashboard to the builderhub VM +4. The dashboard will be automatically loaded by Grafana + +## Example + +```bash +# Add a dashboard +cp my-dashboard.json grafana/dashboards/ + +# Deploy +pulumi up +``` + +Dashboards will be available at: `http://builderhub.flowproxy.internal/dashboards` diff --git a/simulation/azure/grafana/dashboards/flowproxy.json b/simulation/azure/grafana/dashboards/flowproxy.json new file mode 100644 index 00000000..ee0fb9d6 --- /dev/null +++ b/simulation/azure/grafana/dashboards/flowproxy.json @@ -0,0 +1,2405 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 0, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 6, + "panels": [], + "title": "General", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 2, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_builderhub_peer_count", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Peer count", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Requests per second on all endpoints, grouped by method type.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "user eth_sendBundle" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": true, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 19, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum by(method) (rate(flowproxy_ingress_rpc_request_duration_count{instance=\"$Instance\", handler=\"user\"}[$__rate_interval]))", + "legendFormat": "user {{method}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum by(method) (rate(flowproxy_ingress_rpc_request_duration_count{instance=\"$Instance\", handler=\"system\"}[$__rate_interval]))", + "hide": false, + "legendFormat": "system {{method}}", + "range": true, + "refId": "B" + } + ], + "title": "User RPS", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 9 + }, + "id": 12, + "panels": [], + "title": "Forwarder", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_forwarder_http_call_failures{instance=\"$Instance\"}", + "legendFormat": "{{peer_name}} -- {{reason}}", + "range": true, + "refId": "A" + } + ], + "title": "HTTP call failures", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Errors that occur during a connection establishment.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 22, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_forwarder_http_connect_failures{instance=\"$Instance\"}", + "legendFormat": "{{peer_name}} -- {{reason}}", + "range": true, + "refId": "A" + } + ], + "title": "HTTP connection failures", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Aggregated over 5 minutes.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le, big_request, peer_name) (rate(flowproxy_forwarder_rpc_call_duration_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "legendFormat": "p50 {{peer_name}} (big: {{big_request}})", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "sum by(big_request, instance, peer_name) (rate(flowproxy_forwarder_rpc_call_duration_sum{instance=\"$Instance\"}[$__rate_interval])) / (sum by(big_request, instance, peer_name) (rate(flowproxy_forwarder_rpc_call_duration_count{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "legendFormat": "mean {{peer_name}} (big: {{big_request}})", + "range": true, + "refId": "B" + } + ], + "title": "RPC call duration (mean & p50)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Aggregated over 5 minutes.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "p99 vm-eastus (big: true)", + "p99 vm-eastus-2 (big: true)" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": true, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 26 + }, + "id": 21, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "histogram_quantile(0.99, sum by(le, big_request, peer_name) (rate(flowproxy_forwarder_rpc_call_duration_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "legendFormat": "p99 {{peer_name}} (big: {{big_request}})", + "range": true, + "refId": "B" + } + ], + "title": "RPC call duration (p99)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "The number of inflight HTTP requests: requests that were initiated but are still waiting for a response.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 34 + }, + "id": 20, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "sum by(instance) (flowproxy_forwarder_inflight_requests{instance=\"$Instance\"})", + "legendFormat": "total", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "flowproxy_forwarder_inflight_requests{instance=\"$Instance\"}", + "hide": false, + "legendFormat": "peer {{peer_name}}", + "range": true, + "refId": "B" + } + ], + "title": "Inflight HTTP requests", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 42 + }, + "id": 9, + "panels": [], + "title": "Ingress", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 43 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le, priority) (rate(flowproxy_ingress_rpc_request_duration_bucket{instance=\"$Instance\", method=\"eth_sendBundle\", handler=\"user\"}[$__rate_interval])))", + "legendFormat": "p50 {{priority}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "histogram_quantile(0.99, sum by(le, priority) (rate(flowproxy_ingress_rpc_request_duration_bucket{instance=\"$Instance\", method=\"eth_sendBundle\", handler=\"user\"}[$__rate_interval])))", + "hide": false, + "legendFormat": "p99 {{priority}}", + "range": true, + "refId": "B" + } + ], + "title": "User eth_sendBundle RPC", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 43 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum by(le, priority) (rate(flowproxy_ingress_rpc_request_duration_bucket{instance=\"$Instance\", method=\"eth_sendBundle\", handler=\"system\"}[5m])))", + "legendFormat": "p50 {{priority}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum by(le, priority) (rate(flowproxy_ingress_rpc_request_duration_bucket{instance=\"$Instance\", method=\"eth_sendBundle\", handler=\"system\"}[5m])))", + "hide": false, + "legendFormat": "p99 {{priority}}", + "range": true, + "refId": "B" + } + ], + "title": "System eth_sendBundle RPC", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 51 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "sum by(handler) (flowproxy_ingress_signer_cache_hit_ratio{instance=\"$Instance\"})", + "legendFormat": "{{handler}}", + "range": true, + "refId": "A" + } + ], + "title": "Signer cache hit ratio", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 51 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum by(handler) (flowproxy_ingress_order_cache_hit_ratio{instance=\"$Instance\"})", + "legendFormat": "{{handler}}", + "range": true, + "refId": "A" + } + ], + "title": "Order cache hit ratio", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 51 + }, + "id": 23, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_process_open_fds{instance=\"$Instance\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Open FDs", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 51 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_process_threads{instance=\"$Instance\"}", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Threads", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 58 + }, + "id": 18, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "sum by(method) (rate(flowproxy_ingress_request_body_size_bytes_sum{instance=\"$Instance\"}[$__rate_interval])) / (sum by(method) (rate(flowproxy_ingress_request_body_size_bytes_count{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "instant": false, + "legendFormat": "{{method}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum by(le, method) (rate(flowproxy_ingress_request_body_size_bytes_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "instant": false, + "legendFormat": "{{method}} p95", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum by(le, method) (rate(flowproxy_ingress_request_body_size_bytes_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "instant": false, + "legendFormat": "{{method}} p99", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.999, sum by(le, method) (rate(flowproxy_ingress_request_body_size_bytes_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "instant": false, + "legendFormat": "{{method}} p999", + "range": true, + "refId": "D" + } + ], + "title": "Avg payload size", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 66 + }, + "id": 7, + "panels": [], + "title": "System", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 67 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le, big_request, direction) (rate(flowproxy_system_e2e_bundle_processing_time_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "legendFormat": "p50 {{direction}} (big: {{big_request}})", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "histogram_quantile(0.99, sum by(le, big_request, direction) (rate(flowproxy_system_e2e_bundle_processing_time_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "legendFormat": "p99 {{direction}} (big: {{big_request}})", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "histogram_quantile(0.9, sum by(le, big_request, direction) (rate(flowproxy_system_e2e_bundle_processing_time_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "legendFormat": "p90 {{direction}} (big: {{big_request}})", + "range": true, + "refId": "C" + } + ], + "title": "E2E bundle processing time", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 67 + }, + "id": 26, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum by(le, priority) (rate(flowproxy_worker_task_duration_seconds_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "legendFormat": "p99 {{priority}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, sum by(le, priority) (rate(flowproxy_worker_task_duration_seconds_bucket{instance=\"$Instance\"}[$__rate_interval])))", + "hide": false, + "legendFormat": "p50 {{priority}}", + "range": true, + "refId": "B" + } + ], + "title": "Worker task latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 8, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 75 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "flowproxy_process_resident_memory_bytes", + "legendFormat": "{{instance}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "rotmhz" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 75 + }, + "id": 28, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_system_max_cpu_frequency{instance=\"$Instance\"}", + "legendFormat": "max", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "builder", + "expr": "flowproxy_system_min_cpu_frequency{instance=\"$Instance\"}", + "hide": false, + "legendFormat": "min", + "range": true, + "refId": "B" + } + ], + "title": "CPU frequency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "The CPU usage of this process (not system-wide!)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 19, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 60 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 83 + }, + "id": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_process_cpu_usage{instance=\"$Instance\"}", + "legendFormat": "cpu", + "range": true, + "refId": "A" + } + ], + "title": "CPU usage", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 8000000000 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 91 + }, + "id": 25, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_process_disk_written_bytes_total{instance=\"$Instance\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Disk bytes written", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 99 + }, + "id": 30, + "panels": [], + "title": "Indexing", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 100 + }, + "id": 29, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "sum by(error) (flowproxy_indexer_bundle_indexing_failures{instance=\"$Instance\"})", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Bundle indexing failures", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 100 + }, + "id": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "sum by(error) (flowproxy_indexer_bundle_receipt_indexing_failures{instance=\"$Instance\"})", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Bundle receipt indexing failures", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 108 + }, + "id": 32, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.2.1", + "targets": [ + { + "editorMode": "builder", + "expr": "flowproxy_indexer_clickhouse_bytes_committed{instance=\"$Instance\"}", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Clickhouse bytes committed", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "30s", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [ + { + "allowCustomValue": false, + "current": { + "text": "flowproxy-westeurope", + "value": "flowproxy-westeurope" + }, + "definition": "label_values({method=\"eth_sendBundle\"},instance)", + "name": "Instance", + "options": [], + "query": { + "qryType": 1, + "query": "label_values({method=\"eth_sendBundle\"},instance)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "type": "query" + } + ] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "FlowProxy 2", + "uid": "adfhhdd", + "version": 9 +} \ No newline at end of file diff --git a/simulation/azure/grafana/provisioning/dashboards/dashboard.yml b/simulation/azure/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 00000000..78cfb994 --- /dev/null +++ b/simulation/azure/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: 'Default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/dashboards diff --git a/simulation/azure/grafana/provisioning/datasources/datasource.yml b/simulation/azure/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 00000000..3ba3e897 --- /dev/null +++ b/simulation/azure/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://localhost:9090 + isDefault: true + editable: false diff --git a/simulation/azure/haproxy.cfg b/simulation/azure/haproxy.cfg new file mode 100644 index 00000000..597b49e1 --- /dev/null +++ b/simulation/azure/haproxy.cfg @@ -0,0 +1,69 @@ +global + # Maximum number of concurrent connections + maxconn 4096 + # System limits configuration + ulimit-n 32768 + + # CHANGE: Default is 16 KiB, set to 1 MiB + tune.h2.initial-window-size 4194304 + + # CHANGE: Default is 1.6 MiB, set to 100 MiB + tune.h2.fe.rxbuf 104857600 + +defaults + mode http + timeout connect 5s + timeout client 30s + timeout server 30s + timeout http-request 2s + timeout http-keep-alive 10s + log global + + compression algo gzip + compression type text/html text/plain application/json + +frontend default + bind *:80 + # CHANGE: Allow connections without SNI (IP access) by removing strict-sni + bind *:443 ssl crt /usr/local/etc/haproxy/certs/ + + # CHANGE: Don't force HTTPS redirect + # http-request redirect scheme https code 301 unless { ssl_fc } + + # Stick table to track connections and bandwidth + stick-table type ip size 100k expire 5m store conn_rate(10s),bytes_in_rate(1m),conn_cur,http_req_rate(10s) + + # Track client in stick table + http-request track-sc0 src + + http-request return status 200 content-type text/plain string "OK\n" if { path '/livez' } + + default_backend user_of + +frontend system + # CHANGE: Allow connections without SNI (IP access) by removing strict-sni + bind *:5544 ssl crt /usr/local/etc/haproxy/certs/ + default_backend system_of + +frontend prometheus + bind 0.0.0.0:8405 + http-request use-service prometheus-exporter if { path /metrics } + no log + +frontend public_cert + bind 127.0.0.1:14727 + http-request return status 200 content-type text/plain file "/usr/local/etc/haproxy/static/le.cer" + +backend user_of + http-reuse always + + option httpchk GET /livez + http-check expect status 200 + server user_of1 127.0.0.1:5543 check inter 5s fall 3 rise 1 + +backend system_of + http-reuse always + + option httpchk GET /livez + http-check expect status 200 + server system_of1 127.0.0.1:5542 check inter 5s fall 3 rise 1 diff --git a/simulation/azure/index.ts b/simulation/azure/index.ts new file mode 100644 index 00000000..c84c1db1 --- /dev/null +++ b/simulation/azure/index.ts @@ -0,0 +1,893 @@ +import * as pulumi from "@pulumi/pulumi"; +import * as resources from "@pulumi/azure-native/resources"; +import * as network from "@pulumi/azure-native/network"; +import * as compute from "@pulumi/azure-native/compute"; +import * as privatedns from "@pulumi/azure-native/privatedns"; +import * as fs from "fs"; +import * as crypto from "crypto"; + +// Configuration +const config = new pulumi.Config(); +const adminUsername = config.get("adminUsername") || "azureuser"; +const sshPublicKey = config.require("sshPublicKey"); // e.g. contents of ~/.ssh/id_rsa.pub +const allowedSshCidr = config.get("allowedSshCidr") || "0.0.0.0/0"; // restrict in config for better security +const tailscaleAuthKey = config.getSecret("tailscaleAuthKey"); // optional: set as Pulumi secret +const haProxyVersion = config.get("haProxyVersion") || "3.0.6"; // can include @sha256:digest for Docker image +const flowProxyArtifact = config.require("flowProxyArtifact"); +const githubToken = config.requireSecret("githubToken"); // required: GH token for artifact download +const haproxyCfg = fs.readFileSync("haproxy.cfg", "utf8"); +const flowproxyEnv = fs.readFileSync("flowproxy.env", "utf8"); +const flowproxyServiceTmpl = fs.readFileSync("flowproxy@.service", "utf8"); +const flowproxyEnvB64 = Buffer.from(flowproxyEnv).toString("base64"); +const flowproxySvcB64 = Buffer.from(flowproxyServiceTmpl).toString("base64"); +// Monitoring configs +const prometheusConfig = fs.readFileSync("prometheus.yml", "utf8"); +const grafanaDatasource = fs.readFileSync("grafana/provisioning/datasources/datasource.yml", "utf8"); +const grafanaDashboardConfig = fs.readFileSync("grafana/provisioning/dashboards/dashboard.yml", "utf8"); +const prometheusConfigB64 = Buffer.from(prometheusConfig).toString("base64"); +const grafanaDatasourceB64 = Buffer.from(grafanaDatasource).toString("base64"); +const grafanaDashboardConfigB64 = Buffer.from(grafanaDashboardConfig).toString("base64"); +// Create a compressed tarball of dashboard files (much smaller than individual base64 encoding) +const dashboardFiles = fs.readdirSync("grafana/dashboards").filter(f => f.endsWith(".json")); +const { execSync } = require("child_process"); +// Create tarball in memory +execSync("tar -czf /tmp/dashboards.tar.gz -C grafana/dashboards " + dashboardFiles.join(" ")); +const dashboardsTarballB64 = fs.readFileSync("/tmp/dashboards.tar.gz").toString("base64"); + +const builderhubExtTag = crypto + .createHash("sha256") + .update(prometheusConfig + grafanaDatasource + grafanaDashboardConfig + dashboardsTarballB64) + .digest("hex") + .slice(0, 32); + +// Make sure FlowProxy is updated when BuilderHub is updated, so we trigger a re-registration of the orderflow proxy credentials. +const haExtTag = crypto + .createHash("sha256") + .update(haproxyCfg + String(haProxyVersion) + flowproxyEnv + flowproxyServiceTmpl + String(flowProxyArtifact) + builderhubExtTag) + .digest("hex") + .slice(0, 32); + +// Target regions (Azure names) +const eastRegion = "eastus"; +const westEuropeRegion = "westeurope"; // user requested "europewest"; Azure region is "westeurope" + +// Resource Groups per region +const rgEast = new resources.ResourceGroup("rg-east", { location: eastRegion }); +const rgWest = new resources.ResourceGroup("rg-west", { location: westEuropeRegion }); + +// Virtual Networks and Subnets +const vnetEast = new network.VirtualNetwork("vnet-east", { + resourceGroupName: rgEast.name, + location: rgEast.location, + addressSpace: { addressPrefixes: ["10.10.0.0/16"] }, +}); + +const vnetWest = new network.VirtualNetwork("vnet-west", { + resourceGroupName: rgWest.name, + location: rgWest.location, + addressSpace: { addressPrefixes: ["10.20.0.0/16"] }, +}); + +// Create explicit Subnets so IDs are definite +const subnetEast = new network.Subnet("subnet-east", { + resourceGroupName: rgEast.name, + virtualNetworkName: vnetEast.name, + addressPrefix: "10.10.1.0/24", +}); + +const subnetWest = new network.Subnet("subnet-west", { + resourceGroupName: rgWest.name, + virtualNetworkName: vnetWest.name, + addressPrefix: "10.20.1.0/24", +}); + +// NSGs allowing SSH and intra-VNet traffic (defaults include AllowVnetInBound) +function createNsg(name: string, rg: resources.ResourceGroup, location: pulumi.Input, allowFromPeerCidr: string) { + return new network.NetworkSecurityGroup(name, { + resourceGroupName: rg.name, + location, + securityRules: [ + { + name: "Allow-SSH", + priority: 1000, + direction: "Inbound", + access: "Allow", + protocol: "Tcp", + sourcePortRange: "*", + destinationPortRange: "22", + sourceAddressPrefix: allowedSshCidr, + destinationAddressPrefix: "*", + }, + { + name: "Allow-HTTP", + priority: 1010, + direction: "Inbound", + access: "Allow", + protocol: "Tcp", + sourcePortRange: "*", + destinationPortRange: "80", + sourceAddressPrefix: "*", + destinationAddressPrefix: "*", + }, + { + name: "Allow-HTTPS", + priority: 1020, + direction: "Inbound", + access: "Allow", + protocol: "Tcp", + sourcePortRange: "*", + destinationPortRange: "443", + sourceAddressPrefix: "*", + destinationAddressPrefix: "*", + }, + { + name: "Allow-HAProxy-5544", + priority: 1030, + direction: "Inbound", + access: "Allow", + protocol: "Tcp", + sourcePortRange: "*", + destinationPortRange: "5544", + sourceAddressPrefix: "*", + destinationAddressPrefix: "*", + }, + { + name: "Allow-ICMP-From-Peer", + priority: 1100, + direction: "Inbound", + access: "Allow", + protocol: "Icmp", + sourcePortRange: "*", + destinationPortRange: "*", + sourceAddressPrefix: allowFromPeerCidr, + destinationAddressPrefix: "*", + }, + ], + }); +} + +const nsgEast = createNsg("nsg-east", rgEast, rgEast.location, "10.20.0.0/16"); +const nsgWest = createNsg("nsg-west", rgWest, rgWest.location, "10.10.0.0/16"); + +// Public IPs +const pipEast = new network.PublicIPAddress("pip-east", { + resourceGroupName: rgEast.name, + location: rgEast.location, + publicIPAllocationMethod: "Static", + sku: { name: "Standard" }, +}); + +const pipWest = new network.PublicIPAddress("pip-west", { + resourceGroupName: rgWest.name, + location: rgWest.location, + publicIPAllocationMethod: "Static", + sku: { name: "Standard" }, +}); + +// NICs +const nicEast = new network.NetworkInterface("nic-east", { + resourceGroupName: rgEast.name, + location: rgEast.location, + networkSecurityGroup: { id: nsgEast.id }, + dnsSettings: { + internalDnsNameLabel: "vm-eastus", + }, + ipConfigurations: [ + { + name: "ipconfig1", + privateIPAllocationMethod: "Dynamic", + subnet: { id: subnetEast.id }, + publicIPAddress: { id: pipEast.id }, + }, + ], +}); + +const nicWest = new network.NetworkInterface("nic-west", { + resourceGroupName: rgWest.name, + location: rgWest.location, + networkSecurityGroup: { id: nsgWest.id }, + dnsSettings: { + internalDnsNameLabel: "vm-westeurope", + }, + ipConfigurations: [ + { + name: "ipconfig1", + privateIPAllocationMethod: "Dynamic", + subnet: { id: subnetWest.id }, + publicIPAddress: { id: pipWest.id }, + }, + ], +}); + +// BuilderHub NIC (internal-only, no public IP) +const nicBuilderhub = new network.NetworkInterface("nic-builderhub", { + resourceGroupName: rgEast.name, + location: rgEast.location, + networkSecurityGroup: { id: nsgEast.id }, + dnsSettings: { + internalDnsNameLabel: "vm-builderhub", + }, + ipConfigurations: [ + { + name: "ipconfig1", + privateIPAllocationMethod: "Dynamic", + subnet: { id: subnetEast.id }, + }, + ], +}); + +// Additional FlowProxy NICs (internal-only, no public IPs) +const nicEast2 = new network.NetworkInterface("nic-east-2", { + resourceGroupName: rgEast.name, + location: rgEast.location, + networkSecurityGroup: { id: nsgEast.id }, + dnsSettings: { + internalDnsNameLabel: "vm-eastus-2", + }, + ipConfigurations: [ + { + name: "ipconfig1", + privateIPAllocationMethod: "Dynamic", + subnet: { id: subnetEast.id }, + }, + ], +}); + +const nicWest2 = new network.NetworkInterface("nic-west-2", { + resourceGroupName: rgWest.name, + location: rgWest.location, + networkSecurityGroup: { id: nsgWest.id }, + dnsSettings: { + internalDnsNameLabel: "vm-westeurope-2", + }, + ipConfigurations: [ + { + name: "ipconfig1", + privateIPAllocationMethod: "Dynamic", + subnet: { id: subnetWest.id }, + }, + ], +}); + +// VM size: 2 vCPU, 4GB RAM +const smallVm = "Standard_B2s"; + +const bigVm = "Standard_B4ls_v2" + +// Ubuntu image reference (22.04 LTS) +const ubuntuImageRef = { + publisher: "Canonical", + offer: "0001-com-ubuntu-server-jammy", + sku: "22_04-lts", + version: "latest", +}; + +function createVm(name: string, rg: resources.ResourceGroup, location: pulumi.Input, nicId: pulumi.Input, vmSize: string, opts?: pulumi.CustomResourceOptions) { + return new compute.VirtualMachine(name, { + resourceGroupName: rg.name, + location, + networkProfile: { + networkInterfaces: [{ id: nicId, primary: true }], + }, + hardwareProfile: { vmSize }, + osProfile: { + adminUsername, + computerName: name, + linuxConfiguration: { + disablePasswordAuthentication: true, + ssh: { + publicKeys: [ + { + path: pulumi.interpolate`/home/${adminUsername}/.ssh/authorized_keys`, + keyData: sshPublicKey, + }, + ], + }, + }, + }, + storageProfile: { + imageReference: ubuntuImageRef, + osDisk: { + name: pulumi.interpolate`${name}-osdisk`, + createOption: "FromImage", + managedDisk: { storageAccountType: "StandardSSD_LRS" }, + }, + }, + }, opts); +} + +// Create BuilderHub VM first (other VMs depend on it for credential registration) +const vmBuilderhub = createVm("vm-builderhub", rgEast, rgEast.location, nicBuilderhub.id, smallVm, { ignoreChanges: ["osProfile", "storageProfile"] }); + +const vmEast = createVm("vm-eastus", rgEast, rgEast.location, nicEast.id, smallVm, { ignoreChanges: ["osProfile", "storageProfile"] }); +const vmWest = createVm("vm-westeurope", rgWest, rgWest.location, nicWest.id, bigVm, { ignoreChanges: ["osProfile", "storageProfile"] }); +const vmEast2 = createVm("vm-eastus-2", rgEast, rgEast.location, nicEast2.id, smallVm, { ignoreChanges: ["osProfile", "storageProfile"] }); +const vmWest2 = createVm("vm-westeurope-2", rgWest, rgWest.location, nicWest2.id, smallVm, { ignoreChanges: ["osProfile", "storageProfile"] }); + +// VNet Peering (bi-directional) +const eastToWest = new network.VirtualNetworkPeering("east-to-west", { + resourceGroupName: rgEast.name, + virtualNetworkName: vnetEast.name, + remoteVirtualNetwork: { id: vnetWest.id }, + allowVirtualNetworkAccess: true, + allowForwardedTraffic: true, +}); + +const westToEast = new network.VirtualNetworkPeering("west-to-east", { + resourceGroupName: rgWest.name, + virtualNetworkName: vnetWest.name, + remoteVirtualNetwork: { id: vnetEast.id }, + allowVirtualNetworkAccess: true, + allowForwardedTraffic: true, +}); + +// VM Extensions: Provision HAProxy via Docker (no VM recreate) +const haproxyCfgB64 = Buffer.from(haproxyCfg).toString("base64"); +function buildInstanceCommand(name: string): pulumi.Input { + const artifactApiUrl = toApiZip(flowProxyArtifact); + const script = (key?: string, token?: string) => `#!/usr/bin/env bash +set -euxo pipefail +export DEBIAN_FRONTEND=noninteractive + +# Install essential packages first (with retry logic for transient apt issues) +rm -rf /var/lib/apt/lists/* +for i in 1 2 3; do apt-get update && break || sleep 5; done +for i in 1 2 3; do apt-get install -y curl ca-certificates openssl unzip jq && break || sleep 5; done + +# Install Docker using official script +curl -fsSL https://get.docker.com | sh + +mkdir -p /usr/local/etc/haproxy/certs /usr/local/etc/haproxy/static +echo "${haproxyCfgB64}" | base64 -d > /usr/local/etc/haproxy/haproxy.cfg +printf "\n" >> /usr/local/etc/haproxy/haproxy.cfg || true +if [ ! -f /usr/local/etc/haproxy/static/le.cer ]; then echo "placeholder-certificate-content" > /usr/local/etc/haproxy/static/le.cer; fi +if [ ! -f /usr/local/etc/haproxy/certs/default.pem ]; then + # Get the private IP address + PRIVATE_IP=$(hostname -I | awk '{print $1}') + + # Create OpenSSL config with SANs + cat > /tmp/openssl.cnf < /usr/local/etc/haproxy/certs/default.pem + rm /tmp/openssl.cnf +fi + + +rm -f /usr/local/etc/haproxy/certs/default.crt || true +systemctl enable --now docker +docker rm -f haproxy || true +docker pull haproxy:${haProxyVersion} +docker run -d --name haproxy --restart unless-stopped \ + --network host \ + --cap-add=NET_BIND_SERVICE \ + --user root \ + -v /usr/local/etc/haproxy:/usr/local/etc/haproxy:ro \ + haproxy:${haProxyVersion} + +# Optionally install Tailscale +${key ? ` +rm -rf /var/lib/apt/lists/* +for i in 1 2 3; do apt-get update && break || sleep 5; done +apt-get install -y curl +curl -fsSL https://tailscale.com/install.sh | sh +systemctl enable --now tailscaled +tailscale up --authkey=${key} --hostname=${name} --ssh +` : ``} + +# --- FlowProxy install --- +tmpzip=$(mktemp /tmp/flowproxy.XXXXXX.zip) +curl -fL -o "$tmpzip" \ + -H "Authorization: Bearer ${token}" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${artifactApiUrl}" +mkdir -p /usr/local/bin +unzip -o "$tmpzip" -d /usr/local/bin || true +chmod +x /usr/local/bin/flowproxy +mkdir -p /etc/flowproxy +echo "${flowproxySvcB64}" | base64 -d > /etc/systemd/system/flowproxy@.service +echo "${flowproxyEnvB64}" | base64 -d > /etc/flowproxy/flowproxy.env +systemctl daemon-reload + +rm /.bashrc || true +export HOME=/root +# This bashrc contains the foundry bin path +# Install Foundry (for cast) to manage ECDSA keys +if ! command -v cast &> /dev/null; then + curl -L https://foundry.paradigm.xyz | bash + export PATH="$HOME/.foundry/bin:$PATH" + + foundryup +fi + +# Install Samply for profiling +if ! command -v samply &> /dev/null; then + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/mstange/samply/releases/download/samply-v0.13.1/samply-installer.sh | sh + echo '1' | sudo tee /proc/sys/kernel/perf_event_paranoid +fi + +# Persist ECDSA key across extension updates +KEY_FILE="/etc/flowproxy/ecdsa_private_key" + +if [ -f "$KEY_FILE" ]; then + # Use existing key + PRIVATE_KEY=$(cat "$KEY_FILE") + ADDRESS=$(cast wallet address "$PRIVATE_KEY") + echo "Using existing ECDSA key with address: $ADDRESS" +else + # Generate new key and save it + key_output=$(cast wallet new --json) + ADDRESS=$(echo "$key_output" | jq -r '.[0].address') + PRIVATE_KEY=$(echo "$key_output" | jq -r '.[0].private_key') + echo "$PRIVATE_KEY" > "$KEY_FILE" + chmod 600 "$KEY_FILE" + echo "Generated new ECDSA key with address: $ADDRESS" +fi + +# Update flowproxy.env with the ECDSA private key (idempotent) +sed -i '/^FLASHBOTS_ORDERFLOW_SIGNER=/d' /etc/flowproxy/flowproxy.env +echo "FLASHBOTS_ORDERFLOW_SIGNER=$PRIVATE_KEY" >> /etc/flowproxy/flowproxy.env + +# Register credentials with BuilderHub before enabling the orderflow proxy +set +e +set +o pipefail +set -x +set -a +source /etc/flowproxy/flowproxy.env || true +set +a +CERT=$(openssl x509 -in /usr/local/etc/haproxy/certs/default.pem -outform PEM 2>/dev/null || true) +PAYLOAD=$(jq -n --arg cert "$CERT" --arg addr "$ADDRESS" '{tls_cert:$cert, ecdsa_pubkey_address:$addr}') +URL="http://builderhub.flowproxy.internal:3000/api/l1-builder/v1/register_credentials/orderflow_proxy" +echo "Registering orderflow proxy credentials to $URL (address=$ADDRESS)" +success=0 +for i in 1 2 3 4 5; do + code=$(curl -sS -o /tmp/register.out -w '%{http_code}' -X POST \ + -H 'Content-Type: application/json' \ + --data "$PAYLOAD" "$URL" || true) + if [ "$code" = "200" ] || [ "$code" = "201" ]; then + echo "Registration succeeded (HTTP $code)" + success=1 + break + fi + echo "Registration attempt $i failed (HTTP $code). Retrying in 10s..." + sleep 10 +done +if [ "$success" != "1" ]; then + echo "Warning: BuilderHub registration did not succeed after 5 attempts; continuing." >&2 +fi +set -e +set -o pipefail + +systemctl enable --now flowproxy@${name} +systemctl restart flowproxy@${name} +systemctl status flowproxy@${name} --no-pager || journalctl -u flowproxy@${name} --no-pager -n 100 +`; + + const build = (k?: string, t?: string) => { + const b64 = Buffer.from(script(k, t)).toString("base64"); + return `/bin/bash -lc 'echo ${b64} | base64 -d > /tmp/provision.sh && bash /tmp/provision.sh'`; + }; + + const ts = (tailscaleAuthKey as any)?.apply ? (tailscaleAuthKey as pulumi.Output) : undefined; + const gh = (githubToken as any)?.apply ? (githubToken as pulumi.Output) : undefined; + if (ts && gh) { + return pulumi.all([ts, gh]).apply(([k, t]) => build(k, t)); + } else if (ts) { + return ts.apply(k => build(k, undefined as any)); + } else if (gh) { + return gh.apply(t => build(undefined as any, t)); + } + return build(undefined, undefined); +} + +// BuilderHub provisioning script - installs Docker only for now +function buildBuilderhubCommand(name: string): pulumi.Input { + const script = (key?: string) => `#!/usr/bin/env bash +set -euxo pipefail +export DEBIAN_FRONTEND=noninteractive + +# Install Docker using official script +curl -fsSL https://get.docker.com | sh +systemctl enable --now docker + +# Configure journald to limit log storage to 12GB +sed -i 's/#SystemMaxUse=/SystemMaxUse=12G/' /etc/systemd/journald.conf +systemctl restart systemd-journald + +# Optionally install Tailscale +${key ? ` +rm -rf /var/lib/apt/lists/* +for i in 1 2 3; do apt-get update && break || sleep 5; done +apt-get install -y curl +curl -fsSL https://tailscale.com/install.sh | sh +systemctl enable --now tailscaled +tailscale up --authkey=${key} --hostname=${name} --ssh +` : ``} + +# Make sure we delete the old builderhub container +docker rm -f builderhub || true +docker run -d --name builderhub --restart unless-stopped -p 3000:3000 -e ENABLE_TLS=true mempirate/mockhub:v0.0.6 + +# === Prometheus Setup === +mkdir -p /etc/prometheus +mkdir -p /var/lib/prometheus +chown -R 65534:65534 /var/lib/prometheus +echo "${prometheusConfigB64}" | base64 -d > /etc/prometheus/prometheus.yml + +docker rm -f prometheus || true +docker run -d --name prometheus --restart unless-stopped \ + --network host \ + -v /etc/prometheus:/etc/prometheus \ + -v /var/lib/prometheus:/prometheus \ + prom/prometheus:latest \ + --config.file=/etc/prometheus/prometheus.yml \ + --storage.tsdb.path=/prometheus + +# === Grafana Setup === +mkdir -p /etc/grafana/provisioning/datasources +mkdir -p /etc/grafana/provisioning/dashboards +mkdir -p /etc/grafana/dashboards +mkdir -p /var/lib/grafana + +echo "${grafanaDatasourceB64}" | base64 -d > /etc/grafana/provisioning/datasources/datasource.yml +echo "${grafanaDashboardConfigB64}" | base64 -d > /etc/grafana/provisioning/dashboards/dashboard.yml + +# Extract dashboard files from compressed tarball +echo "${dashboardsTarballB64}" | base64 -d | tar -xzf - -C /etc/grafana/dashboards/ + +docker rm -f grafana || true +docker run -d --name grafana --restart unless-stopped \ + --network host \ + --user root \ + --cap-add=NET_BIND_SERVICE \ + -v /etc/grafana/provisioning:/etc/grafana/provisioning \ + -v /etc/grafana/dashboards:/etc/grafana/dashboards \ + -v /var/lib/grafana:/var/lib/grafana \ + -e GF_SERVER_HTTP_PORT=80 \ + -e GF_SECURITY_ADMIN_USER=admin \ + -e GF_SECURITY_ADMIN_PASSWORD=grafana \ + -e GF_AUTH_ANONYMOUS_ENABLED=true \ + -e GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer \ + grafana/grafana:latest +`; + + const build = (k?: string) => { + const b64 = Buffer.from(script(k)).toString("base64"); + return `/bin/bash -lc 'echo ${b64} | base64 -d > /tmp/provision.sh && bash /tmp/provision.sh'`; + }; + + const ts = (tailscaleAuthKey as any)?.apply ? (tailscaleAuthKey as pulumi.Output) : undefined; + if (ts) { + return ts.apply(k => build(k)); + } + return build(undefined); +} + + +const builderhubExt = new compute.VirtualMachineExtension("builderhub-ext", { + resourceGroupName: rgEast.name, + vmName: vmBuilderhub.name, + location: rgEast.location, + publisher: "Microsoft.Azure.Extensions", + type: "CustomScript", + typeHandlerVersion: "2.1", + forceUpdateTag: builderhubExtTag, + autoUpgradeMinorVersion: true, + protectedSettings: { + commandToExecute: buildBuilderhubCommand("flowproxy-builderhub"), + }, +}, { dependsOn: [vmBuilderhub] }); + +const haExtEast = new compute.VirtualMachineExtension("haproxy-ext-east", { + resourceGroupName: rgEast.name, + vmName: vmEast.name, + location: rgEast.location, + publisher: "Microsoft.Azure.Extensions", + type: "CustomScript", + typeHandlerVersion: "2.1", + forceUpdateTag: haExtTag, + autoUpgradeMinorVersion: true, + protectedSettings: { + commandToExecute: buildInstanceCommand("flowproxy-eastus"), + }, +}, { dependsOn: [vmEast] }); + +const haExtWest = new compute.VirtualMachineExtension("haproxy-ext-west", { + resourceGroupName: rgWest.name, + vmName: vmWest.name, + location: rgWest.location, + publisher: "Microsoft.Azure.Extensions", + type: "CustomScript", + typeHandlerVersion: "2.1", + forceUpdateTag: haExtTag, + autoUpgradeMinorVersion: true, + protectedSettings: { + commandToExecute: buildInstanceCommand("flowproxy-westeurope"), + }, +}, { dependsOn: [vmWest] }); + +const haExtEast2 = new compute.VirtualMachineExtension("haproxy-ext-east-2", { + resourceGroupName: rgEast.name, + vmName: vmEast2.name, + location: rgEast.location, + publisher: "Microsoft.Azure.Extensions", + type: "CustomScript", + typeHandlerVersion: "2.1", + forceUpdateTag: haExtTag, + autoUpgradeMinorVersion: true, + protectedSettings: { + commandToExecute: buildInstanceCommand("flowproxy-eastus-2"), + }, +}, { dependsOn: [vmEast2] }); + +const haExtWest2 = new compute.VirtualMachineExtension("haproxy-ext-west-2", { + resourceGroupName: rgWest.name, + vmName: vmWest2.name, + location: rgWest.location, + publisher: "Microsoft.Azure.Extensions", + type: "CustomScript", + typeHandlerVersion: "2.1", + forceUpdateTag: haExtTag, + autoUpgradeMinorVersion: true, + protectedSettings: { + commandToExecute: buildInstanceCommand("flowproxy-westeurope-2"), + }, +}, { dependsOn: [vmWest2] }); + +function toApiZip(url: string): string { + // Convert GitHub actions artifact page URL to API ZIP endpoint when possible + // Example input: https://github.com/ORG/REPO/actions/runs//artifacts/ + // Output: https://api.github.com/repos/ORG/REPO/actions/artifacts//zip + const m = url.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/actions\/runs\/\d+\/artifacts\/(\d+)/); + if (m) { + const [_, org, repo, id] = m; + return `https://api.github.com/repos/${org}/${repo}/actions/artifacts/${id}/zip`; + } + // If it's already an API URL or something else, return as-is + return url; +} + +// Private DNS zone for cross-VNet name resolution +const privateZoneName = "flowproxy.internal"; +const dnsZone = new privatedns.PrivateZone("private-dns-zone", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName, +}); + +// Link the zone to both VNets +const linkEast = new privatedns.VirtualNetworkLink("dns-link-east", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: dnsZone.name, + virtualNetworkLinkName: "link-east", + registrationEnabled: false, + virtualNetwork: { id: vnetEast.id }, +}); + +const linkWest = new privatedns.VirtualNetworkLink("dns-link-west", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: dnsZone.name, + virtualNetworkLinkName: "link-west", + registrationEnabled: false, + virtualNetwork: { id: vnetWest.id }, +}); + +// Reverse DNS zones for PTR records +const reverseDnsZoneEast = new privatedns.PrivateZone("reverse-dns-east", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: "1.10.10.in-addr.arpa", +}); + +const reverseDnsZoneWest = new privatedns.PrivateZone("reverse-dns-west", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: "1.20.10.in-addr.arpa", +}); + +// Link reverse DNS zones to both VNets +const reverseLinkEastToEast = new privatedns.VirtualNetworkLink("reverse-link-east-east", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: reverseDnsZoneEast.name, + virtualNetworkLinkName: "link-east", + registrationEnabled: false, + virtualNetwork: { id: vnetEast.id }, +}); + +const reverseLinkEastToWest = new privatedns.VirtualNetworkLink("reverse-link-east-west", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: reverseDnsZoneEast.name, + virtualNetworkLinkName: "link-west", + registrationEnabled: false, + virtualNetwork: { id: vnetWest.id }, +}); + +const reverseLinkWestToEast = new privatedns.VirtualNetworkLink("reverse-link-west-east", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: reverseDnsZoneWest.name, + virtualNetworkLinkName: "link-east", + registrationEnabled: false, + virtualNetwork: { id: vnetEast.id }, +}); + +const reverseLinkWestToWest = new privatedns.VirtualNetworkLink("reverse-link-west-west", { + resourceGroupName: rgEast.name, + location: "global", + privateZoneName: reverseDnsZoneWest.name, + virtualNetworkLinkName: "link-west", + registrationEnabled: false, + virtualNetwork: { id: vnetWest.id }, +}); + +// A records for VMs in the private zone +const eastARecord = new privatedns.PrivateRecordSet("east-a", { + resourceGroupName: rgEast.name, + privateZoneName: dnsZone.name, + relativeRecordSetName: "vm-eastus", + recordType: "A", + ttl: 60, + aRecords: [ + { ipv4Address: nicEast.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress || "") }, + ], +}); + +const westARecord = new privatedns.PrivateRecordSet("west-a", { + resourceGroupName: rgEast.name, + privateZoneName: dnsZone.name, + relativeRecordSetName: "vm-westeurope", + recordType: "A", + ttl: 60, + aRecords: [ + { ipv4Address: nicWest.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress || "") }, + ], +}); + +const builderhubARecord = new privatedns.PrivateRecordSet("builderhub-a", { + resourceGroupName: rgEast.name, + privateZoneName: dnsZone.name, + relativeRecordSetName: "builderhub", + recordType: "A", + ttl: 60, + aRecords: [ + { ipv4Address: nicBuilderhub.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress || "") }, + ], +}); + +const east2ARecord = new privatedns.PrivateRecordSet("east-2-a", { + resourceGroupName: rgEast.name, + privateZoneName: dnsZone.name, + relativeRecordSetName: "vm-eastus-2", + recordType: "A", + ttl: 60, + aRecords: [ + { ipv4Address: nicEast2.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress || "") }, + ], +}); + +const west2ARecord = new privatedns.PrivateRecordSet("west-2-a", { + resourceGroupName: rgEast.name, + privateZoneName: dnsZone.name, + relativeRecordSetName: "vm-westeurope-2", + recordType: "A", + ttl: 60, + aRecords: [ + { ipv4Address: nicWest2.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress || "") }, + ], +}); + +// PTR records for reverse DNS lookups (East US VMs in 1.10.10.in-addr.arpa zone) +const eastPtrRecord = new privatedns.PrivateRecordSet("east-ptr", { + resourceGroupName: rgEast.name, + privateZoneName: reverseDnsZoneEast.name, + relativeRecordSetName: nicEast.ipConfigurations.apply(cfg => { + const ip = cfg?.[0]?.privateIPAddress || ""; + return ip.split(".")[3] || ""; + }), + recordType: "PTR", + ttl: 60, + ptrRecords: [ + { ptrdname: pulumi.interpolate`vm-eastus.${privateZoneName}` }, + ], +}); + +const east2PtrRecord = new privatedns.PrivateRecordSet("east-2-ptr", { + resourceGroupName: rgEast.name, + privateZoneName: reverseDnsZoneEast.name, + relativeRecordSetName: nicEast2.ipConfigurations.apply(cfg => { + const ip = cfg?.[0]?.privateIPAddress || ""; + return ip.split(".")[3] || ""; + }), + recordType: "PTR", + ttl: 60, + ptrRecords: [ + { ptrdname: pulumi.interpolate`vm-eastus-2.${privateZoneName}` }, + ], +}); + +const builderhubPtrRecord = new privatedns.PrivateRecordSet("builderhub-ptr", { + resourceGroupName: rgEast.name, + privateZoneName: reverseDnsZoneEast.name, + relativeRecordSetName: nicBuilderhub.ipConfigurations.apply(cfg => { + const ip = cfg?.[0]?.privateIPAddress || ""; + return ip.split(".")[3] || ""; + }), + recordType: "PTR", + ttl: 60, + ptrRecords: [ + { ptrdname: pulumi.interpolate`builderhub.${privateZoneName}` }, + ], +}); + +// PTR records for West Europe VMs in 1.20.10.in-addr.arpa zone +const westPtrRecord = new privatedns.PrivateRecordSet("west-ptr", { + resourceGroupName: rgEast.name, + privateZoneName: reverseDnsZoneWest.name, + relativeRecordSetName: nicWest.ipConfigurations.apply(cfg => { + const ip = cfg?.[0]?.privateIPAddress || ""; + return ip.split(".")[3] || ""; + }), + recordType: "PTR", + ttl: 60, + ptrRecords: [ + { ptrdname: pulumi.interpolate`vm-westeurope.${privateZoneName}` }, + ], +}); + +const west2PtrRecord = new privatedns.PrivateRecordSet("west-2-ptr", { + resourceGroupName: rgEast.name, + privateZoneName: reverseDnsZoneWest.name, + relativeRecordSetName: nicWest2.ipConfigurations.apply(cfg => { + const ip = cfg?.[0]?.privateIPAddress || ""; + return ip.split(".")[3] || ""; + }), + recordType: "PTR", + ttl: 60, + ptrRecords: [ + { ptrdname: pulumi.interpolate`vm-westeurope-2.${privateZoneName}` }, + ], +}); + +// Outputs +export const eastPublicIp = pipEast.ipAddress; +export const westPublicIp = pipWest.ipAddress; +export const eastPrivateIp = nicEast.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress); +export const westPrivateIp = nicWest.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress); +export const builderhubPrivateIp = nicBuilderhub.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress); +export const builderhubNicPrivateFqdn = nicBuilderhub.dnsSettings.apply(ds => ds?.internalFqdn); +export const eastPrivateFqdn = pulumi.interpolate`vm-eastus.${privateZoneName}`; +export const westPrivateFqdn = pulumi.interpolate`vm-westeurope.${privateZoneName}`; +export const east2PrivateFqdn = pulumi.interpolate`vm-eastus-2.${privateZoneName}`; +export const west2PrivateFqdn = pulumi.interpolate`vm-westeurope-2.${privateZoneName}`; +export const builderhubPrivateFqdn = pulumi.interpolate`builderhub.${privateZoneName}`; +export const east2PrivateIp = nicEast2.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress); +export const west2PrivateIp = nicWest2.ipConfigurations.apply(cfg => cfg?.[0]?.privateIPAddress); +export const east2NicPrivateFqdn = nicEast2.dnsSettings.apply(ds => ds?.internalFqdn); +export const west2NicPrivateFqdn = nicWest2.dnsSettings.apply(ds => ds?.internalFqdn); \ No newline at end of file diff --git a/simulation/azure/package.json b/simulation/azure/package.json new file mode 100644 index 00000000..1e36a6c2 --- /dev/null +++ b/simulation/azure/package.json @@ -0,0 +1,13 @@ +{ + "name": "flowproxy-staging", + "main": "index.ts", + "devDependencies": { + "@types/node": "^18", + "typescript": "^5.0.0" + }, + "dependencies": { + "@pulumi/command": "^1.0.0", + "@pulumi/azure-native": "^3.0.0", + "@pulumi/pulumi": "^3.0.0" + } +} diff --git a/simulation/azure/pnpm-lock.yaml b/simulation/azure/pnpm-lock.yaml new file mode 100644 index 00000000..29af39df --- /dev/null +++ b/simulation/azure/pnpm-lock.yaml @@ -0,0 +1,2270 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@pulumi/azure-native': + specifier: ^3.0.0 + version: 3.8.0(typescript@5.9.3) + '@pulumi/pulumi': + specifier: ^3.0.0 + version: 3.205.0(typescript@5.9.3) + devDependencies: + '@types/node': + specifier: ^18 + version: 18.19.130 + typescript: + specifier: ^5.0.0 + version: 5.9.3 + +packages: + + '@grpc/grpc-js@1.14.0': + resolution: {integrity: sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.8.0': + resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} + engines: {node: '>=6'} + hasBin: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/string-locale-compare@1.1.0': + resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@logdna/tail-file@2.2.0': + resolution: {integrity: sha512-XGSsWDweP80Fks16lwkAUIr54ICyBs6PsI4mpfTLQaWgEJRtY9xEV+PeyDpJ+sJEGZxqINlpmAwe/6tS1pP8Ng==} + engines: {node: '>=10.3.0'} + + '@npmcli/agent@2.2.2': + resolution: {integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@npmcli/arborist@7.5.4': + resolution: {integrity: sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==} + engines: {node: ^16.14.0 || >=18.0.0} + hasBin: true + + '@npmcli/fs@3.1.1': + resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/git@5.0.8': + resolution: {integrity: sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@npmcli/installed-package-contents@2.1.0': + resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + '@npmcli/map-workspaces@3.0.6': + resolution: {integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/metavuln-calculator@7.1.1': + resolution: {integrity: sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@npmcli/name-from-folder@2.0.0': + resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/node-gyp@3.0.0': + resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/package-json@5.2.1': + resolution: {integrity: sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@npmcli/promise-spawn@7.0.2': + resolution: {integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@npmcli/query@3.1.0': + resolution: {integrity: sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/redact@2.0.1': + resolution: {integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@npmcli/run-script@8.1.0': + resolution: {integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@opentelemetry/api-logs@0.55.0': + resolution: {integrity: sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==} + engines: {node: '>=14'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/context-async-hooks@1.30.1': + resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@1.30.1': + resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-zipkin@1.30.1': + resolution: {integrity: sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/instrumentation-grpc@0.55.0': + resolution: {integrity: sha512-n2ZH4pRwOy0Vhag/3eKqiyDBwcpUnGgJI9iiIRX7vivE0FMncaLazWphNFezRRaM/LuKwq1TD8pVUvieP68mow==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.55.0': + resolution: {integrity: sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/propagator-b3@1.30.1': + resolution: {integrity: sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-jaeger@1.30.1': + resolution: {integrity: sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/resources@1.30.1': + resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@1.30.1': + resolution: {integrity: sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@1.30.1': + resolution: {integrity: sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.27.0': + resolution: {integrity: sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.28.0': + resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} + engines: {node: '>=14'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@pulumi/azure-native@3.8.0': + resolution: {integrity: sha512-emIoAW5gl2i0S03XRdEiX25GcUxjwH/0HZha8Jaz8YO58rlm+tr5MR6+DSgj0Fy71rcODt+SE/4p+6/ElEmkuw==} + + '@pulumi/pulumi@3.205.0': + resolution: {integrity: sha512-AcYCPNAPYpRX9D2D6vwnEfNP+1hg1R45eQYrBTJlbGY3rcR22sHP8fYheY5nJQy1g9KPY21kkkvfBDu6Ch6UIw==} + engines: {node: '>=20'} + peerDependencies: + ts-node: '>= 7.0.1 < 12' + typescript: '>= 3.8.3 < 6' + peerDependenciesMeta: + ts-node: + optional: true + typescript: + optional: true + + '@sigstore/bundle@2.3.2': + resolution: {integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@sigstore/core@1.1.0': + resolution: {integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@sigstore/protobuf-specs@0.3.3': + resolution: {integrity: sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/sign@2.3.2': + resolution: {integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@sigstore/tuf@2.3.4': + resolution: {integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@sigstore/verify@1.2.1': + resolution: {integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + + '@tufjs/canonical-json@2.0.0': + resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@tufjs/models@2.0.1': + resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@types/cacheable-request@6.0.3': + resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + + '@types/google-protobuf@3.15.12': + resolution: {integrity: sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/keyv@3.1.4': + resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + + '@types/responselike@1.0.3': + resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + + '@types/tmp@0.2.6': + resolution: {integrity: sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==} + + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bin-links@4.0.4: + resolution: {integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + cacache@18.0.4: + resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} + engines: {node: ^16.14.0 || >=18.0.0} + + cacheable-lookup@5.0.4: + resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} + engines: {node: '>=10.6.0'} + + cacheable-request@7.0.4: + resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} + engines: {node: '>=8'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone-response@1.0.3: + resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} + + cmd-shim@6.0.3: + resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + google-protobuf@3.21.4: + resolution: {integrity: sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==} + + got@11.8.6: + resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} + engines: {node: '>=10.19.0'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hosted-git-info@7.0.2: + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http2-wrapper@1.0.3: + resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} + engines: {node: '>=10.19.0'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + import-in-the-middle@1.15.0: + resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + json-stringify-nice@1.1.4: + resolution: {integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + just-diff-apply@5.5.0: + resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} + + just-diff@6.0.2: + resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lowercase-keys@2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + make-fetch-happen@13.0.1: + resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==} + engines: {node: ^16.14.0 || >=18.0.0} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-response@1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@3.0.5: + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + node-gyp@10.3.1: + resolution: {integrity: sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==} + engines: {node: ^16.14.0 || >=18.0.0} + hasBin: true + + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + normalize-package-data@6.0.2: + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + npm-bundled@3.0.1: + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-install-checks@6.3.0: + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-package-arg@11.0.3: + resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} + engines: {node: ^16.14.0 || >=18.0.0} + + npm-packlist@8.0.2: + resolution: {integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-pick-manifest@9.1.0: + resolution: {integrity: sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==} + engines: {node: ^16.14.0 || >=18.0.0} + + npm-registry-fetch@17.1.0: + resolution: {integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==} + engines: {node: ^16.14.0 || >=18.0.0} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + p-cancelable@2.1.1: + resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} + engines: {node: '>=8'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pacote@18.0.6: + resolution: {integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==} + engines: {node: ^16.14.0 || >=18.0.0} + hasBin: true + + parse-conflict-json@3.0.1: + resolution: {integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + picomatch@3.0.1: + resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==} + engines: {node: '>=10'} + + pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + proc-log@4.2.0: + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + proggy@2.0.0: + resolution: {integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + promise-all-reject-late@1.0.1: + resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} + + promise-call-limit@3.0.2: + resolution: {integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + read-cmd-shim@4.0.0: + resolution: {integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@7.5.2: + resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} + engines: {node: '>=8.6.0'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + responselike@2.0.1: + resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sigstore@2.3.1: + resolution: {integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==} + engines: {node: ^16.14.0 || >=18.0.0} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + ssri@10.0.6: + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} + + treeverse@3.0.0: + resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + tuf-js@2.2.1: + resolution: {integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==} + engines: {node: ^16.14.0 || >=18.0.0} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + unique-filename@3.0.0: + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + walk-up-path@3.0.1: + resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + +snapshots: + + '@grpc/grpc-js@1.14.0': + dependencies: + '@grpc/proto-loader': 0.8.0 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.8.0': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/string-locale-compare@1.1.0': {} + + '@js-sdsl/ordered-map@4.4.2': {} + + '@logdna/tail-file@2.2.0': {} + + '@npmcli/agent@2.2.2': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/arborist@7.5.4': + dependencies: + '@isaacs/string-locale-compare': 1.1.0 + '@npmcli/fs': 3.1.1 + '@npmcli/installed-package-contents': 2.1.0 + '@npmcli/map-workspaces': 3.0.6 + '@npmcli/metavuln-calculator': 7.1.1 + '@npmcli/name-from-folder': 2.0.0 + '@npmcli/node-gyp': 3.0.0 + '@npmcli/package-json': 5.2.1 + '@npmcli/query': 3.1.0 + '@npmcli/redact': 2.0.1 + '@npmcli/run-script': 8.1.0 + bin-links: 4.0.4 + cacache: 18.0.4 + common-ancestor-path: 1.0.1 + hosted-git-info: 7.0.2 + json-parse-even-better-errors: 3.0.2 + json-stringify-nice: 1.1.4 + lru-cache: 10.4.3 + minimatch: 9.0.5 + nopt: 7.2.1 + npm-install-checks: 6.3.0 + npm-package-arg: 11.0.3 + npm-pick-manifest: 9.1.0 + npm-registry-fetch: 17.1.0 + pacote: 18.0.6 + parse-conflict-json: 3.0.1 + proc-log: 4.2.0 + proggy: 2.0.0 + promise-all-reject-late: 1.0.1 + promise-call-limit: 3.0.2 + read-package-json-fast: 3.0.2 + semver: 7.7.3 + ssri: 10.0.6 + treeverse: 3.0.0 + walk-up-path: 3.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + + '@npmcli/fs@3.1.1': + dependencies: + semver: 7.7.3 + + '@npmcli/git@5.0.8': + dependencies: + '@npmcli/promise-spawn': 7.0.2 + ini: 4.1.3 + lru-cache: 10.4.3 + npm-pick-manifest: 9.1.0 + proc-log: 4.2.0 + promise-inflight: 1.0.1 + promise-retry: 2.0.1 + semver: 7.7.3 + which: 4.0.0 + transitivePeerDependencies: + - bluebird + + '@npmcli/installed-package-contents@2.1.0': + dependencies: + npm-bundled: 3.0.1 + npm-normalize-package-bin: 3.0.1 + + '@npmcli/map-workspaces@3.0.6': + dependencies: + '@npmcli/name-from-folder': 2.0.0 + glob: 10.4.5 + minimatch: 9.0.5 + read-package-json-fast: 3.0.2 + + '@npmcli/metavuln-calculator@7.1.1': + dependencies: + cacache: 18.0.4 + json-parse-even-better-errors: 3.0.2 + pacote: 18.0.6 + proc-log: 4.2.0 + semver: 7.7.3 + transitivePeerDependencies: + - bluebird + - supports-color + + '@npmcli/name-from-folder@2.0.0': {} + + '@npmcli/node-gyp@3.0.0': {} + + '@npmcli/package-json@5.2.1': + dependencies: + '@npmcli/git': 5.0.8 + glob: 10.4.5 + hosted-git-info: 7.0.2 + json-parse-even-better-errors: 3.0.2 + normalize-package-data: 6.0.2 + proc-log: 4.2.0 + semver: 7.7.3 + transitivePeerDependencies: + - bluebird + + '@npmcli/promise-spawn@7.0.2': + dependencies: + which: 4.0.0 + + '@npmcli/query@3.1.0': + dependencies: + postcss-selector-parser: 6.1.2 + + '@npmcli/redact@2.0.1': {} + + '@npmcli/run-script@8.1.0': + dependencies: + '@npmcli/node-gyp': 3.0.0 + '@npmcli/package-json': 5.2.1 + '@npmcli/promise-spawn': 7.0.2 + node-gyp: 10.3.1 + proc-log: 4.2.0 + which: 4.0.0 + transitivePeerDependencies: + - bluebird + - supports-color + + '@opentelemetry/api-logs@0.55.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/exporter-zipkin@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/instrumentation-grpc@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.27.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.55.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.15.0 + require-in-the-middle: 7.5.2 + semver: 7.7.3 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/propagator-b3@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/propagator-jaeger@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/sdk-trace-node@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-b3': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-jaeger': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + semver: 7.7.3 + + '@opentelemetry/semantic-conventions@1.27.0': {} + + '@opentelemetry/semantic-conventions@1.28.0': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@pulumi/azure-native@3.8.0(typescript@5.9.3)': + dependencies: + '@pulumi/pulumi': 3.205.0(typescript@5.9.3) + transitivePeerDependencies: + - bluebird + - supports-color + - ts-node + - typescript + + '@pulumi/pulumi@3.205.0(typescript@5.9.3)': + dependencies: + '@grpc/grpc-js': 1.14.0 + '@logdna/tail-file': 2.2.0 + '@npmcli/arborist': 7.5.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/exporter-zipkin': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-grpc': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) + '@types/google-protobuf': 3.15.12 + '@types/semver': 7.7.1 + '@types/tmp': 0.2.6 + execa: 5.1.1 + fdir: 6.5.0(picomatch@3.0.1) + google-protobuf: 3.21.4 + got: 11.8.6 + ini: 2.0.0 + js-yaml: 3.14.1 + minimist: 1.2.8 + normalize-package-data: 6.0.2 + picomatch: 3.0.1 + pkg-dir: 7.0.0 + require-from-string: 2.0.2 + semver: 7.7.3 + source-map-support: 0.5.21 + tmp: 0.2.5 + upath: 1.2.0 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bluebird + - supports-color + + '@sigstore/bundle@2.3.2': + dependencies: + '@sigstore/protobuf-specs': 0.3.3 + + '@sigstore/core@1.1.0': {} + + '@sigstore/protobuf-specs@0.3.3': {} + + '@sigstore/sign@2.3.2': + dependencies: + '@sigstore/bundle': 2.3.2 + '@sigstore/core': 1.1.0 + '@sigstore/protobuf-specs': 0.3.3 + make-fetch-happen: 13.0.1 + proc-log: 4.2.0 + promise-retry: 2.0.1 + transitivePeerDependencies: + - supports-color + + '@sigstore/tuf@2.3.4': + dependencies: + '@sigstore/protobuf-specs': 0.3.3 + tuf-js: 2.2.1 + transitivePeerDependencies: + - supports-color + + '@sigstore/verify@1.2.1': + dependencies: + '@sigstore/bundle': 2.3.2 + '@sigstore/core': 1.1.0 + '@sigstore/protobuf-specs': 0.3.3 + + '@sindresorhus/is@4.6.0': {} + + '@szmarczak/http-timer@4.0.6': + dependencies: + defer-to-connect: 2.0.1 + + '@tufjs/canonical-json@2.0.0': {} + + '@tufjs/models@2.0.1': + dependencies: + '@tufjs/canonical-json': 2.0.0 + minimatch: 9.0.5 + + '@types/cacheable-request@6.0.3': + dependencies: + '@types/http-cache-semantics': 4.0.4 + '@types/keyv': 3.1.4 + '@types/node': 18.19.130 + '@types/responselike': 1.0.3 + + '@types/google-protobuf@3.15.12': {} + + '@types/http-cache-semantics@4.0.4': {} + + '@types/keyv@3.1.4': + dependencies: + '@types/node': 18.19.130 + + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + + '@types/responselike@1.0.3': + dependencies: + '@types/node': 18.19.130 + + '@types/semver@7.7.1': {} + + '@types/shimmer@1.2.0': {} + + '@types/tmp@0.2.6': {} + + abbrev@2.0.0: {} + + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + agent-base@7.1.4: {} + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + balanced-match@1.0.2: {} + + bin-links@4.0.4: + dependencies: + cmd-shim: 6.0.3 + npm-normalize-package-bin: 3.0.1 + read-cmd-shim: 4.0.0 + write-file-atomic: 5.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + buffer-from@1.1.2: {} + + cacache@18.0.4: + dependencies: + '@npmcli/fs': 3.1.1 + fs-minipass: 3.0.3 + glob: 10.4.5 + lru-cache: 10.4.3 + minipass: 7.1.2 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 4.0.0 + ssri: 10.0.6 + tar: 6.2.1 + unique-filename: 3.0.0 + + cacheable-lookup@5.0.4: {} + + cacheable-request@7.0.4: + dependencies: + clone-response: 1.0.3 + get-stream: 5.2.0 + http-cache-semantics: 4.2.0 + keyv: 4.5.4 + lowercase-keys: 2.0.0 + normalize-url: 6.1.0 + responselike: 2.0.1 + + chownr@2.0.0: {} + + cjs-module-lexer@1.4.3: {} + + clean-stack@2.2.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-response@1.0.3: + dependencies: + mimic-response: 1.0.1 + + cmd-shim@6.0.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + common-ancestor-path@1.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + defer-to-connect@2.0.1: {} + + eastasianwidth@0.2.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + env-paths@2.2.1: {} + + err-code@2.0.3: {} + + escalade@3.2.0: {} + + esprima@4.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exponential-backoff@3.1.3: {} + + fdir@6.5.0(picomatch@3.0.1): + optionalDependencies: + picomatch: 3.0.1 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + get-stream@5.2.0: + dependencies: + pump: 3.0.3 + + get-stream@6.0.1: {} + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + google-protobuf@3.21.4: {} + + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + + graceful-fs@4.2.11: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hosted-git-info@7.0.2: + dependencies: + lru-cache: 10.4.3 + + http-cache-semantics@4.2.0: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + http2-wrapper@1.0.3: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + + ignore-walk@6.0.5: + dependencies: + minimatch: 9.0.5 + + import-in-the-middle@1.15.0: + dependencies: + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + ini@2.0.0: {} + + ini@4.1.3: {} + + ip-address@10.0.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-fullwidth-code-point@3.0.0: {} + + is-lambda@1.0.1: {} + + is-stream@2.0.1: {} + + isexe@2.0.0: {} + + isexe@3.1.1: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@3.0.2: {} + + json-stringify-nice@1.1.4: {} + + jsonparse@1.3.1: {} + + just-diff-apply@5.5.0: {} + + just-diff@6.0.2: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash.camelcase@4.3.0: {} + + long@5.3.2: {} + + lowercase-keys@2.0.0: {} + + lru-cache@10.4.3: {} + + make-fetch-happen@13.0.1: + dependencies: + '@npmcli/agent': 2.2.2 + cacache: 18.0.4 + http-cache-semantics: 4.2.0 + is-lambda: 1.0.1 + minipass: 7.1.2 + minipass-fetch: 3.0.5 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.4 + proc-log: 4.2.0 + promise-retry: 2.0.1 + ssri: 10.0.6 + transitivePeerDependencies: + - supports-color + + merge-stream@2.0.0: {} + + mimic-fn@2.1.0: {} + + mimic-response@1.0.1: {} + + mimic-response@3.1.0: {} + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.2 + + minipass-fetch@3.0.5: + dependencies: + minipass: 7.1.2 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} + + module-details-from-path@1.0.4: {} + + ms@2.1.3: {} + + negotiator@0.6.4: {} + + node-gyp@10.3.1: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + glob: 10.4.5 + graceful-fs: 4.2.11 + make-fetch-happen: 13.0.1 + nopt: 7.2.1 + proc-log: 4.2.0 + semver: 7.7.3 + tar: 6.2.1 + which: 4.0.0 + transitivePeerDependencies: + - supports-color + + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + normalize-package-data@6.0.2: + dependencies: + hosted-git-info: 7.0.2 + semver: 7.7.3 + validate-npm-package-license: 3.0.4 + + normalize-url@6.1.0: {} + + npm-bundled@3.0.1: + dependencies: + npm-normalize-package-bin: 3.0.1 + + npm-install-checks@6.3.0: + dependencies: + semver: 7.7.3 + + npm-normalize-package-bin@3.0.1: {} + + npm-package-arg@11.0.3: + dependencies: + hosted-git-info: 7.0.2 + proc-log: 4.2.0 + semver: 7.7.3 + validate-npm-package-name: 5.0.1 + + npm-packlist@8.0.2: + dependencies: + ignore-walk: 6.0.5 + + npm-pick-manifest@9.1.0: + dependencies: + npm-install-checks: 6.3.0 + npm-normalize-package-bin: 3.0.1 + npm-package-arg: 11.0.3 + semver: 7.7.3 + + npm-registry-fetch@17.1.0: + dependencies: + '@npmcli/redact': 2.0.1 + jsonparse: 1.3.1 + make-fetch-happen: 13.0.1 + minipass: 7.1.2 + minipass-fetch: 3.0.5 + minizlib: 2.1.2 + npm-package-arg: 11.0.3 + proc-log: 4.2.0 + transitivePeerDependencies: + - supports-color + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + p-cancelable@2.1.1: {} + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@4.0.0: + dependencies: + aggregate-error: 3.1.0 + + package-json-from-dist@1.0.1: {} + + pacote@18.0.6: + dependencies: + '@npmcli/git': 5.0.8 + '@npmcli/installed-package-contents': 2.1.0 + '@npmcli/package-json': 5.2.1 + '@npmcli/promise-spawn': 7.0.2 + '@npmcli/run-script': 8.1.0 + cacache: 18.0.4 + fs-minipass: 3.0.3 + minipass: 7.1.2 + npm-package-arg: 11.0.3 + npm-packlist: 8.0.2 + npm-pick-manifest: 9.1.0 + npm-registry-fetch: 17.1.0 + proc-log: 4.2.0 + promise-retry: 2.0.1 + sigstore: 2.3.1 + ssri: 10.0.6 + tar: 6.2.1 + transitivePeerDependencies: + - bluebird + - supports-color + + parse-conflict-json@3.0.1: + dependencies: + json-parse-even-better-errors: 3.0.2 + just-diff: 6.0.2 + just-diff-apply: 5.5.0 + + path-exists@5.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + picomatch@3.0.1: {} + + pkg-dir@7.0.0: + dependencies: + find-up: 6.3.0 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + proc-log@4.2.0: {} + + proggy@2.0.0: {} + + promise-all-reject-late@1.0.1: {} + + promise-call-limit@3.0.2: {} + + promise-inflight@1.0.1: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 18.19.130 + long: 5.3.2 + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + quick-lru@5.1.1: {} + + read-cmd-shim@4.0.0: {} + + read-package-json-fast@3.0.2: + dependencies: + json-parse-even-better-errors: 3.0.2 + npm-normalize-package-bin: 3.0.1 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-in-the-middle@7.5.2: + dependencies: + debug: 4.4.3 + module-details-from-path: 1.0.4 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + resolve-alpn@1.2.1: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + responselike@2.0.1: + dependencies: + lowercase-keys: 2.0.0 + + retry@0.12.0: {} + + safer-buffer@2.1.2: + optional: true + + semver@7.7.3: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shimmer@1.2.1: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sigstore@2.3.1: + dependencies: + '@sigstore/bundle': 2.3.2 + '@sigstore/core': 1.1.0 + '@sigstore/protobuf-specs': 0.3.3 + '@sigstore/sign': 2.3.2 + '@sigstore/tuf': 2.3.4 + '@sigstore/verify': 1.2.1 + transitivePeerDependencies: + - supports-color + + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.0.1 + smart-buffer: 4.2.0 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.22 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + sprintf-js@1.0.3: {} + + ssri@10.0.6: + dependencies: + minipass: 7.1.2 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-final-newline@2.0.0: {} + + supports-preserve-symlinks-flag@1.0.0: {} + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + tmp@0.2.5: {} + + treeverse@3.0.0: {} + + tuf-js@2.2.1: + dependencies: + '@tufjs/models': 2.0.1 + debug: 4.4.3 + make-fetch-happen: 13.0.1 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + undici-types@5.26.5: {} + + unique-filename@3.0.0: + dependencies: + unique-slug: 4.0.0 + + unique-slug@4.0.0: + dependencies: + imurmurhash: 0.1.4 + + upath@1.2.0: {} + + util-deprecate@1.0.2: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + validate-npm-package-name@5.0.1: {} + + walk-up-path@3.0.1: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@4.0.0: + dependencies: + isexe: 3.1.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrappy@1.0.2: {} + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@1.2.1: {} diff --git a/simulation/azure/prometheus.yml b/simulation/azure/prometheus.yml new file mode 100644 index 00000000..8ba9d44b --- /dev/null +++ b/simulation/azure/prometheus.yml @@ -0,0 +1,81 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + # FlowProxy metrics (eastus) + - job_name: 'flowproxy-eastus' + static_configs: + - targets: ['vm-eastus.flowproxy.internal:8090'] + labels: + region: 'eastus' + instance: 'flowproxy-eastus' + + # FlowProxy metrics (westeurope) + - job_name: 'flowproxy-westeurope' + static_configs: + - targets: ['vm-westeurope.flowproxy.internal:8090'] + labels: + region: 'westeurope' + instance: 'flowproxy-westeurope' + + # HAProxy metrics (eastus) + - job_name: 'haproxy-eastus' + static_configs: + - targets: ['vm-eastus.flowproxy.internal:8405'] + labels: + region: 'eastus' + instance: 'flowproxy-eastus' + params: + extra-counters: ["on"] + + # HAProxy metrics (westeurope) + - job_name: 'haproxy-westeurope' + static_configs: + - targets: ['vm-westeurope.flowproxy.internal:8405'] + labels: + region: 'westeurope' + instance: 'flowproxy-westeurope' + params: + extra-counters: ["on"] + + # FlowProxy metrics (eastus-2) + - job_name: 'flowproxy-eastus-2' + static_configs: + - targets: ['vm-eastus-2.flowproxy.internal:8090'] + labels: + region: 'eastus' + instance: 'flowproxy-eastus-2' + + # FlowProxy metrics (westeurope-2) + - job_name: 'flowproxy-westeurope-2' + static_configs: + - targets: ['vm-westeurope-2.flowproxy.internal:8090'] + labels: + region: 'westeurope' + instance: 'flowproxy-westeurope-2' + + # HAProxy metrics (eastus-2) + - job_name: 'haproxy-eastus-2' + static_configs: + - targets: ['vm-eastus-2.flowproxy.internal:8405'] + labels: + region: 'eastus' + instance: 'flowproxy-eastus-2' + params: + extra-counters: ["on"] + + # HAProxy metrics (westeurope-2) + - job_name: 'haproxy-westeurope-2' + static_configs: + - targets: ['vm-westeurope-2.flowproxy.internal:8405'] + labels: + region: 'westeurope' + instance: 'flowproxy-westeurope-2' + params: + extra-counters: ["on"] + + # Prometheus self-monitoring + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] diff --git a/simulation/azure/tsconfig.json b/simulation/azure/tsconfig.json new file mode 100644 index 00000000..f960d517 --- /dev/null +++ b/simulation/azure/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "es2020", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.ts" + ] +} diff --git a/simulation/mockhub/.dockerignore b/simulation/mockhub/.dockerignore new file mode 100644 index 00000000..7ff934fe --- /dev/null +++ b/simulation/mockhub/.dockerignore @@ -0,0 +1,11 @@ +# Ignore target directory (compiled artifacts) +target/ + +# Ignore Cargo lock for libraries (keep it for binaries) +# Cargo.lock + +# Ignore any local config or test data +*.log +*.tmp +.DS_Store + diff --git a/simulation/mockhub/Cargo.lock b/simulation/mockhub/Cargo.lock index a9b1721d..87ec1b1b 100644 --- a/simulation/mockhub/Cargo.lock +++ b/simulation/mockhub/Cargo.lock @@ -345,7 +345,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -597,6 +597,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "dns-lookup" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e39034cee21a2f5bbb66ba0e3689819c4bb5d00382a282006e802a7ffa6c41d" +dependencies = [ + "cfg-if", + "libc", + "socket2", + "windows-sys 0.60.2", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -1211,6 +1223,7 @@ dependencies = [ "alloy-primitives", "axum", "dashmap", + "dns-lookup", "futures", "serde", "tokio", @@ -1309,7 +1322,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2196,9 +2209,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" @@ -2206,7 +2219,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2215,7 +2228,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -2233,14 +2255,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -2249,48 +2288,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.7.13" diff --git a/simulation/mockhub/Cargo.toml b/simulation/mockhub/Cargo.toml index 19417aff..01ba3c12 100644 --- a/simulation/mockhub/Cargo.toml +++ b/simulation/mockhub/Cargo.toml @@ -8,6 +8,8 @@ publish = false # alloy alloy-primitives = { version = "1.3.1", features = ["serde"] } +dns-lookup = "3" + # rt tokio = { version = "1", default-features = false, features = [ "sync", diff --git a/simulation/mockhub/Dockerfile b/simulation/mockhub/Dockerfile new file mode 100644 index 00000000..329d9e32 --- /dev/null +++ b/simulation/mockhub/Dockerfile @@ -0,0 +1,41 @@ +# Build stage +FROM rust:1.89-slim AS builder + +WORKDIR /app + +# Copy manifests first +COPY Cargo.toml ./ + +# Create a dummy main.rs to build dependencies +RUN mkdir src && \ + echo "fn main() {}" > src/main.rs && \ + cargo build --release && \ + rm -rf src target/release/mockhub* target/release/deps/mockhub* + +# Now copy the real source code +COPY src ./src + +# Build the actual binary (dependencies are cached, but binary is rebuilt) +RUN cargo build --release + +# Runtime stage +FROM debian:bookworm-slim + +WORKDIR /app + +# Install ca-certificates for HTTPS +RUN apt-get update && \ + apt-get install -y ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Copy binary from builder +COPY --from=builder /app/target/release/mockhub /app/mockhub + +# Expose the port the app runs on +EXPOSE 3000 + +# Set default environment variables (can be overridden) +ENV SYSTEM_PORT=5544 + +# Run the binary +CMD ["/app/mockhub"] \ No newline at end of file diff --git a/simulation/mockhub/src/main.rs b/simulation/mockhub/src/main.rs index d155c0b2..c0549714 100644 --- a/simulation/mockhub/src/main.rs +++ b/simulation/mockhub/src/main.rs @@ -2,11 +2,11 @@ use std::{net::SocketAddr, sync::Arc}; use alloy_primitives::Address; use axum::{ + Json, Router, extract::{ConnectInfo, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, - Json, Router, }; use dashmap::DashMap; use serde::{Deserialize, Serialize}; @@ -19,9 +19,12 @@ async fn main() { .parse() .expect("SYSTEM_PORT must be a valid port number"); + let enable_tls: bool = + std::env::var("ENABLE_TLS").map_or(false, |v| v.parse().unwrap_or(false)); + let _ = tracing_subscriber::fmt().try_init(); - let registry = Registry::new(system_port); + let registry = Registry::new(system_port, enable_tls); let router = Router::new() .route( @@ -33,7 +36,7 @@ async fn main() { let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); let listener = TcpListener::bind(addr).await.unwrap(); - tracing::info!("Listening on {}", addr); + tracing::info!(tls_enabled = enable_tls, "Listening on {}", addr); axum::serve(listener, router.into_make_service_with_connect_info::()) .await .unwrap(); @@ -48,16 +51,33 @@ async fn register_credentials( tracing::info!("Registering credentials for builder: {:?}", creds.ecdsa_pubkey_address); let signer = creds.ecdsa_pubkey_address; + let cert = creds.tls_cert.clone().unwrap_or_default(); + + let name = if let Some(name) = dns_lookup::lookup_addr(&addr.ip()) + .ok() + .and_then(|dns| dns.split('.').next().map(|s| s.to_string())) + { + name + } else { + signer.to_string() + }; let builder = BuilderHubBuilder { - name: format!("{:?}", signer), + name: name.to_string(), ip: format!("{}:{}", addr.ip(), registry.system_port), dns_name: addr.ip().to_string(), orderflow_proxy: creds, // NOTE: Empty TLS certificate to ensure proxies use plain HTTP. - instance: BuilderHubInstanceData { tls_cert: String::new() }, + // If TLS is enabled, proxies will use the TLS certificate. + instance: BuilderHubInstanceData { + tls_cert: if registry.enable_tls { cert } else { String::new() }, + }, }; + if registry.builders.contains_key(&signer) { + return StatusCode::OK; + } + registry.builders.insert(signer, builder); StatusCode::OK @@ -83,12 +103,13 @@ async fn get_builders( #[derive(Debug, Clone)] struct Registry { system_port: u16, + enable_tls: bool, builders: Arc>, } impl Registry { - pub fn new(system_port: u16) -> Self { - Self { builders: Arc::new(DashMap::new()), system_port } + pub fn new(system_port: u16, enable_tls: bool) -> Self { + Self { builders: Arc::new(DashMap::new()), system_port, enable_tls } } } diff --git a/src/utils.rs b/src/utils.rs index ce2236f0..58453d0c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -434,11 +434,13 @@ pub mod testutils { impl Random for RawBundle { /// Generate a random bundle with transactions of type Eip1559. fn random(rng: &mut R) -> Self { - let txs_len = rng.random_range(1..=10); + let txs_len = rng.random_range(1..=2); // We only generate Eip1559 here. let txs = (0..txs_len) .map(|_| { - let signer = PrivateKeySigner::random(); + let signer = + PrivateKeySigner::from_bytes(&alloy_primitives::B256::random_with(rng)) + .unwrap(); let tx = EthereumTypedTransaction::Eip1559(TxEip1559::random(rng)); let sighash = tx.signature_hash(); let signature = signer.sign_hash_sync(&sighash).unwrap(); diff --git a/tests/spam.rs b/tests/spam.rs new file mode 100644 index 00000000..ab41477d --- /dev/null +++ b/tests/spam.rs @@ -0,0 +1,115 @@ +mod common; +use std::{ + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, + time::{Duration, Instant}, +}; + +use alloy_signer_local::PrivateKeySigner; +use flowproxy::utils::testutils::Random as _; +use futures::stream::StreamExt; +use rand::SeedableRng; +use rbuilder_primitives::serialize::RawBundle; +use revm_primitives::B256; +use tokio_stream::wrappers::ReceiverStream; + +use crate::common::IngressClient; + +#[tokio::test(flavor = "multi_thread", worker_threads = 8)] +#[ignore] +async fn spam() { + let mut rng = rand::rng(); + + let _east = "http://172.190.190.141".to_string(); + let west = "http://20.234.206.56".to_string(); + + let signer_seed = B256::random_with(&mut rng); + + let client = IngressClient { + url: west, + signer: PrivateKeySigner::from_bytes(&signer_seed).unwrap(), + client: reqwest::Client::default(), + }; + + // Create a channel for bundle generation + let (bundle_tx, bundle_rx) = tokio::sync::mpsc::channel::(2000); + + // Spawn multiple blocking tasks to generate bundles in parallel + let num_generators = 4; + let bundles_per_generator = 100000 / num_generators; + + for i in 0..num_generators { + let tx = bundle_tx.clone(); + let seed = rand::random::(); + + std::thread::spawn(move || { + let mut thread_rng = rand::rngs::StdRng::seed_from_u64(seed); + let count = if i == num_generators - 1 { + // Last generator handles any remainder + bundles_per_generator + (100000 % num_generators) + } else { + bundles_per_generator + }; + + for _ in 0..count { + let bundle = RawBundle::random(&mut thread_rng); + // If send fails, receiver is dropped (test ended) + if tx.blocking_send(bundle).is_err() { + break; + } + } + }); + } + + // Drop the original sender so the channel closes when all generators finish + drop(bundle_tx); + + // Track requests and RPS + let total_requests = Arc::new(AtomicU64::new(0)); + let start_time = Instant::now(); + + // Spawn a task to print RPS every second + let rps_counter = total_requests.clone(); + let rps_task = tokio::spawn(async move { + let mut last_count = 0u64; + let mut interval = tokio::time::interval(Duration::from_secs(1)); + loop { + interval.tick().await; + let current_count = rps_counter.load(Ordering::Relaxed); + let rps = current_count - last_count; + let elapsed = start_time.elapsed().as_secs_f64(); + let avg_rps = current_count as f64 / elapsed; + println!("RPS: {} | Total: {} | Avg RPS: {:.2}", rps, current_count, avg_rps); + last_count = current_count; + } + }); + + // Convert receiver into a stream and send bundles concurrently + ReceiverStream::new(bundle_rx) + .map(|bundle| { + let client = &client; + let counter = total_requests.clone(); + async move { + client.send_bundle(&bundle).await; + + // Increment counter after successful request + counter.fetch_add(1, Ordering::Relaxed); + } + }) + .buffered(200) + .collect::>() + .await; + + // Cancel the RPS monitoring task + rps_task.abort(); + + // Print final stats + let total = total_requests.load(Ordering::Relaxed); + let elapsed = start_time.elapsed(); + println!("\n=== Final Stats ==="); + println!("Total requests: {}", total); + println!("Total time: {:.2}s", elapsed.as_secs_f64()); + println!("Average RPS: {:.2}", total as f64 / elapsed.as_secs_f64()); +}