Skip to content

feat(decdn_node): opt-in host-side keystore generation + fix provisioning docs - #21

Merged
thiras merged 2 commits into
mainfrom
feat/decdn-node-optin-keystore-gen
Jul 11, 2026
Merged

thiras merged 2 commits into
mainfrom
feat/decdn-node-optin-keystore-gen

Conversation

@thiras

@thiras thiras commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds an opt-in, never-overwrite decdn_node_generate_keystore toggle to the decdn_node role and fixes the keystore-provisioning guidance so the documented manual steps actually work on a fresh host.

Feature (decdn_node_generate_keystore, default false)

  • When true, mints a fresh eth wallet on the host via decdn key-gen — only if the keystore is absent. Never overwrites an existing (possibly funded) wallet.
  • Generates a random 0600 password file first, but only when the keystore is also absent. A password beside a missing keystore is treated as operator-provided and reused; a missing password beside a present keystore is not auto-minted (it couldn't decrypt the wallet) — the gate fails loud so the operator supplies the matching one.
  • Default keeps the operator-provisioned posture. Funding + on-chain staking/registration (ADR 019 Phase 2) stay manual regardless.

Gate hardening

  • The pre-start gate now requires node.secret too (not just keystore + password) and locks it to 0600. This closes a partial-key-gen mask: if key-gen writes keystore.json but dies before node.secret, the creates: sentinel would skip the rerun — but the gate now fails loud on the missing identity.

Docs fix (works on a fresh host)

  • The assert fail_msg and both READMEs now: create the password file FIRST (key-gen reads it, never creates it), pass --output-dir explicitly (a bare decdn key-gen writes to ~/.decdn, which the node won't read), and include an ls -l verify step. Path/behavior facts verified against decdn/decdn source.

Tests

  • Shared molecule stub learns a key-gen subcommand.
  • New generate-keystore scenario (no staged keystore, generate=true) proves the role mints keystore + password + node.secret at 0600 from nothing and is idempotent.
  • CI + Makefile switch to molecule test --all (runs both scenarios); default scenario stages node.secret to match the widened gate. Note: this roughly doubles molecule CI wall-time (two containers).

Verification

  • make lint-ansible (production profile) + make lint (yamllint / markdownlint / shellcheck) — clean.
  • make molecule (--all) — both scenarios green, idempotence changed=0.
  • fail_msg render checked via a failing assert (vars substituted, command block copy-pasteable).

Follow-up (separate repo, not in this PR)

Cleanest general fix is a decdn/decdn CLI change: have key-gen --password-file <PATH> generate+write the password when the file is absent, collapsing provisioning to one self-contained command. Flagged only.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an opt-in setting to generate node wallet, keystore, password, and identity files automatically during deployment.
    • Existing wallet material is preserved, while generated secrets receive restrictive permissions.
    • Added coverage for generated-wallet deployments, including service startup and artifact validation.
  • Documentation

    • Expanded setup guidance for password files, key generation, output locations, and manual funding or staking requirements.
    • Documented the new configuration option and its default behavior.

…isioning docs

Add `decdn_node_generate_keystore` (default false): when true the role mints a
fresh eth wallet on the host via `decdn key-gen`, but ONLY when the keystore is
absent — it never overwrites an existing (possibly funded) wallet, and it refuses
to mint a password beside a pre-existing keystore it couldn't decrypt (the gate
fails loud instead so the operator supplies the matching one). The default keeps
the operator-provisioned posture; funding + on-chain staking (ADR 019 Phase 2)
stay manual.

Harden the pre-start gate: it now requires `node.secret` too (not just keystore +
password) and locks it to 0600, so a partial key-gen (keystore written, identity
not) can't be masked by the `creates:` sentinel on rerun.

Fix the keystore-provisioning guidance so the documented steps work on a fresh
host: the assert `fail_msg` and both READMEs now create the password file FIRST
(`key-gen` reads it, never creates it) and pass `--output-dir` explicitly (a bare
`key-gen` writes to ~/.decdn, which the node won't read), with an `ls -l` verify.

Test: shared molecule stub learns a `key-gen` subcommand; a new `generate-keystore`
scenario (no staged keystore, generate=true) proves the role mints keystore +
password + node.secret at 0600 from nothing and is idempotent (changed=0). CI +
Makefile run `molecule test --all`; default scenario stages node.secret to match
the widened gate.

Verified: ansible-lint (production profile) + yamllint + markdownlint clean;
`molecule --all` green with both scenarios idempotent; fail_msg render checked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 11, 2026 19:16
@coderabbitai

coderabbitai Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@thiras, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2b8a6eb1-9050-4094-b917-2dc2f13d50bb

📥 Commits

Reviewing files that changed from the base of the PR and between 6755e42 and a91348b.

📒 Files selected for processing (3)
  • ansible/molecule/generate-keystore/verify.yml
  • ansible/roles/decdn_node/README.md
  • ansible/roles/decdn_node/tasks/main.yml
📝 Walkthrough

Walkthrough

The Ansible role now supports opt-in host-side keystore generation, preserves existing wallet material, requires and hardens node secrets, adds a generation-focused Molecule scenario, extends the stub CLI, updates operator documentation, and runs all Molecule scenarios in CI.

Changes

Keystore generation flow

Layer / File(s) Summary
Generation contract and operator guidance
ansible/roles/decdn_node/defaults/main.yml, ansible/roles/decdn_node/README.md, ansible/README.md, ansible/galaxy/CHANGELOG.md
Documents and defines decdn_node_generate_keystore, defaulting to false, with conditional generation, non-overwrite behavior, strict password-file handling, and manual funding or staking.
Role generation and readiness
ansible/roles/decdn_node/tasks/main.yml
Validates the boolean setting, conditionally runs decdn key-gen, creates missing password material, requires node.secret alongside the keystore and password, and hardens all three files to mode 0600.
Molecule generation scenarios
ansible/molecule/default/files/decdn-node-stub, ansible/molecule/default/prepare.yml, ansible/molecule/default/verify.yml, ansible/molecule/generate-keystore/*
Adds stubbed key-gen behavior, a no-prepare generation scenario with idempotence checks, and verification of generated files, ownership, permissions, and running service state.
All-scenario CI execution
.github/workflows/molecule.yml, ansible/Makefile
Changes Molecule execution to molecule test --all for both configured scenarios.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Molecule
  participant decdn_node_role
  participant decdn_node_stub
  participant Verify
  Molecule->>decdn_node_role: Converge with generation enabled
  decdn_node_role->>decdn_node_stub: Invoke key-gen
  decdn_node_stub->>decdn_node_role: Create keystore.json and node.secret
  Molecule->>decdn_node_role: Run idempotence converge
  Molecule->>Verify: Validate secrets, service, ownership, and permissions
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the primary change: opt-in host-side keystore generation, with docs updates as a secondary detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/decdn-node-optin-keystore-gen

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an opt-in turnkey wallet generation feature (decdn_node_generate_keystore) to the decdn_node Ansible role, allowing automatic generation of the keystore, password, and node identity (node.secret) when they are absent. It also adds a new Molecule test scenario (generate-keystore) to verify this path. The review feedback suggests two key improvements: using Ansible's built-in password lookup instead of an external openssl command for generating the password, and expanding the Molecule verification assertions to ensure the newly introduced node.secret file is also verified for correct ownership.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread ansible/roles/decdn_node/tasks/main.yml
Comment thread ansible/molecule/generate-keystore/verify.yml Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in path for host-side eth wallet/identity generation in the decdn_node Ansible role, hardens the pre-start gate to also require node.secret, and updates docs + Molecule coverage to match the revised provisioning flow.

Changes:

  • Add decdn_node_generate_keystore (default false) to optionally mint keystore + password + node.secret on first converge (never overwriting existing material).
  • Harden the pre-start gate to require node.secret and lock all wallet artifacts to 0600.
  • Add a new Molecule scenario for the generation path and run Molecule with --all in CI/Makefile.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ansible/roles/decdn_node/tasks/main.yml Implements the opt-in generation flow and expands the pre-start gate to include node.secret.
ansible/roles/decdn_node/README.md Updates operator provisioning steps and documents the new decdn_node_generate_keystore toggle.
ansible/roles/decdn_node/defaults/main.yml Defines decdn_node_generate_keystore: false with detailed guidance comments.
ansible/README.md Updates top-level deployment prerequisites to reference the corrected keystore provisioning flow / toggle.
ansible/molecule/generate-keystore/verify.yml Verifies generation scenario created keystore/password/node.secret with correct modes and service is running.
ansible/molecule/generate-keystore/molecule.yml Adds a Molecule scenario specifically for decdn_node_generate_keystore: true without a prepare step.
ansible/molecule/generate-keystore/converge.yml Converges the role with decdn_node_generate_keystore: true against the stub binary.
ansible/molecule/default/verify.yml Extends default scenario verification to include node.secret permission lockdown.
ansible/molecule/default/prepare.yml Stages a placeholder node.secret so the widened gate can pass in the default scenario.
ansible/molecule/default/files/decdn-node-stub Extends the stub to implement key-gen to support the new scenario.
ansible/Makefile Runs Molecule with molecule test --all to cover both scenarios locally/CI.
ansible/galaxy/CHANGELOG.md Notes the new decdn_node_generate_keystore option in the collection changelog.
.github/workflows/molecule.yml Updates CI to execute molecule test --all (both scenarios).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ansible/roles/decdn_node/tasks/main.yml
Comment thread ansible/roles/decdn_node/tasks/main.yml
Comment thread ansible/roles/decdn_node/tasks/main.yml Outdated
Comment thread ansible/roles/decdn_node/README.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
ansible/molecule/generate-keystore/verify.yml (1)

47-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add ownership check for node.secret.

The ownership assertion checks results[0] (keystore.json) and results[1] (keystore.password) but omits results[2] (node.secret). The role's lock-down task sets owner: "{{ decdn_user }}" for all three files in a loop, so node.secret ownership should be verified too.

♻️ Proposed fix
     - name: Assert the keystore + password are owned by the decdn user
       ansible.builtin.assert:
         that:
           - decdn_wallet.results[0].stat.pw_name == 'decdn'
           - decdn_wallet.results[1].stat.pw_name == 'decdn'
+          - decdn_wallet.results[2].stat.pw_name == 'decdn'
-        fail_msg: "generated wallet material is not owned by the decdn user"
+        fail_msg: "generated wallet material (keystore.json, keystore.password, or node.secret) is not owned by the decdn user"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ansible/molecule/generate-keystore/verify.yml` around lines 47 - 52, Extend
the ownership assertion in the keystore verification task to also validate
decdn_wallet.results[2].stat.pw_name equals 'decdn', covering node.secret
alongside keystore.json and keystore.password.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@ansible/molecule/generate-keystore/verify.yml`:
- Around line 47-52: Extend the ownership assertion in the keystore verification
task to also validate decdn_wallet.results[2].stat.pw_name equals 'decdn',
covering node.secret alongside keystore.json and keystore.password.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 177dedce-c4c9-4eeb-9436-abc100aea45d

📥 Commits

Reviewing files that changed from the base of the PR and between 9910ee4 and 6755e42.

📒 Files selected for processing (13)
  • .github/workflows/molecule.yml
  • ansible/Makefile
  • ansible/README.md
  • ansible/galaxy/CHANGELOG.md
  • ansible/molecule/default/files/decdn-node-stub
  • ansible/molecule/default/prepare.yml
  • ansible/molecule/default/verify.yml
  • ansible/molecule/generate-keystore/converge.yml
  • ansible/molecule/generate-keystore/molecule.yml
  • ansible/molecule/generate-keystore/verify.yml
  • ansible/roles/decdn_node/README.md
  • ansible/roles/decdn_node/defaults/main.yml
  • ansible/roles/decdn_node/tasks/main.yml

…nership, docs

- Skip host-side wallet generation (openssl/copy/key-gen) under `--check` via
  `not ansible_check_mode`, so `make check` is non-mutating and deterministic and
  no longer produces a misleading empty-content password diff (Copilot).
- Assert node.secret ownership (decdn) in the generate-keystore verify, alongside
  keystore + password (Gemini).
- README: the pre-start gate paragraph now names node.secret as a hard requirement
  (Copilot).

Not adopted: openssl → lookup('ansible.builtin.password') (Gemini) — the lookup
runs on the control node, but AGENTS.md rule #1 requires secrets generated on the
target host; on-host `openssl rand` is the intended posture.

Verified: ansible-lint (production) + yamllint + markdownlint clean; molecule --all
green with both scenarios idempotent (changed=0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thiras
thiras merged commit fb5635f into main Jul 11, 2026
9 checks passed
@thiras
thiras deleted the feat/decdn-node-optin-keystore-gen branch July 11, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants