Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: ["1.24"]
go: ["1.25.10"]

steps:
- name: Checkout code
Expand Down Expand Up @@ -66,7 +66,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.25.10"

- name: Download dependencies
run: go mod download
Expand Down Expand Up @@ -132,7 +132,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.25.10"

- name: Run golangci-lint
uses: golangci/golangci-lint-action@v7
Expand All @@ -151,7 +151,14 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
go-version: "1.25.10"

- name: Run Go vulnerability scanner
uses: golang/govulncheck-action@v1
with:
go-version-input: "1.25.10"
go-package: ./...
repo-checkout: false

- name: Run Gosec Security Scanner
uses: securego/gosec@master
Expand Down
27 changes: 15 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.25"
go-version: "1.25.10"

- name: Get version
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"

- name: Extract changelog
id: changelog
Expand All @@ -42,18 +42,18 @@ jobs:
flag { print }
' CHANGELOG.md > /tmp/release_notes.md

# 如果没有提取到内容,使用默认消息
# A release without versioned notes is incomplete; fail before publishing.
if [ ! -s /tmp/release_notes.md ]; then
echo "No changelog entry found for version $VERSION_NUMBER" > /tmp/release_notes.md
echo "Please check CHANGELOG.md for details." >> /tmp/release_notes.md
echo "No changelog entry found for version $VERSION_NUMBER" >&2
exit 1
fi

# 输出到环境变量
{
echo 'CHANGELOG<<EOF'
cat /tmp/release_notes.md
echo EOF
} >> $GITHUB_OUTPUT
} >> "$GITHUB_OUTPUT"

- name: Build binaries
run: |
Expand All @@ -76,12 +76,14 @@ jobs:
-o "$output" \
./cmd/sshx

cp skills/sshx/SKILL.md dist/SKILL.md

# 压缩二进制文件
if [ "$os" = "windows" ]; then
zip "dist/sshx-${os}-${arch}.zip" "$output"
(cd dist && zip "sshx-${os}-${arch}.zip" "$(basename "$output")" SKILL.md)
rm "$output"
else
tar czf "dist/sshx-${os}-${arch}.tar.gz" -C dist "$(basename $output)"
tar czf "dist/sshx-${os}-${arch}.tar.gz" -C dist "$(basename "$output")" SKILL.md
rm "$output"
fi
}
Expand All @@ -93,19 +95,20 @@ jobs:
build darwin arm64
build windows amd64
build windows arm64
rm dist/SKILL.md

- name: Generate checksums
run: |
cd dist
sha256sum * > checksums.txt
sha256sum -- * > checksums.txt
cat checksums.txt

- name: Create Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
name: Release ${{ steps.get_version.outputs.VERSION }}
body: |
## SSHX - Secure SSH & SFTP Client with Built-in Password Manager
## SSHX - Agent-native remote host execution over SSH

### 📋 What's Changed

Expand Down Expand Up @@ -211,7 +214,7 @@ jobs:

cat > tap/Formula/sshx.rb <<EOF
class Sshx < Formula
desc "Barrier-free SSH/SFTP CLI with a built-in OS-keyring password manager"
desc "Agent-native remote host execution over SSH"
homepage "https://github.com/talkincode/sshx"
version "${VERSION}"
license "MIT"
Expand Down
27 changes: 14 additions & 13 deletions AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Operating guide for humans and AI coding agents working on **sshx**. It defines
what this project is, what it deliberately is *not*, how it is built, and how to
make changes that fit. Read this before making non-trivial changes.

Module: `github.com/talkincode/sshx` · Language: Go 1.24 · License: MIT
Module: `github.com/talkincode/sshx` · Language: Go 1.25.10 · License: MIT

---

Expand Down Expand Up @@ -151,18 +151,18 @@ pkg/logger/ → leveled logger (SSHX_LOG_LEVEL)

## 5. Tech Stack

- **Language:** Go (module directive pinned to **`go 1.24`** — see constraint below).
- **Language:** Go (module directive pinned to **`go 1.25.10`** — see constraint below).
- **SSH/crypto:** `golang.org/x/crypto/ssh`
- **SFTP:** `github.com/pkg/sftp`
- **Keyring:** `github.com/zalando/go-keyring`
- **Terminal input:** `golang.org/x/term` (no-echo password prompts)
- **Dotenv:** `github.com/joho/godotenv`
- **Tests:** `github.com/stretchr/testify`

