Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b67c1ab
chore: drop the shared Claude plugin setting and the stale Solidity s…
thiras Sep 23, 2026
2ec2471
refactor(ansible): make the fleet template generic, move launch conte…
thiras Sep 23, 2026
bf825fa
feat(ansible): derive the release target from the host, add network p…
thiras Sep 23, 2026
19bd693
test(ansible): converge on Debian 13 and Ubuntu 24.04/26.04, claim on…
thiras Sep 23, 2026
90b3db3
feat(ansible): encrypted backups and a guarded decommission
thiras Sep 23, 2026
cb99fac
feat(chart): ship upstream's dashboards and alert rules
thiras Sep 23, 2026
23d4fe4
feat(compose): a Docker Compose deploy path for single hosts
thiras Sep 23, 2026
7fb79b2
ci: run pre-commit in CI, watch upstream drift, add issue templates
thiras Sep 23, 2026
731f1ad
ci: tag-driven release of the collection and chart, publish behind a …
thiras Sep 23, 2026
bb74005
docs: one home per topic, a requirements guide, and no stale claims
thiras Sep 23, 2026
51f8502
ci(molecule): note the suite's new runtime
thiras Sep 23, 2026
75b23a4
fix: address review — byte-check vendored monitoring files, precise s…
thiras Sep 23, 2026
8dff785
fix(ansible): make backup and decommission safe on the edges review f…
thiras Sep 24, 2026
983a08f
fix(chart): unique dashboard names, fail on missing dashboards
thiras Sep 24, 2026
a9774df
fix(ci): release signing, fail-loud mirrors, structural image digest,…
thiras Sep 24, 2026
72bc1c3
docs: correct the on-chain CLI guidance and the remaining review find…
thiras Sep 24, 2026
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
5 changes: 0 additions & 5 deletions .claude/settings.json

This file was deleted.

