term.ReadPassword reads the terminal with a raw read that returns 0
bytes at the end of input and takes that as nothing yet, so Ctrl-D at
the prompt did nothing. The prompt now sets the terminal as
ReadPassword does and reads the line from standard input, which
reports the end of input. Ctrl-D with nothing typed gives an empty
mnemonic, refused with status 1, and the terminal settings are put
back.
A test runs the tool on a pseudo-terminal (github.com/creack/pty, at a
commit past v1.1.24, whose Open on Linux can return another pty's
terminal) and types Ctrl-D at the prompt.
Model: opus-5-5
Every way a mnemonic could be invalid was reported as a checksum
failure, including an empty one. It is now refused as empty, as having
a word count other than 12, 15, 18, 21 or 24, as having a word that is
not on the BIP-39 word list, or as failing the checksum. A word not on
the list is named by its position, never by the word itself.
Judgement call: words are not lower-cased, so a capitalised word is
reported as not on the word list.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
ssh to now gives ssh -o IdentitiesOnly=no before the host, next to
-o IdentityAgent. With IdentitiesOnly yes in the user's ssh
configuration, ssh listed the agent's key but never offered it, and
the login failed with Permission denied. An option on the command
line wins over the configuration, and the tool's agent holds only the
derived key, so turning the option off adds no other key. A test
against the stand-in ssh requires the option before the host, and the
README's ssh to section names both options.
Judgement call: a user's own -o IdentitiesOnly=yes after the host is
now overridden.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
The mnemonic command ran with the tool's whole environment, so a
mnemonic set in KEYFUNC_MNEMONIC reached it, although the command is
there to supply one of its own. It now gets the environment ssh and
sftp already get, with KEYFUNC_MNEMONIC and KEYFUNC_MNEMONIC_COMMAND
removed. The function that builds that environment moves from the ssh
command group into internal/mnemonic, beside the two variable names,
so all three programs use the one copy. The README says so.
A mnemonic command that runs keyfunc itself no longer finds the main
mnemonic in either variable.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
sneak/secret now carries its own copy of go-bip39
(sneak/secret#130), so requiring it at that
commit, ef0ae90, lets go mod tidy drop go-bip39 from go.sum. Earlier,
secret renamed its module to sneak.berlin/go/secret
(sneak/secret#43), so the requirement, the
two imports of its bip85 package and the README sentence naming that
package move to the new path. The bip85 functions keyfunc calls are
unchanged, and no other requirement moves.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
go-bip39's repository no longer exists, so keyfunc now carries the
part of v1.1.0 it uses, with the upstream LICENSE beside it, and
imports it from internal/bip39. Upstream's tests and test vectors for
the kept code come along unchanged; where they called a removed
function, crypto/rand stands in for NewEntropy and EntropyFromMnemonic
for MnemonicToByteArray.
Changes beyond the trimming are what the linter asked for: the
package-level variables moved into the functions that use them, three
error strings were lower-cased, and the unknown-word error now wraps
ErrInvalidMnemonic.
go.mod no longer requires go-bip39. go mod tidy keeps its two go.sum
lines, because a test in sneak/secret's bip85 package imports it, and
drops six lines that only go-bip39's own requirements needed.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
package.json now carries "license": "MIT", as the sneak/prompts template does since 2026-10-04, so yarn install in script/bootstrap no longer warns about a missing license field on every image build.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
ssh install now takes the host alone before --: any other word there, or a second argument without --, is refused before the mnemonic is read or sftp runs, so keyfunc ssh install alice@host frank@host no longer installs the key for frank@host. The first listing of ~/.ssh is now ls -n, which shows the file type, so a symlinked authorized_keys is refused before any upload instead of being replaced by a regular file; the README says so.
Judgement calls: install -- host is refused; a symlinked authorized_keys is refused even when its target already holds the key.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
A signal during ssh install now sends sftp a SIGTERM instead of killing it, as ssh to already does for ssh, so sftp stops the ssh it started. The sftp command also has a WaitDelay of a quarter second, so a child that still holds sftp's output cannot keep the tool waiting on a host that does not answer: it ends with status 1 and removes its working directory. The test's sftp stand-in now leaves such a child.
Judgement call: exec.ErrWaitDelay counts as success, so a run that used ControlPersist does not report a failure for a key it added.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
The README now gives the child mnemonic keyfunc prints for the abandon ... about test mnemonic at index 0, checked by the README vectors test; the BIP-85 specification vector stays, marked as starting from a master key keyfunc cannot take. It also says ssh install needs the host key in known_hosts already, and how to get round that. ssh install now also lists ~/.ssh/. on its first connection and refuses, before any upload, a ~/.ssh it can read but not enter, which sftp shows as empty; a file where ~/.ssh belongs is refused the same way.
Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>