> ⚠️ **Toolchain constraint:** CI's test/lint/security jobs run on **Go 1.24**.
> The `go` directive in `go.mod` must stay at `1.24.0`. When adding a dependency,
> pin it to a version whose own `go` directive is ≤ 1.24 (e.g. `x/term v0.37.0`,
> `x/sys v0.38.0`). Do not let `go get` silently bump the directive to 1.25+.
> ⚠️ **Toolchain constraint:** CI's test/lint/security jobs run on **Go 1.25.10**.
> The `go` directive in `go.mod` must stay at `1.25.10` unless a deliberate
> security or compatibility review changes the baseline. New dependencies must
> support that toolchain; do not let `go get` silently bump the directive.

## 6. Development Workflow (Methods)

Expand Down Expand Up @@ -198,10 +198,11 @@ Notes:

### CI (`.github/workflows/`)

- `ci.yml`: **Test** (ubuntu + macOS, Go 1.24, `-race -cover`), **Lint**
(golangci-lint), **Security Scan** (`gosec` via golangci-lint and the
standalone scanner), **Analyze** (CodeQL, Go).
- `release.yml`: builds release artifacts (Go 1.25 in the release job only).
- `ci.yml`: **Test** (ubuntu + macOS, Go 1.25.10, `-race -cover`), **Lint**
(golangci-lint), **Security Scan** (`gosec` plus `govulncheck`), **Analyze**
(CodeQL, Go).
- `release.yml`: builds release artifacts with Go 1.25.10 and bundles the matching
Agent skill in every archive.

All `ci.yml` checks must be green before merge.

Expand Down Expand Up @@ -364,7 +365,7 @@ unless the mission in §1–§3 is formally revised.
## 11. Release Process

- Semantic Versioning; changes recorded in `CHANGELOG.md` (Keep a Changelog).
- Tagging is scripted (`scripts/tag.sh`, `make tag`); release notes via
- Tagging is scripted (`scripts/tag.sh`, `make tag TAG=vX.Y.Z`); release notes via
`scripts/release-note.sh` (`make renote`).
- `release.yml` cross-compiles and publishes artifacts on tag push.
- Install paths: `go install`, `install.sh` (Linux/macOS), `install.ps1`
Expand All @@ -378,9 +379,9 @@ When working in this repo:
1. **Stay within the mission.** Re-read §3 before adding features. Default to a
smaller change. Never reintroduce MCP, a daemon, a connection pool, tunneling,
or a GUI.
2. **Hold the toolchain line.** Keep `go.mod` at `go 1.24.0`. If a dependency
2. **Hold the toolchain line.** Keep `go.mod` at `go 1.25.10`. If a dependency
forces a newer directive, pin an older compatible version instead of bumping
the directive (CI runs Go 1.24).
the directive (CI runs Go 1.25.10).
3. **Verify before declaring done.** Run `make check` (and `golangci-lint run`)
locally; reproduce the original symptom and confirm it is gone. For PR work,
watch CI to green (`gh pr checks <n> --watch`).
Expand Down
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.0] - 2026-08-12

### Added

- Add `sshx inspect` with built-in system/resource/network capabilities and a
Expand All @@ -24,12 +26,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
SFTP, server-to-server transfer, keyring-backed sudo, and audit recovery.
- Run the E2E suite on Linux and macOS CI, including production-binary checks
against an ephemeral macOS Keychain.
- Bundle the matching Agent skill in release archives and install it alongside
the binary through the Linux/macOS installer.

### Changed

- Separate SSH login and sudo password fields while preserving the documented
keyring boundary: stored password keys are used for sudo, not SSH login.
- Upgrade the CI cache and Codecov actions to their supported major versions.
- Raise the minimum Go toolchain to 1.25.10 so sshx consumes patched standard
library code and the patched SSH
implementation in `golang.org/x/crypto v0.52.0`.
- Fail the release workflow when a tag has no exact versioned changelog entry.
- Allow the tag script to accept an explicit semantic version and reject tags
without a matching changelog section.

### Security

