davidandClaude Sonnet 5 cd1ee3631d Fix become-precedence bug, dead Tailscale install path, and make meshcore config self-sufficient
- ansible_become: true as a group_vars connection variable silently took
  precedence over every task-level `become: false` in this repo, so
  every "unprivileged" task (pipx installs, user script deploys,
  legacy .env.local writes) was actually running as root the whole
  time. Move become to a play-level keyword in site.yml instead, which
  correctly loses to a task's own become: false. Also stop relying on
  ansible_env.HOME (which reflected root's home once facts were
  gathered under the old broken setup) and use /home/{{ remote_user }}
  directly, with explicit owner/group so ownership self-heals.
- meshcore_capture: 99-user.toml.j2 now defines every broker
  (including letsmesh-us/eu) fully rather than assuming
  config.d/10-letsmesh.toml was auto-installed — that preset only gets
  created on some install paths (fresh install) and not others
  (migrate), so the old partial-override form silently dropped
  brokers on migrated nodes.
- base: install libffi-dev/pkg-config on armv6 (Pi Zero W) — cffi has
  no prebuilt wheel for that architecture and fails to compile
  without the headers.
- dm-ashwell: migrated off the legacy layout to the current
  installer's system-service layout; drop its layout override now
  that it matches the group default.
- meshcore-capture-update.sh: fix a YAML folded scalar (>-) collapsing
  the shebang and command onto one line, corrupting the script.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-07-07 06:32:37 +01:00

dotmesh-monitor

Ansible playbooks for deploying MeshCore monitoring nodes (Raspberry Pi Zero W / Zero 2 W).

Hosts

Host Hardware Group
dm-baldock Pi Zero W (armv6) zero_w
dm-ashwell Pi Zero 2 W (armv7) zero2_w
dm-edworth Pi Zero 2 W (armv7) zero2_w

Prerequisites

Local machine:

pip install ansible
# or: sudo apt install ansible