68 changes: 68 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: Bug report
description: A deploy path (Ansible, Helm, Compose) does something wrong.
labels: [bug]
body:
- type: markdown
attributes:
value: >-
Thanks for reporting. **Redact before you paste**: RPC URLs (they often embed an
API key), Grafana Cloud tokens, keystore passwords and real host addresses must
not appear in a public issue.
- type: dropdown
id: path
attributes:
label: Deploy path
options:
- Ansible (this repo's playbooks)
- Ansible (decdn.node Galaxy collection)
- Helm chart
- Docker Compose
validations:
required: true
- type: dropdown
id: install
attributes:
label: Install method
description: Ansible only.
options:
- release (signed tarball)
- manual (locally built binaries)
- not applicable
validations:
required: true
- type: input
id: version
attributes:
label: decdn version
description: "`decdn-node --version`, or the image digest."
validations:
required: true
- type: input
id: revision
attributes:
label: devops revision
description: Commit, tag or collection/chart version of this repo.
validations:
required: true
- type: input
id: platform
attributes:
label: Target OS and architecture
placeholder: Ubuntu 26.04, aarch64
validations:
required: true
- type: textarea
id: what
attributes:
label: What happened, and what did you expect?
validations:
required: true
- type: textarea
id: repro
attributes:
label: How to reproduce
description: Command run, relevant (redacted) inventory or values, and the failing task or error.
render: shell
validations:
required: true
9 changes: 9 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/decdn/devops/blob/main/SECURITY.md
about: Report privately by email (see SECURITY.md), never in a public issue. That includes a secret or real host address you find committed here.
- name: The node daemon or the protocol
url: https://github.com/decdn/decdn/issues
about: Bugs in decdn-node itself, the CLI or the contracts belong in decdn/decdn.
26 changes: 26 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
name: Feature request
description: Something operators need from the deploy tooling.
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: What are you trying to do?
description: The operator task, and where the current tooling gets in the way.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: What would help?
- type: dropdown
id: path
attributes:
label: Deploy path
multiple: true
options:
- Ansible
- Helm chart
- Docker Compose
- All / not specific
36 changes: 28 additions & 8 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
---
# Dependabot — keeps the SHA-pinned GitHub Actions current (it rewrites
# `uses: owner/repo@<sha> # vX.Y.Z` to the new sha + version comment).
# Dependabot — keeps the SHA-pinned GitHub Actions and the pre-commit hook revs
# current.
#
# github-actions is the only applicable ecosystem: the repo has no pip/npm/etc.
# manifests, and `ansible/requirements.yml` is Ansible Galaxy, which Dependabot
# does not support.
# github-actions rewrites `uses: owner/repo@<sha> # vX.Y.Z` to the new sha + comment
# pre-commit bumps the `rev:` of each repo in .pre-commit-config.yaml
#
# NOT covered here, bump manually:
# - the KICS engine image digest in the Makefile — see CONTRIBUTING.md
# Considered and not applicable here, so not listed:
# docker-compose compose/compose.yaml builds its image from ${DECDN_IMAGE_REPO} and
# ${DECDN_IMAGE_DIGEST}, which the operator pins to a signed
# release digest; nothing to bump
# helm the chart has no dependencies, and its image is operator-set
# pip the repo has no pip manifest (CI installs are unpinned)
# Ansible Galaxy (ansible/requirements.yml) is not a Dependabot ecosystem.
#
# NOT covered, bump manually (CONTRIBUTING.md § Supply-chain / pinning rules):
# - the KICS and kubeconform image digests in the Makefile
# - the molecule image digests in ansible/molecule/*/molecule.yml
# - the four setup-helm `version:` inputs (two in ci.yml, two in release.yml)
# - the Galaxy collections in ansible/requirements.yml
# - pre-commit hook revs — run `pre-commit autoupdate`
# - the local yamllint hook's `additional_dependencies` pin
version: 2
updates:
- package-ecosystem: github-actions
Expand All @@ -23,3 +32,14 @@ updates:
groups:
actions:
patterns: ["*"]
- package-ecosystem: pre-commit
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
commit-message:
prefix: ci
include: scope
groups:
pre-commit-hooks:
patterns: ["*"]
86 changes: 75 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ concurrency:
# malicious code — cf. the March 2026 KICS action compromise). The trailing
# comment records the human-readable version; .github/dependabot.yml bumps them.
jobs:
# Detect whether ansible/ or the Helm chart changed so heavy jobs skip unrelated PRs.
# Detect whether ansible/, the Helm chart or compose/ changed so heavy jobs skip
# unrelated PRs.
changes:
runs-on: ubuntu-latest
timeout-minutes: 5 # ~5s in practice
Expand All @@ -28,6 +29,7 @@ jobs:
outputs:
ansible: ${{ steps.filter.outputs.ansible }}
helm: ${{ steps.filter.outputs.helm }}
compose: ${{ steps.filter.outputs.compose }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
Expand All @@ -45,6 +47,11 @@ jobs:
- 'ansible/molecule/schema/files/**'
- 'Makefile'
- '.github/workflows/ci.yml'
# lint-compose and security-compose live in the root Makefile.
compose:
- 'compose/**'
- 'Makefile'
- '.github/workflows/ci.yml'

# Ansible style + best-practice + the production-profile SECURITY rules,
# plus a syntax-check of every playbook. Runs only when ansible/ changed.
Expand Down Expand Up @@ -108,10 +115,14 @@ jobs:
key: galaxy-${{ runner.os }}-${{ hashFiles('ansible/requirements.yml') }}
- name: Lint (yamllint + ansible-lint)
run: make lint
- name: Syntax-check playbook
- name: Syntax-check playbooks
# Dummy inventory: no real hosts.yml is committed upstream (only hosts.yml.example),
# and --syntax-check only parses, it never connects.
run: ansible-playbook playbooks/site.yml --syntax-check -i localhost,
# and --syntax-check only parses, it never connects. Every playbook, so a new
# one (backup, decommission, ...) is covered without editing this step.
run: |
for pb in playbooks/*.yml; do
ansible-playbook "$pb" --syntax-check -i localhost,
done

# Build the public `decdn.node` collection and run galaxy-importer's checks —
# the same validation Galaxy runs on upload (metadata, license, README, embedded
Expand Down Expand Up @@ -175,7 +186,8 @@ jobs:
timeout-minutes: 20 # pulls the kubeconform image; ~1m in practice
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Helm version is pinned here AND in the kics job below; bump both.
# Helm version is pinned here, in the kics job below and twice in release.yml;
# bump all four.
- uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
with:
version: v4.3.0
Expand All @@ -188,6 +200,20 @@ jobs:
# DECDN_CLI=... before bumping the decdn version.
run: make lint-helm

# Docker Compose path: render compose/compose.yaml with its example env files and
# assert the invariants its README promises (host network, no published ports,
# digest-pinned image, read-only rootfs, no capabilities, graceful stop). Runs
# only when compose/ or the root Makefile changed.
compose:
needs: changes
if: needs.changes.outputs.compose == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5 # docker compose + jq ship on ubuntu-latest; seconds in practice
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Compose invariants
run: make lint-compose

# Render roles/grafana_alloy's templates and validate them with the REAL pinned
# Grafana Alloy binary. The molecule `grafana-cloud` scenario deliberately runs
# against a stub that exits 0 for every subcommand, so it proves plumbing but
Expand Down Expand Up @@ -218,8 +244,9 @@ jobs:
- name: Validate the rendered Alloy configuration
run: make lint-alloy

# Dedicated IaC security scan of the Ansible tree and the rendered Helm chart, driven straight from the
# digest-pinned KICS *engine* image by `make security` — the exact command
# Dedicated IaC security scan of the Ansible tree, the rendered Helm chart and
# compose/, driven straight from the digest-pinned KICS *engine* image by
# `make security` — the exact command
# developers run locally, so CI and local results cannot drift. KICS severities
# are CRITICAL/HIGH/MEDIUM/LOW/INFO; the engine's own `--fail-on high` exit
# code is the gate.
Expand All @@ -237,23 +264,25 @@ jobs:
# hijacked action). See CONTRIBUTING.md.
kics:
needs: changes
if: needs.changes.outputs.ansible == 'true' || needs.changes.outputs.helm == 'true'
if: >-
needs.changes.outputs.ansible == 'true' || needs.changes.outputs.helm == 'true'
|| needs.changes.outputs.compose == 'true'
runs-on: ubuntu-latest
timeout-minutes: 20 # pulls the KICS engine image; ~30s in practice
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
with:
version: v4.3.0
- name: KICS security scan of ansible/ + the rendered chart (fail on HIGH)
- name: KICS security scan of ansible/, the rendered chart and compose/ (fail on HIGH)
run: make security
# Replaces the action's `enable_jobs_summary`.
- name: Summarise KICS findings
if: always()
run: |
for scan in ansible helm; do
for scan in ansible helm compose; do
results=kics-results/results.json
[ "$scan" = helm ] && results=kics-results/helm/results.json
[ "$scan" != ansible ] && results="kics-results/$scan/results.json"
{
echo "### KICS IaC scan ($scan)"
echo
Expand Down Expand Up @@ -289,6 +318,41 @@ jobs:
# with:
# sarif_file: kics-results/results.sarif

# Every pre-commit hook on every file: hygiene, shellcheck, markdownlint and
# yamllint. The same gate `make lint` runs locally, so contributors who never
# installed the hook still meet it. The manual-stage ansible-lint hook is skipped
# (the ansible-lint job above covers it). Always runs: it is cheap and covers
# every path.
pre-commit:
runs-on: ubuntu-latest
timeout-minutes: 10 # hook environments are cached; ~1m cold
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
- name: Install pre-commit
run: python -m pip install --upgrade pre-commit
# Hook environments (node for markdownlint, the shellcheck image, yamllint's
# virtualenv) keyed on the config that pins them.
- uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run every hook on every file
run: pre-commit run --all-files --show-diff-on-failure

# The repo's own guard rails: ansible/Makefile's scoping guards (dry runs), the
# release gate, and lint-compose's negative cases (tests/scripts-test.sh). Always
# runs: seconds, and it covers files every path filter above would miss.
scripts:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Script and guard tests
run: make test-scripts

# Lint the workflow files themselves.
actionlint:
runs-on: ubuntu-latest
Expand Down
13 changes: 7 additions & 6 deletions .github/workflows/molecule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ concurrency:
jobs:
molecule:
runs-on: ubuntu-latest # Docker is preinstalled
# ~4m41s at JOBS=3. A bound, not a target: without one a wedged privileged
# systemd container burns the 360-minute default, and with cancel-in-progress
# above, some branch-protection setups read the resulting cancelled check as
# "not failed" rather than as a failure.
# ~4m41s at JOBS=3 with 8 scenarios. os-matrix (3 containers) and lifecycle took the
# suite to ~10m at JOBS=3 on a local 16-thread box; re-measure from CI's first run.
# A bound, not a target: without one a wedged privileged systemd container burns
# the 360-minute default, and with cancel-in-progress above, some branch-protection
# setups read the resulting cancelled check as "not failed" rather than as a failure.
timeout-minutes: 45
defaults:
run:
Expand Down Expand Up @@ -66,8 +67,8 @@ jobs:
# flaky galaxy.ansible.com, for no added coverage. The cache above wraps that
# Make-owned invocation instead.
#
# JOBS is capped at 3 rather than the default (one job per scenario, currently
# 7): every scenario is a privileged systemd container, and they share this
# JOBS is capped at 3 rather than the default (one job per scenario): every
# scenario is a privileged systemd container, and they share this
# runner's cores and cgroup hierarchy. If this job turns flaky, drop to JOBS=1
# or swap in `make molecule-serial` — the latter also serialises the output.
run: make molecule JOBS=3
Expand Down
Loading
Loading