[main]: Add server-notes

This commit is contained in:
John Gatward committed 2026-10-06 16:33:29 +01:00
1 parent 68a3130159
commit 81b43de3f1
18 files changed
+1406 -1

No files matched your search

+1
View File
@@ -0,0 +1 @@
3.12
+19
View File
@@ -1,3 +1,22 @@
# Jupiter Notes # Jupiter Notes
A repository to store the notes taken. A repository to store the notes taken.
## Development
Requires [uv](https://docs.astral.sh/uv/getting-started/installation/).
Python 3.12 and `.venv` are managed by uv.
```sh
uv sync --locked
uv run --locked zensical serve
```
Build the static site into `site/`:
```sh
uv run --locked zensical build
```
Dependencies: `pyproject.toml`; resolved versions: `uv.lock`.
After editing dependencies, run `uv lock` and commit both files.
+79
View File
@@ -0,0 +1,79 @@
---
schema_version: 1
id: home-server.authentication
type: reference
scope: [jupiter, authelia, forward-auth, secrets]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Deployed Authelia file matches repository; Portainer supplies environment variables, 2026-10-06"
- kind: owner-report
reference: "Portainer state added to Backrest plan, 2026-10-06"
- kind: owner-report
reference: "All stack environment values/secrets managed in Portainer; GitHub access uses PAT, 2026-10-06"
- kind: repository
repository: jupiter-stacks
revision: 610e325ef6a98850511ce7a089b0b9a77bfecf15
paths:
- stacks/authelia/configuration.yml
- stacks/authelia/docker-compose.yml
related: [home-server.reference, home-server.networking, home-server.portainer, home-server.backups, home-server.deployment]
update_triggers: [access-policy-change, authentication-change, secret-rotation, middleware-change]
unknowns:
- authelia-file-placeholder-expansion-mechanism
- off-host-authelia-secret-and-user-database-recovery
---
# Authentication
| Component | Configuration |
| --- | --- |
| Portal | `https://auth.umbra.mom` |
| Backend | File `/config/users_database.yml`; Argon2id |
| Second factor | TOTP; issuer `Jupiter` |
| Policy | Default deny; listed domains require `two_factor` |
| Session cookie domain | `umbra.mom` |
| Default redirect | `https://sonarr.umbra.mom` |
| Storage | SQLite `/data/db.sqlite3`; storage encryption key |
| Notifications | Filesystem `/data/notification.txt` |
Two-factor domains: `sonarr`, `radarr`, `prowlarr`, `logs`, `notes`, `backups`
under `umbra.mom`.
## Enforcement
Protected routers attach `authelia@docker`:
```text
Traefik -> http://authelia:9091/api/authz/forward-auth
```
`trustForwardHeader=true`; response headers:
`Remote-User, Remote-Groups, Remote-Email, Remote-Name`.
Policy applies only to requests routed through forward-auth. It is not a
global gate for every service or directly published port.
## Configuration / secrets
Host `/data/authelia/{configuration.yml,users_database.yml}` mounts read-only
under `/config`; `/data/authelia` also mounts read-write at `/data`.
Stack variable overrides/secrets are managed in Portainer; Compose retains
non-secret configuration. Authelia environment:
```text
AUTHELIA_SESSION_SECRET
AUTHELIA_STORAGE_ENCRYPTION_KEY
AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET
```
Checked-in YAML also contains `${AUTHELIA_*}` placeholders. Container environment
injection alone does not prove mounted-file substitution; expansion mechanism
unconfirmed.
Authelia data remains outside the supplied backup plan.
[Portainer state](portainer.md) is covered; secret recovery remains untested.
GitHub repository access uses a PAT; host SSH keys and tunnel credential files
are separate from stack environment variables.
+95
View File
@@ -0,0 +1,95 @@
---
schema_version: 1
id: home-server.backups
type: reference
scope: [jupiter, backrest, restic, google-drive, recovery]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Backrest plan JSON, destination, key custody and no restore test, 2026-10-06"
- kind: owner-report
reference: "Updated four-service Backrest plan and deployed Portainer bind mount/container name, 2026-10-06"
- kind: repository
repository: jupiter-stacks
revision: 610e325ef6a98850511ce7a089b0b9a77bfecf15
paths:
- stacks/backrest/docker-compose.yml
- stacks/gitea/docker-compose.yml
- stacks/paperless-ngx/docker-compose.yml
- stacks/vaultwarden/docker-compose.yml
related: [home-server.reference, home-server.storage, home-server.portainer, home-server.authentication]
update_triggers: [backup-plan-change, destination-change, hook-change, credential-change, restore-test]
unknowns:
- last-successful-backup-and-repository-check
- google-drive-repository-path-and-credential-recovery
- off-host-backrest-config-recovery
- measured-rpo-and-rto
---
# Backups
| Setting | Value |
| --- | --- |
| Engine | Backrest / restic; plan `jupiter-backup` |
| Repository | `jayo60013-gdrive`; Google Drive; account `jayo60013` |
| Schedule | `0 3 * * *`; UTC; daily 03:00 |
| Retention | `policyKeepLastN: 4`; last four snapshots, not four days |
| Encryption key | iCloud password app; no key stored here |
| Restore test | Never performed |
## Coverage
Backrest bind-mounts host `/data` at container `/userdata:ro`.
| Host source | Plan path |
| --- | --- |
| `/data/gitea` | `/userdata/gitea` |
| `/data/paperless-ngx` | `/userdata/paperless-ngx` |
| `/data/portainer` | `/userdata/portainer` |
| `/data/vaultwarden` | `/userdata/vaultwarden` |
Case-insensitive exclusion: `/userdata/gitea/gitea/packages`.
`excludes: []`; CLI `--skip-if-unchanged` supplied despite
plan `skipIfUnchanged: false`. Unchanged runs may create no snapshot.
## Consistency hooks
Stop timeout `120s`; chains use `&&`. Errors: `ON_ERROR_FATAL`,
except Portainer restart (`ON_ERROR_IGNORE`).
| Event | Ordered commands / containers |
| --- | --- |
| `SNAPSHOT_START` | Stop `paperless` -> `paperless_db` |
| `SNAPSHOT_START` | Stop `gitea_runner` -> `gitea` -> `gitea_db` |
| `SNAPSHOT_START` | Stop `vaultwarden` |
| `SNAPSHOT_START` | Stop `portainer` |
| `SNAPSHOT_END`, `SNAPSHOT_ERROR` | Start `paperless_db` -> `paperless` |
| `SNAPSHOT_END`, `SNAPSHOT_ERROR` | Start `gitea_db` -> `gitea` -> `gitea_runner` |
| `SNAPSHOT_END`, `ANY_ERROR` | Start `vaultwarden` |
| `SNAPSHOT_END`, `SNAPSHOT_ERROR` | Start `portainer`; ignore hook failure |
Cold filesystem copies of applications/databases; no database dumps supplied.
Broker stays running. `docker start` does not wait for database readiness.
Hook failure can leave services stopped; recovery is not demonstrated.
Portainer restart failure does not fail the backup operation.
## Backrest state
| Host path | Container path |
| --- | --- |
| `/data/backrest/data` | `/data` |
| `/data/backrest/config` | `/config`; plan/repository configuration `config.json` |
| `/data/backrest/rclone` | `/root/.config/rclone` |
| `/home/jay/data/backrest/cache` | `/cache` |
| `/data/backrest/restore` | `/restore` |
| `/var/run/docker.sock` | `/var/run/docker.sock`; lifecycle hooks |
## Recovery limits
Only the four listed directories are covered. No supplied coverage for Authelia,
Traefik, Backrest configuration, Docker named volumes, OS, or Mercury.
Full-host recovery requires independently retrievable repository credentials,
configuration and restic key. Key custody alone does not establish recoverability.
`/restore` is on the same RAID array; not an independent backup.
+43
View File
@@ -0,0 +1,43 @@
---
schema_version: 1
id: home-server.deployment
type: reference
scope: [jupiter, portainer, github, stack-deployment, secrets]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Portainer GitHub/main/PAT configuration, environment management and manual pull/redeploy workflow, 2026-10-06"
related: [home-server.reference, home-server.host, home-server.portainer, home-server.authentication, home-server.backups]
update_triggers: [deployment-workflow-change, repository-change, credential-rotation, rollback-policy-change]
unknowns:
- github-pat-scopes-expiration-and-independent-recovery
- rollback-and-post-deployment-health-procedure
- rules-for-portainer-ui-versus-repository-drift
---
# Deployment
| Setting | Source / workflow |
| --- | --- |
| Stack definitions | <https://github.com/jayo60013/jupiter-stacks> |
| Git ref | `main` |
| Repository authentication | GitHub PAT configured in Portainer |
| Environment values/secrets | Portainer stack environment settings |
| Deployment | Manually pull/redeploy each affected stack in Portainer |
| Portainer itself | [Host shell script](portainer.md), not a managed Compose stack |
```text
Compose change -> push main -> manual Portainer pull/redeploy -> running stack
```
A Git commit is not deployment proof. Installed host Compose version is not
proof of Portainer's Compose implementation.
Recovering deployment requires Portainer state or independently retrievable
repository credentials and stack environment values. [Portainer backups](backups.md)
exist; credential/state restoration is untested. Do not put secret values in
this site or Git.
Host apt packages, fstab, RAID assembly, systemd units and Mercury nginx are
outside the Compose deployment workflow.
+74
View File
@@ -0,0 +1,74 @@
---
schema_version: 1
id: home-server.host
type: reference
scope: [jupiter, hardware, operating-system, docker]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Hardware, OS, bare-metal Docker and data-root confirmation, 2026-10-06"
- kind: owner-report
reference: "hostnamectl, timedatectl, user/group records, Docker info/version and systemd units, 2026-10-06"
related: [home-server.reference, home-server.storage, home-server.networking, home-server.deployment, home-server.operations]
update_triggers: [hardware-change, os-upgrade, kernel-upgrade, docker-reconfiguration, account-change, unit-change]
unknowns:
- exact-gpu-model
- jupiter-firewall-policy
- docker-package-installation-source
- backup-puller-account-purpose
---
# Host
| Component | Configuration |
| --- | --- |
| Host | Jupiter; bare metal |
| OS | Debian GNU/Linux 13 (trixie); x86_64 |
| Kernel | `6.12.94+deb13-amd64` |
| CPU | AMD Ryzen 5 5600X; 6 cores / 12 threads; reported 4.65 GHz |
| RAM | 1 x 16 GB Corsair Vengeance LPX |
| GPU | AMD Radeon HD 5000/6000/7350/8350 Series; discrete; exact model unresolved |
| Motherboard | Gigabyte B550M DS3H |
| Firmware | `FB`; dated `2023-06-08` |
| PSU | Corsair CV550 |
| SSD | Samsung 860 EVO; 1 TB; OS and SSD-backed caches |
| HDD | 2 x Seagate BarraCuda; 2 TB each; RAID 0 |
| Docker | Native host engine; data root `/var/lib/docker` |
| LAN | `192.168.1.56` |
| WireGuard | `10.0.0.2/24`; Mercury peer/server `10.0.0.1/24` |
| Time | `Europe/London`; RTC UTC; NTP active; clock synchronized at review |
## Docker runtime
| Setting | Observed value |
| --- | --- |
| Engine / API | Docker CE `29.8.2` / `1.56` |
| Compose CLI plugin | `v5.6.0`; not proof of Portainer's Compose implementation |
| containerd / runc | `v2.3.6` / `1.5.1` |
| Storage | `overlayfs`; containerd snapshotter |
| Cgroups | `systemd`; v2 |
| Logging | `json-file`; default |
| Firewall backend | `iptables` |
| Runtime security | AppArmor, seccomp, cgroup namespaces |
| Swarm / live restore | Inactive / disabled |
Docker/containerd services and `docker.socket` are enabled.
Docker starts `/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock`.
Supplied Docker unit has no explicit `/data` mount dependency or ordering against
`rclone-seedbox.service`.
## Host administration
| Account / group | Identity |
| --- | --- |
| `jay` | UID/GID `1000:1000`; `/home/jay`; bash; member of `sudo`, `docker`, `video`, `backups` |
| `backup-puller` | UID/GID `1001:1001`; `/home/backup-puller`; bash; member of `backups`; purpose unconfirmed |
| `backups` | Shared group; GID `1002` |
WireGuard and rclone: apt-managed. `wg-quick@wg0.service` is active in the supplied
snapshot. No host-level nginx on Jupiter; nginx still exists in application
containers. `unattended-upgrades.service` is enabled; effective update policy
not supplied.
Hardware/versions are a dated snapshot, not live telemetry.
+52
View File
@@ -0,0 +1,52 @@
---
schema_version: 1
id: home-server.reference
type: reference
scope: [jupiter, mercury, seedbox, shared-infrastructure]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Owner interview, 2026-10-06"
related:
- home-server.host
- home-server.mercury
- home-server.seedbox
- home-server.portainer
- home-server.deployment
- home-server.operations
- home-server.networking
- home-server.storage
- home-server.authentication
- home-server.backups
update_triggers: [topology-change, source-change, infrastructure-change]
unknowns: []
---
# Home server
| Reference | Scope |
| --- | --- |
| [Host](host.md) | Jupiter hardware; OS; Docker |
| [Mercury VPS](mercury.md) | Hosting; ingress; WireGuard; firewall |
| [Seedbox](seedbox.md) | Ultra.cc; qBittorrent; rclone mount |
| [Portainer](portainer.md) | Script-managed container; state; backups |
| [Deployment](deployment.md) | GitHub; Portainer; environment values |
| [Operations / decisions](operations.md) | Tradeoffs; monitoring; recovery gaps |
| [Networking](networking.md) | Mercury; WireGuard; DNS; ingress |
| [Storage](storage.md) | SSD; RAID 0; mounts; volumes |
| [Authentication](authentication.md) | Authelia; policy; secrets |
| [Backups](backups.md) | Backrest; coverage; retention; recovery |
## Evidence / agent contract
- `owner-report`: supplied host state/configuration; not independently inspected.
- `repository`: intended configuration at `revision`; not deployment proof.
- `revision: working-tree`: uncommitted source; `base_revision`: base commit.
- `unknowns`: unresolved facts; never infer defaults as deployed state.
- `related`: stable document IDs; `update_triggers`: re-review conditions.
- On change: update affected prose, Mermaid, sources, unknowns, and `last_reviewed`.
- No credentials. Preserve evidence distinctions; no live status implied.
Mermaid renders from versioned text; diagrams are manually maintained, not
automatically discovered.
+75
View File
@@ -0,0 +1,75 @@
---
schema_version: 1
id: home-server.mercury
type: reference
scope: [mercury, vps, hosting, nginx, wireguard, firewall]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Fasthosts plan, fastfetch, nginx sites, iptables and fail2ban output, 2026-10-06"
- kind: owner-report
reference: "nginx/WireGuard apt management and public IPv4/CGNAT rationale, 2026-10-06"
related: [home-server.reference, home-server.host, home-server.networking, home-server.operations]
update_triggers: [hosting-renewal, vps-resize, os-upgrade, proxy-change, firewall-change, certificate-change]
unknowns:
- renewal-price-and-renewal-date
- creation-timestamp-timezone
- certificate-renewal-method
- wireguard-allowedips-and-keepalive
- backup-and-restore-policy
---
# Mercury VPS
## Host / subscription
| Field | Configuration |
| --- | --- |
| Provider | [Fasthosts](https://www.fasthosts.co.uk/); UK data centre |
| Role / OS hostname | Mercury / `my-vps` |
| Plan | `1-1-10`; KVM virtual machine |
| CPU | 1 vCore; reported AMD EPYC-Milan @ 2.00 GHz |
| RAM | 1 GB plan; guest reports 864.37 MiB |
| Disk | 10 GB NVMe SSD plan; guest root ext4, 9.64 GiB |
| OS | Debian GNU/Linux 13 (trixie); x86_64 |
| Kernel | `6.12.85+deb13-amd64` |
| Swap | Disabled |
| Public interface | `ens6`; `185.230.217.66/32` |
| WireGuard | Server `10.0.0.1/24`; Jupiter peer `10.0.0.2/24` |
| Created | `2026-03-01 20:48:51`; timezone unconfirmed |
| Payment | GBP 10.00 prepaid for one year; renewal terms unconfirmed |
Capacities distinguish provider allocation from guest-visible values.
## Ingress
nginx runs directly in the VM, not in Docker; nginx and WireGuard are
apt-managed. Mercury provides static public IPv4 ingress because Jupiter's
home connection uses CGNAT. Residential-IP exposure is avoided by design.
| Hostname | Upstream |
| --- | --- |
| `umbra.mom`, `*.umbra.mom` | `https://10.0.0.2:443` over WireGuard |
| `gitea.umbra.mom` | Same; dedicated registry location `/v2/` |
| `john.gatward.dev`, `samstoreymusic.com` | `https://10.0.0.2:443` |
| `uptime.umbra.mom` | `http://127.0.0.1:3001` |
- HTTP -> HTTPS: `301`; unmatched default server: `418`.
- TLS terminates at nginx; upstream HTTPS terminates again at Jupiter Traefik.
- Upstream certificate verification disabled: `proxy_ssl_verify off`.
- Certificates: `/etc/letsencrypt/live/<domain>/{fullchain.pem,privkey.pem}`;
`umbra.mom` certificate also serves its subdomains.
- Preserves `Host`; sets `X-Real-IP`, `X-Forwarded-For`, `X-Forwarded-Proto`;
forwards WebSocket upgrade headers.
- Gitea `/v2/`: unlimited request body; proxy timeouts `900s`.
## Firewall / fail2ban
- UFW-managed iptables: INPUT/FORWARD `DROP`; OUTPUT `ACCEPT`.
- UFW allows TCP `22,80,443`; UDP `80,443,51820`.
- Docker forwarding has separate rules; INPUT policy alone does not define
container exposure.
- Fail2ban jails: `nginx-botsearch`, `nginx-http-auth`, `sshd`.
Rules and versions are supplied snapshots, not live inspection.
+203
View File
@@ -0,0 +1,203 @@
---
schema_version: 1
id: home-server.networking
type: reference
scope: [jupiter, mercury, dns, ingress, wireguard, cloudflare]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Network topology, Mercury nginx/iptables/fail2ban output, LAN DNS, 2026-10-06"
- kind: owner-report
reference: "Cloudflare ingress YAML and client SSH configurations, 2026-10-06"
- kind: owner-report
reference: "CGNAT rationale, Docker network and container inspection, 2026-10-06"
- kind: repository
repository: jupiter-stacks
revision: 610e325ef6a98850511ce7a089b0b9a77bfecf15
paths:
- stacks/traefik/docker-compose.yml
- stacks/cloudflared/docker-compose.yml
- stacks/gitea/docker-compose.yml
- stacks/jellyfin/docker-compose.yml
- stacks/audiobookshelf/docker-compose.yml
related: [home-server.reference, home-server.host, home-server.mercury, home-server.seedbox, home-server.authentication, home-server.operations]
update_triggers: [dns-change, proxy-change, tunnel-change, firewall-change, certificate-change]
unknowns:
- public-dns-record-types-and-cloudflare-proxy-status
- cloudflare-access-applications-and-login-policies
- wireguard-allowedips-and-keepalive
- jupiter-firewall-policy
- traefik-forwarded-header-trust-configuration
---
# Networking
## HTTPS ingress
```mermaid
---
config:
themeCSS: |
.node rect { rx: 8px; ry: 8px; stroke-width: 1.5px; }
.cluster rect { fill: var(--infra-zone) !important; stroke: var(--infra-border) !important; stroke-dasharray: 5 5; rx: 12px; ry: 12px; }
.label, .nodeLabel, .cluster-label { color: var(--infra-text) !important; }
.edgeLabel, .edgeLabel p { background-color: var(--infra-zone) !important; color: var(--infra-text) !important; }
.flowchart-link { stroke: var(--infra-line) !important; }
marker path { fill: var(--infra-line) !important; stroke: var(--infra-line) !important; }
.infraClient rect { fill: var(--infra-client-fill) !important; stroke: var(--infra-client-stroke) !important; }
.infraEdge rect { fill: var(--infra-edge-fill) !important; stroke: var(--infra-edge-stroke) !important; }
.infraService rect { fill: var(--infra-service-fill) !important; stroke: var(--infra-service-stroke) !important; }
flowchart:
curve: basis
nodeSpacing: 35
rankSpacing: 55
---
flowchart TB
Internet["<b>Public clients</b>"]
subgraph VPS["MERCURY / PUBLIC EDGE"]
Nginx["<b>nginx</b><br/>185.230.217.66<br/>WireGuard 10.0.0.1"]
Uptime["<b>Uptime service</b><br/>127.0.0.1:3001"]
Nginx -->|"uptime.umbra.mom / HTTP"| Uptime
end
subgraph Home["JUPITER / HOME SERVER"]
LAN["<b>LAN clients</b><br/>Jellyfin / Bookshelf local DNS"]
Traefik["<b>Traefik</b><br/>192.168.1.56<br/>WireGuard 10.0.0.2"]
Apps["<b>Application containers</b><br/>Docker network: traefik"]
LAN -->|"HTTPS :443"| Traefik
Traefik -->|"Application routing"| Apps
end
Internet -->|"HTTPS :443"| Nginx
Nginx -->|"HTTPS :443 over WireGuard"| Traefik
class Internet,LAN infraClient
class Nginx,Traefik infraEdge
class Apps,Uptime infraService
```
## Cloudflare Tunnel
```mermaid
---
config:
themeCSS: |
.node rect { rx: 8px; ry: 8px; stroke-width: 1.5px; }
.cluster rect { fill: var(--infra-zone) !important; stroke: var(--infra-border) !important; stroke-dasharray: 5 5; rx: 12px; ry: 12px; }
.label, .nodeLabel, .cluster-label { color: var(--infra-text) !important; }
.edgeLabel, .edgeLabel p { background-color: var(--infra-zone) !important; color: var(--infra-text) !important; }
.flowchart-link { stroke: var(--infra-line) !important; }
marker path { fill: var(--infra-line) !important; stroke: var(--infra-line) !important; }
.infraClient rect { fill: var(--infra-client-fill) !important; stroke: var(--infra-client-stroke) !important; }
.infraCloud rect { fill: var(--infra-cloud-fill) !important; stroke: var(--infra-cloud-stroke) !important; }
.infraEdge rect { fill: var(--infra-edge-fill) !important; stroke: var(--infra-edge-stroke) !important; }
.infraService rect { fill: var(--infra-service-fill) !important; stroke: var(--infra-service-stroke) !important; }
flowchart:
curve: basis
nodeSpacing: 35
rankSpacing: 45
---
flowchart TB
Shell["<b>SSH client</b><br/>Host jupiter / user jay"]
Git["<b>Git over SSH</b><br/>Host git.umbra.mom / user git"]
Cloud["<b>Cloudflare Tunnel edge</b><br/>Access policy unconfirmed"]
subgraph Home["JUPITER / HOME SERVER"]
Connector["<b>cloudflared</b><br/>Locally configured ingress"]
SSH["<b>Host sshd</b><br/>192.168.1.56:22"]
Gitea["<b>Gitea SSH</b><br/>192.168.1.56:2222"]
Connector -->|"ssh.umbra.mom / SSH :22"| SSH
Connector -->|"git.umbra.mom / SSH :2222"| Gitea
end
Shell -->|"cloudflared access ssh / ssh.umbra.mom"| Cloud
Git -->|"cloudflared access ssh / git.umbra.mom"| Cloud
Cloud <-->|"Tunnel traffic / initiated outbound by Jupiter"| Connector
class Shell,Git infraClient
class Cloud infraCloud
class Connector infraEdge
class SSH,Gitea infraService
```
Client `cloudflared` transports SSH through Cloudflare; Jupiter's connector opens
the outbound tunnel. Origins still perform SSH authentication. No Mercury,
WireGuard, or Traefik hop on this path; no home-router port forwarding.
| Ingress hostname | Origin | Client SSH user |
| --- | --- | --- |
| `ssh.umbra.mom` | `ssh://192.168.1.56:22` | `jay` |
| `git.umbra.mom` | `ssh://192.168.1.56:2222` | `git` |
| Unmatched | `http_status:404` | N/A |
Both client configurations use `IdentityFile ~/.ssh/id_ed25519`.
```sshconfig
Host jupiter
User jay
IdentityFile ~/.ssh/id_ed25519
ProxyCommand cloudflared access ssh --hostname ssh.umbra.mom
Host git.umbra.mom
User git
IdentityFile ~/.ssh/id_ed25519
ProxyCommand cloudflared access ssh --hostname %h
```
Local tunnel configuration is separate from Cloudflare Access applications.
Access login/provider/allow rules remain unconfirmed; `ProxyCommand` alone
does not establish enforcement.
Ingress source: host `/data/cloudflared/config.yaml`, mounted at
`/etc/cloudflared/config.yaml`.
## DNS / routes
| DNS / route | Destination |
| --- | --- |
| Public service ingress | Mercury `185.230.217.66` |
| `jellyfin.local.umbra.mom` | Jupiter `192.168.1.56` |
| `bookshelf.local.umbra.mom` | Jupiter `192.168.1.56` |
| `ssh.umbra.mom`, `git.umbra.mom` | Cloudflare Tunnel |
| `uptime.umbra.mom` | Mercury nginx -> `http://127.0.0.1:3001` |
## Mercury
[Mercury VPS](mercury.md): native nginx; WireGuard server `10.0.0.1/24`;
Jupiter client `10.0.0.2/24`. Hosting, TLS, firewall and fail2ban details are
maintained on that page.
Home ISP uses CGNAT. Mercury supplies a static public IPv4 and forwards over
WireGuard; owner preference is to avoid public ingress via the residential IP.
## Observed Docker networks
Snapshot from supplied inspection, not fixed-address configuration. Container
IPs may change; use Docker service names.
| Network | IPv4 subnet | Internal | Observed role |
| --- | --- | --- | --- |
| `traefik` | `172.18.0.0/16` | No | Reverse proxy and application ingress |
| `gitea_network` | `172.23.0.0/16` | No | Gitea, runner, notes builder, Havox sync |
| `cloudflared_cloudflare` | `172.26.0.0/16` | No | Tunnel connector |
| `gitea_data` | `192.168.96.0/20` | Yes | Gitea and PostgreSQL |
| `paperless_data` | `192.168.112.0/20` | Yes | Paperless, PostgreSQL, Redis |
| `dozzle_dozzle` | `172.27.0.0/16` | Yes | Dozzle and socket proxy |
Cloudflared's observed `172.26.0.2` matches the supplied proxied SSH login source.
An internal network alone does not isolate a multi-network container from
egress through its other networks. Empty network entries do not establish
whether they are obsolete or safe to remove.
## Jupiter: repository configuration
- Traefik publishes TCP `80,443`; HTTP redirects to HTTPS.
- Docker provider: opt-in `traefik.enable=true`; external network `traefik`.
- File provider: `/data/traefik/dynamic`.
- ACME: Let's Encrypt; Cloudflare DNS-01; `/data/traefik/acme/acme.json`;
apex + `*.umbra.mom` certificate requested.
- HTTPS fallback: priority `1`; unmatched non-apex hosts redirect to `https://umbra.mom`.
- `cloudflared`: `/data/cloudflared` -> `/etc/cloudflared`;
stack-local bridge `cloudflare`; no published ports.
+51
View File
@@ -0,0 +1,51 @@
---
schema_version: 1
id: home-server.operations
type: reference
scope: [jupiter, mercury, design-decisions, monitoring, recovery]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "RAID/media tradeoff, CGNAT/public IPv4 rationale, no alerting and running monitoring/log containers, 2026-10-06"
related: [home-server.reference, home-server.host, home-server.mercury, home-server.networking, home-server.storage, home-server.backups, home-server.deployment]
update_triggers: [data-priority-change, architecture-change, alerting-change, recovery-test]
unknowns:
- acceptable-downtime-and-data-loss-by-service
- data-criticality-outside-the-four-backed-up-directories
- tested-full-host-recovery-order
- monitoring-review-frequency
---
# Operations / decisions
## Owner decisions
| Decision | Reason / accepted tradeoff |
| --- | --- |
| RAID 0 for HDD data | Capacity prioritized over disk redundancy; downloaded media can be reacquired |
| Back up important state | Four application directories covered; media redundancy is not the objective |
| Mercury public edge | Static public IPv4; home ISP uses CGNAT |
| No residential public ingress | Owner preference to avoid exposing the home IP |
RAID 0 still stores important state alongside media: either HDD failure loses
the array; recovery depends on working backups. Owner intent is not evidence
that every important dataset is covered.
## Observability
| Facility | Current configuration |
| --- | --- |
| Host/container monitoring | Beszel hub and host-network agent present |
| Container logs | Dozzle with Docker socket proxy present |
| Automatic alerting | None configured |
Monitoring does not imply notification delivery. Backup-hook failures,
stopped services and storage failures require manual detection.
## Recovery status
No restore test performed. Backup scope is documented in [Backups](backups.md);
OS, host configuration and named-volume coverage must not be inferred from
the four-directory plan. Recovery priorities, RPO/RTO and validated bootstrap
instructions remain unconfirmed.
+65
View File
@@ -0,0 +1,65 @@
---
schema_version: 1
id: home-server.portainer
type: reference
scope: [jupiter, portainer, deployment, configuration-state]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Deployed container, ~/portainer_start.sh and updated Backrest plan, 2026-10-06"
- kind: owner-report
reference: "GitHub main/PAT access, Portainer environment values, manual redeployment and container inspection, 2026-10-06"
- kind: repository
repository: jupiter-stacks
revision: working-tree
base_revision: 610e325ef6a98850511ce7a089b0b9a77bfecf15
paths:
- stacks/portainer/portainer_start.sh
related: [home-server.reference, home-server.storage, home-server.authentication, home-server.backups, home-server.deployment]
update_triggers: [script-change, portainer-upgrade, state-migration, backup-plan-change]
unknowns: [local-hostname-dns-resolution, successful-state-and-secret-restore]
---
# Portainer
## Deployment
Managed by handwritten `~/portainer_start.sh`, **not a Compose stack**.
Reference script: [portainer_start.sh](https://github.com/jayo60013/jupiter-stacks).
| Setting | Deployed value |
| --- | --- |
| Image | `portainer/portainer-ce:2.45.1` |
| Container | `portainer` |
| Restart policy | `always` |
| Network | External Docker network `traefik` |
| State | Host `/data/portainer` -> container `/data` |
| Docker API | `/var/run/docker.sock` bind mount |
| Host publication | TCP `9123` -> container `9000` |
| HTTPS routers | `portainer.umbra.mom`, `portainer.local.umbra.mom` |
| Traefik backend | HTTP; container port `9000` |
| TLS | `https` entrypoint; `letsencrypt` resolver |
Script stops/removes the existing container, then recreates it; bind-mounted
state persists. Requires Docker, the `traefik` network, and mounted `/data`.
Supplied routers attach no Authelia middleware. Local router does not establish
local DNS resolution.
## Stack management
Portainer fetches <https://github.com/jayo60013/jupiter-stacks>, branch `main`,
using a GitHub PAT. Stack environment values/secrets are managed in Portainer.
Repository changes are pulled/redeployed manually; committing alone does not
update containers. See [deployment](deployment.md).
## Backups
Daily 03:00 UTC; Google Drive; last four snapshots. Backrest reads
`/userdata/portainer` from host `/data/portainer`.
- Before snapshot: stop `portainer`; timeout `120s`; errors fatal.
- Snapshot end/error: start `portainer`; restart errors ignored.
- Restore remains untested; script itself is outside the listed backup paths.
The former `portainer_data` named volume is not used by the deployed container.
+92
View File
@@ -0,0 +1,92 @@
---
schema_version: 1
id: home-server.seedbox
type: reference
scope: [seedbox, ultra-cc, qbittorrent, rclone, sftp, media-import]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "Ultra.cc plan, remaining capacity, rclone remote/process and enabled systemd unit, 2026-10-06"
- kind: owner-report
reference: "apt-managed rclone, active FUSE mount and Radarr/Sonarr bind-mount inspection, 2026-10-06"
- kind: repository
repository: jupiter-stacks
revision: 610e325ef6a98850511ce7a089b0b9a77bfecf15
paths:
- stacks/radarr/docker-compose.yml
- stacks/sonarr/docker-compose.yml
related: [home-server.reference, home-server.networking, home-server.storage, home-server.backups]
update_triggers: [hosting-renewal, quota-change, remote-change, mount-change, media-import-change]
unknowns:
- total-traffic-allowance-and-reset-period
- renewal-behaviour
- rclone-cache-location-and-size-limit
- import-copy-move-and-remote-deletion-policy
- mount-startup-ordering-relative-to-docker-containers
- seedbox-backup-policy
---
# Seedbox
## Service / subscription
| Field | Configuration |
| --- | --- |
| Provider / plan | [Ultra.cc](https://ultra.cc/); `Lancer-v2` |
| Host | `terra.usbx.me` |
| Account / home | `jayo60013` / `/home/jayo60013` |
| Cost | GBP 4.50/month |
| Reported expiration | `2026-10-25`; renewal behaviour unconfirmed |
| Disk | 1001 GB allocation; 984 GB available at review |
| Remaining traffic | 2 TB at review; total allowance/reset period unconfirmed |
| Download speed | Provider reports `Unlimited`; no numeric rate supplied |
| Upload speed | Provider reports `50000 Mbps`; not measured throughput |
| qBittorrent UI | <https://jayo60013.terra.usbx.me/qbittorrent/> |
## Jupiter connection
Actual connection: **SFTP via rclone/FUSE**, not WebDAV or scheduled sync.
| Component | Path / value |
| --- | --- |
| rclone remote | `seedbox`; type `sftp`; host `terra.usbx.me`; user `jayo60013` |
| SSH key file | `/home/jay/.ssh/id_ed25519`; key contents excluded |
| Remote source | `seedbox:downloads/qbittorrent` |
| Remote absolute path | `/home/jayo60013/downloads/qbittorrent` |
| Jupiter mount | `/mnt/seedbox` |
| Container source | `/downloads` in Radarr and Sonarr |
| Radarr library | Container `/movies` -> host `/data/jellyfin/movies` |
| Sonarr library | Container `/tv` -> host `/data/jellyfin/tv_shows` |
rclone remote: `shell_type=unix`; checksum commands `md5sum`, `sha1sum`.
rclone is apt-managed. Observed FUSE mount: UID/GID `1000:1000`;
`rw,nosuid,nodev,allow_other`.
## Mount lifecycle
`/etc/systemd/system/rclone-seedbox.service`; enabled at boot.
User/group `jay`; `Type=simple`; wants/starts after `network-online.target`.
```sh
/usr/bin/rclone mount seedbox:downloads/qbittorrent /mnt/seedbox \
--allow-other \
--dir-cache-time 1m \
--vfs-cache-mode writes
```
Stop: `/bin/fusermount -u /mnt/seedbox`.
Restart: `on-failure`; delay `10s`; target `multi-user.target`.
`--allow-other` permits other local users, subject to filesystem permissions.
`writes` caches write/read-write opens; read-only opens stream from the remote.
No explicit cache path/size limit is supplied in the unit.
The remote mount is not a complete local replica or backup. Radarr/Sonarr
library destinations are on the HDD array; cross-filesystem hardlinks are
impossible. Mount startup ordering relative to Docker containers is unconfirmed.
Their `/downloads` binds use `rprivate`; host remounts are not automatically
propagated into existing containers.
The FUSE-reported capacity is not evidence of the account quota; use the
provider allocation above.
+163
View File
@@ -0,0 +1,163 @@
---
schema_version: 1
id: home-server.storage
type: reference
scope: [jupiter, disks, raid, filesystems, docker-volumes]
sensitivity: private-infrastructure
last_reviewed: "2026-10-06"
sources:
- kind: owner-report
reference: "lsblk filesystem output, RAID level, mount placement and Docker data root, 2026-10-06"
- kind: owner-report
reference: "Seedbox rclone mount and systemd unit, 2026-10-06"
- kind: owner-report
reference: "Deployed Portainer uses /data/portainer bind mount, not portainer_data, 2026-10-06"
- kind: owner-report
reference: "mdstat, mdadm scans/detail, fstab, findmnt and Docker volume/container inspection, 2026-10-06"
- kind: repository
repository: jupiter-stacks
revision: 610e325ef6a98850511ce7a089b0b9a77bfecf15
paths:
- stacks/backrest/docker-compose.yml
- stacks/jellyfin/docker-compose.yml
- stacks/authelia/docker-compose.yml
- stacks/traefik/docker-compose.yml
related: [home-server.reference, home-server.host, home-server.seedbox, home-server.portainer, home-server.backups, home-server.operations]
update_triggers: [disk-change, raid-change, mount-change, volume-change, backup-coverage-change]
unknowns: [persistent-mdadm-config-and-initramfs-content, disk-health]
---
# Storage
```mermaid
---
config:
themeCSS: |
.node rect { rx: 8px; ry: 8px; stroke-width: 1.5px; }
.cluster rect { fill: var(--infra-zone) !important; stroke: var(--infra-border) !important; stroke-dasharray: 5 5; rx: 12px; ry: 12px; }
.label, .nodeLabel, .cluster-label { color: var(--infra-text) !important; }
.flowchart-link { stroke: var(--infra-line) !important; }
marker path { fill: var(--infra-line) !important; stroke: var(--infra-line) !important; }
.infraStorage rect, .infraStorage path { fill: var(--infra-storage-fill) !important; stroke: var(--infra-storage-stroke) !important; }
.infraCloud rect { fill: var(--infra-cloud-fill) !important; stroke: var(--infra-cloud-stroke) !important; }
.infraClient rect { fill: var(--infra-client-fill) !important; stroke: var(--infra-client-stroke) !important; }
.infraService rect { fill: var(--infra-service-fill) !important; stroke: var(--infra-service-stroke) !important; }
flowchart:
curve: basis
nodeSpacing: 30
rankSpacing: 45
---
flowchart LR
subgraph Array["HDD ARRAY / APPLICATION DATA"]
direction TB
A["<b>Seagate BarraCuda</b><br/>sda1 / 2 TB"]
B["<b>Seagate BarraCuda</b><br/>sdb1 / 2 TB"]
RAID["<b>Linux MD RAID 0</b><br/>md0 / no redundancy"]
DATA[("<b>/data</b><br/>ext4")]
A --> RAID
B --> RAID
RAID --> DATA
end
subgraph System["SSD / SYSTEM"]
direction TB
SSD["<b>Samsung 860 EVO</b><br/>sdc / 1 TB"]
EFI["<b>/boot/efi</b><br/>sdc1 / FAT32"]
SWAP["<b>Swap</b><br/>sdc2"]
ROOT["<b>/</b><br/>sdc3 / ext4"]
DOCKER["<b>/var/lib/docker</b><br/>Named volumes"]
CACHE["<b>/home/jay/data</b><br/>Backrest / Jellyfin caches"]
SSD --> EFI
SSD --> SWAP
SSD --> ROOT
ROOT --> DOCKER
ROOT --> CACHE
end
class A,B,SSD,DATA,ROOT infraStorage
class RAID infraCloud
class EFI,SWAP infraClient
class DOCKER,CACHE infraService
```
RAID 0: striping; no redundancy; either HDD failure loses the array.
## Filesystems
Device names reflect the supplied snapshot; UUIDs identify filesystems.
| Mount | Device | Filesystem | UUID |
| --- | --- | --- | --- |
| `/data` | `/dev/md0` | ext4 | `d60390a1-7db3-44cc-bbde-693c23e42001` |
| `/` | `/dev/sdc3` | ext4 | `7a5e0ee6-4a65-4f68-9128-b3b4f3406447` |
| `/boot/efi` | `/dev/sdc1` | FAT32 | `1DDE-CDF9` |
| swap | `/dev/sdc2` | swap | `4781ea5d-fba5-438d-8d97-6a0801669ca1` |
MD members: `/dev/sda1`, `/dev/sdb1`; metadata `1.2`; array label `jupiter:0`;
MD UUID `c67fccaa-e541-bf2d-2bf6-83221086e0e9`.
## RAID assembly / geometry
Linux MD, inspected with mdadm; ext4 directly on the array, no intervening LVM.
| Setting | Observed value |
| --- | --- |
| Device / scan name | `/dev/md0` / `/dev/md/0` |
| mdadm UUID | `c67fccaa:e541bf2d:2bf68322:1086e0e9` |
| Capacity | 3.64 TiB / 4.00 TB |
| Layout / chunk | `original` / 512 KiB |
| Member order | Slot 0: `/dev/sdb1`; slot 1: `/dev/sda1` |
| State at review | `clean`; 2 active members; no failed/spare devices |
Scan output identifies the array; persisted mdadm configuration/initramfs were
not supplied. Assembly is not array creation; do not substitute `mdadm --create`.
## Mount configuration
Supplied `/etc/fstab`:
```fstab
UUID=7a5e0ee6-4a65-4f68-9128-b3b4f3406447 / ext4 errors=remount-ro 0 1
UUID=1DDE-CDF9 /boot/efi vfat utf8 0 0
UUID=d60390a1-7db3-44cc-bbde-693c23e42001 /data ext4 defaults 0 2
UUID=4781ea5d-fba5-438d-8d97-6a0801669ca1 none swap sw 0 0
```
Observed `/data`: `rw,relatime,stripe=256`; `/tmp`: tmpfs.
Seedbox FUSE mount is service-managed, not listed in fstab.
## Placement
| Host path | Backing / purpose |
| --- | --- |
| `/data/<service>` | HDD array; service bind mounts |
| `/mnt/seedbox` | Remote SFTP/FUSE mount; [seedbox](seedbox.md); not a local replica |
| `/home/jay/data/backrest/cache` | SSD; Backrest cache |
| `/home/jay/data/jellyfin/cache` | SSD; Jellyfin cache |
| `/var/lib/docker` | SSD; Docker state and default local named volumes |
| `/var/lib/containerd` | SSD; observed containerd overlayfs snapshot storage |
| `/data/portainer` | HDD array; deployed [Portainer](portainer.md) state; backed up |
| `/data/authelia` | Configuration, users, SQLite, notifications |
| `/data/traefik/{acme,dynamic,certs}` | ACME state, dynamic configuration, certificates |
Cache placement is per mount, not globally SSD-backed. `/data` backups do not
implicitly include SSD-backed Docker volumes.
## Named-volume snapshot
Inspected volumes below use driver `local`, no driver options, and
`/var/lib/docker/volumes/<name>/_data` on SSD.
| Volume | Running consumer / container path |
| --- | --- |
| `audiobookshelf_config` | Audiobookshelf `/config` |
| `jellyfin_config` | Jellyfin `/config` |
| `notes_static` | Notes web `/usr/share/nginx/html`; builder `/site-output` |
| `dozzle_dozzle_data` | Dozzle `/data` |
| `ctq_ctq_postgres_data` | CTQ PostgreSQL `/var/lib/postgresql/data` |
| `havox_src` | Havox web/sync `/git` |
Other volumes are present without a running consumer shown, including
`jellyfin_cache`, `notes_notes_static`, and monitoring volumes. Presence does
not prove use or justify deletion. Named volumes are outside the supplied
four-directory backup plan.
+48
View File
@@ -0,0 +1,48 @@
:root {
--infra-panel: #f8fafc;
--infra-panel-glow: #eef2ff;
--infra-border: #cbd5e1;
--infra-zone: #ffffff;
--infra-text: #1e293b;
--infra-line: #64748b;
--infra-client-fill: #f1f5f9;
--infra-client-stroke: #94a3b8;
--infra-edge-fill: #eef2ff;
--infra-edge-stroke: #818cf8;
--infra-service-fill: #ecfdf5;
--infra-service-stroke: #34d399;
--infra-cloud-fill: #fff7ed;
--infra-cloud-stroke: #fb923c;
--infra-storage-fill: #eff6ff;
--infra-storage-stroke: #60a5fa;
}
[data-md-color-scheme="slate"] {
--infra-panel: #141b2b;
--infra-panel-glow: #202944;
--infra-border: #475569;
--infra-zone: #1e293b;
--infra-text: #e2e8f0;
--infra-line: #94a3b8;
--infra-client-fill: #293548;
--infra-client-stroke: #94a3b8;
--infra-edge-fill: #30335c;
--infra-edge-stroke: #a5b4fc;
--infra-service-fill: #153c35;
--infra-service-stroke: #6ee7b7;
--infra-cloud-fill: #493324;
--infra-cloud-stroke: #fdba74;
--infra-storage-fill: #203654;
--infra-storage-stroke: #93c5fd;
}
.md-typeset .mermaid {
--md-mermaid-label-fg-color: var(--infra-text);
--md-mermaid-label-bg-color: var(--infra-zone);
margin: 1.25rem 0;
padding: 1rem;
overflow-x: auto;
border: 1px solid var(--infra-border);
border-radius: 16px;
background: linear-gradient(135deg, var(--infra-panel), var(--infra-panel-glow));
}
+11
View File
@@ -0,0 +1,11 @@
[project]
name = "jupiter-notes"
version = "0.1.0"
description = "University notes and personal infrastructure reference"
requires-python = ">=3.12"
dependencies = [
"zensical==0.0.67",
]
[tool.uv]
package = false
-1
View File
@@ -1 +0,0 @@
zensical==0.0.67
Generated
+316
View File
@@ -0,0 +1,316 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "click"
version = "8.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" },
]
[[package]]
name = "deepmerge"
version = "3.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/38/6e/5cb3548b4d3112fea529375e55e6f3cdc52b8054e3a66f203b1f888ba885/deepmerge-3.0.1.tar.gz", hash = "sha256:35b39a4cb92cf328d6eca61cbbf65f68a37c2ceb3085f0f853cbb2e52a59fc23", size = 22328, upload-time = "2026-09-01T14:09:44.383Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/91/600003aaad107e27553fbc9cbfc57e96fa37e0223d2ef9d7d3a0e8d8d070/deepmerge-3.0.1-py3-none-any.whl", hash = "sha256:35c96f6a68fcf90719a5b31d9f8042ecef6c00fb56836660d33455d0f5cfda65", size = 14909, upload-time = "2026-09-01T14:09:43.364Z" },
]
[[package]]
name = "jinja2"
version = "3.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "jupiter-notes"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "zensical" },
]
[package.metadata]
requires-dist = [{ name = "zensical", specifier = "==0.0.67" }]
[[package]]
name = "markdown"
version = "3.11"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/4f/700155c8c20d9e655dd0732b5fc3c7614f291b9148da271d7388e50bf774/markdown-3.11.tar.gz", hash = "sha256:180224db6aed87ba9ce1f2781ebcd5826253de8ff637112090e24b84502bbf9f", size = 485623, upload-time = "2026-09-25T13:46:23.473Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/1e/32971905a7ab47f8b66866ed949fa48b104ba1c4a6fa57794c4f2c4b2cb8/markdown-3.11-py3-none-any.whl", hash = "sha256:cd6c89e7eb308c8b332ed673215a52d208a43f8bacc030b1419376129408719e", size = 111296, upload-time = "2026-09-25T13:46:22.163Z" },
]
[[package]]
name = "markupsafe"
version = "3.0.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/38/9b/e422a865e1d5d57d0e509b4e0bf1c1a70a7f6382c29a5aa428df994c8bc8/markupsafe-3.0.4.tar.gz", hash = "sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6", size = 153777, upload-time = "2026-10-02T23:07:22.29Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/09/4c59d56b8461ae8eb0d8ba34bb25b7e618547044679d58a82ef9b2479fc1/markupsafe-3.0.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:61631e08084be9e21a8967ec3139c7616ed7c5e9368e05c86d1b39562c8a57b6", size = 11658, upload-time = "2026-10-02T23:04:51.876Z" },
{ url = "https://files.pythonhosted.org/packages/a2/f0/d6613774d86fbf6d145751d43c59875e47a6f9f17daee0aef173bd36d90e/markupsafe-3.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0930db9bdc62d22944e10b066448bb65dc9abe9112880c7cab8da54db4284d5f", size = 12050, upload-time = "2026-10-02T23:04:52.931Z" },
{ url = "https://files.pythonhosted.org/packages/0d/f2/8f18e0b806eb13c1f8d07d917a720831ead54253a6dec011fbc78098a6f8/markupsafe-3.0.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a45c3d514f2436064db00d7fc8778d888f0236ebfed649b53d13a59e69ad51b", size = 24363, upload-time = "2026-10-02T23:04:53.895Z" },
{ url = "https://files.pythonhosted.org/packages/60/ce/fa07dbe8a5675558fa36dea033e19995bc783de2dec5f540ccb9030b06aa/markupsafe-3.0.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1e1451fab512d1bcc3dc26988ec1edb0b82c2db909132872cd9356070a6b63df", size = 28525, upload-time = "2026-10-02T23:04:54.905Z" },
{ url = "https://files.pythonhosted.org/packages/85/40/be87c01f3868ec217f8a2015089d71c22c8c5a75324822e5ed1cdd87210d/markupsafe-3.0.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bd3ce56ae2cbae3ba82b683bc425cd7e48d2ed8b10f3e818186b6f5646d9271c", size = 24733, upload-time = "2026-10-02T23:04:56.229Z" },
{ url = "https://files.pythonhosted.org/packages/4f/a7/aeedb5140afa41fc74c225e9184ab96723a6e873b6ee1c9fede7283456d8/markupsafe-3.0.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e124f974786f831d6043728e38296969d3579db8896fe004682f5758e613581", size = 22985, upload-time = "2026-10-02T23:04:57.521Z" },
{ url = "https://files.pythonhosted.org/packages/c3/fc/e91352bb08c6a59da3ef0909d457bf95a5f5908fbf151b30a06d9dbcfbb4/markupsafe-3.0.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c02e8f18bdedba082cef725942ac823b9b60656db07f7e265cb31618dfd00d77", size = 22001, upload-time = "2026-10-02T23:04:58.597Z" },
{ url = "https://files.pythonhosted.org/packages/5d/f8/bffee5e7d2a3deb59748a797650a48af7e672025cf641a79344a771ad106/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f098115c247e11d138ab83a28fa0323c77015007ea2df73ba5fd714dfefd67c", size = 23793, upload-time = "2026-10-02T23:04:59.686Z" },
{ url = "https://files.pythonhosted.org/packages/ed/59/b853d6628ecb4d658e1d637224846d5e9bb4adf4f8df97f3be9f29dce2ec/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d5f93ebbeb8032d47e349328ec8662d973d9b05a70b3c35df1f91fe419b84749", size = 22640, upload-time = "2026-10-02T23:05:00.768Z" },
{ url = "https://files.pythonhosted.org/packages/09/b2/1506df394f0f075797c418d0301498f49e43be194e3ffcb49e6fe6ccf022/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:64511c54db4e4987aef4c41923235927428729e8174c5dba488429be70a998ed", size = 24074, upload-time = "2026-10-02T23:05:01.813Z" },
{ url = "https://files.pythonhosted.org/packages/c7/81/5ed69cda630ac69ef60d06c09ba5a7f84ff66a2e28cf986fd5614ab3c6e6/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e1a622f13970d81f95d0c72f9dc090dce9085fccfa4c9f2174377ee32bd15786", size = 21563, upload-time = "2026-10-02T23:05:03.239Z" },
{ url = "https://files.pythonhosted.org/packages/0c/fe/fb1e79be0fea60aa32602ebefc9c35a82bb42b4df157285ab7dfec12341a/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9a7f43c0b202b334cc9184af09bb8f21d3a209e038efaf106936fb69e6b026e", size = 23048, upload-time = "2026-10-02T23:05:04.479Z" },
{ url = "https://files.pythonhosted.org/packages/c8/52/7632a53360671a9b750cdbabaf9cdd89f18b42248b8e4cb42c0b0296e459/markupsafe-3.0.4-cp312-cp312-win32.whl", hash = "sha256:f0ec3b750b59375eab5b0fb2b9254810c00a3375be6d789899f1055a1d556237", size = 14108, upload-time = "2026-10-02T23:05:05.513Z" },
{ url = "https://files.pythonhosted.org/packages/3f/bf/62495e180b7000aaf30000fff849e933f74264638057176cf46852500adc/markupsafe-3.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:11935df9bf455ed0c04eb87bcd720f02b1fe5e02128a9430f23aed6f93336fc7", size = 14303, upload-time = "2026-10-02T23:05:06.538Z" },
{ url = "https://files.pythonhosted.org/packages/c5/8e/4c24208776a65878d656996945aacfbfe010d3720d1a98fc0eb8491fc03b/markupsafe-3.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:a4bbd2d87dd233b9fc5812160c3d0ffbe42edc22a26ce0469f58479ede633fe9", size = 14174, upload-time = "2026-10-02T23:05:07.617Z" },
{ url = "https://files.pythonhosted.org/packages/6d/18/4bc5ba32499e87bb2b0ef5b3a9bb9c00a131fa961ddf0be548cb550f548b/markupsafe-3.0.4-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1", size = 12786, upload-time = "2026-10-02T23:05:08.709Z" },
{ url = "https://files.pythonhosted.org/packages/4e/6f/17f0c099bf25f3e31e63cc19244d9f6af861a9a4ab778c203997903cfdd0/markupsafe-3.0.4-cp313-cp313-android_24_x86_64.whl", hash = "sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1", size = 12510, upload-time = "2026-10-02T23:05:09.93Z" },
{ url = "https://files.pythonhosted.org/packages/11/af/1a141081b905036ee904ec4bd945e1f70b4e1b32d33c4e59e8cf1d58b247/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:6bd9e1788e15bfcf6a9082de42e30387e7b85d211ab21e57a939bb8cfaaf8d96", size = 11583, upload-time = "2026-10-02T23:05:10.884Z" },
{ url = "https://files.pythonhosted.org/packages/e7/0a/a89385ae590232622a03e091805cff12f24fabe6c11e0e8bae096cece81c/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:5066b244f576f91afc8ee3ba029a89f99d39c79b1853fe9d39bea9f0afbec148", size = 11801, upload-time = "2026-10-02T23:05:11.913Z" },
{ url = "https://files.pythonhosted.org/packages/ed/85/ea548dc013962eb73653124bc595635fbf9e0fa41d1f181a967ccb784dfb/markupsafe-3.0.4-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7a83aa6e4805df46fed18e989d3d16f86ef60cb50bbc8d9ce3a6be89165fbf6e", size = 11443, upload-time = "2026-10-02T23:05:12.887Z" },
{ url = "https://files.pythonhosted.org/packages/cc/72/15f2e5ec9cf2eb00d5cdfe968d94e4156a7bd7303832c3f3b2c403a36839/markupsafe-3.0.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2d1b7d9308288661f56672b1b157d75fc536714d3638487bbea17b6318a78248", size = 11665, upload-time = "2026-10-02T23:05:13.829Z" },
{ url = "https://files.pythonhosted.org/packages/ca/e0/4030bea613677e333c8a2c901fd405055f657f9d06acba5b7357984b6ef7/markupsafe-3.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73e77980c7207854f00fc4e71fb1626868d5740ab4012623d55c7a99ad122a72", size = 12061, upload-time = "2026-10-02T23:05:14.807Z" },
{ url = "https://files.pythonhosted.org/packages/f3/a5/28b76a7449eb702966b88bef599e2360b411fbb3afeee8fe560939be06ec/markupsafe-3.0.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7018d4af1cd272e847aa5917983ab5e83e4f6579f9dbfecd4a79c0ca80b144c2", size = 24395, upload-time = "2026-10-02T23:05:15.909Z" },
{ url = "https://files.pythonhosted.org/packages/07/6c/21232811afc3a063b5e934b1ae2efda52f46154ec382f585149c020e61fe/markupsafe-3.0.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c90d5b3d4e944e065a301d741b3c1d784f6bd1f503aa68b4967e32b2ba313d85", size = 27183, upload-time = "2026-10-02T23:05:16.976Z" },
{ url = "https://files.pythonhosted.org/packages/14/38/6ccdfa5b59049cb36fb80cbc80aee9cf1fc9bb77d1335ad435f2070b08cf/markupsafe-3.0.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:18a801868a884f216e784d7d14db2a4077143ce7610440aee2ce8f734e7cfcde", size = 24772, upload-time = "2026-10-02T23:05:18.209Z" },
{ url = "https://files.pythonhosted.org/packages/63/e0/cec6865dfe88cb48fedd4b20aed6af5158e41092adcbf3e028bcc6ec2108/markupsafe-3.0.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434139499bb20b502ed3baa1f169e618f924a97e7a777fea1a49446d80106cf6", size = 23017, upload-time = "2026-10-02T23:05:19.286Z" },
{ url = "https://files.pythonhosted.org/packages/ee/76/6ed4940bb7648a9aac457c14f870cfdd5105f139a0fb1f29cd61fafa47d1/markupsafe-3.0.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9e227f3dbe6bde7491cf0a9965d00b88c6b1a4a95d11480ddf88bb96d397c19f", size = 22036, upload-time = "2026-10-02T23:05:20.352Z" },
{ url = "https://files.pythonhosted.org/packages/a1/4f/ed476226d4fe46a09090a36025bf319296810028df55eb12f1253b540f3a/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b8cd1f918b26fd7b1832ece557cc18f2d8747309ff8b3f0ef9d4250c5ad67a39", size = 23786, upload-time = "2026-10-02T23:05:21.576Z" },
{ url = "https://files.pythonhosted.org/packages/9a/35/66ff30450e35ef5fba9ebc930c9411747e537fd9447b65e44f5007e2b84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a5fcffb37e602b0b3c1638a97746b9b96125caa9bcf6fa41d337a9261de231ee", size = 22672, upload-time = "2026-10-02T23:05:22.922Z" },
{ url = "https://files.pythonhosted.org/packages/32/0b/72f45ce4b4efcbca4b80cf1b06703eff0be8d37e82abb78f66c85a7ead1e/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5989cb26b2e1efc6a42216a9f6b5ee495ce5ace2e5b352a9af489976b32d1ee2", size = 24107, upload-time = "2026-10-02T23:05:24.175Z" },
{ url = "https://files.pythonhosted.org/packages/d2/03/71776e5fdcba04614b384cc102e8a4198208579d896fd1394cb7cb9aa900/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:add96447a86d205ab616665d53b2950ee81083757f56e6ea833c8b2917646b46", size = 21615, upload-time = "2026-10-02T23:05:25.215Z" },
{ url = "https://files.pythonhosted.org/packages/ab/5f/801ce02a02e7aee0f784b1ec7843026178f6adeb9c93ac67eb1992a9a84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2628d3a8cb648ecebb3c5d6b0a1052d400e4d8b7ac0fb786be8d285b50040d17", size = 23076, upload-time = "2026-10-02T23:05:26.423Z" },
{ url = "https://files.pythonhosted.org/packages/4a/85/c43776625428f3bb4a61e8633940400e3efe6409e3c6f5bff26de5e45618/markupsafe-3.0.4-cp313-cp313-win32.whl", hash = "sha256:672d207103e6b16ca098611b0f9efad6bc00afd47c03d6ef62186495ca677dc0", size = 14118, upload-time = "2026-10-02T23:05:27.716Z" },
{ url = "https://files.pythonhosted.org/packages/6f/36/163da64de88a13db79214ef75fa041be7fa13bdb42261cf5b7484de14bfb/markupsafe-3.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:1f1f9477e174582b0a1b583d60b66e1f2cf5d3fe12cee985e4aedf44766600e5", size = 14313, upload-time = "2026-10-02T23:05:28.749Z" },
{ url = "https://files.pythonhosted.org/packages/9f/a8/9b662783ffaa1149221432a923cee562f78b9cbbb8baa3df9b3753e63e1e/markupsafe-3.0.4-cp313-cp313-win_arm64.whl", hash = "sha256:06de8ef6331f6e822c28d577dc8bf43fe398800477c49498f38fc38b67ff33fc", size = 14189, upload-time = "2026-10-02T23:05:29.917Z" },
{ url = "https://files.pythonhosted.org/packages/5c/c3/a944f3b0df22bd129e96915b9f4e98d2eeca6516687d7618304a966c3c74/markupsafe-3.0.4-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:4ed644d75aa94a2baf7ec3a96eaa160ea58c742eb9d27c6506053c5c40fc84ed", size = 12787, upload-time = "2026-10-02T23:05:30.971Z" },
{ url = "https://files.pythonhosted.org/packages/d4/d6/a44863f69d88b6c7e27889108f70d47aed259edf89d5df3c5fca1eac87d6/markupsafe-3.0.4-cp314-cp314-android_24_x86_64.whl", hash = "sha256:6d2a9efe686f9de00d0d1ea32a4a5a86d558a2277501bd78d964214eab625e59", size = 12525, upload-time = "2026-10-02T23:05:32.263Z" },
{ url = "https://files.pythonhosted.org/packages/17/8f/168ba80e532dd6a93f96f8f706f1ad41d7990b6e1aeedc1cc0d211a33497/markupsafe-3.0.4-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8781a792a070cf2bd1b86d3aa943894115faaba6e88122a7bf32d62072742453", size = 11589, upload-time = "2026-10-02T23:05:33.251Z" },
{ url = "https://files.pythonhosted.org/packages/32/b3/aa2c95a574d3af39403a469b295886eb9b6d448da568cbebb5a2cbfdc2e5/markupsafe-3.0.4-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:971a3bbb75d97ae4e2e8f7d4834236f86f85f0c85e04ab2e191db1123b04f80b", size = 11801, upload-time = "2026-10-02T23:05:34.315Z" },
{ url = "https://files.pythonhosted.org/packages/60/d0/34b810107d83840e768bf485de795893ebbae35b26ab061b487adfa0a692/markupsafe-3.0.4-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:8909c2f1c6dd65e054ac4b573a91c8384d1492281e55d82d159d653f7a13adf6", size = 11447, upload-time = "2026-10-02T23:05:35.302Z" },
{ url = "https://files.pythonhosted.org/packages/6c/ab/2f8488f0f817a39fca068d2b17daf446bf5cdb3eae28c3720af534d873b4/markupsafe-3.0.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4cf3468d5ec187ffffcaca8e61929a37448f215dafc1386a12c750a72fe53634", size = 11722, upload-time = "2026-10-02T23:05:36.363Z" },
{ url = "https://files.pythonhosted.org/packages/ad/40/e2d117b048d47282ade906fbfd92814cbee5647afc13fda88a3406039372/markupsafe-3.0.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:52704c5d36eb6dda8866493decd61111fff86244c9b1ad225ca01b9e91e5970f", size = 12061, upload-time = "2026-10-02T23:05:37.397Z" },
{ url = "https://files.pythonhosted.org/packages/9a/a8/73a81135e85ba66217f5af7facb03bbb386807e1a729ab64532e4c802652/markupsafe-3.0.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1caa2fa5a6184fb233153b35f654e6687bd555476f6170f29d8ee9be1a8b0af9", size = 24434, upload-time = "2026-10-02T23:05:38.407Z" },
{ url = "https://files.pythonhosted.org/packages/ac/ca/fa9216dd01efee2dfdacafe7df32b4d0170fbac694b0c258a193d6e53999/markupsafe-3.0.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:387d8cd30e69b3f0a72877b9ae717033396404e19095b17fe89753a981fda44f", size = 27183, upload-time = "2026-10-02T23:05:39.581Z" },
{ url = "https://files.pythonhosted.org/packages/fa/4e/a469509e538d37af51103b17b073126973f2b1cbf197ff32c7ddf025cfe5/markupsafe-3.0.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:051417f74bcaaefa316276e0ff723f541616ca51043d070da00249d9bddd3e3c", size = 24813, upload-time = "2026-10-02T23:05:40.671Z" },
{ url = "https://files.pythonhosted.org/packages/8f/db/d7282caf7ab03af44d5d6fdbaa019b35c7d7f1c90588b839c07cba640d6a/markupsafe-3.0.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8e9f292fcda89b324f2f5c91d13f1424a153e40fc2756f38ee23b15835ff300", size = 23049, upload-time = "2026-10-02T23:05:41.864Z" },
{ url = "https://files.pythonhosted.org/packages/30/f3/b6a425206e6964efda6acee544d0eb01d1501784d0b8e2dcc74986f33b17/markupsafe-3.0.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:df1ae86ff54725a01fa1a0510b914ca53a161b7050be74f6204e24aded5971d0", size = 22088, upload-time = "2026-10-02T23:05:43.014Z" },
{ url = "https://files.pythonhosted.org/packages/ea/8a/84d3582fc1f0d5bd466cdf2eebf175e172158a6e70701aacec1de1b35430/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8965520ac587c94a4ac48b729be3d8b8de00af39699b17585dfb599babe77977", size = 23846, upload-time = "2026-10-02T23:05:44.098Z" },
{ url = "https://files.pythonhosted.org/packages/1c/65/db101cce51b7ba4864ac491a9859d297dd1adf0e55b103fee9db9c47c527/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:340cbb1957ba99929cbf19a75626d36ba1ae21d1730b287d1cf7f824a20c4fc7", size = 22674, upload-time = "2026-10-02T23:05:45.23Z" },
{ url = "https://files.pythonhosted.org/packages/e0/49/ddee9813d71db0c7a5c9d97c832125e6758a0c844777f1cf076569bb0e22/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:3a93d9616ddecfb393727a0041a562cf0b15a244e20f2bd25efc7949be4c4f17", size = 24104, upload-time = "2026-10-02T23:05:46.398Z" },
{ url = "https://files.pythonhosted.org/packages/aa/0e/7d8518d726726870a2399d69fd30d0fa36c5e57a2132c336b58d7c491073/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d2e56fd3b00222722abfb3f5f0759ddbae4b90811b5ad4343c64030ad1bde70c", size = 21637, upload-time = "2026-10-02T23:05:47.48Z" },
{ url = "https://files.pythonhosted.org/packages/b4/b0/b505e8a361ba557dbf3b3aa7331ea39b00d2022a26e925ff8463b9714bb3/markupsafe-3.0.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0d9c47709875fdb321452056622e930c52afbc07a7d780762fbb8b4d91ce6fa4", size = 23081, upload-time = "2026-10-02T23:05:48.611Z" },
{ url = "https://files.pythonhosted.org/packages/1c/ea/9cc3cea873f980c75cbdb6f4277ce30ee955de38be0b3d02f14c108e0698/markupsafe-3.0.4-cp314-cp314-win32.whl", hash = "sha256:38fc55594dab834470b6733dead2ee9e3f657fb0608c769dcafa0ba5ab52f45c", size = 14272, upload-time = "2026-10-02T23:05:49.707Z" },
{ url = "https://files.pythonhosted.org/packages/80/f0/5792ff768a410f93ee3f84fc19345295ffc352d2c936b424cb37e514714c/markupsafe-3.0.4-cp314-cp314-win_amd64.whl", hash = "sha256:c1bc67752d5f21013cfe430df4062441714eab79f65a6a05e01505957e9c35fe", size = 14501, upload-time = "2026-10-02T23:05:50.788Z" },
{ url = "https://files.pythonhosted.org/packages/5f/cf/3d074a8edffcc6899355232ff2543ae8d929733239596423b7db79698bc9/markupsafe-3.0.4-cp314-cp314-win_arm64.whl", hash = "sha256:7e1636da3d8dfc220b6dd10264db5f2b165e4888c4518594898fbe381049af8a", size = 14364, upload-time = "2026-10-02T23:05:51.857Z" },
{ url = "https://files.pythonhosted.org/packages/d9/31/87ce42159aae2163cf3bbbd0c44bc87780510eecab1ea3859099aed95dcb/markupsafe-3.0.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:805c8b84534fa10891890f0e4be39f3a99e94615d93e8836bf9fa1fdca2feeb2", size = 11759, upload-time = "2026-10-02T23:05:52.951Z" },
{ url = "https://files.pythonhosted.org/packages/5f/53/b047207eeb7752e960aca3eb1df5fb7eefa7dd4c62ac49bb156456c8a702/markupsafe-3.0.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fa95848c929b6a75f6848d3c9793e59db365ee436776e57db835cdbfa79ba977", size = 12096, upload-time = "2026-10-02T23:05:54.066Z" },
{ url = "https://files.pythonhosted.org/packages/ee/51/4326c88a13c7b755657d44b4bb986f8c3d9843ecba7e22d98661d87f9a57/markupsafe-3.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e916035e3e9930cbdfdd10abf48861340221857f45509565898e012263f7b289", size = 25699, upload-time = "2026-10-02T23:05:55.15Z" },
{ url = "https://files.pythonhosted.org/packages/f2/bb/990581b7474bfcf2cf34bed6ba5ea23bd87adb9d671213d68e88620e7a6b/markupsafe-3.0.4-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b4d12837e0203bbace818ff4a7461afdcd78bcd782351cea148139180d7bcffe", size = 27804, upload-time = "2026-10-02T23:05:56.29Z" },
{ url = "https://files.pythonhosted.org/packages/6b/89/89491878c28e8291f5aa2fffe2c2d57230d10ae366d55dd810b840513d78/markupsafe-3.0.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5086f9975abb1ab531ee6afca1761e4b59a19b446f3f6522ed776963228cfe5a", size = 25912, upload-time = "2026-10-02T23:05:57.416Z" },
{ url = "https://files.pythonhosted.org/packages/30/77/680998b54efdea06fc114565cd739b6d059f826a0279219b218dfa750d29/markupsafe-3.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b4a635a0487774f841cb1fb62e907e7195cc95bc761e053184b8acc3ceb20733", size = 23623, upload-time = "2026-10-02T23:05:58.557Z" },
{ url = "https://files.pythonhosted.org/packages/ae/75/2709f5ac5de9467b40b10e2bb8f89cc63dfb74582e09aa734b1124a217de/markupsafe-3.0.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb96e6e088d6cf71c1ea977510948320234824cf226e32f6f6e044f7a9c82b34", size = 23301, upload-time = "2026-10-02T23:05:59.94Z" },
{ url = "https://files.pythonhosted.org/packages/a0/c8/39eadc6c5b14c9c7679bfb98f4d4c6a97863b5beb91839aca4d2d6e16e55/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8b5d563170ff8ba3181caa967c99a3c804d1dedb702c7cb93a6a7c32247da978", size = 24950, upload-time = "2026-10-02T23:06:01.289Z" },
{ url = "https://files.pythonhosted.org/packages/1a/5e/01037f8a43e8ccb0bffb4fbdc5212db05bf080fdd7286cd392332d58128a/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:396ec4e65cc889f69786b3b89478b471cee5a3bcf468b9d9bb03e1a30fb291fc", size = 23330, upload-time = "2026-10-02T23:06:02.441Z" },
{ url = "https://files.pythonhosted.org/packages/d4/f4/23e83ce0596bb0cbe670502d31df8f757bbd01a392aa486fa3b40d1ed399/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:15ba9e28640feef770374b116a6f019c21f52404aeabe516aa7f800587b98cfc", size = 25239, upload-time = "2026-10-02T23:06:03.579Z" },
{ url = "https://files.pythonhosted.org/packages/88/5b/3708897368073cc683d524750474f41a77d2986152c380dcc55b20fdf340/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d920abdfa61279ba1a2ef9484aab07bf03331f8c08a10120fa332353d06e6932", size = 22767, upload-time = "2026-10-02T23:06:04.699Z" },
{ url = "https://files.pythonhosted.org/packages/c6/61/ebda1307864b409e6b3115757a3d4a09cca46cfb6cc65191b5de226b424b/markupsafe-3.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a9f54054101545a9a9cccefddf54316aa6e4491611fcbef9e91b3b6bebec04f6", size = 23668, upload-time = "2026-10-02T23:06:05.9Z" },
{ url = "https://files.pythonhosted.org/packages/09/15/98075cceac3b5ba0dbb8e4762a847be967d2befc349a2cf2d0ac77f62c9d/markupsafe-3.0.4-cp314-cp314t-win32.whl", hash = "sha256:12a606a492de952afcb43b59a14aaaaad120e708d3663dd0fdf2d738d427a691", size = 14301, upload-time = "2026-10-02T23:06:07.109Z" },
{ url = "https://files.pythonhosted.org/packages/0b/a3/768b560fcc4156685cb563d922b217810cfa7bc135773367f62f1f9d2078/markupsafe-3.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a18f38cafc329bac5e3c2b96c765b4c96d3d103421ed22ab7988c1e3fce27464", size = 14526, upload-time = "2026-10-02T23:06:08.276Z" },
{ url = "https://files.pythonhosted.org/packages/93/63/da554b4c97a6b0ea3229ca7fe8cbfb620be81613d517f482e85958550537/markupsafe-3.0.4-cp314-cp314t-win_arm64.whl", hash = "sha256:eba154571c16e032112afac0dc2dfe9e63c2ceb7aedd07bb7eecf2ce26d4dd4c", size = 14421, upload-time = "2026-10-02T23:06:09.402Z" },
{ url = "https://files.pythonhosted.org/packages/a9/30/54d11c8ca027114898cab97421fb39e4ffd9ddf47cdbc44df2ec76722da9/markupsafe-3.0.4-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:737c9c3981998eba27f11786f84fddcbabc74068b72a4a1f454ea02094b57b65", size = 12791, upload-time = "2026-10-02T23:06:10.485Z" },
{ url = "https://files.pythonhosted.org/packages/10/6d/97c913e253a14bd3cd0e15a5c56d13203b823fa7ee32498342896a072dc4/markupsafe-3.0.4-cp315-cp315-android_24_x86_64.whl", hash = "sha256:489505b03f692c3f376394e49194fa7a7f9e8558d6e293a7056a0032b0c38163", size = 12527, upload-time = "2026-10-02T23:06:11.834Z" },
{ url = "https://files.pythonhosted.org/packages/26/f9/b86d032042a4d597d9e1997f0e5f63a3eedaf11258e0a05760b0a0a826ea/markupsafe-3.0.4-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:077293e425f28ec737dbcad442a71752e28f8ae27cde3d68acd1fb212091cd92", size = 11602, upload-time = "2026-10-02T23:06:13.122Z" },
{ url = "https://files.pythonhosted.org/packages/f2/dc/73c14c1eedf0ac5fa3292ba43435e6c49d2c2050f33cebde541f8f4807f1/markupsafe-3.0.4-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9348cbb300d224fe3b89793262cb093504d4ae927004468463f745188a193e4a", size = 11817, upload-time = "2026-10-02T23:06:14.227Z" },
{ url = "https://files.pythonhosted.org/packages/8f/69/2c2fcaa5fcee22d72c7819c0d536fd181c74a688e6143845419579cd2863/markupsafe-3.0.4-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:b807e598953730f82e4eae3bd30f6a122cf6b31c398c6b504c0e04c13c170429", size = 11446, upload-time = "2026-10-02T23:06:15.574Z" },
{ url = "https://files.pythonhosted.org/packages/88/54/9e5ec76c62e6e2834d5a93623018c943e8b3bb41d663e3fd4c03303b9b85/markupsafe-3.0.4-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:799c39bdf5e2f1292fedd3009f7b3c9e760f10b2420cb9638d56920840ff6db8", size = 11719, upload-time = "2026-10-02T23:06:16.701Z" },
{ url = "https://files.pythonhosted.org/packages/96/24/3ec292b44064c16229e064d770b2625bd8ea941aa61f44905a9fa44942c0/markupsafe-3.0.4-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:ae9dcb8fbe244cb82f8a6458b455b927a03685e383d9bacf1ea5ce180b96dc97", size = 12075, upload-time = "2026-10-02T23:06:17.855Z" },
{ url = "https://files.pythonhosted.org/packages/aa/85/b64fdb1f304848518742136983c24e96d967bfb59a0ea160e92736901ab0/markupsafe-3.0.4-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bced6e2a6dba6a28f7dd3c6ce14df1b2dd495923f16ea484cad03decd463b2b", size = 24656, upload-time = "2026-10-02T23:06:18.963Z" },
{ url = "https://files.pythonhosted.org/packages/9c/18/23997d4c65b355da6390d61cd56e0ab3befd6ba8dda25cb40c602bd0fa6b/markupsafe-3.0.4-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3882fb412298575bae3b9c46868251f15cc69307359f87bb1b382e53d6e5a2c9", size = 27737, upload-time = "2026-10-02T23:06:20.117Z" },
{ url = "https://files.pythonhosted.org/packages/d4/36/35998dead3c6af88c38265a56e58100211f036234ab88eb2283fd4cbce44/markupsafe-3.0.4-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04e7902ba80ee4bac1d50a549606527a1dcf0476cd81403db41099d3b60ec653", size = 25003, upload-time = "2026-10-02T23:06:21.284Z" },
{ url = "https://files.pythonhosted.org/packages/82/96/ef49135ce260db4ca4a12b119ed468449cd248db6b1468e2112b546d7a2e/markupsafe-3.0.4-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:925f929d6b59a8b3f8b8c6ac363cd0af7eecc81efb3071770b3c6717c450a369", size = 23111, upload-time = "2026-10-02T23:06:22.524Z" },
{ url = "https://files.pythonhosted.org/packages/50/7d/83126e338bd88c17a220668235368ad719fd4638e426739858cbb8508f77/markupsafe-3.0.4-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f68edfc67aabac33708941f26f22a7b8e9f81429bc0cf249fcf7d66b23af8d19", size = 22403, upload-time = "2026-10-02T23:06:23.785Z" },
{ url = "https://files.pythonhosted.org/packages/83/dd/daf7e420de23c8206c365204e7b85e1251d8e19d34196a56336f316e5ed2/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:e5c802729725bd07e2bc3ab7b76dc7e0bbfc53129d8f1eb1c002c24cf774717e", size = 24083, upload-time = "2026-10-02T23:06:25.037Z" },
{ url = "https://files.pythonhosted.org/packages/19/3c/11eecdc06bc44ad5570350085b572ebf049e8f9a38d1ece6d76640b739cd/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:55ffd6ce583d97dc71dc92e930324c8c0d25aea7e3ade6ae54ef77cedb096811", size = 22931, upload-time = "2026-10-02T23:06:26.328Z" },
{ url = "https://files.pythonhosted.org/packages/0d/9e/ac0fd77f2a726e56ecc3ca0235d095feace1358d1b822406c2a2ef26a4dc/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:2cb3dd71fc6be918ad4264346a8ed69485f9b7ed7bf35495d8e22807cd6b8bea", size = 24299, upload-time = "2026-10-02T23:06:27.742Z" },
{ url = "https://files.pythonhosted.org/packages/d7/09/c6bd842ad58ff5b3bc76eeed7e9a42a6f11adc5d090ec697b72c9672731e/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:94f5407f7bc64fa6463906b896f9904beeeb7dd8dc116ee8e9056c8714ff9916", size = 21948, upload-time = "2026-10-02T23:06:29.274Z" },
{ url = "https://files.pythonhosted.org/packages/a3/46/82f586711fed61e86faa1ee1bc317d68cd45a10c8bdbe3f7d1fdf9026ad8/markupsafe-3.0.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2dad610540cb2e6272855c178f08ae9a1c7ac258a7fb71660553a5f104b42741", size = 23150, upload-time = "2026-10-02T23:06:30.583Z" },
{ url = "https://files.pythonhosted.org/packages/19/2d/2dfdce99318abbfa26925195fbc17db188c46a1ec6457be121b6f9cfeb42/markupsafe-3.0.4-cp315-cp315-win32.whl", hash = "sha256:03470d1a8268e692ecf79ecd565593e59d44219377a7ead61f1f1b94c1f7ff6b", size = 14269, upload-time = "2026-10-02T23:06:31.949Z" },
{ url = "https://files.pythonhosted.org/packages/5b/ec/6000fd82e8791e58fcd0456ec20f098957e2b03d5ed02eb73241a577c0ba/markupsafe-3.0.4-cp315-cp315-win_amd64.whl", hash = "sha256:d882a373d8093c2941e01291b7ced96e9cbe4781da9a7751ca7e6c70385e5214", size = 14496, upload-time = "2026-10-02T23:06:33.258Z" },
{ url = "https://files.pythonhosted.org/packages/bc/66/e73bd5016421d5d6e2fb6de7dd609f9de020942ac8c626526bd8c6eeaf82/markupsafe-3.0.4-cp315-cp315-win_arm64.whl", hash = "sha256:353bd63081912ab8cfa6a0c7d185934cdf8426f04c618bba6bc4b394f2069b67", size = 14361, upload-time = "2026-10-02T23:06:34.539Z" },
{ url = "https://files.pythonhosted.org/packages/90/df/cb8c3dc98d313a951df2f8968f44e4cb5643df6d3cab749a530ce2f7d972/markupsafe-3.0.4-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c61750fadcd119d0825bcb7d7d675dd264dcc89cc05292aab5be68ebdbb374ad", size = 11760, upload-time = "2026-10-02T23:06:35.807Z" },
{ url = "https://files.pythonhosted.org/packages/d6/bb/4af9b3ca0753d654ac75f9531d5bd741bb77ca6e696f36807c475ffc099a/markupsafe-3.0.4-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:1c0df495a977d10460a94941799c72d5b5ab03d3858d949b55b5a66c8f371c99", size = 12108, upload-time = "2026-10-02T23:06:37.089Z" },
{ url = "https://files.pythonhosted.org/packages/3f/d4/b56429313aee5fd59b079c3df5615299959e25e7113eb6d8caadbdd7d38a/markupsafe-3.0.4-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02fa4acbc6a3fc5c693c34d4dd8c1130b7fe99cc915181b0ddd6f72aeb296002", size = 25762, upload-time = "2026-10-02T23:06:38.419Z" },
{ url = "https://files.pythonhosted.org/packages/65/f5/34c181e891aa4f7d59c918584672e0c5eb7fffe76c1387d1246008bf4081/markupsafe-3.0.4-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:05295589e619b9bed252a86b532b8e27350abc372d18ba89b59375325e91ec1e", size = 28400, upload-time = "2026-10-02T23:06:39.819Z" },
{ url = "https://files.pythonhosted.org/packages/ce/b5/ad14694fd0ac9a5ce30bc6498f2999378f418583dd1679cca5a1b512957e/markupsafe-3.0.4-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:be6cb0c799abb0e2ba3e618e6d28ddddf7e485f6c2ce938dfa237daf3905072c", size = 25946, upload-time = "2026-10-02T23:06:41.381Z" },
{ url = "https://files.pythonhosted.org/packages/d6/a8/26b606445387d0ceb1eb1f21840094b84e4e3c3c3983d80d10b89823b490/markupsafe-3.0.4-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26e9867520db70d37f7fb421a7f0d8adb40171011fb84ce869afa1a83370dfa8", size = 23634, upload-time = "2026-10-02T23:06:42.748Z" },
{ url = "https://files.pythonhosted.org/packages/39/a2/b8814de672f1f0094d498bf646f2fec9d6356b503d28ef500b71c5095377/markupsafe-3.0.4-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f03460ff076f70ab595bb45a0205ccea1971443575b6920c52e755dec2b3fbfe", size = 23488, upload-time = "2026-10-02T23:06:44.176Z" },
{ url = "https://files.pythonhosted.org/packages/db/c7/287223376fb73335a3cc5d6eb22c6ab01358cf33945a9c39c06b9dac3f4b/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:436e3ffc6310d3c41878c601db29098102fe5d8a467c49da4a4125254e0980f2", size = 25037, upload-time = "2026-10-02T23:06:45.646Z" },
{ url = "https://files.pythonhosted.org/packages/f9/29/4df8355e313426d19e62ba33e0253c009ca12a0894ee77d67fa67255361c/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:4e2c4809c14559aa7ef426f27fb35afbb38104c349a903bf8f3600456764bb38", size = 23543, upload-time = "2026-10-02T23:06:47.264Z" },
{ url = "https://files.pythonhosted.org/packages/71/e5/8377731e8495668dcc768f645e717df18318c841edaf023a99395f6da9b4/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:da2af0d7aebfc2074080d72efa6ab8317c62481ef1f896f65d9999c1c01f4494", size = 25282, upload-time = "2026-10-02T23:06:48.795Z" },
{ url = "https://files.pythonhosted.org/packages/ed/5f/373456e37ceb1478d657d6fe769cbe0a39f0a8dfc1548eeb19c471eefdd9/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:aa2c838cc024642cc04c6854232f32b43e5e22833dd11119c1766c7873b8370d", size = 22993, upload-time = "2026-10-02T23:06:50.31Z" },
{ url = "https://files.pythonhosted.org/packages/d7/93/2cbd5628435afb6f541bbaced4bce0c2edac4b09a142e6e928b8b0da9858/markupsafe-3.0.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:b91cc9d336957239ff200f30097e6fea2dc6d6fb3c81e853eaa09eac904fd894", size = 23712, upload-time = "2026-10-02T23:06:51.759Z" },
{ url = "https://files.pythonhosted.org/packages/81/99/157e10966b033b363aeda5263e82596ee232a0b1d082fdbf90aa417ff083/markupsafe-3.0.4-cp315-cp315t-win32.whl", hash = "sha256:e49fb0d1ce92cfa0cb198cc5b1b11cdf9d0638658e2a2db2687e39db7c87fc78", size = 14300, upload-time = "2026-10-02T23:06:53.241Z" },
{ url = "https://files.pythonhosted.org/packages/33/05/55884815414c9706a23deca150b72c25a62109e65b0b6ce232077802c719/markupsafe-3.0.4-cp315-cp315t-win_amd64.whl", hash = "sha256:4f6e0852a0283b1b1fd776eeb7b766a5f440b3e2bd31ab51af3b400585f3965c", size = 14522, upload-time = "2026-10-02T23:06:54.729Z" },
{ url = "https://files.pythonhosted.org/packages/92/f9/ecbde7149e95b8a0f18e16d5d747f7dc06049d5da2e4f77f6f5e4a1f46a8/markupsafe-3.0.4-cp315-cp315t-win_arm64.whl", hash = "sha256:39dbacefc411633db5b4378b066a9aca70a3d7e2922c9e578d825f844026eeba", size = 14417, upload-time = "2026-10-02T23:06:56.246Z" },
]
[[package]]
name = "pathspec"
version = "1.1.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
]
[[package]]
name = "pygments"
version = "2.21.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
]
[[package]]
name = "pymdown-extensions"
version = "12.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown" },
{ name = "pyyaml" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2a/94/858e0163cb4d83d6d8234018a01792c749a56daee41794cac7d6c46661e8/pymdown_extensions-12.1.tar.gz", hash = "sha256:fdb8f47f5d7fd069d10ef2a7d8908e613d7865ff0419d544a0ffd3984e884f11", size = 868245, upload-time = "2026-09-23T00:08:42Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/d1/98313da89960a604402266a115311b510254900ff1295ea426403f9423cc/pymdown_extensions-12.1-py3-none-any.whl", hash = "sha256:4a254b771acfcddc6c110a7f4590d818f1f849e2d5aacf69b4b07c8dd2a9700d", size = 277069, upload-time = "2026-09-23T00:08:40.069Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]
[[package]]
name = "tomli"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
{ url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
{ url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
{ url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
{ url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
{ url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
{ url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
{ url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
{ url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
{ url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
{ url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
{ url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
{ url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
{ url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
{ url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
{ url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
{ url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
{ url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
{ url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
{ url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
{ url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
{ url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
{ url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
{ url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
{ url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
{ url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
{ url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
{ url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
{ url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
{ url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
{ url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
{ url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
{ url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
{ url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
{ url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
{ url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
]
[[package]]
name = "zensical"
version = "0.0.67"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "deepmerge" },
{ name = "jinja2" },
{ name = "markdown" },
{ name = "pathspec" },
{ name = "pygments" },
{ name = "pymdown-extensions" },
{ name = "pyyaml" },
{ name = "tomli" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/11/d655563a922eb96bc930543132a6cb404971d980b36dcf7aa90751f4af8f/zensical-0.0.67.tar.gz", hash = "sha256:6691e79d1fec9e6235ded01a7d98c19342bfff144fceae7d043c4d3830890038", size = 4300450, upload-time = "2026-09-30T09:26:24.153Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/75/88/15bf6b4eef8895c37e96d6beb1c57fedcdcaa51527ba728869b81db0341d/zensical-0.0.67-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2fbb86fb0773d32aab2ce5bb40be64a6962871ef049c925d633996a6f54d7130", size = 18057715, upload-time = "2026-09-30T09:26:01.536Z" },
{ url = "https://files.pythonhosted.org/packages/00/c0/bb8dd996633ba4ad2a0d73bd0b09381fff309cdccec965b0978746e22189/zensical-0.0.67-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:679a513ffb0013b6db3bd5aacbc73484f6a734df702e5ba41baf049753d38995", size = 17588275, upload-time = "2026-09-30T09:26:04.495Z" },
{ url = "https://files.pythonhosted.org/packages/c6/6b/f3aa259b9ee24a26de148d0d49a239683a43ecd985dd35b30cf0c3f5dead/zensical-0.0.67-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:100b21a42eb6f48c5f3fb89c7a4b3e3b12dcf6f7e2bd7da65b344842b1da30ad", size = 18039684, upload-time = "2026-09-30T09:26:06.963Z" },
{ url = "https://files.pythonhosted.org/packages/94/9c/f850af0905eb7319201f823a9fdedf32d0a479845dd3186e4a80f845f181/zensical-0.0.67-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:610c577a3d1200e21c443590e12334b46cb38ce0ef2dad12110afe19925160e2", size = 17988405, upload-time = "2026-09-30T09:26:09.578Z" },
{ url = "https://files.pythonhosted.org/packages/4b/ac/9a9210bb96cbeb848df301462ac2d557e67548366014f4fa33dac10385d8/zensical-0.0.67-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9ee6369a253c9ff18f3c221bb48ac8c82eda22cb46f03650b937be8786b00b9", size = 18508864, upload-time = "2026-09-30T09:26:11.917Z" },
{ url = "https://files.pythonhosted.org/packages/f5/5d/7180698732ca5cf566f88300fa9ec487d81e852392be98d84d7cbc7cca80/zensical-0.0.67-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7f9baff27363551dcdb4970936a126a714f7ac84fd646bb673e2676ff2ab526d", size = 18204439, upload-time = "2026-09-30T09:26:14.152Z" },
{ url = "https://files.pythonhosted.org/packages/9b/89/04d8768895a39bb0ea65c1275fca0af0b1e1e5f3722a075fbd77476c199a/zensical-0.0.67-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e1a7b7d5edf58394df235ade589b187da8f97107b11056ca989dbd6d7e646cc", size = 18769301, upload-time = "2026-09-30T09:26:16.585Z" },
{ url = "https://files.pythonhosted.org/packages/79/63/45108c9b220d73af5dc3577edf48c63f4336d7d80295fa2abc8c6ee3a4fe/zensical-0.0.67-cp310-abi3-win_amd64.whl", hash = "sha256:df5cc21899b4a5f3956d959e403dc754d7abcdcc2704c40dc43cff84e257b037", size = 18700647, upload-time = "2026-09-30T09:26:19.028Z" },
{ url = "https://files.pythonhosted.org/packages/93/43/09836dcb02085b7da0339d753b2b153352205209bfe14a3e16ec56740a0d/zensical-0.0.67-cp310-abi3-win_arm64.whl", hash = "sha256:20fb40bd0b8d05be92a793887e312de5b522fbda8fbdf80267395fd97baefc7b", size = 18120901, upload-time = "2026-09-30T09:26:21.683Z" },
]
+19
View File
@@ -5,8 +5,24 @@ site_description = "University notes & personal notes"
extra_javascript = [ extra_javascript = [
"javascripts/mathjax.js", "javascripts/mathjax.js",
] ]
extra_css = [
"stylesheets/diagrams.css",
]
nav = [ nav = [
{ "Home" = "index.md" }, { "Home" = "index.md" },
{ "Home Server" = [
{ "Reference" = "home-server/index.md" },
{ "Host" = "home-server/host.md" },
{ "Mercury VPS" = "home-server/mercury.md" },
{ "Seedbox" = "home-server/seedbox.md" },
{ "Portainer" = "home-server/portainer.md" },
{ "Deployment" = "home-server/deployment.md" },
{ "Operations / decisions" = "home-server/operations.md" },
{ "Networking" = "home-server/networking.md" },
{ "Storage" = "home-server/storage.md" },
{ "Authentication" = "home-server/authentication.md" },
{ "Backups" = "home-server/backups.md" },
] },
{ "Books" = [ { "Books" = [
{ "Designing Data-Intensive Applications" = [ { "Designing Data-Intensive Applications" = [
{ "Preface" = "books/designing_data_intensive_applications/preface/index.md" }, { "Preface" = "books/designing_data_intensive_applications/preface/index.md" },
@@ -201,6 +217,9 @@ permalink = true
[project.markdown_extensions.extra] [project.markdown_extensions.extra]
[project.markdown_extensions."pymdownx.highlight"] [project.markdown_extensions."pymdownx.highlight"]
[project.markdown_extensions."pymdownx.superfences"] [project.markdown_extensions."pymdownx.superfences"]
custom_fences = [
{ name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" },
]
[project.markdown_extensions."pymdownx.inlinehilite"] [project.markdown_extensions."pymdownx.inlinehilite"]
[project.markdown_extensions."pymdownx.magiclink"] [project.markdown_extensions."pymdownx.magiclink"]
[project.markdown_extensions."pymdownx.betterem"] [project.markdown_extensions."pymdownx.betterem"]