Skip to content

feat(1463-pr-a): bootstrap-node.sh -- idempotent node bootstrap - #1488

Merged
POWERFULMOVES merged 4 commits into
mainfrom
feat/issue-1463-bootstrap
May 15, 2026
Merged

POWERFULMOVES merged 4 commits into
mainfrom
feat/issue-1463-bootstrap

Conversation

@POWERFULMOVES

@POWERFULMOVES POWERFULMOVES commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds pmoves/scripts/bootstrap-node.sh — idempotent fleet node bootstrap, safe to re-run
  • 6 sequential steps: Tailscale enrollment, profile existence check, Docker network pre-flight, NATS connectivity probe, runner label (placeholder), mesh announce
  • Exits 0 = fully enrolled, 1 = profile missing or fatal, 2 = Docker unavailable
  • Follows audit_network_reality.sh style (ANSI helpers, counters, summary block)

Step details

Step Action If unavailable
1 Tailscale enrollment check (idempotent, captures status with || true) Warns if authkey missing
2 Profile YAML existence check Exit 1 with json-to-profile.py hint
3 pmoves_bus Docker network (172.30.3.0/24) Creates if missing; exit 1 on failure
4 NATS connectivity probe (nats server ping) Warns if nats CLI absent
5 Runner label assignment Warns — manual via GH UI
6 mesh.node.announce.v1 publish Warns if nats CLI absent

Quality notes

  • || true guard on tailscale status prevents spurious re-enrollment when daemon is unhealthy
  • Exits immediately after Docker network failure — prevents false mesh announce
  • --node-id / --profile values validated as [a-zA-Z0-9_-]+ before any step runs
  • Runner step uses warn() not ok() — no misleading green checkmarks for no-ops

Test plan

  • bash pmoves/scripts/bootstrap-node.sh --help exits 0
  • No-args invocation exits 1 with usage
  • --node-id --profile foo exits 1 with "requires a value" error
  • Missing profile exits 1 with json-to-profile.py hint including --profile
  • Docker not running exits 2
  • Second run on an enrolled node skips Step 1 cleanly (idempotent)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a node bootstrap script that automates setup for nodes joining the PMOVES mesh, handling Tailscale configuration, environment validation, Docker network setup, connectivity verification, and mesh registration in a single operation.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@POWERFULMOVES has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 58 minutes and 37 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9f32448f-a628-4840-afd0-a671e12025d2

📥 Commits

Reviewing files that changed from the base of the PR and between 17173a8 and 65d22bb.

📒 Files selected for processing (1)
  • pmoves/scripts/mesh-bind.sh
📝 Walkthrough

Walkthrough

New Bash script pmoves/scripts/bootstrap-node.sh orchestrates idempotent node enrollment into the PMOVES mesh: validates CLI arguments, enrolls in Tailscale, provisions Docker network infrastructure, validates configuration profiles, probes NATS connectivity, publishes mesh announcements, and exits with status tracking.

Changes

Node Bootstrap Mesh Enrollment

Layer / File(s) Summary
CLI setup and initialization
pmoves/scripts/bootstrap-node.sh
Script enforces strict bash mode with colored logging helpers and global error/warning counters. Parses --node-id and --profile required arguments with character validation, handles --help, derives REPO_ROOT from script location, and prints startup banner with node identity and NATS URL.
Network infrastructure setup
pmoves/scripts/bootstrap-node.sh
Idempotent Tailscale enrollment checks existing hostname in tailscale status and conditionally runs tailscale up with TAILSCALE_AUTHKEY. Profile file validation checks for pmoves/config/profiles/<profile>.yaml and exits with generation command if missing. Docker pre-flight verifies daemon availability and creates pmoves_bus bridge network at 172.30.3.0/24 if absent.
NATS connectivity and mesh registration
pmoves/scripts/bootstrap-node.sh
Optional NATS connectivity probe using nats server ping (non-fatal warning on failure). Prints runner registration instructions when RUNNER_TOKEN is set. Constructs JSON node announcement payload and publishes to mesh.node.announce.v1 topic via nats pub with warning on publish failure.
Completion and exit handling
pmoves/scripts/bootstrap-node.sh
Final summary output driven by collected error and warning counters. Exits with code 1 if errors occurred, 0 on success, or 2 if Docker unavailable.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • POWERFULMOVES/PMOVES.AI#1466: Introduces Docker network hardening and alias-enforcing tooling that standardizes how pmoves_* networks (notably pmoves_bus used by this bootstrap script) are created, configured, and attached to services.

