pro-ssh · diff

v2.1.0 to v2.1.0

49 added, 47 removed. Audit A to A.

---
name: pro-ssh
description: "Run commands on remote servers over SSH (AWS EC2, Synology NAS, any Linux host), by saved name or direct host/user/key. Use for 'check the server', 'look at logs', 'is the deploy live'."
version: 2.1.0
---
- # SSH — 원격 서버 SSH 접근
+ # SSH: remote server access
## When to use
- - '서버 확인해줘', '로그 봐줘', 'EC2 접속해', '시놀로지 접속해'
- - 'prod 검수해줘', '서버 상태 확인', '배포 됐는지 확인해줘', '서버에서 ~해줘'
- - 저장된 서버 이름으로도, 저장 없이 호스트·포트·계정·비밀번호/키를 직접 넘겨서도 접속한다.
+ - "Check the server", "show the logs", "connect to EC2", "connect to the Synology" (Korean: '서버 확인해줘', '로그 봐줘', 'EC2 접속해', '시놀로지 접속해')
+ - "Inspect prod", "check server status", "check whether the deploy is live", "do ~ on the server" (Korean: 'prod 검수해줘', '서버 상태 확인', '배포 됐는지 확인해줘', '서버에서 ~해줘')
+ - It connects by a saved server name, or without saving by passing host, port, account and password/key directly.
- 접속 실행기는 **`pro-launch` 의 `launch_cli.py ssh`** 다 (#841). 저장된 서버(`--cred`)로도, **저장 없이 접속 정보를 직접 넘겨서도** 된다.
+ The connection runner is **`launch_cli.py ssh` from `pro-launch`** (#841). It works with a saved server (`--cred`) or **with connection details passed directly, without saving**.
- - 쓰는 때: 서버 상태·로그·파일 확인, 배포 후 서버 검수, "서버 들어가서 ~해줘".
- - **쓰지 않는 때**: 시놀로지 DSM 역방향 프록시·도메인 설정 → `pro-synology-expose` (선택 플러그인 projectops-extras에 있음).
+ Talk to the user in the user's language (or the configured `language` preference).
- `{SCRIPTS}` 는 pro-launch 의 `scripts/` (찾는 법은 `../pro-launch/SKILL.md`). 호출은 `{PYTHON} {SCRIPTS}/launch_cli.py …`.
+ - Use it for: checking server status, logs and files, post-deploy server checks, "go into the server and do ~".
+ - **Do not use it for**: Synology DSM reverse proxy and domain setup → `pro-synology-expose` (in the optional projectops-extras plugin).
- ## 작업 흐름
+ `{SCRIPTS}` is pro-launch's `scripts/` (how to find it: `../pro-launch/SKILL.md`). Calls are `{PYTHON} {SCRIPTS}/launch_cli.py …`.
- ### Phase 0 — 어느 서버인가
+ ## Workflow
- **사용자에게 묻기 전에 프로젝트에서 찾는다.** "이룸 서버 DB" 처럼 이름만 말해도 답은 대개 레포에 있다.
+ ### Phase 0: Which server
- 1. `access show` — 이 레포에 적어 둔 붙는 법(`ssh.<환경>` · `db` · `logs`)이 있으면 그것부터 쓴다.
- 2. 프로젝트 파일에서 대상 서버를 찾는다 (호스트 · 계정 · 포트 · DB 호스트/이름):
+ **Look in the project before asking the user.** Even when the user only gives a name, such as "the Iroom server DB", the answer is usually in the repo.
- | 어디 | 무엇 |
+ 1. `access show`: if this repo has recorded how to connect (`ssh.<env>` · `db` · `logs`), use that first.
+ 2. Find the target server in the project files (host · account · port · DB host/name):
+
+ | Where | What |
|---|---|
- | `application-*.yml` · `application-*.properties` (`prod` · `dev` …) | `spring.datasource.url` 의 DB 호스트·이름, 외부 API 주소 |
- | `.env*` · `docker-compose*.yml` | `DB_HOST` · `DB_NAME` · 서비스 이름 · 포트 |
- | `.github/workflows/*DEPLOY*` · `*CICD*` | `ssh` 대상 호스트 · 계정 · 포트 · 배포 경로 · 컨테이너 이름 (값이 `secrets.*` 면 이름만 안다) |
+ | `application-*.yml` · `application-*.properties` (`prod` · `dev` …) | DB host and name in `spring.datasource.url`, external API addresses |
+ | `.env*` · `docker-compose*.yml` | `DB_HOST` · `DB_NAME` · service names · ports |
+ | `.github/workflows/*DEPLOY*` · `*CICD*` | `ssh` target host · account · port · deploy path · container name (if the value is `secrets.*`, you only know the name) |
- - **비밀 값(비밀번호 · 토큰 · 키)은 출력 · 보고에 옮기지 않는다.** 읽어도 "`application-prod.yml` 의 `spring.datasource.password`" 처럼 **위치만** 말한다.
- - 찾은 호스트를 `cred list` 의 `host` 와 맞춰 본다. 맞는 저장 서버가 있으면 그것을 쓴다.
- 3. `cred list` 로 저장된 서버(`kind: ssh`)를 본다. `use_when` 이 지금 일에 맞는 것만 쓴다. 하나뿐이면 자동 선택, 여럿이면 번호로 고르게 한다.
- - **`scope` 가 `ask-first` 면 접속 전에 사용자에게 확인한다** ("○○ 서버에 접속해 △△ 를 보겠습니다. 진행할까요?"). 승인 없이 쓰지 않는다.
- 4. 위에서 못 찾았거나 사용자가 접속 정보를 이미 말했으면(예: "host 1.2.3.4 포트 2222 계정 ubuntu 키 ~/k.pem") **저장 없이 직접 접속**한다. 빠진 정보는 **한 메시지에 하나씩** 묻는다:
- 1) 호스트(IP/도메인) 2) 포트(모르면 22 제안) 3) 계정 4) 인증 방식(비밀번호 / PEM 키) 5) 비밀번호 또는 키 파일 경로.
- 5. **기록되지 않은 경로로 접속에 성공하면 남긴다** — 다음에 다시 찾지 않게.
- - 붙는 법(비밀 없음)은 이 레포에: `access set --key ssh.<환경> --json '{"host":…,"port":…,"user":…,"cred":"<저장 이름>","source":"<찾은 파일>"}'`,
- DB 는 `access set --key db --json '{"how":"ssh",…}'` (`../pro-launch/references/server.md`). 비밀 값을 넣으면 `secret_in_value` 로 거절된다.
- - 비밀번호 · 키 같은 자격증명은 저장해도 되는지 묻고 `cred set` (아래). 사용자가 일회성이라고 하면 저장하지 않는다.
+ - **Never copy secret values (passwords, tokens, keys) into output or reports.** Even if you read them, give **only the location**, such as "`spring.datasource.password` in `application-prod.yml`".
+ - Match the host you found against `host` in `cred list`. If a saved server matches, use it.
+ 3. Look at saved servers (`kind: ssh`) with `cred list`. Use only those whose `use_when` fits the current task. If there is only one, select it automatically; if several, let the user pick by number.
+ - **If `scope` is `ask-first`, confirm with the user before connecting** ("I'll connect to the ○○ server and check △△. Proceed?"). Never use it without approval.
+ 4. If nothing was found above, or the user already gave connection details (for example "host 1.2.3.4 port 2222 user ubuntu key ~/k.pem"), **connect directly without saving**. Ask for missing details **one per message**:
+ 1) host (IP/domain) 2) port (suggest 22 if unknown) 3) account 4) auth method (password / PEM key) 5) password or key file path.
+ 5. **When a connection succeeds through an unrecorded path, record it** so you do not search again next time.
+ - How to connect (no secrets) goes in this repo: `access set --key ssh.<env> --json '{"host":…,"port":…,"user":…,"cred":"<saved name>","source":"<file where found>"}'`,
+ and for a DB `access set --key db --json '{"how":"ssh",…}'` (`../pro-launch/references/server.md`). Putting a secret value in is rejected with `secret_in_value`.
+ - For credentials such as passwords and keys, ask whether they may be saved, then `cred set` (below). If the user says it is one-off, do not save.
- ### Phase 1 — 명령 실행
+ ### Phase 1: Run commands
- 사용자가 명령을 안 정했으면 목적에 맞는 명령을 agent 가 판단해 실행한다.
+ If the user did not choose a command, the agent picks one that fits the goal and runs it.
- | 상황 | 명령 |
+ | Situation | Command |
|---|---|
- | 저장된 서버 | `ssh --cred <서버> --command '…'` (비밀번호 sudo 는 `--sudo` + `SUDO <명령>`) |
- | 저장 없이 · 비밀번호 | `ssh --host H --port P --user U --password-env 변수 --command '…'` (**비밀번호를 명령줄에 쓰지 않는다** — 권한 분류기가 막고 기록에 남는다) |
- | 저장 없이 · PEM 키 | `ssh --host H --port P --user U --key-path ~/k.pem --command '…'` |
- | 저장된 서버의 포트·계정만 바꿔 시도 | `ssh --cred <서버> --port 2022 --command '…'` (직접 준 값이 우선) |
- | 새 서버 저장 | 저장해도 되는지 묻고, 비밀번호는 사용자가 직접 넣게 한다: `! {PYTHON} {SCRIPTS}/launch_cli.py cred set --name <서버> --prompt --json '{"kind":"ssh","host":…,"port":…,"user":…,"use_when":…,"scope":…}'` (비밀번호는 `--json` 에 싣지 않는다). 키 접속이면 `--prompt` 없이 `key_path` 를 `--json` 에 넣는다 |
- | 옛 `ssh` 섹션 서버 가져오기 | `cred import-ssh --dry-run` 으로 목록 확인 → 빼고 실행(값까지 복사) → 접속 확인 후 `--prune` |
- | 로그 · DB | `logs --cred …` · `db --cred …` (`../pro-launch/references/server.md`) |
+ | Saved server | `ssh --cred <server> --command '…'` (for password sudo, `--sudo` + `SUDO <command>`) |
+ | No saving · password | `ssh --host H --port P --user U --password-env VAR --command '…'` (**never put the password on the command line**: the permission classifier blocks it and it stays in logs) |
+ | No saving · PEM key | `ssh --host H --port P --user U --key-path ~/k.pem --command '…'` |
+ | Try a saved server with a different port or account | `ssh --cred <server> --port 2022 --command '…'` (values given directly win) |
+ | Save a new server | Ask whether it may be saved, and let the user type the password themselves: `! {PYTHON} {SCRIPTS}/launch_cli.py cred set --name <server> --prompt --json '{"kind":"ssh","host":…,"port":…,"user":…,"use_when":…,"scope":…}'` (do not put the password in `--json`). For key auth, put `key_path` in `--json` without `--prompt` |
+ | Import servers from the old `ssh` section | Check the list with `cred import-ssh --dry-run` → run without it (copies the values too) → after confirming connections, `--prune` |
+ | Logs · DB | `logs --cred …` · `db --cred …` (`../pro-launch/references/server.md`) |
- - **결과 code 읽기**: `remote_exit_nonzero` 는 연결은 됐고 원격 명령이 0 이 아니게 끝난 것이다(grep 결과 없음 등 — 접속 정보를 의심하지 않는다). `auth_failed`·`connect_failed`·`host_key_*` 가 접속 실패다. Windows(cmd) 서버는 `&` 로 이은 명령의 종료코드가 마지막 것만 남으니 단계마다 따로 부르거나 PowerShell 로 감싼다.
- - 출력에 비밀번호가 섞이면 `***` 로 가려진다. 비밀번호 접속에는 `sshpass` 가 필요하다(없으면 `sshpass_missing`) — 키 접속을 쓰거나 아래 호환 경로를 쓴다.
- - 저장된 서버로 접속하면 응답의 `memory` 에 그 서버에서 알아낸 사실(OS · docker 경로 · 컨테이너 · 로그 위치)이 실린다. 새로 알아낸 것은
- `learn --area server --scope machine --key server.<서버>.<주제> --how '…' --result ok` 로 남긴다 (계정·비밀 금지). 저장 없는 접속은 기억하지 않는다.
- - 자격증명 규칙 전체: `../pro-launch/references/credentials.md`.
+ - **Reading result codes**: `remote_exit_nonzero` means the connection worked and the remote command ended non-zero (no grep match, etc.; do not suspect the connection details). `auth_failed`, `connect_failed` and `host_key_*` are connection failures. On Windows (cmd) servers, commands joined with `&` keep only the last exit code, so call each step separately or wrap them in PowerShell.
+ - Passwords that appear in the output are masked as `***`. Password connections need `sshpass` (`sshpass_missing` if absent); use key auth or the compatibility path below.
+ - When connecting to a saved server, `memory` in the response carries facts learned about that server (OS, docker path, containers, log locations). Record new findings with
+ `learn --area server --scope machine --key server.<server>.<topic> --how '…' --result ok` (no accounts or secrets). Connections without saving are not remembered.
+ - Full credential rules: `../pro-launch/references/credentials.md`.
- ### Phase 2 — 결과 보고
+ ### Phase 2: Report results
- 실행 결과를 요약해 보고한다. 에러가 있으면 원인과 해결 방법을 함께 제시한다 (`references/troubleshooting.md`).
+ Summarize the results for the user. If there are errors, give the cause and the fix together (`references/troubleshooting.md`).
- ## 이 스킬에 남은 것
+ ## What remains in this skill
- - `references/troubleshooting.md` — 시놀로지 docker 절대경로·`wget`·Windows PowerShell·자주 만나는 함정 (서버 종류별 지식).
- - `scripts/ssh_connect.py` — paramiko 기반 호환 경로. `launch_cli` 를 못 쓰는 환경(sshpass 없는 Windows)에서만. 비밀번호는 환경변수 `SSH_PASSWORD` 로도 받는다.
+ - `references/troubleshooting.md`: Synology docker absolute path, `wget`, Windows PowerShell, common pitfalls (per-server-type knowledge).
+ - `scripts/ssh_connect.py`: paramiko-based compatibility path. Use it only where `launch_cli` cannot be used (Windows without sshpass). It also takes the password from the `SSH_PASSWORD` environment variable.
## Customize
Your own language, tone and rules apply: see `../references/preferences.md`. Read them once at the start: run the snippet in `../references/preferences.md` with `SKILL_ID=pro-ssh`.
Keys for this skill: the common `language`, `tone`, `rules` only.
Preferences change presentation only; approval gates, secret protection and `--dry-run` stay in force.