Add Fly.io public reverse proxy for docs.eblu.me (#120)
Some checks failed
Deploy Fly.io Proxy / deploy (push) Failing after 9s
Some checks failed
Deploy Fly.io Proxy / deploy (push) Failing after 9s
## Summary - Adds a Fly.io reverse proxy (`blumeops-proxy`) that tunnels public traffic to homelab services over Tailscale - First service exposed: `docs.eblu.me` — the Quartz static docs site - Includes Pulumi IaC for Tailscale auth key/ACLs and Gandi DNS CNAME - Adds mise tasks (`fly-deploy`, `fly-setup`, `fly-shutoff`) and Forgejo CI workflow ## Key details - Fly.io Firecracker VMs support TUN devices natively — no userspace networking needed - Tailscale auth key is `preauthorized=True` to avoid device approval hangs on container restarts - nginx caches aggressively for the static site; health check is on the default_server block - ACLs restrict `tag:flyio-proxy` to `tag:k8s` on port 443 only - DNS CNAME deployed and verified: `docs.eblu.me` → `blumeops-proxy.fly.dev` ## Test plan - [x] `curl -sf https://blumeops-proxy.fly.dev/healthz` returns `ok` - [x] `curl -I -H "Host: docs.eblu.me" https://blumeops-proxy.fly.dev/` returns 200 with `X-Cache-Status` - [x] `curl -I https://docs.eblu.me/` returns 200 with valid Let's Encrypt cert - [x] `dig forge.ops.eblu.me` still resolves to 100.98.163.89 (private services unaffected) - [x] Set `FLY_DEPLOY_TOKEN` Forgejo Actions secret for CI auto-deploy 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/120
This commit is contained in:
parent
fbedaf2833
commit
64a78422b1
35 changed files with 928 additions and 208 deletions
37
.forgejo/workflows/deploy-fly.yaml
Normal file
37
.forgejo/workflows/deploy-fly.yaml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
name: Deploy Fly.io Proxy
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'fly/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: k8s
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install flyctl
|
||||||
|
run: |
|
||||||
|
curl -L https://fly.io/install.sh | sh
|
||||||
|
echo "/root/.fly/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Deploy to Fly.io
|
||||||
|
env:
|
||||||
|
FLY_API_TOKEN: ${{ secrets.FLY_DEPLOY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
cd fly
|
||||||
|
fly deploy
|
||||||
|
|
||||||
|
- name: Verify health
|
||||||
|
env:
|
||||||
|
FLY_API_TOKEN: ${{ secrets.FLY_DEPLOY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
fly status -a blumeops-proxy
|
||||||
|
echo ""
|
||||||
|
echo "Health check:"
|
||||||
|
sleep 10
|
||||||
|
curl -sf https://blumeops-proxy.fly.dev/healthz || echo "Warning: health check failed (may need DNS propagation)"
|
||||||
1
Brewfile
1
Brewfile
|
|
@ -4,4 +4,5 @@ brew "argocd" # ArgoCD CLI for GitOps management
|
||||||
brew "bat" # Syntax-highlighted file concatenation
|
brew "bat" # Syntax-highlighted file concatenation
|
||||||
brew "mise" # Task runner and toolchain manager
|
brew "mise" # Task runner and toolchain manager
|
||||||
brew "tea" # Gitea/Forgejo CLI for forge.ops.eblu.me
|
brew "tea" # Gitea/Forgejo CLI for forge.ops.eblu.me
|
||||||
|
brew "flyctl" # Fly.io CLI for public proxy management
|
||||||
brew "podman" # Container CLI (uses VM on macOS, for building/pushing images)
|
brew "podman" # Container CLI (uses VM on macOS, for building/pushing images)
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,6 @@ This repo uses [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Documentation lives in `docs/` and follows the [Diataxis](https://diataxis.fr/) framework. Published at https://docs.ops.eblu.me.
|
Documentation lives in `docs/` and follows the [Diataxis](https://diataxis.fr/) framework. Published at https://docs.eblu.me.
|
||||||
|
|
||||||
Docs use [Obsidian](https://obsidian.md) wiki-link syntax (`[[link]]`) for cross-references. Edit with any markdown editor, or use [obsidian.nvim](https://github.com/obsidian-nvim/obsidian.nvim) for enhanced navigation.
|
Docs use [Obsidian](https://obsidian.md) wiki-link syntax (`[[link]]`) for cross-references. Edit with any markdown editor, or use [obsidian.nvim](https://github.com/obsidian-nvim/obsidian.nvim) for enhanced navigation.
|
||||||
|
|
|
||||||
|
|
@ -82,10 +82,21 @@
|
||||||
check_mode: false
|
check_mode: false
|
||||||
tags: [forgejo_actions_secrets]
|
tags: [forgejo_actions_secrets]
|
||||||
|
|
||||||
|
- name: Fetch Fly.io deploy token for Forgejo Actions
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get on5slfaygtdjrxmdwezyhfmqsq --fields deploy-token --reveal
|
||||||
|
delegate_to: localhost
|
||||||
|
register: _fly_deploy_token
|
||||||
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
check_mode: false
|
||||||
|
tags: [forgejo_actions_secrets]
|
||||||
|
|
||||||
- name: Set Forgejo Actions secrets facts
|
- name: Set Forgejo Actions secrets facts
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
forgejo_api_token: "{{ _forgejo_api_token.stdout }}"
|
forgejo_api_token: "{{ _forgejo_api_token.stdout }}"
|
||||||
forgejo_secret_argocd_token: "{{ _forgejo_argocd_token.stdout }}"
|
forgejo_secret_argocd_token: "{{ _forgejo_argocd_token.stdout }}"
|
||||||
|
forgejo_secret_fly_deploy_token: "{{ _fly_deploy_token.stdout }}"
|
||||||
no_log: true
|
no_log: true
|
||||||
tags: [forgejo_actions_secrets]
|
tags: [forgejo_actions_secrets]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,5 @@ forgejo_actions_secrets_repo: blumeops
|
||||||
forgejo_actions_secrets_list:
|
forgejo_actions_secrets_list:
|
||||||
- name: ARGOCD_AUTH_TOKEN
|
- name: ARGOCD_AUTH_TOKEN
|
||||||
value_var: forgejo_secret_argocd_token
|
value_var: forgejo_secret_argocd_token
|
||||||
|
- name: FLY_DEPLOY_TOKEN
|
||||||
|
value_var: forgejo_secret_fly_deploy_token
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ metadata:
|
||||||
gethomepage.dev/group: "Apps"
|
gethomepage.dev/group: "Apps"
|
||||||
gethomepage.dev/icon: "mdi-book-open-page-variant"
|
gethomepage.dev/icon: "mdi-book-open-page-variant"
|
||||||
gethomepage.dev/description: "BlumeOps Documentation"
|
gethomepage.dev/description: "BlumeOps Documentation"
|
||||||
gethomepage.dev/href: "https://docs.ops.eblu.me"
|
gethomepage.dev/href: "https://docs.eblu.me"
|
||||||
gethomepage.dev/pod-selector: "app=docs"
|
gethomepage.dev/pod-selector: "app=docs"
|
||||||
spec:
|
spec:
|
||||||
ingressClassName: tailscale
|
ingressClassName: tailscale
|
||||||
|
|
|
||||||
1
docs/changelog.d/feature-flyio-proxy.doc.md
Normal file
1
docs/changelog.d/feature-flyio-proxy.doc.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update docs for public proxy: canonical URL is now docs.eblu.me, add Fly.io proxy reference card and operations how-to
|
||||||
1
docs/changelog.d/feature-flyio-proxy.feature.md
Normal file
1
docs/changelog.d/feature-flyio-proxy.feature.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add Fly.io public reverse proxy infrastructure for exposing services to the internet (first target: docs.eblu.me)
|
||||||
|
|
@ -11,8 +11,6 @@ id: expose-service-publicly
|
||||||
|
|
||||||
# Expose a Service Publicly via Fly.io + Tailscale
|
# Expose a Service Publicly via Fly.io + Tailscale
|
||||||
|
|
||||||
> **Status:** Plan — not yet implemented. First target: `docs.eblu.me`.
|
|
||||||
|
|
||||||
This guide describes how to expose a BlumeOps service to the public internet
|
This guide describes how to expose a BlumeOps service to the public internet
|
||||||
using a reverse proxy container on [Fly.io](https://fly.io) that tunnels back
|
using a reverse proxy container on [Fly.io](https://fly.io) that tunnels back
|
||||||
to [[indri]] over [[tailscale]]. The approach keeps the home IP hidden,
|
to [[indri]] over [[tailscale]]. The approach keeps the home IP hidden,
|
||||||
|
|
@ -50,7 +48,7 @@ infrastructure. They can continue to operate in parallel for private access.
|
||||||
|
|
||||||
| Decision | Choice | Rationale |
|
| Decision | Choice | Rationale |
|
||||||
|----------|--------|-----------|
|
|----------|--------|-----------|
|
||||||
| Proxy host | Fly.io (free tier) | Managed container, no server to maintain via Ansible |
|
| Proxy host | Fly.io (free tier) | Managed container, no server to maintain via Ansible. Shared IPv4 + IPv6 are free for HTTP/HTTPS; dedicated IPv4 is $2/mo if a service needs non-HTTP(S) protocols |
|
||||||
| Tunnel | Tailscale (existing) | Already in use, WireGuard encryption, ACL control |
|
| Tunnel | Tailscale (existing) | Already in use, WireGuard encryption, ACL control |
|
||||||
| DNS | CNAME at [[gandi]] | No DNS migration needed, no Cloudflare dependency |
|
| DNS | CNAME at [[gandi]] | No DNS migration needed, no Cloudflare dependency |
|
||||||
| TLS (public) | Fly.io auto-provisions Let's Encrypt | No cert management, `$0.10/mo` per hostname |
|
| TLS (public) | Fly.io auto-provisions Let's Encrypt | No cert management, `$0.10/mo` per hostname |
|
||||||
|
|
@ -146,7 +144,8 @@ COPY --from=docker.io/tailscale/tailscale:stable \
|
||||||
COPY --from=docker.io/tailscale/tailscale:stable \
|
COPY --from=docker.io/tailscale/tailscale:stable \
|
||||||
/usr/local/bin/tailscale /usr/local/bin/tailscale
|
/usr/local/bin/tailscale /usr/local/bin/tailscale
|
||||||
|
|
||||||
RUN mkdir -p /var/run/tailscale /var/lib/tailscale
|
RUN mkdir -p /var/run/tailscale /var/lib/tailscale \
|
||||||
|
&& apk add --no-cache iptables ip6tables
|
||||||
|
|
||||||
COPY nginx.conf /etc/nginx/nginx.conf
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
COPY start.sh /start.sh
|
COPY start.sh /start.sh
|
||||||
|
|
@ -163,8 +162,9 @@ CMD ["/start.sh"]
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Start tailscale in userspace networking mode (no TUN device needed)
|
# Start tailscale daemon. Fly.io runs Firecracker microVMs which support
|
||||||
tailscaled --tun=userspace-networking --statedir=/var/lib/tailscale &
|
# TUN devices natively — no need for --tun=userspace-networking.
|
||||||
|
tailscaled --statedir=/var/lib/tailscale &
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
# Authenticate and join tailnet
|
# Authenticate and join tailnet
|
||||||
|
|
@ -174,7 +174,7 @@ tailscale up --authkey="${TS_AUTHKEY}" --hostname=flyio-proxy
|
||||||
until tailscale status > /dev/null 2>&1; do sleep 1; done
|
until tailscale status > /dev/null 2>&1; do sleep 1; done
|
||||||
echo "Tailscale connected"
|
echo "Tailscale connected"
|
||||||
|
|
||||||
# Start nginx
|
# Start nginx — MagicDNS resolves *.tail8d86e.ts.net hostnames
|
||||||
nginx -g "daemon off;"
|
nginx -g "daemon off;"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -211,6 +211,7 @@ http {
|
||||||
location / {
|
location / {
|
||||||
proxy_pass https://docs.tail8d86e.ts.net;
|
proxy_pass https://docs.tail8d86e.ts.net;
|
||||||
proxy_ssl_verify off;
|
proxy_ssl_verify off;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
|
||||||
# Cache aggressively — static site only.
|
# Cache aggressively — static site only.
|
||||||
# Do NOT use these settings for dynamic services.
|
# Do NOT use these settings for dynamic services.
|
||||||
|
|
@ -228,17 +229,20 @@ http {
|
||||||
|
|
||||||
add_header X-Cache-Status $upstream_cache_status;
|
add_header X-Cache-Status $upstream_cache_status;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Catch-all: reject unknown hosts, but serve health check
|
||||||
|
server {
|
||||||
|
listen 8080 default_server;
|
||||||
|
|
||||||
location /healthz {
|
location /healthz {
|
||||||
return 200 "ok\n";
|
return 200 "ok\n";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Catch-all: reject unknown hosts
|
location / {
|
||||||
server {
|
|
||||||
listen 8080 default_server;
|
|
||||||
return 444;
|
return 444;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -250,16 +254,22 @@ Extend the existing `pulumi/tailscale/` project.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Auth key for Fly.io proxy container
|
# Auth key for Fly.io proxy container
|
||||||
flyio_key = tailscale.TailscaleKey(
|
flyio_key = tailscale.TailnetKey(
|
||||||
"flyio-proxy-key",
|
"flyio-proxy-key",
|
||||||
reusable=True,
|
reusable=True,
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
|
preauthorized=True, # Skip device approval on the tailnet
|
||||||
tags=["tag:flyio-proxy"],
|
tags=["tag:flyio-proxy"],
|
||||||
expiry=7776000, # 90 days
|
expiry=7776000, # 90 days
|
||||||
)
|
)
|
||||||
pulumi.export("flyio_authkey", flyio_key.key)
|
pulumi.export("flyio_authkey", flyio_key.key)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note:** `preauthorized=True` is required if your tailnet has device
|
||||||
|
> approval enabled. Without it, each new container start (including
|
||||||
|
> health-check restarts) creates a node that needs manual approval,
|
||||||
|
> causing the container to hang before nginx starts.
|
||||||
|
|
||||||
**Add to `pulumi/tailscale/policy.hujson`:**
|
**Add to `pulumi/tailscale/policy.hujson`:**
|
||||||
|
|
||||||
Tag owner:
|
Tag owner:
|
||||||
|
|
@ -323,10 +333,19 @@ set -euo pipefail
|
||||||
|
|
||||||
APP="blumeops-proxy"
|
APP="blumeops-proxy"
|
||||||
|
|
||||||
# Fetch Tailscale auth key from 1Password
|
# Fetch Tailscale auth key from Pulumi state
|
||||||
TS_AUTHKEY=$(op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get <FLY_ITEM_ID> --fields ts-authkey --reveal)
|
echo "Fetching Tailscale auth key from Pulumi..."
|
||||||
fly secrets set TS_AUTHKEY="$TS_AUTHKEY" -a "$APP"
|
TS_AUTHKEY=$(cd "$(dirname "$0")/../pulumi/tailscale" && pulumi stack select tail8d86e && pulumi stack output flyio_authkey --show-secrets)
|
||||||
echo "Tailscale auth key set"
|
fly secrets set TS_AUTHKEY="$TS_AUTHKEY" --stage -a "$APP"
|
||||||
|
echo "Tailscale auth key staged (will take effect on next deploy)"
|
||||||
|
|
||||||
|
# Allocate IPs (idempotent — fly errors if already allocated)
|
||||||
|
# Shared IPv4 is free and sufficient for HTTP/HTTPS services.
|
||||||
|
# Use 'fly ips allocate-v4' (no --shared) for dedicated IPv4 ($2/mo)
|
||||||
|
# if the service needs non-HTTP protocols.
|
||||||
|
fly ips allocate-v4 --shared -a "$APP" 2>/dev/null || true
|
||||||
|
fly ips allocate-v6 -a "$APP" 2>/dev/null || true
|
||||||
|
echo "IPs allocated"
|
||||||
|
|
||||||
# Add certs for all public domains (idempotent — fly ignores duplicates)
|
# Add certs for all public domains (idempotent — fly ignores duplicates)
|
||||||
fly certs add docs.eblu.me -a "$APP" 2>/dev/null || true
|
fly certs add docs.eblu.me -a "$APP" 2>/dev/null || true
|
||||||
|
|
@ -497,9 +516,41 @@ Key differences for dynamic services:
|
||||||
- **WebSocket support** — many modern web apps use WebSockets
|
- **WebSocket support** — many modern web apps use WebSockets
|
||||||
- **Larger body size** — git pushes and file uploads need more than the default 1MB
|
- **Larger body size** — git pushes and file uploads need more than the default 1MB
|
||||||
|
|
||||||
### 2. Add DNS CNAME (Pulumi)
|
### 2. Add Fly.io certificate
|
||||||
|
|
||||||
Add to `pulumi/gandi/__main__.py`:
|
```bash
|
||||||
|
fly certs add wiki.eblu.me -a blumeops-proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
Or add it to `mise-tasks/fly-setup` so it's captured for future runs.
|
||||||
|
|
||||||
|
### 3. Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mise run fly-deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Or push the `fly/nginx.conf` change to main — the Forgejo workflow deploys automatically.
|
||||||
|
|
||||||
|
### 4. Verify against fly.dev
|
||||||
|
|
||||||
|
Test the proxy before touching DNS. Use the `Host` header to simulate
|
||||||
|
the real domain:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check
|
||||||
|
curl -sf https://blumeops-proxy.fly.dev/healthz
|
||||||
|
|
||||||
|
# Simulate real domain request
|
||||||
|
curl -I -H "Host: wiki.eblu.me" https://blumeops-proxy.fly.dev/
|
||||||
|
# Should return 200 with X-Cache-Status header
|
||||||
|
```
|
||||||
|
|
||||||
|
If this fails, debug without any public DNS impact.
|
||||||
|
|
||||||
|
### 5. Add DNS CNAME (Pulumi)
|
||||||
|
|
||||||
|
Only after verifying the proxy works. Add to `pulumi/gandi/__main__.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
wiki_public = gandi.livedns.Record(
|
wiki_public = gandi.livedns.Record(
|
||||||
|
|
@ -514,30 +565,14 @@ wiki_public = gandi.livedns.Record(
|
||||||
|
|
||||||
Deploy: `mise run dns-preview` then `mise run dns-up`.
|
Deploy: `mise run dns-preview` then `mise run dns-up`.
|
||||||
|
|
||||||
### 3. Add Fly.io certificate
|
### 6. Verify with real domain
|
||||||
|
|
||||||
```bash
|
|
||||||
fly certs add wiki.eblu.me -a blumeops-proxy
|
|
||||||
```
|
|
||||||
|
|
||||||
Or add it to `mise-tasks/fly-setup` so it's captured for future runs.
|
|
||||||
|
|
||||||
### 4. Deploy
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mise run fly-deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
Or push the `fly/nginx.conf` change to main — the Forgejo workflow deploys automatically.
|
|
||||||
|
|
||||||
### 5. Verify
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -I https://wiki.eblu.me
|
curl -I https://wiki.eblu.me
|
||||||
# Should return 200 with X-Cache-Status header
|
# Should return 200 with X-Cache-Status header
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Update Tailscale ACLs if needed
|
### 7. Update Tailscale ACLs if needed
|
||||||
|
|
||||||
The one-time setup grants `tag:flyio-proxy` access to `tag:k8s` on port
|
The one-time setup grants `tag:flyio-proxy` access to `tag:k8s` on port
|
||||||
443. If the new service needs a different grant, add it to
|
443. If the new service needs a different grant, add it to
|
||||||
|
|
@ -620,22 +655,13 @@ Setup considerations for Forgejo specifically:
|
||||||
|
|
||||||
### Break-glass shutoff
|
### Break-glass shutoff
|
||||||
|
|
||||||
If the proxy is causing issues (DDoS, unexpected traffic, bandwidth consumption on the home network):
|
If the proxy is causing issues, stop it immediately:
|
||||||
|
|
||||||
**Level 1 — Stop the container (seconds, reversible):**
|
|
||||||
```bash
|
```bash
|
||||||
mise run fly-shutoff
|
mise run fly-shutoff
|
||||||
# or: fly scale count 0 -a blumeops-proxy --yes
|
|
||||||
```
|
```
|
||||||
All public services go offline immediately. Tailscale tunnel drops. Zero traffic reaches indri. Restore with `fly scale count 1 -a blumeops-proxy`.
|
|
||||||
|
|
||||||
**Level 2 — Revoke Tailscale access (seconds):**
|
This stops all machines in seconds — zero traffic reaches indri. See [[manage-flyio-proxy#Emergency Shutoff]] for the full escalation ladder (container stop → Tailscale revoke → DNS removal).
|
||||||
Remove the `flyio-proxy` node in the Tailscale admin console. Even if the container is running, it cannot reach the tailnet. Use this if the container itself may be compromised.
|
|
||||||
|
|
||||||
**Level 3 — Remove DNS (minutes to hours):**
|
|
||||||
Delete the CNAME records at Gandi. Takes time for DNS propagation but is the permanent shutoff.
|
|
||||||
|
|
||||||
**Level 1 is the primary response.** It is a single command, takes effect in seconds, and is trivially reversible. Document the `mise run fly-shutoff` command somewhere easily accessible (e.g., pinned in a notes app) so it can be run quickly under stress.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -688,12 +714,23 @@ The "semi" for Fly.io secrets is a one-time operation backed by a repeatable mis
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
After initial deployment of a service (using `docs.eblu.me` as example):
|
### Pre-DNS (verify against fly.dev)
|
||||||
|
|
||||||
|
Test the proxy works before creating any public DNS records:
|
||||||
|
|
||||||
|
1. `curl -sf https://blumeops-proxy.fly.dev/healthz` — returns `ok`
|
||||||
|
2. `curl -I -H "Host: docs.eblu.me" https://blumeops-proxy.fly.dev/` — returns 200 with `X-Cache-Status` header
|
||||||
|
3. `fly status -a blumeops-proxy` — shows healthy machine
|
||||||
|
4. All `*.ops.eblu.me` services still work from tailnet (unchanged)
|
||||||
|
5. `mise run services-check` passes
|
||||||
|
|
||||||
|
If anything fails here, debug without public DNS impact.
|
||||||
|
|
||||||
|
### Post-DNS (after CNAME is live)
|
||||||
|
|
||||||
|
After deploying DNS (`mise run dns-up`):
|
||||||
|
|
||||||
1. `curl -I https://docs.eblu.me` — returns 200 with `X-Cache-Status` header
|
1. `curl -I https://docs.eblu.me` — returns 200 with `X-Cache-Status` header
|
||||||
2. `dig docs.eblu.me` — resolves to Fly.io IPs (not Tailscale IP)
|
2. `dig docs.eblu.me` — resolves to Fly.io IPs (not Tailscale IP)
|
||||||
3. `dig forge.ops.eblu.me` — still resolves to `100.98.163.89` (unchanged)
|
3. `dig forge.ops.eblu.me` — still resolves to `100.98.163.89` (unchanged)
|
||||||
4. All `*.ops.eblu.me` services work from tailnet
|
4. Second request to same URL shows `X-Cache-Status: HIT`
|
||||||
5. `mise run services-check` passes
|
|
||||||
6. `fly status -a blumeops-proxy` shows healthy machine
|
|
||||||
7. Second request to same URL shows `X-Cache-Status: HIT`
|
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,5 @@ Task-oriented instructions for common BlumeOps operations. These guides assume y
|
||||||
| Guide | Description |
|
| Guide | Description |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
| [[restart-indri]] | Safely shut down and restart indri |
|
| [[restart-indri]] | Safely shut down and restart indri |
|
||||||
|
| [[manage-flyio-proxy]] | Deploy, shutoff, and troubleshoot the public proxy |
|
||||||
| [[troubleshooting]] | Diagnose and fix common issues |
|
| [[troubleshooting]] | Diagnose and fix common issues |
|
||||||
|
|
|
||||||
88
docs/how-to/manage-flyio-proxy.md
Normal file
88
docs/how-to/manage-flyio-proxy.md
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
---
|
||||||
|
title: Manage Fly.io Proxy
|
||||||
|
tags:
|
||||||
|
- how-to
|
||||||
|
- fly-io
|
||||||
|
- networking
|
||||||
|
- operations
|
||||||
|
---
|
||||||
|
|
||||||
|
# Manage Fly.io Proxy
|
||||||
|
|
||||||
|
Operational tasks for the [[flyio-proxy]] public reverse proxy.
|
||||||
|
|
||||||
|
## Deploy Changes
|
||||||
|
|
||||||
|
After modifying files in `fly/`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mise run fly-deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Pushes to `fly/` on main also trigger automatic deployment via the Forgejo CI workflow.
|
||||||
|
|
||||||
|
## Add a New Public Service
|
||||||
|
|
||||||
|
See [[expose-service-publicly#Per-service setup]] for the full walkthrough. In short:
|
||||||
|
|
||||||
|
1. Add a `server` block to `fly/nginx.conf`
|
||||||
|
2. Add a Fly.io certificate: `fly certs add <domain> -a blumeops-proxy`
|
||||||
|
3. Deploy: `mise run fly-deploy`
|
||||||
|
4. Verify against `blumeops-proxy.fly.dev` with a `Host` header
|
||||||
|
5. Add DNS CNAME via Pulumi: `mise run dns-preview` then `mise run dns-up`
|
||||||
|
|
||||||
|
## Emergency Shutoff
|
||||||
|
|
||||||
|
If the proxy is causing issues (DDoS, unexpected traffic, bandwidth consumption on the home network):
|
||||||
|
|
||||||
|
**Level 1 — Stop the container (seconds, reversible):**
|
||||||
|
```bash
|
||||||
|
mise run fly-shutoff
|
||||||
|
# or: fly scale count 0 -a blumeops-proxy --yes
|
||||||
|
```
|
||||||
|
All public services go offline immediately. Tailscale tunnel drops. Zero traffic reaches indri. Restore with `fly scale count 1 -a blumeops-proxy`.
|
||||||
|
|
||||||
|
**Level 2 — Revoke Tailscale access (seconds):**
|
||||||
|
Remove the `flyio-proxy` node in the Tailscale admin console. Even if the container is running, it cannot reach the tailnet. Use this if the container itself may be compromised.
|
||||||
|
|
||||||
|
**Level 3 — Remove DNS (minutes to hours):**
|
||||||
|
Delete the CNAME records at Gandi. Takes time for DNS propagation but is the permanent shutoff.
|
||||||
|
|
||||||
|
**Level 1 is the primary response.** It is a single command, takes effect in seconds, and is trivially reversible. Keep `mise run fly-shutoff` somewhere easily accessible (e.g., pinned in a notes app) so it can be run quickly under stress.
|
||||||
|
|
||||||
|
## Check Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# App and machine status
|
||||||
|
fly status -a blumeops-proxy
|
||||||
|
|
||||||
|
# Live logs
|
||||||
|
fly logs -a blumeops-proxy
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
curl -sf https://blumeops-proxy.fly.dev/healthz
|
||||||
|
|
||||||
|
# Certificate status
|
||||||
|
fly certs list -a blumeops-proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rotate Tailscale Auth Key
|
||||||
|
|
||||||
|
The auth key expires every 90 days. To rotate:
|
||||||
|
|
||||||
|
1. Re-apply Pulumi to generate a new key: `mise run tailnet-up`
|
||||||
|
2. Re-run setup to stage the new secret: `mise run fly-setup`
|
||||||
|
3. Deploy to pick up the new secret: `mise run fly-deploy`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**502 Bad Gateway**: Check `fly logs` for nginx upstream errors. Verify the backend Tailscale service is running (`tailscale status` from inside the container via `fly ssh console`).
|
||||||
|
|
||||||
|
**Health check failing**: `fly ssh console -a blumeops-proxy` then `curl localhost:8080/healthz` to test locally.
|
||||||
|
|
||||||
|
**TLS errors on custom domain**: Check cert status with `fly certs show <domain> -a blumeops-proxy`. Certs auto-provision via Let's Encrypt and may take a few minutes.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [[flyio-proxy]] - Service reference card
|
||||||
|
- [[expose-service-publicly]] - Full setup guide and architecture
|
||||||
|
|
@ -8,7 +8,7 @@ tags:
|
||||||
|
|
||||||
# Update Documentation
|
# Update Documentation
|
||||||
|
|
||||||
How to publish documentation changes to https://docs.ops.eblu.me.
|
How to publish documentation changes to https://docs.eblu.me.
|
||||||
|
|
||||||
## Quick Release
|
## Quick Release
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ editor of choice. (I recommend vim.)
|
||||||
|
|
||||||
These services run on my home [[hosts|infrastructure]], primarily an m1 mac
|
These services run on my home [[hosts|infrastructure]], primarily an m1 mac
|
||||||
mini named [[indri]] and a Synology NAS called [[sifaka]]. The infrastructure
|
mini named [[indri]] and a Synology NAS called [[sifaka]]. The infrastructure
|
||||||
is networked via [[tailscale]], with the domain `eblu.me` hosted via [[gandi]]
|
is networked via [[tailscale]], with the domain `eblu.me` hosted via [[gandi]],
|
||||||
with [[caddy]] providing a reverse proxy to resolve tailnet devices.
|
[[caddy]] providing a private reverse proxy for tailnet devices, and
|
||||||
|
[[flyio-proxy|Fly.io]] serving public-facing services like
|
||||||
|
[this documentation site](https://docs.eblu.me).
|
||||||
|
|
||||||
The goal of BlumeOps is threefold:
|
The goal of BlumeOps is threefold:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ Individual service reference cards with URLs and configuration details.
|
||||||
| [[zot]] | Container registry | indri |
|
| [[zot]] | Container registry | indri |
|
||||||
| [[devpi]] | PyPI caching proxy | k8s |
|
| [[devpi]] | PyPI caching proxy | k8s |
|
||||||
| [[docs]] | Documentation site (Quartz) | k8s |
|
| [[docs]] | Documentation site (Quartz) | k8s |
|
||||||
|
| [[flyio-proxy]] | Public reverse proxy (Fly.io + Tailscale) | Fly.io |
|
||||||
| [[automounter]] | SMB share automounter | indri |
|
| [[automounter]] | SMB share automounter | indri |
|
||||||
|
|
||||||
## Infrastructure
|
## Infrastructure
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ K8s services are proxied via their Tailscale Ingress endpoints:
|
||||||
|-----------|---------|---------|
|
|-----------|---------|---------|
|
||||||
| `grafana.ops.eblu.me` | `grafana.tail8d86e.ts.net` | [[grafana]] |
|
| `grafana.ops.eblu.me` | `grafana.tail8d86e.ts.net` | [[grafana]] |
|
||||||
| `argocd.ops.eblu.me` | `argocd.tail8d86e.ts.net` | [[argocd]] |
|
| `argocd.ops.eblu.me` | `argocd.tail8d86e.ts.net` | [[argocd]] |
|
||||||
| `docs.ops.eblu.me` | `docs.tail8d86e.ts.net` | [[docs]] |
|
| `docs.ops.eblu.me` | `docs.tail8d86e.ts.net` | [[docs]] (now publicly available at `docs.eblu.me` via [[flyio-proxy]]) |
|
||||||
| `feed.ops.eblu.me` | `feed.tail8d86e.ts.net` | [[miniflux]] |
|
| `feed.ops.eblu.me` | `feed.tail8d86e.ts.net` | [[miniflux]] |
|
||||||
| ... | ... | (see defaults/main.yml for full list) |
|
| ... | ... | (see defaults/main.yml for full list) |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,13 @@ Documentation site built with [Quartz](https://quartz.jzhao.xyz/) and served via
|
||||||
|
|
||||||
| Property | Value |
|
| Property | Value |
|
||||||
|----------|-------|
|
|----------|-------|
|
||||||
| **URL** | https://docs.ops.eblu.me |
|
| **Public URL** | https://docs.eblu.me |
|
||||||
|
| **Private URL** | `docs.ops.eblu.me` (tailnet only, via [[caddy]]) |
|
||||||
| **Namespace** | `docs` |
|
| **Namespace** | `docs` |
|
||||||
| **Container** | `registry.ops.eblu.me/blumeops/quartz:v1.0.0` |
|
| **Container** | `registry.ops.eblu.me/blumeops/quartz:v1.0.0` |
|
||||||
| **Source** | `docs/` directory in blumeops repo |
|
| **Source** | `docs/` directory in blumeops repo |
|
||||||
| **Build** | Forgejo workflow `build-blumeops.yaml` |
|
| **Build** | Forgejo workflow `build-blumeops.yaml` |
|
||||||
|
| **Public proxy** | [[flyio-proxy]] (Fly.io → Tailscale tunnel) |
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
|
|
||||||
64
docs/reference/services/flyio-proxy.md
Normal file
64
docs/reference/services/flyio-proxy.md
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
title: Fly.io Proxy
|
||||||
|
tags:
|
||||||
|
- service
|
||||||
|
- networking
|
||||||
|
- fly-io
|
||||||
|
---
|
||||||
|
|
||||||
|
# Fly.io Proxy
|
||||||
|
|
||||||
|
Public reverse proxy on [Fly.io](https://fly.io) that exposes selected BlumeOps services to the internet via a Tailscale tunnel back to the homelab.
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **App** | `blumeops-proxy` |
|
||||||
|
| **Region** | `sjc` (San Jose) |
|
||||||
|
| **Fly.io URL** | `blumeops-proxy.fly.dev` |
|
||||||
|
| **Config** | `fly/` directory in repo |
|
||||||
|
| **IaC** | `fly/fly.toml` (app), Pulumi (DNS + auth key) |
|
||||||
|
|
||||||
|
## Exposed Services
|
||||||
|
|
||||||
|
| Public domain | Backend | Service |
|
||||||
|
|---------------|---------|---------|
|
||||||
|
| `docs.eblu.me` | `docs.tail8d86e.ts.net` | [[docs]] |
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Internet traffic hits Fly.io's Anycast edge, terminates TLS with a Let's Encrypt certificate, and is proxied by nginx to the backend service over a Tailscale WireGuard tunnel. See [[expose-service-publicly]] for the full architecture diagram.
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `fly/fly.toml` | App configuration |
|
||||||
|
| `fly/Dockerfile` | nginx + Tailscale container |
|
||||||
|
| `fly/nginx.conf` | Reverse proxy, caching, rate limiting |
|
||||||
|
| `fly/start.sh` | Entrypoint: start Tailscale, then nginx |
|
||||||
|
| `pulumi/tailscale/__main__.py` | Auth key (`tag:flyio-proxy`) |
|
||||||
|
| `pulumi/tailscale/policy.hujson` | ACL grants for proxy |
|
||||||
|
| `pulumi/gandi/__main__.py` | DNS CNAMEs |
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
Fly.io runs Firecracker microVMs which support TUN devices natively. Tailscale runs with a real TUN interface (not userspace networking), so MagicDNS and direct Tailscale IP routing work normally.
|
||||||
|
|
||||||
|
The Tailscale auth key is `preauthorized=True` to avoid device approval hangs on container restarts.
|
||||||
|
|
||||||
|
## Secrets
|
||||||
|
|
||||||
|
| Secret | Source | Description |
|
||||||
|
|--------|--------|-------------|
|
||||||
|
| `TS_AUTHKEY` | Pulumi state → `fly secrets` | Tailscale auth key for joining tailnet |
|
||||||
|
| `FLY_DEPLOY_TOKEN` | Fly.io → 1Password | Deploy token for CI |
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [[expose-service-publicly]] - Setup guide for adding new public services
|
||||||
|
- [[manage-flyio-proxy]] - Operational tasks (deploy, shutoff, troubleshoot)
|
||||||
|
- [[caddy]] - Private reverse proxy for `*.ops.eblu.me` (separate system)
|
||||||
|
- [[tailscale]] - WireGuard mesh network
|
||||||
|
- [[gandi]] - DNS hosting
|
||||||
|
|
@ -67,7 +67,7 @@ Documentation uses `[[wiki-links]]` for cross-references:
|
||||||
- `[[service-name]]` links to a reference page
|
- `[[service-name]]` links to a reference page
|
||||||
- `[[page|Display Text]]` customizes the link text
|
- `[[page|Display Text]]` customizes the link text
|
||||||
|
|
||||||
When reading on the web (docs.ops.eblu.me), these render as clickable links. The backlinks panel shows what references each page.
|
When reading on the web (docs.eblu.me), these render as clickable links. The backlinks panel shows what references each page.
|
||||||
|
|
||||||
Pre-commit hooks automatically validate that all wiki-links point to existing files and that link targets are unambiguous.
|
Pre-commit hooks automatically validate that all wiki-links point to existing files and that link targets are unambiguous.
|
||||||
|
|
||||||
|
|
|
||||||
18
fly/Dockerfile
Normal file
18
fly/Dockerfile
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Copy tailscale binaries from official image
|
||||||
|
COPY --from=docker.io/tailscale/tailscale:stable \
|
||||||
|
/usr/local/bin/tailscaled /usr/local/bin/tailscaled
|
||||||
|
COPY --from=docker.io/tailscale/tailscale:stable \
|
||||||
|
/usr/local/bin/tailscale /usr/local/bin/tailscale
|
||||||
|
|
||||||
|
RUN mkdir -p /var/run/tailscale /var/lib/tailscale \
|
||||||
|
&& apk add --no-cache iptables ip6tables
|
||||||
|
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY start.sh /start.sh
|
||||||
|
RUN chmod +x /start.sh
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["/start.sh"]
|
||||||
19
fly/fly.toml
Normal file
19
fly/fly.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
app = "blumeops-proxy"
|
||||||
|
primary_region = "sjc"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 8080
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = "off"
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 1
|
||||||
|
|
||||||
|
[checks]
|
||||||
|
[checks.health]
|
||||||
|
port = 8080
|
||||||
|
type = "http"
|
||||||
|
interval = "30s"
|
||||||
|
timeout = "5s"
|
||||||
|
path = "/healthz"
|
||||||
61
fly/nginx.conf
Normal file
61
fly/nginx.conf
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Rate limiting zones — define per-service zones as needed
|
||||||
|
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
|
||||||
|
|
||||||
|
# Proxy cache: 200MB, evict after 24h of no access
|
||||||
|
proxy_cache_path /tmp/cache levels=1:2 keys_zone=services:10m
|
||||||
|
max_size=200m inactive=24h;
|
||||||
|
|
||||||
|
# --- docs.eblu.me (static site) ---
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name docs.eblu.me;
|
||||||
|
|
||||||
|
limit_req zone=general burst=20 nodelay;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass https://docs.tail8d86e.ts.net;
|
||||||
|
proxy_ssl_verify off;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
|
||||||
|
# Cache aggressively — static site only.
|
||||||
|
# Do NOT use these settings for dynamic services.
|
||||||
|
proxy_cache services;
|
||||||
|
proxy_cache_valid 200 1d;
|
||||||
|
proxy_cache_valid 404 1m;
|
||||||
|
proxy_cache_use_stale error timeout updating;
|
||||||
|
proxy_cache_lock on;
|
||||||
|
|
||||||
|
# Prevent cache-busting: ignore query strings and
|
||||||
|
# client cache-control headers.
|
||||||
|
# Safe for static sites; breaks dynamic services.
|
||||||
|
proxy_cache_key $host$uri;
|
||||||
|
proxy_ignore_headers Cache-Control Set-Cookie;
|
||||||
|
|
||||||
|
add_header X-Cache-Status $upstream_cache_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Catch-all: reject unknown hosts, but serve health check
|
||||||
|
server {
|
||||||
|
listen 8080 default_server;
|
||||||
|
|
||||||
|
location /healthz {
|
||||||
|
return 200 "ok\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
fly/start.sh
Normal file
17
fly/start.sh
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Start tailscale daemon. Fly.io runs Firecracker microVMs which support
|
||||||
|
# TUN devices natively — no need for --tun=userspace-networking.
|
||||||
|
tailscaled --statedir=/var/lib/tailscale &
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Authenticate and join tailnet
|
||||||
|
tailscale up --authkey="${TS_AUTHKEY}" --hostname=flyio-proxy
|
||||||
|
|
||||||
|
# Wait for tailscale to be ready
|
||||||
|
until tailscale status > /dev/null 2>&1; do sleep 1; done
|
||||||
|
echo "Tailscale connected"
|
||||||
|
|
||||||
|
# Start nginx — MagicDNS resolves *.tail8d86e.ts.net hostnames
|
||||||
|
nginx -g "daemon off;"
|
||||||
|
|
@ -7,4 +7,6 @@ GANDI_PERSONAL_ACCESS_TOKEN=$(op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get mco
|
||||||
export GANDI_PERSONAL_ACCESS_TOKEN
|
export GANDI_PERSONAL_ACCESS_TOKEN
|
||||||
|
|
||||||
cd "$(dirname "$0")/../pulumi/gandi"
|
cd "$(dirname "$0")/../pulumi/gandi"
|
||||||
|
uv sync --quiet || { echo "uv sync failed — if devpi is down, run 'devpi off' and retry"; exit 1; }
|
||||||
|
pulumi stack select eblu-me
|
||||||
pulumi preview "$@"
|
pulumi preview "$@"
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,6 @@ GANDI_PERSONAL_ACCESS_TOKEN=$(op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get mco
|
||||||
export GANDI_PERSONAL_ACCESS_TOKEN
|
export GANDI_PERSONAL_ACCESS_TOKEN
|
||||||
|
|
||||||
cd "$(dirname "$0")/../pulumi/gandi"
|
cd "$(dirname "$0")/../pulumi/gandi"
|
||||||
|
uv sync --quiet || { echo "uv sync failed — if devpi is down, run 'devpi off' and retry"; exit 1; }
|
||||||
|
pulumi stack select eblu-me
|
||||||
pulumi up --yes "$@"
|
pulumi up --yes "$@"
|
||||||
|
|
|
||||||
7
mise-tasks/fly-deploy
Executable file
7
mise-tasks/fly-deploy
Executable file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#MISE description="Deploy the Fly.io public proxy"
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/../fly"
|
||||||
|
fly deploy "$@"
|
||||||
27
mise-tasks/fly-setup
Executable file
27
mise-tasks/fly-setup
Executable file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#MISE description="One-time setup: configure Fly.io secrets and certs (idempotent)"
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
APP="blumeops-proxy"
|
||||||
|
|
||||||
|
# Fetch Tailscale auth key from Pulumi state
|
||||||
|
echo "Fetching Tailscale auth key from Pulumi..."
|
||||||
|
TS_AUTHKEY=$(cd "$(dirname "$0")/../pulumi/tailscale" && pulumi stack select tail8d86e && pulumi stack output flyio_authkey --show-secrets)
|
||||||
|
fly secrets set TS_AUTHKEY="$TS_AUTHKEY" --stage -a "$APP"
|
||||||
|
echo "Tailscale auth key staged (will take effect on next deploy)"
|
||||||
|
|
||||||
|
# Allocate IPs (idempotent — fly errors if already allocated)
|
||||||
|
# Shared IPv4 is free and sufficient for HTTP/HTTPS services.
|
||||||
|
# Use 'fly ips allocate-v4' (no --shared) for dedicated IPv4 ($2/mo)
|
||||||
|
# if the service needs non-HTTP protocols.
|
||||||
|
fly ips allocate-v4 --shared -a "$APP" 2>/dev/null || true
|
||||||
|
fly ips allocate-v6 -a "$APP" 2>/dev/null || true
|
||||||
|
echo "IPs allocated"
|
||||||
|
|
||||||
|
# Add certs for all public domains (idempotent — fly ignores duplicates)
|
||||||
|
fly certs add docs.eblu.me -a "$APP" 2>/dev/null || true
|
||||||
|
# fly certs add wiki.eblu.me -a "$APP" 2>/dev/null || true # future services
|
||||||
|
echo "Certificates configured"
|
||||||
|
|
||||||
|
echo "Done. Run 'mise run fly-deploy' to deploy."
|
||||||
11
mise-tasks/fly-shutoff
Executable file
11
mise-tasks/fly-shutoff
Executable file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#MISE description="Emergency shutoff: stop all Fly.io proxy machines"
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
APP="blumeops-proxy"
|
||||||
|
|
||||||
|
echo "EMERGENCY SHUTOFF: Stopping all machines for $APP"
|
||||||
|
fly scale count 0 -a "$APP" --yes
|
||||||
|
echo "All machines stopped. Public services are offline."
|
||||||
|
echo "To restore: fly scale count 1 -a $APP"
|
||||||
|
|
@ -10,4 +10,6 @@ export TAILSCALE_OAUTH_CLIENT_SECRET
|
||||||
export TAILSCALE_TAILNET="tail8d86e.ts.net"
|
export TAILSCALE_TAILNET="tail8d86e.ts.net"
|
||||||
|
|
||||||
cd "$(dirname "$0")/../pulumi/tailscale"
|
cd "$(dirname "$0")/../pulumi/tailscale"
|
||||||
|
uv sync --quiet || { echo "uv sync failed — if devpi is down, run 'devpi off' and retry"; exit 1; }
|
||||||
|
pulumi stack select tail8d86e
|
||||||
pulumi preview "$@"
|
pulumi preview "$@"
|
||||||
|
|
|
||||||
|
|
@ -10,4 +10,6 @@ export TAILSCALE_OAUTH_CLIENT_SECRET
|
||||||
export TAILSCALE_TAILNET="tail8d86e.ts.net"
|
export TAILSCALE_TAILNET="tail8d86e.ts.net"
|
||||||
|
|
||||||
cd "$(dirname "$0")/../pulumi/tailscale"
|
cd "$(dirname "$0")/../pulumi/tailscale"
|
||||||
|
uv sync --quiet || { echo "uv sync failed — if devpi is down, run 'devpi off' and retry"; exit 1; }
|
||||||
|
pulumi stack select tail8d86e
|
||||||
pulumi up --yes "$@"
|
pulumi up --yes "$@"
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,22 @@ base_record = gandi.livedns.Record(
|
||||||
values=[tailscale_ip],
|
values=[tailscale_ip],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ============== Public Services (Fly.io proxy) ==============
|
||||||
|
# CNAME records pointing public subdomains to Fly.io for reverse proxying
|
||||||
|
# back to the tailnet. See docs/how-to/expose-service-publicly.md
|
||||||
|
|
||||||
|
docs_public = gandi.livedns.Record(
|
||||||
|
"docs-public",
|
||||||
|
zone=domain,
|
||||||
|
name="docs",
|
||||||
|
type="CNAME",
|
||||||
|
ttl=300,
|
||||||
|
values=["blumeops-proxy.fly.dev."],
|
||||||
|
)
|
||||||
|
|
||||||
# ============== Exports ==============
|
# ============== Exports ==============
|
||||||
pulumi.export("domain", domain)
|
pulumi.export("domain", domain)
|
||||||
pulumi.export("wildcard_fqdn", f"*.{subdomain}.{domain}")
|
pulumi.export("wildcard_fqdn", f"*.{subdomain}.{domain}")
|
||||||
pulumi.export("base_fqdn", f"{subdomain}.{domain}")
|
pulumi.export("base_fqdn", f"{subdomain}.{domain}")
|
||||||
pulumi.export("target_ip", tailscale_ip)
|
pulumi.export("target_ip", tailscale_ip)
|
||||||
|
pulumi.export("docs_public_fqdn", f"docs.{domain}")
|
||||||
|
|
|
||||||
288
pulumi/gandi/uv.lock
generated
288
pulumi/gandi/uv.lock
generated
|
|
@ -9,19 +9,19 @@ resolution-markers = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arpeggio"
|
name = "arpeggio"
|
||||||
version = "2.0.3"
|
version = "2.0.3"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/9e8/5ad35cfc6c938/Arpeggio-2.0.3.tar.gz", hash = "sha256:9e85ad35cfc6c938676817c7ae9a1000a7c72a34c71db0c687136c460d12b85e" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/9e8/5ad35cfc6c938/Arpeggio-2.0.3.tar.gz", hash = "sha256:9e85ad35cfc6c938676817c7ae9a1000a7c72a34c71db0c687136c460d12b85e" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/937/4d9c531b62018/Arpeggio-2.0.3-py2.py3-none-any.whl", hash = "sha256:9374d9c531b62018b787635f37fd81c9a6ee69ef2d28c5db3cd18791b1f7db2f" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/937/4d9c531b62018/Arpeggio-2.0.3-py2.py3-none-any.whl", hash = "sha256:9374d9c531b62018b787635f37fd81c9a6ee69ef2d28c5db3cd18791b1f7db2f" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "attrs"
|
name = "attrs"
|
||||||
version = "25.4.0"
|
version = "25.4.0"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/16d/5969b87f0859e/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/16d/5969b87f0859e/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/adc/f7e2a1fb3b36a/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/adc/f7e2a1fb3b36a/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -42,128 +42,128 @@ requires-dist = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "debugpy"
|
name = "debugpy"
|
||||||
version = "1.8.19"
|
version = "1.8.19"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/eea/7e5987445ab0b/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/eea/7e5987445ab0b/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb" }
|
||||||
wheels = [
|
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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/360/ffd231a780abb/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dill"
|
name = "dill"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/423/092df4182177d/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/423/092df4182177d/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/1e1/ce33e978ae97f/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/1e1/ce33e978ae97f/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grpcio"
|
name = "grpcio"
|
||||||
version = "1.76.0"
|
version = "1.76.0"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "typing-extensions" },
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/7be/78388d6da1a25/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/7be/78388d6da1a25/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73" }
|
||||||
wheels = [
|
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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/922/fa70ba549fce3/grpcio-1.76.0-cp314-cp314-win_amd64.whl", hash = "sha256:922fa70ba549fce362d2e2871ab542082d66e2aaf0c19480ea453905b01f384e" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parver"
|
name = "parver"
|
||||||
version = "0.5"
|
version = "0.5"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "arpeggio" },
|
{ name = "arpeggio" },
|
||||||
{ name = "attrs" },
|
{ name = "attrs" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b9f/de1e6bb9ce9f0/parver-0.5.tar.gz", hash = "sha256:b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/b9f/de1e6bb9ce9f0/parver-0.5.tar.gz", hash = "sha256:b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/228/1b187276c8e8e/parver-0.5-py3-none-any.whl", hash = "sha256:2281b187276c8e8e3c15634f62287b2fb6fe0efe3010f739a6bd1e45fa2bf2b2" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/228/1b187276c8e8e/parver-0.5-py3-none-any.whl", hash = "sha256:2281b187276c8e8e3c15634f62287b2fb6fe0efe3010f739a6bd1e45fa2bf2b2" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pip"
|
name = "pip"
|
||||||
version = "25.3"
|
version = "25.3"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/8d0/538dbbd7babbd/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/8d0/538dbbd7babbd/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/965/5943313a94722/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/965/5943313a94722/pip-25.3-py3-none-any.whl", hash = "sha256:9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "protobuf"
|
name = "protobuf"
|
||||||
version = "5.29.5"
|
version = "5.29.5"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/bc1/463bafd4b0929/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/bc1/463bafd4b0929/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84" }
|
||||||
wheels = [
|
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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/6cf/42630262c59b2/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulumi"
|
name = "pulumi"
|
||||||
version = "3.217.0"
|
version = "3.217.0"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "debugpy" },
|
{ name = "debugpy" },
|
||||||
{ name = "dill" },
|
{ name = "dill" },
|
||||||
|
|
@ -174,92 +174,92 @@ dependencies = [
|
||||||
{ name = "semver" },
|
{ name = "semver" },
|
||||||
]
|
]
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/b5e/fb2e9fb34c2d2/pulumi-3.217.0-py3-none-any.whl", hash = "sha256:b5efb2e9fb34c2d2902d3ec39af0150775c27b80e38c5e421a77454d69dbae25" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/b5e/fb2e9fb34c2d2/pulumi-3.217.0-py3-none-any.whl", hash = "sha256:b5efb2e9fb34c2d2902d3ec39af0150775c27b80e38c5e421a77454d69dbae25" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pulumiverse-gandi"
|
name = "pulumiverse-gandi"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "parver" },
|
{ name = "parver" },
|
||||||
{ name = "pulumi" },
|
{ name = "pulumi" },
|
||||||
{ name = "semver" },
|
{ name = "semver" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/59a/621d46fc35be4/pulumiverse_gandi-2.3.2.tar.gz", hash = "sha256:59a621d46fc35be46196d6484a026cae8d6ab973a7241cbc44e0849c931d5ac6" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/59a/621d46fc35be4/pulumiverse_gandi-2.3.2.tar.gz", hash = "sha256:59a621d46fc35be46196d6484a026cae8d6ab973a7241cbc44e0849c931d5ac6" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/13d/be5b0bb9c080d/pulumiverse_gandi-2.3.2-py3-none-any.whl", hash = "sha256:13dbe5b0bb9c080d6c389b1d0fcda907adf8904fa363c053b36bbbf60918220d" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/13d/be5b0bb9c080d/pulumiverse_gandi-2.3.2-py3-none-any.whl", hash = "sha256:13dbe5b0bb9c080d6c389b1d0fcda907adf8904fa363c053b36bbbf60918220d" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyyaml"
|
name = "pyyaml"
|
||||||
version = "6.0.3"
|
version = "6.0.3"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/d76/623373421df22/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/d76/623373421df22/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" }
|
||||||
wheels = [
|
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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/ebc/55a14a21cb140/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "3.0.4"
|
version = "3.0.4"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
sdist = { url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/afc/7d8c584a5ed0a/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/afc/7d8c584a5ed0a/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/9c8/24d87ba7f7ab4/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/9c8/24d87ba7f7ab4/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.15.0"
|
version = "4.15.0"
|
||||||
source = { registry = "https://pypi.tail8d86e.ts.net/root/pypi/+simple/" }
|
source = { registry = "https://pypi.ops.eblu.me/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" }
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/0ce/a48d173cc12fa/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://pypi.tail8d86e.ts.net/root/pypi/+f/f0f/a19c6845758ab/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" },
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/f0f/a19c6845758ab/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
3
pulumi/tailscale/.gitignore
vendored
3
pulumi/tailscale/.gitignore
vendored
|
|
@ -3,8 +3,5 @@
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
||||||
# uv
|
|
||||||
uv.lock
|
|
||||||
|
|
||||||
# Pulumi
|
# Pulumi
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
|
||||||
|
|
@ -70,9 +70,22 @@ sifaka_tags = tailscale.DeviceTags(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ============== Auth Keys ==============
|
||||||
|
|
||||||
|
# Auth key for Fly.io proxy container (public reverse proxy)
|
||||||
|
flyio_key = tailscale.TailnetKey(
|
||||||
|
"flyio-proxy-key",
|
||||||
|
reusable=True,
|
||||||
|
ephemeral=True,
|
||||||
|
preauthorized=True,
|
||||||
|
tags=["tag:flyio-proxy"],
|
||||||
|
expiry=7776000, # 90 days
|
||||||
|
)
|
||||||
|
|
||||||
# ============== Exports ==============
|
# ============== Exports ==============
|
||||||
pulumi.export("acl_id", acl.id)
|
pulumi.export("acl_id", acl.id)
|
||||||
pulumi.export("policy_hash", policy_hash)
|
pulumi.export("policy_hash", policy_hash)
|
||||||
|
pulumi.export("flyio_authkey", flyio_key.key)
|
||||||
|
|
||||||
pulumi.export("indri_device_id", indri.node_id)
|
pulumi.export("indri_device_id", indri.node_id)
|
||||||
pulumi.export("indri_tags", indri_tags.tags)
|
pulumi.export("indri_tags", indri_tags.tags)
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,14 @@
|
||||||
"ip": ["*"],
|
"ip": ["*"],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// --- Fly.io proxy ---
|
||||||
|
// Public reverse proxy can reach k8s services on HTTPS only
|
||||||
|
{
|
||||||
|
"src": ["tag:flyio-proxy"],
|
||||||
|
"dst": ["tag:k8s"],
|
||||||
|
"ip": ["tcp:443"],
|
||||||
|
},
|
||||||
|
|
||||||
// --- CI Gateway ---
|
// --- CI Gateway ---
|
||||||
// Ephemeral CI containers can push images to registry
|
// Ephemeral CI containers can push images to registry
|
||||||
{
|
{
|
||||||
|
|
@ -136,6 +144,7 @@
|
||||||
"tag:k8s-operator": ["autogroup:admin", "tag:blumeops"],
|
"tag:k8s-operator": ["autogroup:admin", "tag:blumeops"],
|
||||||
"tag:k8s": ["autogroup:admin", "tag:blumeops", "tag:k8s-operator"],
|
"tag:k8s": ["autogroup:admin", "tag:blumeops", "tag:k8s-operator"],
|
||||||
"tag:ci-gateway": ["autogroup:admin", "tag:blumeops"],
|
"tag:ci-gateway": ["autogroup:admin", "tag:blumeops"],
|
||||||
|
"tag:flyio-proxy": ["autogroup:admin", "tag:blumeops"],
|
||||||
},
|
},
|
||||||
|
|
||||||
// ============== ACL Tests ==============
|
// ============== ACL Tests ==============
|
||||||
|
|
@ -166,5 +175,11 @@
|
||||||
"src": "tag:ci-gateway",
|
"src": "tag:ci-gateway",
|
||||||
"accept": ["tag:registry:443"],
|
"accept": ["tag:registry:443"],
|
||||||
},
|
},
|
||||||
|
// Fly.io proxy can reach k8s services (HTTPS only), nothing else
|
||||||
|
{
|
||||||
|
"src": "tag:flyio-proxy",
|
||||||
|
"accept": ["tag:k8s:443"],
|
||||||
|
"deny": ["tag:homelab:22", "tag:nas:445", "tag:registry:443"],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
265
pulumi/tailscale/uv.lock
generated
Normal file
265
pulumi/tailscale/uv.lock
generated
Normal 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.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/9e8/5ad35cfc6c938/Arpeggio-2.0.3.tar.gz", hash = "sha256:9e85ad35cfc6c938676817c7ae9a1000a7c72a34c71db0c687136c460d12b85e" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/16d/5969b87f0859e/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/adc/f7e2a1fb3b36a/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blumeops-tailnet"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pulumi" },
|
||||||
|
{ name = "pulumi-tailscale" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "pulumi", specifier = ">=3.0.0" },
|
||||||
|
{ name = "pulumi-tailscale", specifier = ">=0.24.0" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "debugpy"
|
||||||
|
version = "1.8.19"
|
||||||
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/eea/7e5987445ab0b/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/c5d/cfa21de1f735a/debugpy-1.8.19-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:c5dcfa21de1f735a4f7ced4556339a109aa0f618d366ede9da0a3600f2516d8b" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/806/d680024624400/debugpy-1.8.19-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:806d6800246244004625d5222d7765874ab2d22f3ba5f615416cf1342d61c488" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/783/a519e6dfb1f3c/debugpy-1.8.19-cp311-cp311-win32.whl", hash = "sha256:783a519e6dfb1f3cd773a9bda592f4887a65040cb0c7bd38dde410f4e53c40d4" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/140/35cbdbb1fe4b6/debugpy-1.8.19-cp311-cp311-win_amd64.whl", hash = "sha256:14035cbdbb1fe4b642babcdcb5935c2da3b1067ac211c5c5a8fdc0bb31adbcaa" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/bcc/b1540a49cde77/debugpy-1.8.19-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:bccb1540a49cde77edc7ce7d9d075c1dbeb2414751bc0048c7a11e1b597a4c2e" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/e9c/68d9a382ec754/debugpy-1.8.19-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:e9c68d9a382ec754dc05ed1d1b4ed5bd824b9f7c1a8cd1083adb84b3c93501de" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/659/9cab8a783d149/debugpy-1.8.19-cp312-cp312-win32.whl", hash = "sha256:6599cab8a783d1496ae9984c52cb13b7c4a3bd06a8e6c33446832a5d97ce0bee" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/66e/3d2fd8f2035a8/debugpy-1.8.19-cp312-cp312-win_amd64.whl", hash = "sha256:66e3d2fd8f2035a8f111eb127fa508469dfa40928a89b460b41fd988684dc83d" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/91e/35db2672a0aba/debugpy-1.8.19-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:91e35db2672a0abaf325f4868fcac9c1674a0d9ad9bb8a8c849c03a5ebba3e6d" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/850/16a73ab84dea1/debugpy-1.8.19-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:85016a73ab84dea1c1f1dcd88ec692993bcbe4532d1b49ecb5f3c688ae50c606" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/b60/5f17e89ba0ece/debugpy-1.8.19-cp313-cp313-win32.whl", hash = "sha256:b605f17e89ba0ecee994391194285fada89cee111cfcd29d6f2ee11cbdc40976" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/c30/639998a9f9cd9/debugpy-1.8.19-cp313-cp313-win_amd64.whl", hash = "sha256:c30639998a9f9cd9699b4b621942c0179a6527f083c72351f95c6ab1728d5b73" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/1e8/c4d1bd230067b/debugpy-1.8.19-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:1e8c4d1bd230067bf1bbcdbd6032e5a57068638eb28b9153d008ecde288152af" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/d40/c016c1f538dbf/debugpy-1.8.19-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d40c016c1f538dbf1762936e3aeb43a89b965069d9f60f9e39d35d9d25e6b809" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/060/1708223fe1cd0/debugpy-1.8.19-cp314-cp314-win32.whl", hash = "sha256:0601708223fe1cd0e27c6cce67a899d92c7d68e73690211e6788a4b0e1903f5b" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/8e1/9a725f5d486f2/debugpy-1.8.19-cp314-cp314-win_amd64.whl", hash = "sha256:8e19a725f5d486f20e53a1dde2ab8bb2c9607c40c00a42ab646def962b41125f" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/360/ffd231a780abb/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dill"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/063/3f1d2df477324/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/44f/54bf6412c2c84/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "grpcio"
|
||||||
|
version = "1.76.0"
|
||||||
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/7be/78388d6da1a25/grpcio-1.76.0.tar.gz", hash = "sha256:7be78388d6da1a25c0d5ec506523db58b18be22d9c37d8d3a32c08be4987bd73" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/2e1/743fbd7f5fa71/grpcio-1.76.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2e1743fbd7f5fa713a1b0a8ac8ebabf0ec980b5d8809ec358d488e273b9cf02a" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/a8c/2cf1209497cf6/grpcio-1.76.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a8c2cf1209497cf659a667d7dea88985e834c24b7c3b605e6254cbb5076d985c" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/08c/aea849a9d3c71/grpcio-1.76.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08caea849a9d3c71a542827d6df9d5a69067b0a1efbea8a855633ff5d9571465" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/f0e/34c2079d47ae9/grpcio-1.76.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f0e34c2079d47ae9f6188211db9e777c619a21d4faba6977774e8fa43b085e48" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/8ed/dfb4d203a237d/grpcio-1.76.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8eddfb4d203a237da6f3cc8a540dad0517d274b5a1e9e636fd8d2c79b5c1d397" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/324/83fe2aab2c379/grpcio-1.76.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:32483fe2aab2c3794101c2a159070584e5db11d0aa091b2c0ea9c4fc43d0d749" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/dcf/e41187da8992c/grpcio-1.76.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dcfe41187da8992c5f40aa8c5ec086fa3672834d2be57a32384c08d5a05b4c00" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/210/7b0c024d1b35f/grpcio-1.76.0-cp311-cp311-win32.whl", hash = "sha256:2107b0c024d1b35f4083f11245c0e23846ae64d02f40b2b226684840260ed054" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/522/175aba7af9113/grpcio-1.76.0-cp311-cp311-win_amd64.whl", hash = "sha256:522175aba7af9113c48ec10cc471b9b9bd4f6ceb36aeb4544a8e2c80ed9d252d" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/81f/d9652b37b36f1/grpcio-1.76.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:81fd9652b37b36f16138611c7e884eb82e0cec137c40d3ef7c3f9b3ed00f6ed8" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/04b/be1bfe3a68bbf/grpcio-1.76.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:04bbe1bfe3a68bbfd4e52402ab7d4eb59d72d02647ae2042204326cf4bbad280" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/d38/8087771c837cd/grpcio-1.76.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d388087771c837cdb6515539f43b9d4bf0b0f23593a24054ac16f7a960be16f4" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/9f8/f757bebaaea11/grpcio-1.76.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f8f757bebaaea112c00dba718fc0d3260052ce714e25804a03f93f5d1c6cc11" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/f92/f88e6c033db65/grpcio-1.76.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f92f88e6c033db65a5ae3d97905c8fea9c725b63e28d5a75cb73b49bda5024d8" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/4ba/f3cbe2f0be328/grpcio-1.76.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4baf3cbe2f0be3289eb68ac8ae771156971848bb8aaff60bad42005539431980" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/615/ba64c208aaceb/grpcio-1.76.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:615ba64c208aaceb5ec83bfdce7728b80bfeb8be97562944836a7a0a9647d882" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/45d/59a649a82df57/grpcio-1.76.0-cp312-cp312-win32.whl", hash = "sha256:45d59a649a82df5718fd9527ce775fd66d1af35e6d31abdcdc906a49c6822958" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/c08/8e7a90b601730/grpcio-1.76.0-cp312-cp312-win_amd64.whl", hash = "sha256:c088e7a90b6017307f423efbb9d1ba97a22aa2170876223f9709e9d1de0b5347" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/26e/f06c73eb53267/grpcio-1.76.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:26ef06c73eb53267c2b319f43e6634c7556ea37672029241a056629af27c10e2" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/45e/0111e73f43f73/grpcio-1.76.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:45e0111e73f43f735d70786557dc38141185072d7ff8dc1829d6a77ac1471468" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/83d/57312a58dcfe2/grpcio-1.76.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:83d57312a58dcfe2a3a0f9d1389b299438909a02db60e2f2ea2ae2d8034909d3" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/3e2/a27c89eb9ac3d/grpcio-1.76.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3e2a27c89eb9ac3d81ec8835e12414d73536c6e620355d65102503064a4ed6eb" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/6a1/5c17af8839b68/grpcio-1.76.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a15c17af8839b6801d554263c546c69c4d7718ad4321e3166175b37eaacca77" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/25a/18e9810fbc7e7/grpcio-1.76.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:25a18e9810fbc7e7f03ec2516addc116a957f8cbb8cbc95ccc80faa072743d03" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/931/091142fd8cc14/grpcio-1.76.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:931091142fd8cc14edccc0845a79248bc155425eee9a98b2db2ea4f00a235a42" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/5e8/571632780e085/grpcio-1.76.0-cp313-cp313-win32.whl", hash = "sha256:5e8571632780e08526f118f74170ad8d50fb0a48c23a746bef2a6ebade3abd6f" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/f9f/7bd5faab55f47/grpcio-1.76.0-cp313-cp313-win_amd64.whl", hash = "sha256:f9f7bd5faab55f47231ad8dba7787866b69f5e93bc306e3915606779bbfb4ba8" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/ff8/a59ea85a1f219/grpcio-1.76.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:ff8a59ea85a1f2191a0ffcc61298c571bc566332f82e5f5be1b83c9d8e668a62" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/06c/3d6b076e7b593/grpcio-1.76.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06c3d6b076e7b593905d04fdba6a0525711b3466f43b3400266f04ff735de0cd" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/fd5/ef5932f6475c4/grpcio-1.76.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd5ef5932f6475c436c4a55e4336ebbe47bd3272be04964a03d316bbf4afbcbc" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/b33/1680e46239e09/grpcio-1.76.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:b331680e46239e090f5b3cead313cc772f6caa7d0fc8de349337563125361a4a" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/490/fa6d203992c47/grpcio-1.76.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:490fa6d203992c47c7b9e4a9d39003a0c2bcc1c9aa3c058730884bbbb0ee9f09" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/479/496325ce55479/grpcio-1.76.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:479496325ce554792dba6548fae3df31a72cef7bad71ca2e12b0e58f9b336bfc" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/1c9/b93f79f48b03a/grpcio-1.76.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c9b93f79f48b03ada57ea24725d83a30284a012ec27eab2cf7e50a550cbbbcc" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/747/fa73efa9b8b14/grpcio-1.76.0-cp314-cp314-win32.whl", hash = "sha256:747fa73efa9b8b1488a95d0ba1039c8e2dca0f741612d80415b1e1c560febf4e" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "arpeggio" },
|
||||||
|
{ name = "attrs" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/b9f/de1e6bb9ce9f0/parver-0.5.tar.gz", hash = "sha256:b9fde1e6bb9ce9f07e08e9c4bea8d8825c5e78e18a0052d02e02bf9517eb4777" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/8d0/538dbbd7babbd/pip-25.3.tar.gz", hash = "sha256:8d0538dbbd7babbd207f261ed969c65de439f6bc9e5dbd3b3b9a77f25d95f343" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/bc1/463bafd4b0929/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/3f1/c6468a2cfd102/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/3f7/6e3a3675b4a4d/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/e38/c5add5a311f2a/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/fa1/8533a299d7ab6/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/638/48923da3325e1/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/6cf/42630262c59b2/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulumi"
|
||||||
|
version = "3.215.0"
|
||||||
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "debugpy" },
|
||||||
|
{ name = "dill" },
|
||||||
|
{ name = "grpcio" },
|
||||||
|
{ name = "pip" },
|
||||||
|
{ name = "protobuf" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
{ name = "semver" },
|
||||||
|
]
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/97a/380c26414f9ea/pulumi-3.215.0-py3-none-any.whl", hash = "sha256:97a380c26414f9ea14d7265513f7f667b139cfc44576e8d492117d304a70283c" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pulumi-tailscale"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "parver" },
|
||||||
|
{ name = "pulumi" },
|
||||||
|
{ name = "semver" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/4bd/da5ca09b07efd/pulumi_tailscale-0.24.0.tar.gz", hash = "sha256:4bdda5ca09b07efd89886cc3d20c4e45cde5430b7a8559a25cc26643ad86a46a" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/6da/c592c7812c821/pulumi_tailscale-0.24.0-py3-none-any.whl", hash = "sha256:6dac592c7812c82164e06228f833fa93a8ccb73afbb599c36ce417f773729ba9" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyyaml"
|
||||||
|
version = "6.0.3"
|
||||||
|
source = { registry = "https://pypi.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/d76/623373421df22/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/44e/dc64787392855/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/652/cb6edd41e7185/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/1d3/7d57ad971609c/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/375/03bfbfc9d2c40/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/809/8f252adfa6c80/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/9f3/bfb4965eb8744/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/7f0/47e29dcae4460/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/fc0/9d0aa354569bc/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/8dc/52c23056b9ddd/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/417/15c910c881bc0/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/96b/533f0e99f6579/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/5fc/d34e47f6e0b79/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/643/86e5e707d03a7/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/8da/9669d359f02c0/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/228/3a07e2c21a2aa/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/f70/57c9a337546ed/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/eda/16858a3cab07b/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/d0e/ae10f8159e8fd/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/790/05a0d97d5ddab/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/549/8cd1645aa724a/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/8d1/fab6bb153a416/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/34d/5fcd24b8445fa/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/7c6/610def4f16354/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/519/0d403f121660c/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/4a2/e8cebe2ff6ab7/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/93d/da82c9c22deb0/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/028/93d100e99e03e/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/c1f/f362665ae5072/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/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.ops.eblu.me/root/pypi/+f/66e/1674c3ef6f541/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/162/49ee61e95f858/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/4ad/1906908f2f5ae/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9" },
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/afc/7d8c584a5ed0a/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/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.ops.eblu.me/root/pypi/+simple/" }
|
||||||
|
sdist = { url = "https://pypi.ops.eblu.me/root/pypi/+f/0ce/a48d173cc12fa/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://pypi.ops.eblu.me/root/pypi/+f/f0f/a19c6845758ab/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" },
|
||||||
|
]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue