Repository navigation
feat(ansible): prepare the launch fleet (private overlay, catalogue sizing, runbook) #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| # Security | ||
|
|
||
| ## Reporting a vulnerability | ||
|
|
||
| Email `security@decdn.org`. Please do not open a public issue for security | ||
| reports. That includes a secret or real host address you find committed here. | ||
|
|
||
| ## Scope | ||
|
|
||
| This repo holds the deployment tooling for a deCDN node: the Ansible roles, the | ||
| `decdn.node` Galaxy collection, and the Helm chart. Reports about the node daemon or | ||
| protocol belong to [decdn/decdn](https://github.com/decdn/decdn), but the same address | ||
| reaches both. | ||
|
|
||
| The security model these tools follow is described in [README.md § Security | ||
| model](README.md#security-model): nothing secret committed, loopback-only backends, | ||
| default-deny inbound, DevSec host hardening. | ||
|
|
||
| ## Release verification | ||
|
|
||
| In `release` install mode, and while `decdn_verify_release_signature` keeps its | ||
| default of `true`, the `decdn_node` role checks every downloaded tarball against the | ||
| release's GPG-signed `SHA256SUMS`, using the maintainer key | ||
| vendored at `ansible/roles/decdn_node/files/decdn-release-KEYS.asc`. That key is a | ||
| copy of upstream's `KEYS`: | ||
|
|
||
| ``` | ||
| Ant Somers <ant@decdn.org> | ||
| Fingerprint: DA75 1570 6F18 73D2 74D8 A369 9E11 A9FF D62D AADB | ||
| ``` | ||
|
|
||
| The same fingerprint is published in | ||
| [decdn/decdn's SECURITY.md](https://github.com/decdn/decdn/blob/main/SECURITY.md). | ||
| Check a new copy of the vendored key against it before trusting that copy, with | ||
| `gpg --show-keys --with-fingerprint <file>`. | ||
|
|
||
| Setting `decdn_verify_release_signature: false` (meant for an air-gapped mirror that | ||
| strips signatures) drops that guarantee, and the role prints a warning when it's off. | ||
| `manual` mode verifies nothing: it installs whatever binaries you point it at. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # Launch fleet runbook | ||
|
|
||
| The ops sequence for putting the launch fleet up with this project. The plan it | ||
| serves, with targets, owners and dates, is `Launch/fleet-plan.md` in the private | ||
| `decdn/internal` repo. This file records only the mechanics. Protocol and economic | ||
| facts (bond sizing, fees) come from the deCDN ADRs; nothing here restates them. | ||
|
|
||
| ## 0. Inventory | ||
|
|
||
| 1. Copy [`inventory/fleet.example/`](../inventory/fleet.example/hosts.yml) into a | ||
| **private** repo or dir (see [README § Private fleet inventory](../README.md#private-fleet-inventory)). | ||
| Never commit real IPs to this public repo. | ||
| 2. For each host set `ansible_host` and `decdn_region`, then put it in exactly one | ||
| **role** group (`decdn_seed` / `decdn_edge`) and one **billing** group | ||
| (`decdn_metered` / `decdn_unmetered`). Hero and wow-factor traffic belongs on | ||
| unmetered nodes. | ||
| 3. In `group_vars/decdn_nodes.yml`, fill the MUST-EDIT lines: binary source and | ||
| contract addresses copied verbatim from `decdn/contracts/deployments/421614.json`. | ||
| Size `decdn_cache_size_mb` to each host's disk. | ||
| 4. Provision each node's secret on the host, the preferred way (`0600 /etc/decdn/decdn.env`, | ||
| [roles/decdn_node/README.md § Secrets](../roles/decdn_node/README.md#secrets)). | ||
| Provision the keystore too, or set `decdn_node_generate_keystore: true`. | ||
| 5. For every metered node, set a **billing alert** in the provider console (Hetzner / | ||
| OVHcloud). `decdn_load_shed_egress_budget_mbps` caps throughput, and the alert makes | ||
| the spend visible. | ||
|
|
||
| ## 1. Deploy | ||
|
|
||
| ```bash | ||
| cd ansible | ||
| make deps | ||
| INV=../../decdn-fleet/hosts.yml # your private overlay | ||
| make check INVENTORY=$INV LIMIT=<host> ANSIBLE_ARGS='-u root' # first converge: bootstrap user | ||
| make deploy INVENTORY=$INV LIMIT=<host> ANSIBLE_ARGS='-u root' | ||
| make deploy INVENTORY=$INV # every run after bootstrap, fleet-wide | ||
| ``` | ||
|
|
||
| The role runs `decdn config validate` against the installed binary after templating, | ||
| so a key the binary doesn't know fails the deploy instead of crash-looping the node. | ||
|
|
||
| ## 2. Stake and register each node (manual) | ||
|
|
||
| On-chain onboarding ([ADR 019](https://github.com/decdn/decdn/blob/main/adr/019-node-onboarding.md) | ||
| Phase 2) is an operator step, not automated here. Fund the node's eth address first | ||
| (`key-gen` printed it). Then run `decdn setup` on the node. It reads the rendered | ||
| config plus `DECDN_RPC_URL` from `decdn.env`, and `systemd-run` loads that file with | ||
| the same parser the node's unit uses, so the RPC key never lands in argv: | ||
|
|
||
| ```bash | ||
| # Preview first: pre-flight checks and the bond it would post, no transactions. | ||
| sudo systemd-run --pty --wait --collect -p User=decdn \ | ||
| -p EnvironmentFile=/etc/decdn/decdn.env \ | ||
| /usr/local/bin/decdn --config /etc/decdn/node.toml setup \ | ||
| --mbps <declared-capacity> --region <same code as decdn_region> \ | ||
| --multiaddr /ip4/<public-ip>/udp/4433/quic-v1 \ | ||
| --keystore-password-file /etc/decdn/keystore.password --dry-run | ||
| # Then the same command without --dry-run. It shows the operator terms and the bond to confirm. | ||
| ``` | ||
|
|
||
| - `--region` is the on-chain `regionHint` ([ADR 030](https://github.com/decdn/decdn/blob/main/adr/030-node-region-self-attestation.md)). | ||
| It must match the host's `decdn_region`, since the stats map and takedown scope read it. | ||
| - The bond is read from the on-chain `bondRequired(mbps)` curve | ||
| ([ADR 026](https://github.com/decdn/decdn/blob/main/adr/026-tokenomics.md)). Declare | ||
| `--mbps` for what the node can really serve; there's no flat minimum to quote. | ||
| - The flags, the `node bond` / `node register` primitives and the exit path are in | ||
| [roles/decdn_node/README.md § On-chain onboarding](../roles/decdn_node/README.md#on-chain-onboarding). | ||
|
|
||
| A node counts toward launch metrics only once it is registered and serving. | ||
|
|
||
| ## 3. Seed the catalogue (seed nodes) | ||
|
|
||
| On each `decdn_seed` host, run `Launch/seed-model.sh` (from `decdn/internal`) as the | ||
| service user, writing into the fs origin that group_vars configure: | ||
|
|
||
| ```bash | ||
| sudo -u decdn OUT_DIR=/var/lib/decdn/origin ./seed-model.sh | ||
| ``` | ||
|
|
||
| It needs the `hf` CLI and disk for **both** the origin copy and the cache, on the same | ||
| `/var/lib/decdn` volume. With the template values that's about 352 GiB of origin (the | ||
| current whole-repo set, measured 2026-09-22), plus `decdn_cache_size_mb` (400 GiB), plus | ||
| `decdn_disk_headroom_mb` (8 GiB): **about 760 GiB per seed**. `hf download` also stages | ||
| a full copy before import, so allow for the largest model (~177 GiB, Mixtral) on top | ||
| while seeding, or lower the seeds' `decdn_cache_size_mb` in `group_vars/decdn_seed.yml`. | ||
| Record every BLAKE3 hash it prints: the bundle manifests plus the large blobs. | ||
|
|
||
| ## 4. Pin and fill the edges | ||
|
|
||
| 1. Add the recorded hashes (lowercase 64-hex, no `b3:` prefix) to | ||
| `decdn_pinned_hashes` in the overlay's `group_vars/decdn_nodes.yml`, then | ||
| `make deploy INVENTORY=$INV`. | ||
| 2. A pin only stops eviction. Each edge still fetches a blob on its first miss, and | ||
| it's launch-ready once the blob is resident on enough nodes per region. | ||
|
|
||
| ## 5. Verify: the fleet part of the go/no-go | ||
|
|
||
| - From a cold machine near each region, pull the **largest** blob (currently the | ||
| 13.5 GiB Mistral v0.3 `consolidated.safetensors`) by hash. A `BlobTooLarge` error | ||
| means that node's `decdn_max_blob_size_mb` is too small. | ||
| - `curl -s 127.0.0.1:9090/metrics` on a node (over SSH, since metrics are loopback) | ||
| shows cache hits and misses. With `decdn_grafana_cloud_enabled`, watch egress and | ||
| errors in Grafana Cloud. | ||
| - Every node shows up registered, with its region, on the explorer / stats map. | ||
|
|
||
| ## 6. When `v0.1.0` is tagged | ||
|
|
||
| Switch the overlay to the signed tarballs (`decdn_node_install_method: release`, | ||
| `decdn_node_version: "0.1.0"`) and redeploy. The role verifies them against the | ||
| release's GPG-signed `SHA256SUMS`, using the vendored maintainer key. Re-check the config | ||
| schema coupling (`molecule/schema`, `make lint-helm`) against the tagged `types.rs`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # Travels with `cp -r inventory/fleet.example <private-overlay>`, so the private | ||
| # repo never tracks a per-node secret either (decdn_rpc_url may embed an API key). | ||
| host_vars/*/secret.* | ||
| !host_vars/*/secret.*.example |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| # Edge nodes have no origin of their own. With no decdn_cache_origin_kind set, the | ||
| # role defaults node-to-node pull-through ON, so an edge fills a cache miss from | ||
| # a seed or peer that holds the blob. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| # Metered nodes bill per TB of egress, so a model that trends on r/LocalLLaMA | ||
| # must not run up the bill (Launch/fleet-plan.md). Cap their egress. | ||
| # This is OPERATOR COST CONFIG, not a protocol fact. Size it from the provider's | ||
| # per-TB price and the monthly spend you accept, e.g. 250 Mbps sustained is about | ||
| # 81 TB/month. | ||
| decdn_load_shed_egress_budget_mbps: 250 | ||
| # Also set a billing alert in the provider console (Hetzner / OVHcloud) for every | ||
| # metered node. The budget above caps throughput, the alert makes the spend visible. |
59 changes: 59 additions & 0 deletions
59
ansible/inventory/fleet.example/group_vars/decdn_nodes.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| --- | ||
| # Settings shared by every launch node. Copied with the overlay; edit the private copy. | ||
|
|
||
| # --- Binaries ----------------------------------------------------------------- | ||
| # Until upstream tags v0.1.0, install locally built binaries. Build them from a | ||
| # decdn/decdn checkout on the release commit: | ||
| # cargo build --release -p decdn-node -p decdn-cli | ||
| decdn_node_install_method: manual | ||
| decdn_release_target_dir: "" # MUST-EDIT - control-machine path to decdn's target/release | ||
| # Once the v0.1.0 release exists (Launch/release-checklist.md §1), switch to the | ||
| # GPG-verified tarballs and drop the two lines above: | ||
| # decdn_node_install_method: release | ||
| # decdn_node_version: "0.1.0" | ||
|
|
||
| # --- Contracts (Arbitrum Sepolia, 421614) ---------------------------------------- | ||
| # Protocol facts. Copy them VERBATIM from the upstream deployment manifest | ||
| # decdn/contracts/deployments/421614.json. The public repo's | ||
| # inventory/host_vars/decdn-node-1/main.yml carries the current copy with its sync | ||
| # note. Never guess. Upstream redeploys change every address at once. | ||
| decdn_chain_id: 421614 | ||
| decdn_payment_pool_address: "" # MUST-EDIT | ||
| decdn_capacity_bond_address: "" # MUST-EDIT | ||
| decdn_slash_judge_address: "" # MUST-EDIT | ||
| decdn_content_blacklist_address: "" # MUST-EDIT | ||
| decdn_slash_appeal_address: "" | ||
| decdn_origin_assignment_address: "" | ||
| decdn_publisher_registry_address: "" | ||
| decdn_usdc_address: "" | ||
|
|
||
| # --- Cache sizing for the launch model catalogue --------------------------------- | ||
| # `decdn origin import` stores every file as ONE whole-file blob (--optimize only | ||
| # adds chunk hints to the manifest), and a node rejects any blob over | ||
| # max_blob_size_mb with BlobTooLarge on pull-through. The role default (1024) is far | ||
| # too small for model weights. Measured from the Hugging Face file trees on | ||
| # 2026-09-22 for the seed-model.sh set (whole repos, as that script downloads them): | ||
| # largest single file: 13.5 GiB Mistral-7B-Instruct-v0.3 consolidated.safetensors | ||
| # 12.8 GiB gpt-oss-20b original/model.safetensors | ||
| # 11.3 GiB Mixtral-8x7B consolidated.02.pt | ||
| # 9.6 GiB SDXL unet (fp32) | ||
| # whole set: ~352 GiB (Mixtral alone ~177 GiB) | ||
| # Re-measure if the seed list or its --exclude patterns change. | ||
| decdn_max_blob_size_mb: 16384 # 16 GiB: largest file 13.5 GiB, plus headroom | ||
| # Must hold the catalogue you expect a node to serve, plus room for LRU churn, and | ||
| # must be >= max_blob_size_mb. The daemon also keeps decdn_disk_headroom_mb (default | ||
| # 8 GiB) free on the volume. Size it to each host's disk: 400 GiB covers the whole | ||
| # set above. Override per host in host_vars if disks differ. | ||
| decdn_cache_size_mb: 409600 # 400 GiB | ||
|
|
||
| # --- Pinning the catalogue -------------------------------------------------------- | ||
| # Eviction-exempt pins. Fill these after seeding: the lowercase 64-hex BLAKE3 hashes | ||
| # `decdn origin import` prints for each model's bundle manifest AND its large blobs. | ||
| # A pin only stops eviction. The node still has to fetch the blob once before it | ||
| # holds it. | ||
| decdn_pinned_hashes: [] | ||
|
|
||
| # --- Observability ---------------------------------------------------------------- | ||
| # Recommended for launch: live metrics + journald per node (egress, errors). | ||
| # Provision the token first (roles/grafana_alloy/README.md). | ||
| # decdn_grafana_cloud_enabled: true |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.