feat(skills): add tailscale-fleet — SSH mesh verification and repair - #10
Conversation
Add software-development/tailscale-fleet skill with embedded fleet_ssh_mesh.sh reference script. Enables any agent to: - Verify fleet-wide SSH mesh health (--verify-only) - Run idempotent mesh repair - Report per-host OK/FAIL matrix Fleet: 4 Macs (bernie-macbookpro-m4, bernie-mac-studio-11119, bernie-macmini-m4, bernie-macmini-m4-02) + 5 Linux AI servers (ai-server-01/02/03/05/06). Excludes kaylyn-macbookpro-m3 (family, P0.43) and ai-server-04 (hardware fault).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ac5cb34b0
ℹ️ 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".
|
|
||
| ```bash | ||
| # Verify only — no changes | ||
| bash ~/.agents/scripts/fleet_ssh_mesh.sh --verify-only |
There was a problem hiding this comment.
Point the skill at the bundled script
The script added by this commit is bundled under skills/software-development/tailscale-fleet/references/fleet_ssh_mesh.sh, but the quick reference tells users to run ~/.agents/scripts/fleet_ssh_mesh.sh; I searched the repo for .agents/scripts and there is no installer or copy step that creates that path. On a normal checkout or packaged install, invoking the skill will fail with “No such file” before any verify/repair runs, so the examples should point at ${HERMES_SKILL_DIR}/references/fleet_ssh_mesh.sh or the absolute support-file path exposed for this skill.
Useful? React with 👍 / 👎.
| if [ "$VERIFY_ONLY" -eq 0 ]; then | ||
| say "== 1/4 ensure keys + collect pubkeys ==" | ||
| for h in "${FLEET_MACS[@]}"; do | ||
| run_on "$h" 'test -f ~/.ssh/id_ed25519.pub || ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519 -q; cat ~/.ssh/id_ed25519.pub' \ |
There was a problem hiding this comment.
Create ~/.ssh before generating keys
On a fleet Mac that has never used SSH, this command tries to generate ~/.ssh/id_ed25519 before the later step creates ~/.ssh; ssh-keygen -f ~/.ssh/id_ed25519 fails when the parent directory is missing, so that host’s pubkey is skipped and the repair cannot make the mesh complete. Create ~/.ssh with 700 permissions before invoking ssh-keygen.
Useful? React with 👍 / 👎.
| if dscacheutil -q host -a name hello.ts.net >/dev/null 2>&1; then | ||
| echo " resolver OK (app variant): '"$h"'" | ||
| elif sudo -n true 2>/dev/null; then | ||
| printf "nameserver 100.100.100.100\n" | sudo tee /etc/resolver/ts.net >/dev/null |
There was a problem hiding this comment.
Create /etc/resolver before writing ts.net
When a Homebrew tailscaled Mac does not already have /etc/resolver, sudo tee /etc/resolver/ts.net cannot create the file because the parent directory is absent; since the script does not stop on that failure, it still prints resolver INSTALLED and leaves MagicDNS broken. Create the directory first and treat the write as a failure if it does not succeed.
Useful? React with 👍 / 👎.
| < "$TMP/union.pub" 2>/dev/null && say " authorized_keys updated: $h" || { say " !! failed updating $h"; FAIL=1; } | ||
| done | ||
|
|
||
| say "== 3/4 MagicDNS resolver fix (homebrew tailscaled Macs) ==" |
There was a problem hiding this comment.
Run the local DNS repair before remote SSH
On a Homebrew tailscaled Mac whose local /etc/resolver/ts.net is missing, steps 1 and 2 have already tried to reach every $h.$TS_SUFFIX before this resolver fix runs, so the repair records key collection/distribution failures and can only succeed after a second run. Move the local MagicDNS preflight ahead of the remote SSH loops, or retry those loops after fixing DNS, so one full repair handles the supported broken-DNS case.
Useful? React with 👍 / 👎.
| FLEET_MACS=(bernie-macbookpro-m4 bernie-mac-studio-11119 bernie-macmini-m4 bernie-macmini-m4-02) | ||
| HERMES_HOST="bernie-macmini-m4" | ||
| LINUX_SERVERS=(ai-server-01 ai-server-02 ai-server-03 ai-server-05 ai-server-06) # s04 drained (hw fault) | ||
| SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=8 -o StrictHostKeyChecking=accept-new) |
There was a problem hiding this comment.
Keep verify-only from writing known_hosts
The skill advertises --verify-only as making no changes, but this global SSH option is still used by the verification matrix; OpenSSH documents StrictHostKeyChecking=accept-new as automatically adding new host keys to the user's known_hosts, so a first health check against any new fleet edge mutates the local or remote source host. Use separate read-only SSH options for verification or document that the check intentionally trusts new host keys.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| VERIFY_ONLY=0 | ||
| [ "${1:-}" = "--verify-only" ] && VERIFY_ONLY=1 |
There was a problem hiding this comment.
Reject unknown arguments before repairing
With this parser, any typo such as --verify_only or an attempted --help leaves VERIFY_ONLY=0, so the script falls through into the full repair path that appends to authorized_keys and may edit resolver files. For a script with a specifically advertised read-only mode, unknown arguments should fail fast before any mutation can run.
Useful? React with 👍 / 👎.
No description provided.