Add Gandi DNS management via Pulumi #54

Merged
eblume merged 2 commits from feature/gandi-dns-pulumi into main 2026-01-25 08:15:46 -08:00
17 changed files with 466 additions and 3 deletions

10
mise-tasks/dns-preview Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
#MISE description="Preview DNS changes to eblu.me with Pulumi"
set -euo pipefail
GANDI_PERSONAL_ACCESS_TOKEN=$(op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get mco6ka3dc3rmw7zkg2dhia5d2m --fields pat --reveal)
export GANDI_PERSONAL_ACCESS_TOKEN
cd "$(dirname "$0")/../pulumi/gandi"
pulumi preview "$@"

10
mise-tasks/dns-up Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
#MISE description="Apply DNS changes to eblu.me with Pulumi"
set -euo pipefail
GANDI_PERSONAL_ACCESS_TOKEN=$(op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get mco6ka3dc3rmw7zkg2dhia5d2m --fields pat --reveal)
export GANDI_PERSONAL_ACCESS_TOKEN
cd "$(dirname "$0")/../pulumi/gandi"
pulumi up --yes "$@"

View file

@ -9,5 +9,5 @@ TAILSCALE_OAUTH_CLIENT_SECRET=$(op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get w
export TAILSCALE_OAUTH_CLIENT_SECRET
export TAILSCALE_TAILNET="tail8d86e.ts.net"
cd "$(dirname "$0")/../pulumi"
cd "$(dirname "$0")/../pulumi/tailscale"
pulumi preview "$@"

View file

@ -9,5 +9,5 @@ TAILSCALE_OAUTH_CLIENT_SECRET=$(op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get w
export TAILSCALE_OAUTH_CLIENT_SECRET
export TAILSCALE_TAILNET="tail8d86e.ts.net"
cd "$(dirname "$0")/../pulumi"
pulumi up "$@"
cd "$(dirname "$0")/../pulumi/tailscale"
pulumi up --yes "$@"

1
pulumi/gandi/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.venv/

View file

@ -0,0 +1,6 @@
---
config:
blumeops-dns:domain: eblu.me
blumeops-dns:subdomain: ops
# The target IP is resolved dynamically from indri.tail8d86e.ts.net
# indri hosts Caddy, which reverse-proxies all blumeops services

7
pulumi/gandi/Pulumi.yaml Normal file
View file

@ -0,0 +1,7 @@
---
name: blumeops-dns
runtime:
name: python
options:
toolchain: uv
description: DNS configuration for eblu.me via Gandi LiveDNS

98
pulumi/gandi/README.md Normal file
View file

@ -0,0 +1,98 @@
# Gandi DNS Management
This Pulumi project manages DNS records for `eblu.me` via Gandi LiveDNS.
## What It Does
Creates DNS records that point `*.ops.eblu.me` to indri's Tailscale IP.
**Why indri?** indri hosts Caddy, the reverse proxy for all blumeops services.
All `*.ops.eblu.me` requests route through Caddy, which proxies to the appropriate
backend service (either on indri itself or in the k8s cluster).
Since Tailscale IPs (100.x.x.x) are not routable on the public internet, these
DNS records effectively make services accessible only from within the tailnet,
while still using real, resolvable DNS names.
The target IP is resolved dynamically from `indri.tail8d86e.ts.net` at deploy time,
so if indri's Tailscale IP changes, just re-run the deployment.
## Setup
```bash
cd pulumi/gandi
uv sync
pulumi stack select eblu-me # or: pulumi stack init eblu-me
```
## Authentication
This project requires a Gandi Personal Access Token (PAT) with LiveDNS permissions.
**The PAT expires every 30 days and must be cycled manually.**
### Cycling the PAT

I've also enabled the following:

  • See & download SSL certificates
  • Manage domain name technical configurations
  • See and renew domain names
  • Manage Cloud resources
  • See Cloud resources
  • View Organization
  • Deploy Web Hosting instances
  • Manage Web Hosting instances
  • See and renew Web Hosting instances

(If any of those were a mistake to include for security reasons feel free to remove them from this README)

I've also enabled the following: - See & download SSL certificates - Manage domain name technical configurations - See and renew domain names - Manage Cloud resources - See Cloud resources - View Organization - Deploy Web Hosting instances - Manage Web Hosting instances - See and renew Web Hosting instances (If any of those were a mistake to include for security reasons feel free to remove them from this README)
1. Go to [Gandi PAT Management](https://admin.gandi.net/organizations/1db8d76a-f729-11ed-b8d1-00163e94b645/account/pat)
2. Create a new PAT:
- Name: `blumeops-pulumi` (or similar)
- Expiration: 30 days (maximum)
- Permissions required:
- **Manage domain name technical configurations** (required for DNS records)
- See and renew domain names
- Optional permissions (enabled but not strictly required):
- See & download SSL certificates
- Manage Cloud resources
- See Cloud resources
- View Organization
- Deploy Web Hosting instances
- Manage Web Hosting instances
- See and renew Web Hosting instances
3. Update 1Password:
```bash
# Update the existing item with the new PAT value
op item edit mco6ka3dc3rmw7zkg2dhia5d2m pat="<NEW_PAT_VALUE>" --vault vg6xf6vvfmoh5hqjjhlhbeoaie
```
4. Delete the old PAT from Gandi admin console
### Running with Authentication
The mise task handles fetching the PAT from 1Password:
```bash
mise run dns-up # Preview and apply changes
mise run dns-preview # Preview only
```
Or manually:
```bash
export GANDI_PERSONAL_ACCESS_TOKEN=$(op item get mco6ka3dc3rmw7zkg2dhia5d2m --field pat --reveal --vault vg6xf6vvfmoh5hqjjhlhbeoaie)
pulumi up
```
## DNS Records Created
| Record | Type | Value | Purpose |
|--------|------|-------|---------|
| `*.ops.eblu.me` | A | (indri's Tailscale IP) | Wildcard for all services |
| `ops.eblu.me` | A | (indri's Tailscale IP) | Base subdomain |
## Service Hostnames

See the previous comment about maybe making this dynamic

See the previous comment about maybe making this dynamic
Once Caddy is configured on indri, services will be accessible at:
- `forge.ops.eblu.me` - Forgejo git server
- `registry.ops.eblu.me` - Zot container registry
- `grafana.ops.eblu.me` - Grafana dashboards
- `argocd.ops.eblu.me` - ArgoCD
- `feed.ops.eblu.me` - Miniflux RSS reader
- `pypi.ops.eblu.me` - DevPI Python index
- `kiwix.ops.eblu.me` - Kiwix offline content
- `tesla.ops.eblu.me` - TeslaMate
- `torrent.ops.eblu.me` - Transmission
- `prometheus.ops.eblu.me` - Prometheus metrics
- `loki.ops.eblu.me` - Loki logs

61
pulumi/gandi/__main__.py Normal file
View file

@ -0,0 +1,61 @@
"""Pulumi program to manage eblu.me DNS via Gandi LiveDNS.
This program manages DNS records for blumeops infrastructure:
- Wildcard record for *.ops.eblu.me pointing to indri's Tailscale IP
- indri hosts Caddy as the reverse proxy for all services
- This allows services to be accessed via real DNS names while remaining
tailnet-only (Tailscale IPs are not publicly routable)
Authentication:
Set GANDI_PERSONAL_ACCESS_TOKEN environment variable.
See README.md for PAT management instructions.
"""
import os
import socket
import pulumi
import pulumiverse_gandi as gandi
# Get configuration

See previous comments about maybe making this dynamic

See previous comments about maybe making this dynamic
config = pulumi.Config()
domain = config.require("domain") # eblu.me
subdomain = config.require("subdomain") # ops
# Resolve indri's Tailscale IP dynamically via MagicDNS
# This script runs on the tailnet, so we can resolve the hostname directly.
# indri hosts Caddy, which reverse-proxies all services.
# Break-glass: set BLUMEOPS_REVERSE_PROXY_IP env var to override DNS resolution
REVERSE_PROXY_HOST = "indri.tail8d86e.ts.net"
tailscale_ip = os.environ.get("BLUMEOPS_REVERSE_PROXY_IP") or socket.gethostbyname(
REVERSE_PROXY_HOST
)
# Wildcard A record for *.ops.eblu.me
# Points to indri's Tailscale IP, which is only routable within the tailnet.
# This allows containers and other systems to resolve real DNS names
# while keeping services private to the tailnet.
wildcard_record = gandi.livedns.Record(
"ops-wildcard",
zone=domain,
name=f"*.{subdomain}",
type="A",
ttl=300,
values=[tailscale_ip],
)
# Base subdomain record (ops.eblu.me) - same IP
base_record = gandi.livedns.Record(
"ops-base",
zone=domain,
name=subdomain,
type="A",
ttl=300,
values=[tailscale_ip],
)
# ============== Exports ==============
pulumi.export("domain", domain)
pulumi.export("wildcard_fqdn", f"*.{subdomain}.{domain}")
pulumi.export("base_fqdn", f"{subdomain}.{domain}")
pulumi.export("target_ip", tailscale_ip)

View file

@ -0,0 +1,5 @@
[project]
name = "blumeops-dns"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["pulumi>=3.0.0", "pulumiverse-gandi>=2.3.0"]

265
pulumi/gandi/uv.lock generated Normal file
View file

@ -0,0 +1,265 @@
version = 1
revision = 3
requires-python = ">=3.11"
resolution-markers = [
"python_full_version >= '3.14'",
"python_full_version < '3.14'",
]
[[package]]
name = "arpeggio"
version = "2.0.3"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/9e8/5ad35cfc6c938/Arpeggio-2.0.3.tar.gz", hash = "sha256:9e85ad35cfc6c938676817c7ae9a1000a7c72a34c71db0c687136c460d12b85e" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/937/4d9c531b62018/Arpeggio-2.0.3-py2.py3-none-any.whl", hash = "sha256:9374d9c531b62018b787635f37fd81c9a6ee69ef2d28c5db3cd18791b1f7db2f" },
]
[[package]]
name = "attrs"
version = "25.4.0"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/16d/5969b87f0859e/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/adc/f7e2a1fb3b36a/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373" },
]
[[package]]
name = "blumeops-dns"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "pulumi" },
{ name = "pulumiverse-gandi" },
]
[package.metadata]
requires-dist = [
{ name = "pulumi", specifier = ">=3.0.0" },
{ name = "pulumiverse-gandi", specifier = ">=2.3.0" },
]
[[package]]
name = "debugpy"
version = "1.8.19"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/eea/7e5987445ab0b/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/c5d/cfa21de1f735a/debugpy-1.8.19-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:c5dcfa21de1f735a4f7ced4556339a109aa0f618d366ede9da0a3600f2516d8b" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/806/d680024624400/debugpy-1.8.19-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:806d6800246244004625d5222d7765874ab2d22f3ba5f615416cf1342d61c488" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/783/a519e6dfb1f3c/debugpy-1.8.19-cp311-cp311-win32.whl", hash = "sha256:783a519e6dfb1f3cd773a9bda592f4887a65040cb0c7bd38dde410f4e53c40d4" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/140/35cbdbb1fe4b6/debugpy-1.8.19-cp311-cp311-win_amd64.whl", hash = "sha256:14035cbdbb1fe4b642babcdcb5935c2da3b1067ac211c5c5a8fdc0bb31adbcaa" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/bcc/b1540a49cde77/debugpy-1.8.19-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:bccb1540a49cde77edc7ce7d9d075c1dbeb2414751bc0048c7a11e1b597a4c2e" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/e9c/68d9a382ec754/debugpy-1.8.19-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:e9c68d9a382ec754dc05ed1d1b4ed5bd824b9f7c1a8cd1083adb84b3c93501de" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/659/9cab8a783d149/debugpy-1.8.19-cp312-cp312-win32.whl", hash = "sha256:6599cab8a783d1496ae9984c52cb13b7c4a3bd06a8e6c33446832a5d97ce0bee" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/66e/3d2fd8f2035a8/debugpy-1.8.19-cp312-cp312-win_amd64.whl", hash = "sha256:66e3d2fd8f2035a8f111eb127fa508469dfa40928a89b460b41fd988684dc83d" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/91e/35db2672a0aba/debugpy-1.8.19-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:91e35db2672a0abaf325f4868fcac9c1674a0d9ad9bb8a8c849c03a5ebba3e6d" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/850/16a73ab84dea1/debugpy-1.8.19-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:85016a73ab84dea1c1f1dcd88ec692993bcbe4532d1b49ecb5f3c688ae50c606" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b60/5f17e89ba0ece/debugpy-1.8.19-cp313-cp313-win32.whl", hash = "sha256:b605f17e89ba0ecee994391194285fada89cee111cfcd29d6f2ee11cbdc40976" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/c30/639998a9f9cd9/debugpy-1.8.19-cp313-cp313-win_amd64.whl", hash = "sha256:c30639998a9f9cd9699b4b621942c0179a6527f083c72351f95c6ab1728d5b73" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/1e8/c4d1bd230067b/debugpy-1.8.19-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:1e8c4d1bd230067bf1bbcdbd6032e5a57068638eb28b9153d008ecde288152af" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/d40/c016c1f538dbf/debugpy-1.8.19-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d40c016c1f538dbf1762936e3aeb43a89b965069d9f60f9e39d35d9d25e6b809" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/060/1708223fe1cd0/debugpy-1.8.19-cp314-cp314-win32.whl", hash = "sha256:0601708223fe1cd0e27c6cce67a899d92c7d68e73690211e6788a4b0e1903f5b" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/8e1/9a725f5d486f2/debugpy-1.8.19-cp314-cp314-win_amd64.whl", hash = "sha256:8e19a725f5d486f20e53a1dde2ab8bb2c9607c40c00a42ab646def962b41125f" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/360/ffd231a780abb/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38" },
]
[[package]]
name = "dill"
version = "0.4.1"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/423/092df4182177d/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/1e1/ce33e978ae97f/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d" },
]
[[package]]
name = "grpcio"
version = "1.76.0"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/7be/78388d6da1a25/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/2e1/743fbd7f5fa71/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/a8c/2cf1209497cf6/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/08c/aea849a9d3c71/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/f0e/34c2079d47ae9/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/884/3114c0cfce61b/grpcio-1.76.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8843114c0cfce61b40ad48df65abcfc00d4dba82eae8718fab5352390848c5da" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/8ed/dfb4d203a237d/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/324/83fe2aab2c379/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/dcf/e41187da8992c/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/210/7b0c024d1b35f/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/522/175aba7af9113/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/81f/d9652b37b36f1/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/04b/be1bfe3a68bbf/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/d38/8087771c837cd/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/9f8/f757bebaaea11/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/980/a846182ce88c4/grpcio-1.76.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:980a846182ce88c4f2f7e2c22c56aefd515daeb36149d1c897f83cf57999e0b6" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/f92/f88e6c033db65/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/4ba/f3cbe2f0be328/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/615/ba64c208aaceb/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/45d/59a649a82df57/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/c08/8e7a90b601730/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/26e/f06c73eb53267/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/45e/0111e73f43f73/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/83d/57312a58dcfe2/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/3e2/a27c89eb9ac3d/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/61f/69297cba3950a/grpcio-1.76.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61f69297cba3950a524f61c7c8ee12e55c486cb5f7db47ff9dcee33da6f0d3ae" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/6a1/5c17af8839b68/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/25a/18e9810fbc7e7/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/931/091142fd8cc14/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/5e8/571632780e085/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/f9f/7bd5faab55f47/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/ff8/a59ea85a1f219/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/06c/3d6b076e7b593/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/fd5/ef5932f6475c4/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b33/1680e46239e09/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/222/9ae655ec4e899/grpcio-1.76.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2229ae655ec4e8999599469559e97630185fdd53ae1e8997d147b7c9b2b72cba" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/490/fa6d203992c47/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/479/496325ce55479/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/1c9/b93f79f48b03a/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/747/fa73efa9b8b14/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/922/fa70ba549fce3/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e" },
]
[[package]]
name = "parver"
version = "0.5"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
dependencies = [
{ name = "arpeggio" },
{ name = "attrs" },
]
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b9f/de1e6bb9ce9f0/parver-0.5.tar.gz", hash = "sha256:b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/228/1b187276c8e8e/parver-0.5-py3-none-any.whl", hash = "sha256:2281b187276c8e8e3c15634f62287b2fb6fe0efe3010f739a6bd1e45fa2bf2b2" },
]
[[package]]
name = "pip"
version = "25.3"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/8d0/538dbbd7babbd/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/965/5943313a94722/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd" },
]
[[package]]
name = "protobuf"
version = "5.29.5"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/bc1/463bafd4b0929/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/3f1/c6468a2cfd102/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/3f7/6e3a3675b4a4d/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/e38/c5add5a311f2a/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/fa1/8533a299d7ab6/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/638/48923da3325e1/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/6cf/42630262c59b2/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5" },
]
[[package]]
name = "pulumi"
version = "3.217.0"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
dependencies = [
{ name = "debugpy" },
{ name = "dill" },
{ name = "grpcio" },
{ name = "pip" },
{ name = "protobuf" },
{ name = "pyyaml" },
{ name = "semver" },
]
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b5e/fb2e9fb34c2d2/pulumi-3.217.0-py3-none-any.whl", hash = "sha256:b5efb2e9fb34c2d2902d3ec39af0150775c27b80e38c5e421a77454d69dbae25" },
]
[[package]]
name = "pulumiverse-gandi"
version = "2.3.2"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
dependencies = [
{ name = "parver" },
{ name = "pulumi" },
{ name = "semver" },
]
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/59a/621d46fc35be4/pulumiverse_gandi-2.3.2.tar.gz", hash = "sha256:59a621d46fc35be46196d6484a026cae8d6ab973a7241cbc44e0849c931d5ac6" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/13d/be5b0bb9c080d/pulumiverse_gandi-2.3.2-py3-none-any.whl", hash = "sha256:13dbe5b0bb9c080d6c389b1d0fcda907adf8904fa363c053b36bbbf60918220d" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/d76/623373421df22/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/44e/dc64787392855/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/652/cb6edd41e7185/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/108/92704fc220243/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/850/774a7879607d3/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b8b/b0864c5a28024/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/1d3/7d57ad971609c/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/375/03bfbfc9d2c40/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/809/8f252adfa6c80/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/9f3/bfb4965eb8744/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/7f0/47e29dcae4460/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/fc0/9d0aa354569bc/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/914/9cad251584d5f/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/5fd/ec68f91a0c673/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/ba1/cc08a7ccde2d2/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/8dc/52c23056b9ddd/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/417/15c910c881bc0/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/96b/533f0e99f6579/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/5fc/d34e47f6e0b79/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/643/86e5e707d03a7/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/8da/9669d359f02c0/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/228/3a07e2c21a2aa/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/ee2/922902c45ae8c/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/a33/284e20b78bd4a/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/0f2/9edc409a63924/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/f70/57c9a337546ed/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/eda/16858a3cab07b/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/d0e/ae10f8159e8fd/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/790/05a0d97d5ddab/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/549/8cd1645aa724a/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/8d1/fab6bb153a416/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/34d/5fcd24b8445fa/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/501/a031947e3a902/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b3b/c83488de33889/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/c45/8b6d084f9b935/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/7c6/610def4f16354/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/519/0d403f121660c/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/4a2/e8cebe2ff6ab7/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/93d/da82c9c22deb0/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/028/93d100e99e03e/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/c1f/f362665ae5072/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/6ad/c77889b628398/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/a80/cb027f6b34984/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/00c/4bdeba853cc34/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/66e/1674c3ef6f541/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/162/49ee61e95f858/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/4ad/1906908f2f5ae/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9" },
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/ebc/55a14a21cb140/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b" },
]
[[package]]
name = "semver"
version = "3.0.4"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/afc/7d8c584a5ed0a/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/9c8/24d87ba7f7ab4/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/0ce/a48d173cc12fa/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" }
wheels = [
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/f0f/a19c6845758ab/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" },
]