@junior: agents ssh #13
@@ -130,22 +130,25 @@ jobs:
|
|||||||
}' > ~/.config/opencode/opencode.json
|
}' > ~/.config/opencode/opencode.json
|
||||||
echo "opencode config (secrets masked):"; cat ~/.config/opencode/opencode.json
|
echo "opencode config (secrets masked):"; cat ~/.config/opencode/opencode.json
|
||||||
|
|
||||||
- name: Set up read-only SSH alias `node1` (swarm diagnostics)
|
- name: Set up read-only SSH alias `node1` (+ opencode skill so the agent actually knows about it)
|
||||||
# Writes the deploy key + an SSH config alias so the agent can run
|
# 1) Writes the deploy key + an SSH config alias so the agent can run
|
||||||
# `ssh node1 <read-only cmd>` (matches the homelab opencode.json allowlist).
|
# `ssh node1 <read-only cmd>` (matches the homelab opencode.json allowlist).
|
||||||
# No-ops cleanly when the swarm secrets are absent — this shared workflow
|
# 2) Emits a `node1-ssh` opencode Skill file under ~/.config/opencode/skills/ so any
|
||||||
# runs in repos that don't have SWARM_HOST/SWARM_USER/SSH_PRIV_KEY, and
|
# downstream repo's dev agent discovers this capability via OpenCode's skill registry
|
||||||
# must not fail there. Secrets are passed via env (never inlined in shell).
|
# 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.
|
||||||
env:
|
env:
|
||||||
SWARM_HOST: ${{ secrets.SWARM_HOST }}
|
SWARM_HOST: ${{ secrets.SWARM_HOST }}
|
||||||
SWARM_USER: ${{ secrets.SWARM_USER }}
|
SWARM_USER: ${{ secrets.SWARM_USER }}
|
||||||
SSH_PRIV_KEY: ${{ secrets.SSH_PRIV_KEY }}
|
SSH_PRIV_KEY: ${{ secrets.SSH_PRIV_KEY }}
|
||||||
run: |
|
run: |
|
||||||
if [ -z "$SWARM_HOST" ] || [ -z "$SWARM_USER" ] || [ -z "$SSH_PRIV_KEY" ]; then
|
if [ -z "$SWARM_HOST" ] || [ -z "$SWARM_USER" ] || [ -z "$SSH_PRIV_KEY" ]; then
|
||||||
echo "swarm secrets not set in this repo — skipping node1 SSH alias"
|
echo "swarm secrets not set in this repo — skipping node1 SSH alias + skill"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
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.
|
# Write the private key with 600 perms; never echo its contents.
|
||||||
printf '%s\n' "$SSH_PRIV_KEY" > ~/.ssh/agent_node1
|
printf '%s\n' "$SSH_PRIV_KEY" > ~/.ssh/agent_node1
|
||||||
chmod 600 ~/.ssh/agent_node1
|
chmod 600 ~/.ssh/agent_node1
|
||||||
@@ -163,6 +166,55 @@ jobs:
|
|||||||
chmod 600 ~/.ssh/config
|
chmod 600 ~/.ssh/config
|
||||||
echo "node1 SSH alias configured (host=$SWARM_HOST user=$SWARM_USER)"
|
echo "node1 SSH alias configured (host=$SWARM_HOST user=$SWARM_USER)"
|
||||||
|
|
||||||
|
# Emit a reusable opencode Skill that surfaces the capability to downstream agents.
|
||||||
|
# OpenCode's skill tool registers it via the <available_skills> 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 node — 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.
|
||||||
|
- An agent on homelab can't be reached and you want to check the stack from another node.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
Commands run via `ssh node1 <read-only cmd>`. The SSH alias is set up in `${HOME}/.ssh/config`
|
||||||
|
during this workflow (only when swarm secrets are configured for the caller repo).
|
||||||
|
Read access is enforced by the homelab repo's own OpenCode allowlist: **no other ssh hosts or any write commands on node1 are permitted**.
|
||||||
|
|
||||||
|
## Allowed commands
|
||||||
|
|
||||||
|
The following read-only commands work via `ssh node1`. Wrap your command in single quotes and escape any `$` signs used inside the remote shell.
|
||||||
|
|
||||||
|
- List services: `docker service ls`
|
||||||
|
- Service details: `docker service ps <name> [--format ...]`
|
||||||
|
- View logs: `docker service logs <name> [--tail N] [--since 24h] [--timestamps]`
|
||||||
|
- Network info: `docker network ls`, `docker network inspect <name>`
|
||||||
|
- Volumes: `docker volume ls [-f dangling=true]`
|
||||||
|
- Swarm nodes: `docker node ls`, `docker node inspect self`, `docker node ps --host node1`
|
||||||
|
|
||||||
|
The same read-only constraint applies — write commands on node1 are rejected by the allowlist even if they appear in other allowlists.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
> The frontend returned a 5xx after deploy #47.
|
||||||
|
>
|
||||||
|
> Action:
|
||||||
|
> ```
|
||||||
|
> ssh node1 "docker service logs --tail 100 --timestamps 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)"
|
||||||
|
|
||||||
- name: Inspect / fetch image attachments (download only for vision agents)
|
- name: Inspect / fetch image attachments (download only for vision agents)
|
||||||
id: imgs
|
id: imgs
|
||||||
env:
|
env:
|
||||||
|
|||||||
Reference in New Issue
Block a user