Poem

🐰 A rabbit hops to prep the mesh,
With Tailscale, Docker, all things fresh,
The NATS bus hums, announcements fly,
Bootstrap magic—no node left shy! 🚀

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes a comprehensive summary and detailed step breakdown, but is missing the required Testing section with commands/output and the Required Checks section from the template. Add a Testing section documenting test commands and results, and complete the Required Checks section including CHIT Contract Check and documentation updates.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding an idempotent bootstrap script. It is concise, specific, and directly reflects the primary addition in the changeset.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-1463-bootstrap

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 and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17173a8758

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +68 to +72
[[ "${2:-}" == --* ]] && { echo "ERROR: --node-id requires a value" >&2; exit 1; }
NODE_ID="${2:-}"; shift 2 ;;
--profile)
[[ "${2:-}" == --* ]] && { echo "ERROR: --profile requires a value" >&2; exit 1; }
PROFILE="${2:-}"; shift 2 ;;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate missing option values before shifting args

The parser only rejects values that start with --, so a trailing flag like --node-id or --profile with no following token falls through to shift 2 and exits immediately under set -e without the intended requires a value message. This makes common invocation mistakes hard to diagnose and bypasses the script’s own input-validation UX.

Useful? React with 👍 / 👎.

else
echo -e " ${RED}✗${RST} ERROR: Profile not found: pmoves/config/profiles/${PROFILE}.yaml" >&2
echo "" >&2
echo " Run: python deploy/provision/json-to-profile.py --node-id $NODE_ID --profile $PROFILE to generate it" >&2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fix missing-profile remediation command path

The error hint tells operators to run deploy/provision/json-to-profile.py, but that file path does not exist in this repository, so the recommended recovery step fails with a second error when a profile is missing. This leaves the bootstrap flow without a valid remediation path at exactly the failure point where guidance is needed.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
pmoves/scripts/bootstrap-node.sh (1)

121-123: 💤 Low value

Consider stricter hostname matching to avoid false positives.

grep -qF "$NODE_ID" matches substrings, so if NODE_ID is foo and the status output contains a host foobar, it would incorrectly report foo as already enrolled. Using word-boundary matching would be more precise.

Proposed fix using word boundaries
   ts_status=$(tailscale status 2>/dev/null || true)
-  if echo "$ts_status" | grep -qF "$NODE_ID"; then
+  if echo "$ts_status" | grep -qwF "$NODE_ID"; then
     ok "Tailscale: $NODE_ID already enrolled"

The -w flag adds word-boundary matching to prevent substring false positives.

🤖 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 `@pmoves/scripts/bootstrap-node.sh` around lines 121 - 123, The current check
uses grep -qF "$NODE_ID" which matches substrings (e.g., "foo" in "foobar");
update the matching to use word-boundary matching by changing the grep
invocation that checks ts_status for NODE_ID (the line using ts_status and
NODE_ID after calling tailscale status) to use grep's -w flag (e.g., grep -qwF
"$NODE_ID") so only whole-word matches are considered and false positives are
avoided.
🤖 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.

Inline comments:
In `@pmoves/scripts/bootstrap-node.sh`:
- Around line 67-72: The flag handling for --node-id and --profile should
explicitly detect a missing value (empty "${2:-}") before checking for a next
flag; update the blocks that set NODE_ID and PROFILE so they first test -z
"${2:-}" and emit the existing "ERROR: --node-id/--profile requires a value" to
stderr and exit 1, otherwise proceed to assign NODE_ID="${2:-}" (or
PROFILE="${2:-}") and shift 2; apply the same change to both the --node-id and
--profile branches so shift 2 is never called when the value is missing.
- Around line 183-191: Replace the invalid NATS diagnostic command and ensure
piped publishes read stdin: in the connectivity check block that currently calls
"nats server ping --server ${NATS_URL...}" (inside the if command -v nats ...),
call "nats rtt --server ${NATS_URL...}" instead; and wherever JSON is piped into
"nats pub --server ${NATS_URL...} <topic>" (the publish code around the nats pub
usage), add the "--force-stdin" flag so the CLI reads from standard input when
publishing piped JSON.

---