New Pi node checklist:

  1. Flash Raspberry Pi OS Lite (Trixie) with Raspberry Pi Imager, using "Edit Settings" (OS customisation) to set hostname, the david user + password, and the dotnetwork wifi (SSID/password only — the imager only supports one network at flash time; the deployed-location and dotmobile networks get added later by the wifi role, see below).
  2. Before ejecting the card, verify the customisation actually got written — mount the boot partition and check network-config/user-data aren't just the commented-out stock template (see "Imager gotcha" below). Only cmdline.txt's regdomain getting a fresh timestamp while the rest stay at the image's build date is the tell that it silently failed.
  3. Boot the Pi, find its LAN IP (e.g. from the router's DHCP leases), confirm SSH access with the password you set — no key is seeded at flash time.
  4. Run ssh-copy-id david@<ip> from an interactive terminal (not through a non-interactive shell/script — it needs a real TTY to prompt for the password) so ansible can connect with a key.
  5. Connect the MeshCore device via USB, then find its serial ID: ls /dev/serial/by-id/.
  6. Add serial_port (and wifi_ssid_location) to ansible/host_vars/<hostname>/vars.yml, and the location wifi password to ansible/host_vars/<hostname>/vault.yml (see "Vault" below).
  7. Run site.yml against just that host, overriding the host's address since Tailscale/DNS won't resolve it yet: ansible-playbook site.yml --limit <hostname> -e "ansible_host=<ip>". This authorizes your SSH keys, joins the deployed-location + dotmobile wifi networks, installs (but does not authenticate) Tailscale, and deploys everything else in one pass.
  8. Tailscale needs one manual step: SSH in and run sudo tailscale up, then open the printed URL in a browser to approve the device on your tailnet. (You can pass -e tailscale_auth_key=tskey-... — from the admin console — to authenticate non-interactively instead, but there's no stored key anywhere for this repo, so the interactive route is simplest for a one-off node.)
  9. The meshcore-packet-capture installer also needs one manual step (see "meshcore-packet-capture install is interactive" below): SSH in and run sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/agessaman/meshcore-packet-capture/main/install.sh)", answering its ~3 prompts (service account, install method — pick 1, IATA/broker config — defaults are fine, site.yml overwrites .env.local afterward anyway).
  10. Once Tailscale is up, re-run site.yml without the ansible_host override — it'll resolve via the Tailscale hostname from here on, and will now just write .env.local + enable the service since the installer step is already satisfied.

meshcore-packet-capture install is interactive

The install.sh bootstrap has no real non-interactive path for a fresh install — its --update flag only changes behavior when an installation already exists. It also refuses to run at all with piped stdin (curl | sudo bash errors out asking you to download the script first) and its Python layer explicitly opens /dev/tty for prompts, which just hangs forever over plain SSH/ansible (no human there to answer). We tried feeding it scripted answers via script/a pty and it's not worth the fragility — just run it manually once per node (step 9 above); ansible/roles/meshcore_capture/tasks/main.yml's creates: guard means ansible never touches it again afterward.

Imager gotcha (2026-07)

The Raspberry Pi Imager available via Flathub (org.raspberrypi.rpi-imager) is stuck on 1.9.6 and there's no newer .deb/Flatpak in the Ubuntu or Flathub repos either — Flathub hasn't published the 2.0.x rewrite. 1.9.6 silently fails to apply OS customisation (hostname, user, SSH, wifi) on newer Raspberry Pi OS Trixie images: it only writes the kernel cfg80211.ieee80211_regdom= cmdline parameter and leaves user-data/network-config as the stock commented-out template, with no error. The result looks exactly like a wifi problem (Pi never appears on the network) but is actually "the card has no credentials on it at all."

Fix: grab the real .deb from the GitHub releases page (e.g. rpi-imager_2.0.10_amd64.deb) and sudo dpkg -i it — that version writes the customisation correctly.

Usage

Deploy to a single host (recommended for first run / testing):

cd ansible
ansible-playbook -i inventory.yml site.yml --limit dm-edworth

Deploy to all nodes:

ansible-playbook -i inventory.yml site.yml

Dry run:

ansible-playbook -i inventory.yml site.yml --limit dm-edworth --check

If sudo requires a password, add --ask-become-pass.

You'll be prompted for a Tailscale auth key — leave blank if the node is already authenticated.

What it does

  1. wifi — configures NetworkManager connections for dotnetwork (home), dotmobile (phone hotspot, field troubleshooting fallback), and the host's deployed-location network
  2. base — apt upgrade, installs screen/pipx/vnstat/git, sets MOTD, authorizes SSH keys for both laptop partitions, installs Tailscale (always) and authenticates it (only if tailscale_auth_key is set — otherwise run sudo tailscale up manually once, see checklist above)
  3. meshcore_cli — installs meshcore-cli via pipx
  4. meshcore_capture — runs the agessaman/meshcore-packet-capture install script (skipped once already installed — see "meshcore-packet-capture install is interactive" above, this needs a manual first run), writes .env.local config, enables meshcore-capture.service, deploys update/log helper scripts
  5. scripts — deploys voltage.sh and bandwidth.sh

Config

Shared MQTT config lives in group_vars/meshcore.yml. Per-host serial port and wifi SSID are in host_vars/<hostname>/vars.yml.

Running the playbook again re-applies .env.local and restarts the service if it changed — safe to run on already-deployed nodes.

Vault

group_vars/all/vault.yml (shared wifi/SSH secrets) and host_vars/<hostname>/vault.yml (per-host deployed-location wifi password) are encrypted with Ansible Vault. ansible.cfg points at ../.vault_pass (gitignored, not committed) for the password — ask David for a copy, or generate a fresh one and re-encrypt if starting over:

ansible-vault view --vault-password-file ../.vault_pass group_vars/all/vault.yml
ansible-vault edit --vault-password-file ../.vault_pass host_vars/dm-edworth/vault.yml

*/vault.yml.example shows the expected keys.

group_vars/meshcore.yml (MQTT credentials) is still plaintext — consider moving it into the vault too if this repo is shared further.

S
Description
No description provided
Readme
65 KiB
Languages
Jinja 86.6%
Shell 13.4%