# Workflow - branch from `next` - do the Next Step: the next open issue in the `1.0.0` milestone - log it at the top of Completed Steps - commit (`TODO.md` changes in the same commit as the work) - push, and open a PR against `next` # Status pre-1.0. No git tags. Open work is tracked on the issue tracker, which is authoritative. # Next Step Take the next open issue in the `1.0.0` milestone: https://git.eeqj.de/sneak/secret/milestone/12 # Completed Steps - 2026-10-04: A failed `secret unlocker add keychain` or `secret unlocker add secure-enclave` no longer leaves its keychain item or Secure Enclave key behind (https://git.eeqj.de/sneak/secret/issues/89). `CreateSecureEnclaveUnlocker` gets the long-term key before it creates the Secure Enclave key, so that a wrong passphrase creates none, and deletes the key again if encrypting with it or writing the unlocker then fails. `macse.CreateKey` finds the new key's hash right after `sc_auth` creates it, and fails with an error naming the key's label if it cannot; it deletes the key again if getting its public key then fails. The Objective-C was only read, never compiled or run, and so was `macse_darwin.go`, which is cgo only. `CreateKeychainUnlocker` writes all of the unlocker's files, the metadata among them, before it stores the item in the keychain, and deletes the item again if moving the unlocker into place then fails. A failure to delete is reported along with the first error. The tests of this run only on macOS: the Secure Enclave one in a build with cgo on a Mac with a Secure Enclave, the keychain one in a build with cgo. - 2026-10-04: What a command killed part-way left under a `.tmp-` name (https://git.eeqj.de/sneak/secret/issues/75), the temporary directories of `secret.TempDirFor` and the temporary files of `secret.WriteFileAtomic`, encrypted keys included, is deleted by the next command that takes the state directory lock. Before, it stayed until deleted by hand. A command writes `finished` into the lock file just before it releases the lock; the next one to take the lock searches only when it does not find that, so after a command that finished nothing is searched, however many secrets and versions there are. The search looks in the state directory, each vault, each secret and each version, the only directories those helpers make them in. A command that only reads takes no lock and deletes nothing. A failure to delete is warned about and the command goes on. An unlocker directory with no metadata file was already removed by `secret unlocker remove` given its directory name; a test now shows it. - 2026-10-04: An age identity's private key goes into a locked buffer through `secret.IdentityToLockedBuffer` everywhere (https://git.eeqj.de/sneak/secret/issues/38): the vault's long-term key when a passphrase, PGP, keychain or Secure Enclave unlocker is created, the new unlocker's own key, a new secret version's key, and the key `secret encrypt` generates. Before, each place converted the string age returns to bytes and left the string in ordinary memory. The function moves the string's own bytes into the buffer, which overwrites them; the copies age makes while writing the string remain, as its comment says. The 1.0 memory-security entry below no longer lists these places, `internal/cli/crypto.go` among them, nor `version.go:155`, which was `internal/secret/version.go`, not `internal/cli/version.go`. - 2026-10-04: `script/lint-darwin` (`make lint-darwin`) runs `go vet` and `golangci-lint` in docker on the code as a macOS build compiles it (`GOOS=darwin`), with cgo off (https://git.eeqj.de/sneak/secret/issues/50). `script/check` runs it, and the `Dockerfile` lint stage runs its commands, so `script/cibuild` does too. Before, CI on Linux never compiled the files built only for macOS. Compiling cgo code for macOS needs Apple's SDK headers, and both `internal/macse` and `github.com/keybase/go-keychain` are cgo on macOS. So the three functions that call `go-keychain` moved from `keychainunlocker.go` to `keychainunlocker_cgo.go`, built only with cgo on macOS like `macse_darwin.go`. A macOS build without cgo, which before did not compile, gets `keychainunlocker_nocgo.go` and the `macse` stub instead, whose errors say the keychain or Secure Enclave needs a macOS build with cgo. The check covers the rest of the keychain unlocker, the Secure Enclave unlocker and the macOS-only tests other than `keychainunlocker_test.go`, whose lint findings are fixed. For the length and complexity limits, parts of `GetIdentity`, `getLongTermPrivateKey` and `CreateKeychainUnlocker` moved into functions of their own, and the Secure Enclave unlocker derives the long-term key from the mnemonic through the same function as the keychain unlocker instead of a copy of it. Lines over 88 columns in the files the check cannot see are wrapped. - 2026-10-04: `secret rm`, `secret version rm`, `secret vault remove` and `secret unlocker remove` ask `[y/N]` before removing anything (https://git.eeqj.de/sneak/secret/issues/39), naming what they remove: the secret, its vault and its version count; the version, secret and vault; the vault and its secret count; the unlocker, its vault and whether it is the last, and for the last the vault's secret count and that the vault then opens only with its mnemonic. Only `y` or `yes` goes ahead. Without `--force`, a command whose stdin is not a terminal fails at once. `--force` (now also on `rm` and `version rm`) removes without asking; it replaces the old refusals to remove a vault with secrets or the last unlocker of one without `--force`, which the question now covers. The checks run, and the question is asked, before the state directory lock is taken; under the lock the checks run again, and if they would ask a different question, nothing is removed. `secret rm` fails when it cannot count the versions. - 2026-10-04: A crash while an unlocker is being replaced no longer leaves a current unlocker that cannot open the vault (https://git.eeqj.de/sneak/secret/issues/71). Every new unlocker gets a directory of its own, named with the time to the nanosecond: `passphrase-