Expand All @@ -38,6 +48,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Treat remote observation snapshots as untrusted input by enforcing schema and
size limits, owner-only permissions, authenticated UID binding, clean paths,
parent-directory checks, symlink rejection, and host-key/boot-ID identity.
- Reject a symlinked remote observation root before creating any managed cache
directories, preventing writes outside the intended cache tree.

### Fixed

Expand Down Expand Up @@ -273,7 +285,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- CI/CD workflow and automated release process
- Tag creation script

[Unreleased]: https://github.com/talkincode/sshx/compare/v0.0.12...HEAD
[Unreleased]: https://github.com/talkincode/sshx/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/talkincode/sshx/compare/v0.0.14...v0.1.0
[0.0.14]: https://github.com/talkincode/sshx/compare/v0.0.13...v0.0.14
[0.0.13]: https://github.com/talkincode/sshx/compare/v0.0.12...v0.0.13
[0.0.12]: https://github.com/talkincode/sshx/compare/v0.0.11...v0.0.12
[0.0.7]: https://github.com/talkincode/sshx/compare/v0.0.6...v0.0.7
[0.0.6]: https://github.com/talkincode/sshx/compare/v0.0.5...v0.0.6
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ ci: deps check test-coverage ## CI/CD workflow (deps, check, coverage)

tag:
@echo "🏷️ Starting tag creation process..."
@./scripts/tag.sh
@./scripts/tag.sh "$(TAG)"

renote:
@echo "🏷️ 开始更新release note..."
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ This pulls prebuilt binaries from the [talkincode/homebrew-tap](https://github.c
curl -fsSL https://raw.githubusercontent.com/talkincode/sshx/main/install.sh | bash
```

The installer verifies the release checksum and installs both the binary and
the matching Agent skill at `~/.agents/skills/sshx/SKILL.md`.

Or download and run:

```bash
Expand Down
3 changes: 3 additions & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ brew install talkincode/tap/sshx
curl -fsSL https://raw.githubusercontent.com/talkincode/sshx/main/install.sh | bash
```

安装脚本会校验 Release 校验和,并同时安装二进制和对应版本的 Agent skill
到 `~/.agents/skills/sshx/SKILL.md`。

或下载后运行:

```bash
Expand Down
16 changes: 8 additions & 8 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,8 @@ git push origin main
### 3. Create and Push Tag

```bash
# Create tag
git tag -a v1.0.1 -m "Release v1.0.1"

# Push tag to remote repository
git push origin v1.0.1
# Validate CHANGELOG, create the annotated tag, and push it
make tag TAG=v1.0.1
```

### 4. Automated Build
Expand All @@ -55,8 +52,10 @@ After pushing the tag, GitHub Actions will automatically:
- macOS x86_64 (Intel)
- macOS ARM64 (Apple Silicon)
- Windows x86_64
- Windows ARM64

2. ✅ Create compressed archives for each binary:
2. ✅ Create compressed archives containing each binary and the matching
`skills/sshx/SKILL.md`:

- Linux/macOS: `.tar.gz` format
- Windows: `.zip` format
Expand All @@ -80,7 +79,8 @@ https://github.com/talkincode/sshx/releases
Check:

- ✅ Release has been created
- ✅ All 5 platform binaries have been uploaded
- ✅ All 6 platform archives have been uploaded
- ✅ Every archive contains `SKILL.md`
- ✅ checksums.txt file exists
- ✅ Release notes are complete

Expand Down Expand Up @@ -221,7 +221,7 @@ Tasks:
1. Run tests on multiple operating systems
2. Generate code coverage reports
3. Run code checks (golangci-lint)
4. Run security scans (gosec)
4. Run security scans (`gosec` and `govulncheck`)

## Reference Resources

Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
module github.com/talkincode/sshx

go 1.24.0
go 1.25.10

require (
github.com/joho/godotenv v1.5.1
github.com/pkg/sftp v1.13.10
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3
github.com/stretchr/testify v1.11.1
github.com/zalando/go-keyring v0.2.6
golang.org/x/crypto v0.44.0
golang.org/x/term v0.37.0
golang.org/x/crypto v0.52.0
golang.org/x/term v0.43.0
)

require (
Expand All @@ -19,7 +19,7 @@ require (
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.37.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
Loading
Loading