Nitpick comments:
In `@pmoves/scripts/bootstrap-node.sh`:
- Around line 121-123: The current check uses grep -qF "$NODE_ID" which matches
substrings (e.g., "foo" in "foobar"); update the matching to use word-boundary
matching by changing the grep invocation that checks ts_status for NODE_ID (the
line using ts_status and NODE_ID after calling tailscale status) to use grep's
-w flag (e.g., grep -qwF "$NODE_ID") so only whole-word matches are considered
and false positives are avoided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e315ff2d-dc09-44bf-83d8-3e2609f723a7

📥 Commits

Reviewing files that changed from the base of the PR and between 6c77be8 and 17173a8.

📒 Files selected for processing (1)
  • pmoves/scripts/bootstrap-node.sh

Comment on lines +67 to +72
--node-id)
[[ "${2:-}" == --* ]] && { echo "ERROR: --node-id requires a value" >&2; exit 1; }
NODE_ID="${2:-}"; shift 2 ;;
--profile)
[[ "${2:-}" == --* ]] && { echo "ERROR: --profile requires a value" >&2; exit 1; }
PROFILE="${2:-}"; shift 2 ;;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle missing argument value more gracefully.

When --node-id or --profile is the last argument with no value, the check [[ "${2:-}" == --* ]] passes (empty string doesn't start with --), but then shift 2 fails with an unclear shell error due to set -e. Adding a check for empty values would provide the intended error message.

Proposed fix
     --node-id)
-      [[ "${2:-}" == --* ]] && { echo "ERROR: --node-id requires a value" >&2; exit 1; }
+      [[ -z "${2:-}" || "${2:-}" == --* ]] && { echo "ERROR: --node-id requires a value" >&2; exit 1; }
       NODE_ID="${2:-}"; shift 2 ;;
     --profile)
-      [[ "${2:-}" == --* ]] && { echo "ERROR: --profile requires a value" >&2; exit 1; }
+      [[ -z "${2:-}" || "${2:-}" == --* ]] && { echo "ERROR: --profile requires a value" >&2; exit 1; }
       PROFILE="${2:-}"; shift 2 ;;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--node-id)
[[ "${2:-}" == --* ]] && { echo "ERROR: --node-id requires a value" >&2; exit 1; }
NODE_ID="${2:-}"; shift 2 ;;
--profile)
[[ "${2:-}" == --* ]] && { echo "ERROR: --profile requires a value" >&2; exit 1; }
PROFILE="${2:-}"; shift 2 ;;
--node-id)
[[ -z "${2:-}" || "${2:-}" == --* ]] && { echo "ERROR: --node-id requires a value" >&2; exit 1; }
NODE_ID="${2:-}"; shift 2 ;;
--profile)
[[ -z "${2:-}" || "${2:-}" == --* ]] && { echo "ERROR: --profile requires a value" >&2; exit 1; }
PROFILE="${2:-}"; shift 2 ;;
🤖 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 `@pmoves/scripts/bootstrap-node.sh` around lines 67 - 72, The flag handling for
--node-id and --profile should explicitly detect a missing value (empty
"${2:-}") before checking for a next flag; update the blocks that set NODE_ID
and PROFILE so they first test -z "${2:-}" and emit the existing "ERROR:
--node-id/--profile requires a value" to stderr and exit 1, otherwise proceed to
assign NODE_ID="${2:-}" (or PROFILE="${2:-}") and shift 2; apply the same change
to both the --node-id and --profile branches so shift 2 is never called when the
value is missing.

Comment on lines +183 to +191
if command -v nats >/dev/null 2>&1; then
if nats server ping --server "${NATS_URL:-nats://localhost:4222}" 2>/dev/null; then
ok "NATS connectivity: server ping OK"
else
warn "NATS server ping failed — NATS may not be running (non-fatal)"
fi
else
warn "nats CLI not found — skipping NATS connectivity probe"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

nats CLI server ping command syntax

💡 Result:

The NATS CLI command to ping NATS servers and measure network round-trip time (RTT) is: nats server ping This command provides a list of available servers and their respective RTTs, followed by summary statistics including the number of replies, maximum, minimum, and average latency [1][2]. Note that this is distinct from the internal NATS client PING/PONG protocol, which is a mechanism used by client applications to maintain and verify active connections to the NATS service [3]. The nats server ping command is a diagnostic tool provided by the NATS CLI for administrative and monitoring purposes [1][2].

Citations:


🌐 Web query:

nats CLI --server flag nats server ping

💡 Result:

The NATS CLI (nats) uses the --server flag to specify the URL of the NATS server you wish to interact with [1]. This flag is a global option available for most nats commands [1]. Regarding your request to "ping" a NATS server, the NATS CLI does not have a direct command named nats ping. However, you can achieve similar functionality using the nats rtt command, which measures the round-trip time (RTT) to the server [1]. To check the RTT to a specific server, you can use: nats rtt --server If you have configured a NATS context, you can simply run nats rtt to use the default server defined in your context [2][3][1]. Key points: 1. The --server flag (or -s) is used to define the target NATS server URL (e.g., nats://localhost:4222) [1]. 2. There is no nats ping command; nats rtt is the standard tool for checking connectivity and latency [1]. 3. You can also set the server URL via the NATS_URL environment variable or by using NATS contexts (nats context) to avoid typing the flag repeatedly [2][3][1].

Citations:


🌐 Web query:

nats pub command syntax piping JSON

💡 Result:

To publish JSON data using the NATS CLI, you can pipe the JSON content directly into the nats pub command [1]. The NATS CLI treats the message body as a string or raw bytes, so you can provide the JSON payload via standard input (stdin) [1]. To force the NATS CLI to read from stdin, use the --force-stdin flag [1]. Example syntax: echo '{"key": "value"}' | nats pub --force-stdin Alternatively, if you have your JSON stored in a file, you can redirect the file content into the command: nats pub --force-stdin < data.json The nats pub command accepts the subject as a required argument [1]. While the CLI does not have a built-in JSON validator, it will transmit whatever data is provided as the message body [1][2]. Ensure your JSON is correctly formatted before piping it, as NATS treats the payload as opaque bytes [3][2].

Citations:


🌐 Web query:

NATS CLI nats pub --server flag arguments

💡 Result:

In the NATS CLI, the --server flag is a global configuration flag used to specify the URL of the NATS server to connect to [1]. It accepts a URL string (e.g., nats://localhost:4222) as its argument [1]. Key details regarding the --server flag: 1. Usage: It can be used with any NATS CLI command, including nats pub, to override the default connection settings [1]. 2. Precedence: The NATS CLI determines the server connection in the following order of precedence: - The --server CLI flag [1]. - The NATS_URL environment variable [1][2]. - The active NATS Context (if configured) [1]. 3. Alternative: Instead of passing the flag every time, it is common practice to use nats context save to store server configurations and switch between them using nats context select [1][3]. Example: nats pub --server nats://your-server:4222 "your message"

Citations:


nats server ping is not a valid NATS CLI command — use nats rtt instead, and add --force-stdin flag to the nats pub command when piping JSON.

Line 185 uses nats server ping --server URL to check NATS connectivity. The NATS CLI does not have a ping subcommand under server; the correct diagnostic tool is nats rtt (measures round-trip time). Similarly, line 221 (and related code in 219–227) pipes JSON to nats pub --server URL topic, but piping to nats pub requires the --force-stdin flag to read from standard input. The --server flag itself is correct.

🤖 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 `@pmoves/scripts/bootstrap-node.sh` around lines 183 - 191, Replace the invalid
NATS diagnostic command and ensure piped publishes read stdin: in the
connectivity check block that currently calls "nats server ping --server
${NATS_URL...}" (inside the if command -v nats ...), call "nats rtt --server
${NATS_URL...}" instead; and wherever JSON is piped into "nats pub --server
${NATS_URL...} <topic>" (the publish code around the nats pub usage), add the
"--force-stdin" flag so the CLI reads from standard input when publishing piped
JSON.

POWERFULMOVES and others added 4 commits May 15, 2026 18:02
Six-step mesh enrollment: Tailscale check, profile validation, Docker
network pre-flight, NATS subscribe test, runner label hint, mesh announce.
Safe to re-run; warns on missing optional tooling, exits 1 on missing
profile, exits 2 if Docker unavailable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace `timeout 3 nats sub` (always exits 124) with `nats server ping`
for a real connectivity probe; add --profile flag to the json-to-profile
error hint so the suggested command is complete and runnable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…trap-node.sh

- Guard tailscale status with || true to prevent spurious re-enrollment
- Exit immediately after Docker network create failure (no false mesh announces)
- Validate --node-id/--profile values are not flags
- Validate ID chars are safe for JSON payload
- Use warn() for no-op runner step (green checkmark was misleading)
- Remove dead --help pre-scan loop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…announce

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant