#!/usr/bin/env bash # Set up read-only SSH alias `node1` (+ opencode skill so the agent actually knows about it). # 1) Writes the deploy key + an SSH config alias so the agent can run # `ssh node1 ` (matches the homelab opencode.json allowlist). # 2) Emits a `node1-ssh` opencode Skill file under ~/.config/opencode/skills/ so any # downstream repo's dev agent discovers this capability via OpenCode's skill registry # rather than having to trial against the permission allowlist. Only emitted when the # swarm plumbing is actually wired for that caller (SWARM_HOST/SWARM_USER/SSH_PRIV_KEY). # All three secrets are passed via env and never inlined into shell — this shared workflow # runs in repos that don't have them and must not fail there. # # Required env (provided by the workflow step): SWARM_HOST SWARM_USER SSH_PRIV_KEY set -eu if [ -z "$SWARM_HOST" ] || [ -z "$SWARM_USER" ] || [ -z "$SSH_PRIV_KEY" ]; then echo "swarm secrets not set in this repo — skipping node1 SSH alias + skill" exit 0 fi mkdir -p ~/.ssh ~/.config/opencode/skills/node1-ssh && chmod 700 ~/.ssh ~/.config/opencode/skills/node1-ssh # Write the private key with 600 perms; never echo its contents. printf '%s\n' "$SSH_PRIV_KEY" > ~/.ssh/agent_node1 chmod 600 ~/.ssh/agent_node1 # SSH config alias `node1` — last-match-wins in the homelab opencode allowlist # (`deny ssh *` + specific `allow ssh node1 …`), so the alias name is fixed. cat > ~/.ssh/config < block, so any dev agent # can discover "I am allowed to ssh node1" without trial-and-error against the allowlist. cat > ~/.config/opencode/skills/node1-ssh/SKILL.md <<'SKILLET' --- name: node1-ssh description: Read-only diagnostics on the swarm host via `ssh node1 …` — use when debugging a deploy or checking a running service. domains: [swarm] tags: [ssh, swarm, diagnostics, docker] --- # `node1-ssh` Skill Use this skill to run **read-only** commands against **node1** (the Docker Swarm host) when: - A deploy failed and you need to inspect running services. - You need to see a service's logs for debugging. - You want to check the state of the stack on the swarm. ## How it works Commands run via `ssh node1 `. The SSH alias is configured in `${HOME}/.ssh/config` during this workflow (only when swarm secrets are configured for the caller repo). ## What you're actually allowed to run — the allowlist is the source of truth This skill does **not** define which commands are permitted, and you must not assume a fixed list here. The single source of truth for exactly which `ssh node1 …` commands are allowed is the **caller repo's own OpenCode permission config** (e.g. `opencode.json` in the homelab repo: a `deny "ssh *"` with specific `allow "ssh node1 …"` entries, last-match-wins). - Only read-only diagnostics are permitted; any write/mutating command on node1 is denied. - The permission layer enforces this — if a command is not on the caller's allowlist it will be blocked, regardless of what this skill or any other allowlist says. - So: reach for `ssh node1 …` for read-only diagnostics, and treat the caller's `opencode.json` `ssh node1` allow-entries as the authoritative list of what will actually run. ## Example > The frontend returned a 5xx after a deploy. > > Action (a read-only log inspection, subject to the caller's allowlist): > ``` > ssh node1 "docker service logs --tail 100 --timestamps homelab_frontend" > ``` SKILLET chmod -R o=rX ~/.config/opencode/skills/node1-ssh echo "opencode skill node1-ssh installed ($(wc -l < ~/.config/opencode/skills/node1-ssh/SKILL.md) lines)"