1.0: ssh install that never replaces what it did not read, current dependencies, a real --version #28

Open
clawbot wants to merge 10 commits from next into main
Collaborator

What is on next, all reviewed and green under make check; safe to merge at any time.

  • keyfunc ssh install works over sftp and runs nothing on the host. It lists ~/.ssh before fetching, treats the file as empty only when sftp reports the directory or the file as missing, and writes nothing when either is there but unreadable. An existing ~/.ssh keeps its mode. This is the defect #12 was closed for.
  • ssh to and ssh install no longer hand KEYFUNC_MNEMONIC or KEYFUNC_MNEMONIC_COMMAND to the ssh and sftp they start, so the mnemonic cannot be read from the child's environment or forwarded by a SendEnv line.
  • SIGINT, SIGTERM or SIGHUP no longer leaves files behind: ssh to removes its agent socket and directory, ssh install removes its working copy of authorized_keys, and ssh is stopped with SIGTERM so it restores the terminal.
  • Every direct dependency is at its current release, golang.org/x/crypto included (the agent behind ssh to). No test vector changed, so no derived key moves.
  • --version reports the module version for a go install build.
  • The entrypoint is cmd/keyfunc/main.go; make build still writes ./keyfunc.
  • The README has the sections the repo policy requires and publishes test vectors for age and child mnemonics. It names no license: it says the choice is open.

To know before merging:

  • ssh install uses sftp batch mode, so a key or an agent must authenticate; it cannot prompt for a password. Options after -- go to sftp (the port is -P).
  • Every README example was run as written. ssh install and ssh to were run by the implementer against a throwaway local sshd; the reviewer could not repeat that run, and neither command has been run against a remote host.

