Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/grafana-dashboards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ the iroh relay's in `monitoring/iroh-relay/`):
- `monitoring/sponsord/` — the onboarding sponsor's own pair: `dashboard-sponsord.json` (uid
`decdn-sponsord`) and `prometheus-alerts.yml` (group `sponsord`, `job="sponsord"`).
The chart does not render this directory (sponsord has no Kubernetes path). Its
`sponsord_*` names come from upstream `decdn/sponsord` `crates/sponsord/src/metrics.rs`
`sponsord_*` names come from upstream decdn/decdn's `crates/sponsord/src/metrics.rs`
(check them with the recipe after the node one below). Its logs are plain text,
not JSON: select `{unit="sponsord.service"}` and filter on the `level` label the
`grafana_alloy` role parses. The rules have promtool unit tests in
Expand Down
42 changes: 22 additions & 20 deletions .github/workflows/upstream-drift.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
---
name: Upstream drift

# The repo pins one decdn/decdn and one decdn/sponsord release line (decdn_node_version,
# sponsord_version: "X.Y", whose newest finished vX.Y.N the roles install) and carries
# copies of what that release publishes. This job resolves each line as the roles do
# The repo pins one decdn/decdn release line (decdn_node_version = sponsord_version =
# sponsord_onramp_version: "X.Y", whose newest finished vX.Y.N the roles install; one
# release ships the node, the CLI, sponsord and the onramp) and carries copies of what
# that release publishes. This job resolves the line as the roles do
# (tasks/resolve-release.yml) and checks, weekly:
# - the generated mirrors of decdn/decdn at that release's tag:
# roles/decdn_node/vars/main/networks.yml (contract addresses) and its subsets in
Expand All @@ -13,11 +14,11 @@ name: Upstream drift
# - the hand-copied release facts: the onramp's decdn tag and SHA256SUMS digest
# (compose/sponsord-onramp.env.example) and the Compose image digests
# (compose/.env.example), against that release's assets;
# - that each repo's newest vX.Y.Z tag is still in the pinned line.
# - that decdn's newest vX.Y.Z tag is still in the pinned line.
# It is a scheduled early warning, not a PR gate: an upstream release must not turn
# unrelated PRs red. Fix drift by re-running the generator it names; act on a new
# release with the bump checklist (roles/decdn_node/README.md, roles/sponsord/README.md);
# scripts/open-decdn-bump.sh opens the decdn bump's PR.
# release with the bump checklist (roles/decdn_node/README.md);
# scripts/open-decdn-bump.sh opens the bump's PR.
on:
schedule:
- cron: '17 6 * * 1' # Mondays 06:17 UTC
Expand All @@ -40,9 +41,10 @@ jobs:
shell: bash
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# The pins, read from the role defaults (plain scalars, so no YAML parser), and
# each line resolved as the roles resolve it: the highest vX.Y.N that is not a
# The pin, read from the role defaults (plain scalars, so no YAML parser), and
# the line resolved as the roles resolve it: the highest vX.Y.N that is not a
# draft or a prerelease and has a SHA256SUMS.asc asset. An exact pin is itself.
# The sponsord roles must pin the same line (make test-scripts checks too).
- name: Read and resolve the pinned releases
id: pins
env:
Expand All @@ -62,18 +64,21 @@ jobs:
}
decdn="$(pin decdn_node decdn_node_version)"
sponsord="$(pin sponsord sponsord_version)"
if [ -z "$decdn" ] || [ -z "$sponsord" ]; then
onramp="$(pin sponsord_onramp sponsord_onramp_version)"
if [ -z "$decdn" ] || [ -z "$sponsord" ] || [ -z "$onramp" ]; then
echo "::error::a role has no pinned version"
exit 1
fi
if [ "$sponsord" != "$decdn" ] || [ "$onramp" != "$decdn" ]; then
echo "::error::the roles pin different lines (decdn_node $decdn, sponsord $sponsord, sponsord_onramp $onramp); one decdn/decdn release ships all of them"
exit 1
fi
decdn_tag="$(resolve decdn/decdn "$decdn")"
sponsord_tag="$(resolve decdn/sponsord "$sponsord")"
if [ -z "$decdn_tag" ] || [ -z "$sponsord_tag" ]; then
echo "::error::no finished release in decdn/decdn $decdn or decdn/sponsord $sponsord"
if [ -z "$decdn_tag" ]; then
echo "::error::no finished release in decdn/decdn $decdn"
exit 1
fi
{ echo "decdn=$decdn"; echo "sponsord=$sponsord"
echo "decdn_tag=$decdn_tag"; echo "sponsord_tag=$sponsord_tag"; } >> "$GITHUB_OUTPUT"
{ echo "decdn=$decdn"; echo "decdn_tag=$decdn_tag"; } >> "$GITHUB_OUTPUT"
# Public repo, the resolved tag. The sync script reads HEAD through git.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -90,9 +95,7 @@ jobs:
- name: Compare the mirrors and the pins with upstream
env:
DECDN_PIN: ${{ steps.pins.outputs.decdn }}
SPONSORD_PIN: ${{ steps.pins.outputs.sponsord }}
DECDN_TAG: ${{ steps.pins.outputs.decdn_tag }}
SPONSORD_TAG: ${{ steps.pins.outputs.sponsord_tag }}
run: |
rc=0
check() {
Expand Down Expand Up @@ -165,16 +168,15 @@ jobs:
echo "$repo $tag: $var should be $got, compose/.env.example has $want"
return 1
}
echo "### Upstream drift (decdn/decdn $DECDN_PIN: $DECDN_TAG, $(git -C upstream rev-parse --short HEAD); decdn/sponsord $SPONSORD_PIN: $SPONSORD_TAG)" >> "$GITHUB_STEP_SUMMARY"
echo "### Upstream drift (decdn/decdn $DECDN_PIN: $DECDN_TAG, $(git -C upstream rev-parse --short HEAD))" >> "$GITHUB_STEP_SUMMARY"
check "network profiles" python3 scripts/sync-network-profiles.py upstream --ref HEAD --check
check "config schema keys" schema_check
check "decdn/decdn release line" release_check decdn/decdn "$DECDN_PIN"
check "decdn/sponsord release line" release_check decdn/sponsord "$SPONSORD_PIN"
check "Compose onramp decdn pin" sums_check decdn/decdn "$DECDN_TAG"
check "Compose decdn-node digest" digest_check decdn/decdn "$DECDN_TAG" DECDN_IMAGE_DIGEST \
decdn-node-image-digest.txt
check "Compose sponsord digest" digest_check decdn/sponsord "$SPONSORD_TAG" SPONSORD_IMAGE_DIGEST \
check "Compose sponsord digest" digest_check decdn/decdn "$DECDN_TAG" SPONSORD_IMAGE_DIGEST \
sponsord-image-digest.txt
check "Compose sponsord-onramp digest" digest_check decdn/sponsord "$SPONSORD_TAG" \
check "Compose sponsord-onramp digest" digest_check decdn/decdn "$DECDN_TAG" \
SPONSORD_ONRAMP_IMAGE_DIGEST sponsord-onramp-image-digest.txt
exit "$rc"
80 changes: 41 additions & 39 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ scripts/ # upstream-mirror generators, the compose Alloy config g
and `ansible/molecule/schema/files/schema-keys.txt` (`gen-schema-keys.py`), both
generated from the tag of the newest decdn/decdn release in the line
`decdn_node_version` pins. The weekly `upstream-drift` workflow flags staleness
against that tag, and a new upstream release line of either repo. These are the only protocol facts (contract addresses) the
against that tag, and a new upstream release line. These are the only protocol facts (contract addresses) the
repo carries, and `networks.yml` carries its upstream commit.

