Commit Graph
23 Commits
Author SHA1 Message Date
clawbot 1ff02f252d make fmt and make fmt-check cover Markdown with prettier (closes #39)
check / check (push) Successful in 2m41s
script/fmt now runs go fmt and then prettier --write on every Markdown
file; script/fmt-check runs the gofmt check and then prettier --check.
prettier is pinned in package.json and yarn.lock and configured in
.prettierrc with four-space indents and prose wrapped at 80 columns,
all copied from the sneak/prompts templates. After Go and the Go
modules, script/bootstrap uses an installed node or else a pinned one
through a hash-verified nvm archive, then an installed yarn or else a
pinned one, then the yarn dependencies. README.md is reformatted by
make fmt, and its Entrypoints section says what fmt, fmt-check and
bootstrap now do.

Model: opus-5-5
2026-10-04 03:10:16 +00:00
clawbot 7280ee35f4 script/bootstrap installs a pinned Go so script/cibuild runs on the Gitea runner (closes #46)
check / check (push) Successful in 3m12s
script/cibuild runs script/bootstrap on the Gitea runner, which has Docker and git but no Go, and bootstrap could not install it: its apt path never ran apt-get update. Bootstrap now installs Go at the version in the Dockerfile's golang image from go.dev, checked against sha256 values in the script, into ~/.local/go whenever the go first on PATH reports another version; the Makefile and the fmt, fmt-check and precommit scripts put ~/.local/go/bin first on their PATH. apt-get update runs once before the first apt install. Bumping the golang digest now means bumping GO_VERSION and its four hashes.

Partially verified: checked in a clean ubuntu:24.04 container, not yet on the Gitea runner.

Model: opus-5-5
2026-10-04 05:06:31 +02:00
clawbot 90596de901 Lint and test as phases of the Dockerfile (closes #38)
check / check (push) Failing after 3s
Lint and test are now phases of the one Dockerfile, as the current repo policy requires: a lint phase on the pinned golangci-lint image and a test phase on the pinned Go image, and the build stage depends on both, so a plain docker build . fails when either fails. Dockerfile.lint is gone. REPO_POLICIES.md and script/lint, test, docker and cibuild are byte-identical to the current sneak/prompts copies, so every docker build in script/ is uncached and tagged. make test now needs Docker on the host; formatting is checked on the host only.

Judgement calls: the test phase installs gcc and musl-dev unpinned for -race; no -count=1, since a build stage holds no earlier result.

Model: opus-5-5
2026-10-04 02:59:03 +02:00
clawbot d6b87f7502 The linter config, .gitignore and .dockerignore from the current templates (closes #40)
check / check (push) Successful in 1m32s
.golangci.yml is now a byte-identical copy of the current template: depguard is on with the test-support rule, and the gomodguard_v2 block list is in. .gitignore and .dockerignore are the current templates with this repo's own entries added at the end; the .dockerignore secret-file patterns now keep key files and .env files out of the build context, while .git stays in for the version stamp. No Go source needed changes.

Model: opus-5-5
2026-10-04 02:25:50 +02:00
clawbot 8d1c873bb7 Move the Go module to sneak.berlin/go/keyfunc (closes #15)
check / check (push) Successful in 1m33s
The module path becomes sneak.berlin/go/keyfunc, as the repo policy sets for Go modules: go.mod, every import and the -X path in the Makefile. The old path gets no alias. The README gives a go install line for the new path, which resolves with @latest only once main carries the move, and its TODO list now names the open 1.0 issues.

Model: opus-5-5
2026-10-04 01:42:43 +02:00
clawbot f8c796db9b Add the MIT license (closes #14)
check / check (push) Successful in 1m52s
Adds LICENSE with the standard MIT text and "Copyright (c) 2026 sneak", the license sneak chose. The README now calls keyfunc MIT-licensed in its first paragraph, its License section points at LICENSE, and the license line leaves the TODO list.

Model: opus-5-5
2026-10-03 14:42:59 +02:00
clawbot 7f7fe33cd6 Stamp the git tag or short commit in a plain docker build (closes #35)
check / check (push) Successful in 1m6s
.dockerignore left out .git, so make build inside the image fell back to
"dev". The build context now carries .git, without its config, which can
hold a credential in the remote URL. The build stage takes the VERSION
build argument when one is given, otherwise git describe --tags --always,
and fails if the context carries .git and no version comes out.

Model: opus-5-5
2026-10-02 06:12:05 +02:00
clawbot dd14677145 README checked against the tree by running every example (closes #22)
check / check (push) Successful in 42s
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)
2026-09-21 18:07:36 +02:00
clawbot ace7846d57 Use gomodguard_v2 in the linter config (closes #31)
check / check (push) Failing after 1s
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)
2026-09-21 17:58:20 +02:00
clawbot 40e9beea8c Clean up the agent socket and working files when a signal ends the tool (closes #17)
check / check (push) Successful in 5s
`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)
2026-09-21 16:58:24 +02:00
clawbot 15ebe24f7b README: policy sections and age and child mnemonic vectors (closes #21)
check / check (push) Successful in 6s
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)
2026-09-21 16:24:28 +02:00
clawbot 64dcc7f42b Keep the mnemonic out of the ssh and sftp children (closes #16)
check / check (push) Failing after 0s
`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)
2026-09-21 14:58:26 +02:00
clawbot 3d90ac87f1 ssh install tells a missing .ssh from one it cannot enter (closes #10)
check / check (push) Failing after 0s
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)
2026-09-21 09:49:59 +02:00
clawbot e6ddf49acc Report the module version for a go install build (closes #18)
check / check (push) Successful in 1m41s
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)
2026-09-21 09:39:38 +02:00
clawbot 860e590114 Update dependencies to current releases (closes #19)
check / check (push) Failing after 1s
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)
2026-09-21 09:34:26 +02:00
clawbot 63575ce827 Move the entrypoint to cmd/keyfunc (closes #20)
check / check (push) Successful in 16s
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)
2026-09-21 09:25:38 +02:00
clawbot 8aeed7b901 ssh install works over sftp and runs nothing on the host (closes #10)
check / check (push) Successful in 4s
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)
2026-09-08 08:20:17 +02:00
sneak a2a0890ded Merge pull request '0.1.0: deterministic SSH keys, age identities with encryption, and child mnemonics from one mnemonic' (#9) from next into main
check / check (push) Successful in 22s
Reviewed-on: #9
2026-09-08 04:49:04 +02:00
clawbot b9c8631788 The ssh install and ssh to commands (closes #2)
check / check (push) Successful in 2m30s
keyfunc ssh install appends the public line on a host through the system ssh, only when absent, feeding the line on standard input; keyfunc ssh to serves the derived key from an in-process agent on a private socket and runs the system ssh with it, the private key never on disk. Two review rounds; the second passed with no findings.

Model: opus-5 (implementation and review); fable-5-1 (landing)
2026-09-07 18:49:42 +02:00
clawbot 5bbeec86d6 The age commands: pub, priv, encrypt and decrypt (closes #3)
check / check (push) Successful in 28s
keyfunc age derives an age identity at the generic path the way secret's agehd does, prints the recipient or the identity, and encrypts to or decrypts with it, the derived recipient always among encrypt's recipients. Two review rounds; the second passed with no findings, the clamping step now pinned by a fixed identity test.

Model: opus-5 (implementation and review); fable-5-1 (landing)
2026-09-07 18:35:11 +02:00
clawbot d69bed722a The mnemonic command: child mnemonics (closes #4)
check / check (push) Successful in 4s
keyfunc mnemonic derives a 12, 18 or 24 word child mnemonic through BIP-85's own mnemonic application, with the specification's test vectors as tests. One review round, passed with no findings; the reviewer reproduced the vectors from an implementation written from the specification alone.

Model: opus-5 (implementation and review); fable-5-1 (landing)
2026-09-07 18:05:08 +02:00
clawbot 279cba6bcf Skeleton, mnemonic input, derivation, and the ssh pub and priv commands (closes #1)
check / check (push) Successful in 5s
The module, the script entrypoints and Makefile, Docker-only linting, the mnemonic sources in the specified order with their refusals, the BIP-85 derivation, and keyfunc ssh pub and priv with the README test vectors as tests. Two review rounds; the second passed with no findings.

Model: opus-5 (implementation and review); fable-5-1 (landing)
2026-09-07 17:34:54 +02:00
clawbot c3fd26a1de keyfunc: the specification
The README is the specification sneak approved on 2026-09-07, moved
here from the hacks repository: deterministic SSH keys, age identities
with encrypt and decrypt, and child mnemonics, all derived from one
BIP-39 mnemonic and stored nowhere.

Model: fable-5-1
2026-09-07 14:27:57 +00:00