Nothing else is queued for 1.0. Waiting on you: the license (#14) and the module path (#15).

Model: fable-5-1

What is on `next`, all reviewed and green under `make check`; safe to merge at any time. - `keyfunc ssh install` works over `sftp` and runs nothing on the host. It lists `~/.ssh` before fetching, treats the file as empty only when `sftp` reports the directory or the file as missing, and writes nothing when either is there but unreadable. An existing `~/.ssh` keeps its mode. This is the defect https://git.eeqj.de/sneak/keyfunc/pulls/12 was closed for. - `ssh to` and `ssh install` no longer hand `KEYFUNC_MNEMONIC` or `KEYFUNC_MNEMONIC_COMMAND` to the `ssh` and `sftp` they start, so the mnemonic cannot be read from the child's environment or forwarded by a `SendEnv` line. - SIGINT, SIGTERM or SIGHUP no longer leaves files behind: `ssh to` removes its agent socket and directory, `ssh install` removes its working copy of `authorized_keys`, and `ssh` is stopped with SIGTERM so it restores the terminal. - Every direct dependency is at its current release, `golang.org/x/crypto` included (the agent behind `ssh to`). No test vector changed, so no derived key moves. - `--version` reports the module version for a `go install` build. - The entrypoint is `cmd/keyfunc/main.go`; `make build` still writes `./keyfunc`. - The README has the sections the repo policy requires and publishes test vectors for age and child mnemonics. It names no license: it says the choice is open. To know before merging: - `ssh install` uses `sftp` batch mode, so a key or an agent must authenticate; it cannot prompt for a password. Options after `--` go to `sftp` (the port is `-P`). - Every README example was run as written. `ssh install` and `ssh to` were run by the implementer against a throwaway local `sshd`; the reviewer could not repeat that run, and neither command has been run against a remote host. Nothing else is queued for 1.0. Waiting on you: the license (https://git.eeqj.de/sneak/keyfunc/issues/14) and the module path (https://git.eeqj.de/sneak/keyfunc/issues/15). Model: fable-5-1
clawbot added the needs-review label 2026-09-21 09:50:39 +02:00
clawbot self-assigned this 2026-09-21 09:50:39 +02:00
clawbot added 5 commits 2026-09-21 09:50:39 +02:00
ssh install no longer runs a command on the host. It reads .ssh/authorized_keys over sftp, takes the empty reading only from sftp's own message about that path, appends the derived key locally when it is not already present, uploads the result beside the file with mode 0600 and renames it over the original. Any other failure prints what sftp said, writes nothing and exits 1. sftp batch mode disables password prompts, so a key or agent is required; a directory the owner cannot enter reads as a host with no file, which README.md states.

Model: opus-5 (implementation); fable-5-1 (landing)
Move the entrypoint to cmd/keyfunc (closes #20)
check / check (push) Successful in 16s
63575ce827
main.go moves unchanged to cmd/keyfunc/main.go, where REPO_POLICIES.md puts Go entrypoints, and the Makefile build target builds ./cmd/keyfunc. The binary is still written to ./keyfunc and still carries the stamped version.

Model: opus-4-8 (implementation); fable-5-1 (summary)
Every direct dependency moves to its current release, golang.org/x/crypto first: keyfunc ssh to serves keys through its ssh/agent package, which has had security fixes since the pinned 2025-05 version. go.mod and go.sum only; no code changed and the SSH, age and child mnemonic test vectors pass unedited, so no derived key moves.

Model: opus-4-8 (implementation); fable-5-1 (summary)
keyfunc --version printed dev for any binary not built with make build. When no version was stamped at build time, the tool now reports the module version recorded in the binary's build info, which go install fills in. A stamped version still wins, and a local build with neither still prints dev.

Model: opus-4-8 (implementation); fable-5-1 (summary)
The first sftp session now lists .ssh before fetching authorized_keys. The file reads as empty only when sftp reports .ssh itself as missing, or the listing succeeded and the file is reported missing. A directory or file that is there but cannot be read fails the run and nothing is written, so no existing authorized_keys is replaced by content that was not built from what was read. An .ssh that already exists keeps its mode; the directory is made and set to 0700 only when none was found. The README describes the rule and states batch mode's limit: a key or an agent must authenticate.

Model: opus-4-8 (implementation); fable-5-1 (summary)
clawbot added 1 commit 2026-09-21 14:58:28 +02:00
`keyfunc ssh to` and `keyfunc ssh install` started the system `ssh` and `sftp` with the tool's whole environment, so a mnemonic given in `KEYFUNC_MNEMONIC` stayed readable in the child's environment and could be forwarded to the host by a `SendEnv` line. Both children now get the environment with `KEYFUNC_MNEMONIC` and `KEYFUNC_MNEMONIC_COMMAND` removed, through one helper, `childEnv`, in the ssh cli package. The mnemonic command still runs with the full environment. Two tests drive the real commands against the stand-in `ssh` and `sftp` and check that a third variable still arrives.

Model: opus-4-8 (implementation, review); fable-5-1 (merge message)
clawbot added 1 commit 2026-09-21 16:24:30 +02:00
The README gains the sections REPO_POLICIES.md requires: a first sentence naming the category and author, Getting Started, Entrypoints (one line per `script/` file), Rationale, Design, TODO (the open issues between the tree and 1.0), License and Author. It also publishes test vectors for age and child mnemonics, copied from the tests.

Disclosures:
- No license is named; the choice is open on the tracker and the README says so.
- The 12-word child mnemonic is the BIP-85 specification vector, the only one the test asserts, and is labelled as such.
- Markdown is hand-wrapped; `make fmt` here formats Go only.

Model: opus-4-8 (implementation, review); fable-5-1 (merge message)
clawbot added 1 commit 2026-09-21 16:58:27 +02:00
`cli.Main` ran the command tree on a background context, so SIGINT, SIGTERM or SIGHUP killed the process before deferred cleanup ran: `ssh to` left its agent socket and directory behind, and `ssh install` left a copy of the host's `authorized_keys` in its working directory. `Main` now runs the tree on a `signal.NotifyContext` for those signals; the cancelled context ends the child `ssh` or `sftp` and the cleanup runs. `ssh to` stops its child with SIGTERM, not a kill, so `ssh` restores the terminal. Exit status after a signal is 1 unless `ssh` reported its own.

The test re-runs the test binary as the tool, waits for the agent socket, sends each signal and checks the directory is gone.

Disclosure: the repeated `"uptime"` test literal became a `remoteCommand` constant because `goconst` required it.

Model: opus-4-8 (implementation, review); fable-5-1 (merge message)
clawbot added 1 commit 2026-09-21 17:58:21 +02:00
golangci-lint 2.12 deprecated `gomodguard` in favour of `gomodguard_v2` and printed a warning on every `make check`. `.golangci.yml` now disables the old name, the same way it already handles `wsl` and `wsl_v5`. With `linters.default: all` the replacement was already enabled, so what is checked does not change; only the warning goes.

Model: opus-4-8 (implementation, review); fable-5-1 (merge message)
clawbot added 1 commit 2026-09-21 18:07:38 +02:00
Every example in the README was run as written with the published test mnemonic and behaved as the README says, so no sentence changed. The only edit removes the landed work from the TODO section, which now lists the two open owner decisions.

Disclosures:
- `ssh install` and `ssh to` were run by the implementer against a throwaway local `sshd`; the reviewer could not repeat that run and checked those sections by reading the code.
- The child mnemonic vector is reachable only through the test suite and was confirmed there.

Model: opus-4-8 (implementation, review); fable-5-1 (merge message)
All checks were successful
check / check (push) Successful in 42s
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin next:next
git checkout next
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/keyfunc#28