**Generated from this repo — regenerate, never hand-edit:** `compose/alloy/config.alloy`
Expand All @@ -141,37 +141,37 @@ runtime (`scripts/render-compose-alloy.sh`, which runs the `compose`-tagged task
test-scripts` fails while the committed file differs from a fresh render, and `make
lint-alloy` validates and runs it.

**Pinned upstream releases.** One decdn/decdn release line (`decdn_node_version`, today
`0.2`) and one decdn/sponsord release line (`sponsord_version` = `sponsord_onramp_version`,
today `0.2`; decdn/sponsord tags its whole workspace `vX.Y.Z`, there are no per-crate
tags) are pinned across every path. A line, `X.Y`, is what the role defaults, the
onramp's installer pin (`sponsord_onramp_decdn_release`, `vX.Y`) and the chart's
`appVersion` name: the roles install its newest finished `vX.Y.N` on every run
(`tasks/resolve-release.yml`, copied into all three roles: GitHub's release list, then
the same signed `SHA256SUMS` and exact `--version` check against the resolved version;
the onramp reads the installers' digest from that release's verified `SHA256SUMS`),
and the chart's default image tag is the line's floating tag, pulled `Always`. An
exact `X.Y.Z` in inventory (or `image.tag`/`image.digest`) holds a host on one release.
So an upstream patch release needs no release of the collections or the chart; a new
line does. What cannot resolve a line names one release of it and moves on every
upstream release, repo-only: `compose/.env.example`'s image digests, the onramp's
installer pin in `compose/sponsord-onramp.env.example` (tag + `SHA256SUMS` digest), the
chart's example digest, the commented exact-pin examples, and the generated mirrors
(which a patch is not expected to change; when one does, the bump PR says so and the
collections need a release). cloud-init takes the role defaults at `DEVOPS_REF`. Docs
name the pin (`decdn_node_version`, `sponsord_version`), never its number: besides this
paragraph, only the commented examples and the comments beside a pinned value name a
number, and they move with the pin (`bump_decdn.py` moves decdn's; the sponsord
checklist lists sponsord's). `make test-scripts` checks the pins agree with each other,
the Compose example (a release of the line), `appVersion`, the chart's
`artifacthub.io/images` tag and this paragraph, and that the vendored KEYS hold the
keys SECURITY.md publishes; the weekly `upstream-drift` job checks the exact copies and
the mirrors against each line's newest release and flags a newer upstream line. Bump
with the checklists in `roles/decdn_node/README.md` and `roles/sponsord/README.md`;
`scripts/open-decdn-bump.sh` opens the decdn bump's PR (decdn's `publish-crates.sh`
runs it after each release). Both upstream repos are public and also publish their
binaries as crates (`decdn-node`, `decdn-cli`, `sponsord`, `sponsord-onramp`); the
roles do not install from crates.io (no maintainer signature).
**Pinned upstream releases.** One decdn/decdn release line is pinned across every
path, by three role defaults that must agree (`decdn_node_version` = `sponsord_version`
= `sponsord_onramp_version`, today `0.2`): one decdn/decdn release (`vX.Y.Z`, the whole
workspace, no per-crate tags) ships `decdn-node`, `decdn`, `sponsord` and
`sponsord-onramp`, their four images and one `SHA256SUMS`. A line, `X.Y`, is what the
role defaults, the onramp's installer pin (`sponsord_onramp_decdn_release`, `vX.Y`) and
the chart's `appVersion` name: the roles install its newest finished `vX.Y.N` on every
run (`tasks/resolve-release.yml`, copied into all three roles: GitHub's release list,
then the same signed `SHA256SUMS` and exact `--version` check against the resolved
version; the onramp reads the installers' digest from that release's verified
`SHA256SUMS`), and the chart's default image tag is the line's floating tag, pulled
`Always`. An exact `X.Y.Z` in inventory (or `image.tag`/`image.digest`) holds a host on
one release. So an upstream patch release needs no release of the collections or the
chart; a new line does. What cannot resolve a line names one release of it and moves
on every upstream release, repo-only: `compose/.env.example`'s three image digests, the
onramp's installer pin in `compose/sponsord-onramp.env.example` (tag + `SHA256SUMS`
digest), the chart's example digest, the commented exact-pin examples, and the
generated mirrors (which a patch is not expected to change; when one does, the bump PR
says so and the collections need a release). cloud-init takes the role defaults at
`DEVOPS_REF`. Docs name the pin (`decdn_node_version`, `sponsord_version`), never its
number: besides this paragraph, only the commented examples and the comments beside a
pinned value name a number, and they move with the pin (`bump_decdn.py` moves all of
them). `make test-scripts` checks the pins agree with each other, the Compose example (a
release of the line), `appVersion`, the chart's `artifacthub.io/images` tag and this
paragraph, and that the vendored KEYS (three byte copies of decdn's) hold the keys
SECURITY.md publishes; the weekly `upstream-drift` job checks the exact copies and the
mirrors against the line's newest release and flags a newer upstream line. Bump with
the checklist in `roles/decdn_node/README.md`; `scripts/open-decdn-bump.sh` opens the
bump's PR (decdn's `publish-crates.sh` runs it after each release). Upstream is public
and also publishes the binaries as crates (`decdn-node`, `decdn-cli`, `sponsord`,
`sponsord-onramp`); the roles do not install from crates.io (no maintainer signature).

**Monitoring assets are maintained here, by hand.** `monitoring/` holds the deCDN
Grafana dashboards and Prometheus alert rules for every deploy path; upstream ships none.
Expand All @@ -183,7 +183,7 @@ node's `/metrics` and upstream's `adr/appendix-observability.md` registry (a `pl
row emits nothing). Nothing in CI checks the names, so a typo renders `(no data)` or a
rule that never fires. See `.claude/skills/grafana-dashboards/SKILL.md`. sponsord's pair
lives in `monitoring/sponsord/`, which the chart does not render; its `sponsord_*`
names must be ones upstream `decdn/sponsord` `crates/sponsord/src/metrics.rs` exports,
names must be ones upstream decdn/decdn's `crates/sponsord/src/metrics.rs` exports,
and its rules have promtool unit tests (`monitoring/sponsord/prometheus-alerts_test.yml`,
run by `make lint-helm`), as the node's `DecdnNodeDown` does
(`monitoring/decdn-node/prometheus-alerts_test.yml`, which the chart's `.helmignore`
Expand Down Expand Up @@ -308,13 +308,14 @@ the pinned `iroh-relay` (re-capture it when bumping `iroh_relay_version`).
Alloy (every component healthy, the targets per profile), since `alloy validate`
neither parses OTTL nor evaluates `sys.env`.

- **`ansible/roles/sponsord`** — the deCDN onboarding sponsor (`decdn/sponsord`): the
- **`ansible/roles/sponsord`** — the deCDN onboarding sponsor (decdn/decdn's `sponsord` crate): the
treasury wallet's signer for capped capabilities plus the PaymentPool keeper. It is
independent of the node, so `playbooks/sponsord.yml` (`make deploy-sponsord`, also
imported by `publisher.yml`) targets its own `sponsord_hosts` group, standalone or
co-located with a node.
- **Install:** a GPG-verified `v*` decdn/sponsord release by default
(`sponsord_version`: the newest of its line, or an exact release), a host-side
- **Install:** a GPG-verified `v*` decdn/decdn release by default
(`sponsord_version`: the newest of its line, the node's line, or an exact
release; the same release ships both daemons and the node), a host-side
`source` build, or a manual binary.
The `--version` backstop checks the version in `release` mode only (the version
has a default, so other modes would fail against it), as in the other two roles.
Expand Down Expand Up @@ -362,8 +363,9 @@ the pinned `iroh-relay` (re-capture it when bumping `iroh_relay_version`).
`make test-scripts`); the role checks no group name, so collection users keep their
own groups, and its token gate and `/healthz` fail without a daemon.
- **Install / unit / gate:** the sponsord role's patterns, copied: a
GPG-verified `v*` decdn/sponsord release (default), `source` or manual (the
KEYS and the source installer are the sponsord role's files, via `role_path`), `DynamicUser` with the token and the Turnstile secret (inventory
GPG-verified `v*` decdn/decdn release (default), `source` or manual (the
source installer is the sponsord role's file, via `role_path`; the KEYS its own
copy of decdn's), `DynamicUser` with the token and the Turnstile secret (inventory
or operator, with `secret.env`'s provenance record and guards) as
`LoadCredential=`, `PartOf=sponsord.service`, a restart-inputs
record, and a fatal `/healthz` gate on a loopback listener.
Expand Down Expand Up @@ -656,7 +658,7 @@ the pinned `iroh-relay` (re-capture it when bumping `iroh_relay_version`).
- **The allow-lists are deliberate friction.** A new key, mount or inline env var
in `compose.yaml` fails the lint until it is added to the matching list in
`compose/tests/*.jq`, and the PR should say why. Env var names follow upstream
`decdn/sponsord` (`crates/*/src/config.rs`); re-check them when bumping images.
decdn/decdn (`crates/sponsord*/src/config.rs`); re-check them when bumping images.
- **CI's Compose is older than a dev box's.** The runner's release mishandled
`config --no-env-resolution` (v2.33 lacks it), which is why the inline-env render
uses `/dev/null` env files instead. Before relying on a newer `config` flag, try
Expand Down
8 changes: 4 additions & 4 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ 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 keys vendored at
`ansible/roles/decdn_node/files/decdn-release-KEYS.asc`. The `sponsord` and
`sponsord_onramp` roles do the same with
`ansible/roles/sponsord/files/sponsord-release-KEYS.asc`. Both files are copies of
upstream's `KEYS` (decdn/decdn and decdn/sponsord), which hold the same maintainer
keys, and a good signature from any of them is accepted:
`sponsord_onramp` roles do the same with their own `files/decdn-release-KEYS.asc`
(one decdn/decdn release ships all four binaries). The three files are byte copies of
upstream's `KEYS` (`make test-scripts` checks), which holds the maintainer keys, and a
good signature from any of them is accepted:

```
Ant Somers <ant@decdn.org>
Expand Down
4 changes: 2 additions & 2 deletions ansible/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ ignored on the first converge.
`decdn_node_version` defaults to the decdn/decdn release line this repo pins (the
role's `defaults/main.yml`, `"X.Y"`): each run installs the line's newest
finished release, so a patch release arrives with the next deploy. Set an exact
`"X.Y.Z"` to hold a host on one release. The sponsord roles pin a decdn/sponsord
line the same way (`sponsord_version`).
`"X.Y.Z"` to hold a host on one release. The sponsord roles pin the same
decdn/decdn line the same way (`sponsord_version`, `sponsord_onramp_version`).
- **`source`** clones `decdn_node_source_repo` (default `decdn/decdn` on GitHub) at
`decdn_node_source_ref` (any tag, branch or SHA) **on the node** and builds it there
as an unprivileged `decdn-build` user, in a fresh environment per commit, with a
Expand Down
Loading
Loading