Refuse to create a vault that already exists (closes #74) #82

Merged
clawbot merged 1 commits from issue-74-refuse-existing-vault into next 2026-10-04 08:25:41 +02:00
Collaborator

Fixes #74.

vault.CreateVault now refuses a vault that already exists, before writing anything, with vault NAME already exists; secret init reports it as failed to create default vault: vault default already exists. Both init and vault create call it while holding the state directory lock, so no other create can slip in between the check and the writes.

Both commands now ask for the unlocker passphrase before CreateVault writes anything, so one stopped at that prompt (mistyped confirmation, no terminal) leaves no vault behind, instead of a vault with no unlocker that they would then refuse to create again.

Tests: init again, vault create default and vault create work each leave the state directory byte-for-byte unchanged, and each vault's secret is then decrypted once through its passphrase unlocker. init and vault create stopped at the passphrase prompt each leave the state directory unchanged.

What the diff does not show:

  • Both commands ask for the mnemonic and the passphrase before refusing an existing vault, since the check runs after the prompts.
  • init still prints "Initialized secrets manager at" before the refusal.
  • A command killed after the prompt but before its unlocker is written still leaves a vault with no unlocker; TODO.md keeps that narrower exception for #75.
  • The lock tests set up their vault as work, since their init case needs default not to exist.

Judgement call: vault.ErrSecretExists is documented for secrets, so a new vault.ErrVaultExists sits beside vault.ErrVaultNotFound.

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/secret/issues/74. `vault.CreateVault` now refuses a vault that already exists, before writing anything, with `vault NAME already exists`; `secret init` reports it as `failed to create default vault: vault default already exists`. Both `init` and `vault create` call it while holding the state directory lock, so no other create can slip in between the check and the writes. Both commands now ask for the unlocker passphrase before `CreateVault` writes anything, so one stopped at that prompt (mistyped confirmation, no terminal) leaves no vault behind, instead of a vault with no unlocker that they would then refuse to create again. Tests: `init` again, `vault create default` and `vault create work` each leave the state directory byte-for-byte unchanged, and each vault's secret is then decrypted once through its passphrase unlocker. `init` and `vault create` stopped at the passphrase prompt each leave the state directory unchanged. What the diff does not show: - Both commands ask for the mnemonic and the passphrase before refusing an existing vault, since the check runs after the prompts. - `init` still prints "Initialized secrets manager at" before the refusal. - A command killed after the prompt but before its unlocker is written still leaves a vault with no unlocker; `TODO.md` keeps that narrower exception for https://git.eeqj.de/sneak/secret/issues/75. - The lock tests set up their vault as `work`, since their `init` case needs `default` not to exist. Judgement call: `vault.ErrSecretExists` is documented for secrets, so a new `vault.ErrVaultExists` sits beside `vault.ErrVaultNotFound`. Model: opus-5-5
clawbot self-assigned this 2026-10-04 06:23:20 +02:00
clawbot added the needs-review label 2026-10-04 06:23:23 +02:00
Author
Collaborator

FAIL (needs-rework)

  1. internal/cli/init.go and internal/vault/management.go (CreateVault): if the first secret init stops at the passphrase prompt (mistyped confirmation, Ctrl-C, no terminal), it leaves a default vault with no unlocker. Refusing that vault is right, since it may hold secrets. But the user is now stuck with no hint: secret init says vault default already exists, and secret vault rm default says cannot remove the last vault. The only way forward is secret unlocker add passphrase with SB_SECRET_MNEMONIC set, and nothing mentions it. Acceptable: a stopped first init does not leave the user there. For example, ask for the passphrase before CreateVault writes anything, or have the refusal of a vault without an unlocker name the command that finishes it.

  2. internal/cli/create_vault_test.go, the decryption loop inside each case: every case decrypts both vaults' secret through the passphrase unlocker, whose key derivation is slow on purpose. That makes this the slowest internal/cli test after the lock test, at about 7 s. REPO_POLICIES.md caps make test at 20 s, and #80 is cutting exactly this kind of cost. Each case has already shown the state directory is byte-for-byte unchanged, so the three rounds prove the same thing. Acceptable: decrypt each vault's secret once, not in every case.

  • Judgement call: adding vault.ErrVaultExists instead of reusing the existing sentinel is accepted. The only one, vault.ErrSecretExists, is defined for secrets.
  • Judgement call: asking for the mnemonic, and printing Initialized secrets manager at, before the refusal are accepted as disclosed in the PR body.
  • Reviewed rebased onto current next. The only conflict is in TODO.md.

Model: opus-5-5

**FAIL** (`needs-rework`) 1. `internal/cli/init.go` and `internal/vault/management.go` (`CreateVault`): if the first `secret init` stops at the passphrase prompt (mistyped confirmation, Ctrl-C, no terminal), it leaves a `default` vault with no unlocker. Refusing that vault is right, since it may hold secrets. But the user is now stuck with no hint: `secret init` says `vault default already exists`, and `secret vault rm default` says `cannot remove the last vault`. The only way forward is `secret unlocker add passphrase` with `SB_SECRET_MNEMONIC` set, and nothing mentions it. Acceptable: a stopped first `init` does not leave the user there. For example, ask for the passphrase before `CreateVault` writes anything, or have the refusal of a vault without an unlocker name the command that finishes it. 2. `internal/cli/create_vault_test.go`, the decryption loop inside each case: every case decrypts both vaults' secret through the passphrase unlocker, whose key derivation is slow on purpose. That makes this the slowest `internal/cli` test after the lock test, at about 7 s. `REPO_POLICIES.md` caps `make test` at 20 s, and https://git.eeqj.de/sneak/secret/issues/80 is cutting exactly this kind of cost. Each case has already shown the state directory is byte-for-byte unchanged, so the three rounds prove the same thing. Acceptable: decrypt each vault's secret once, not in every case. - Judgement call: adding `vault.ErrVaultExists` instead of reusing the existing sentinel is accepted. The only one, `vault.ErrSecretExists`, is defined for secrets. - Judgement call: asking for the mnemonic, and printing `Initialized secrets manager at`, before the refusal are accepted as disclosed in the PR body. - Reviewed rebased onto current `next`. The only conflict is in `TODO.md`. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-04 06:57:50 +02:00
clawbot force-pushed issue-74-refuse-existing-vault from 5586169396 to 0fbbc5332a 2026-10-04 07:04:54 +02:00 Compare
clawbot force-pushed issue-74-refuse-existing-vault from 0fbbc5332a to 549353b94f 2026-10-04 07:11:20 +02:00 Compare
clawbot force-pushed issue-74-refuse-existing-vault from 549353b94f to 0444d9aba5 2026-10-04 07:11:43 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-04 07:29:31 +02:00
Author
Collaborator

Reworked:

  1. secret init and secret vault create now ask for the unlocker passphrase before CreateVault writes anything, so a stop at that prompt leaves no vault; a new test covers both. The TODO.md exception now covers only a command killed after the prompt, and #75 is narrowed to match.
  2. internal/cli/create_vault_test.go decrypts each vault's secret once, after all cases; each case still compares the whole state directory.

Rebased onto current next; TODO.md keeps both entries.

Model: opus-5-5

Reworked: 1. `secret init` and `secret vault create` now ask for the unlocker passphrase before `CreateVault` writes anything, so a stop at that prompt leaves no vault; a new test covers both. The `TODO.md` exception now covers only a command killed after the prompt, and https://git.eeqj.de/sneak/secret/issues/75 is narrowed to match. 2. `internal/cli/create_vault_test.go` decrypts each vault's secret once, after all cases; each case still compares the whole state directory. Rebased onto current `next`; `TODO.md` keeps both entries. Model: opus-5-5
Author
Collaborator

PASS: vault.CreateVault now refuses an existing vault before writing anything, under the state directory lock, and secret init or secret vault create stopped at the passphrase prompt no longer leaves a vault behind.

  • Reviewed rebased onto current next; the only conflict is in TODO.md, where both entries are kept.

Model: opus-5-5

PASS: `vault.CreateVault` now refuses an existing vault before writing anything, under the state directory lock, and `secret init` or `secret vault create` stopped at the passphrase prompt no longer leaves a vault behind. - Reviewed rebased onto current `next`; the only conflict is in `TODO.md`, where both entries are kept. Model: opus-5-5
clawbot added 1 commit 2026-10-04 08:23:33 +02:00
vault.CreateVault now checks for the vault before writing anything and
fails with "vault NAME already exists" (vault.ErrVaultExists). secret
init and secret vault create call it while holding the state directory
lock, so two creates at once cannot both pass the check. Before, either
command over an existing vault replaced its metadata, passphrase
unlocker and longterm.age, so none of its secrets could be decrypted.

Both commands now ask for the unlocker passphrase before creating the
vault, so one stopped at that prompt leaves no vault without an
unlocker behind, which they would then refuse to create again.

The lock tests set up the vault "work" instead of "default", which init
now refuses to create again.

Model: opus-5-5
clawbot force-pushed issue-74-refuse-existing-vault from 0444d9aba5 to a3c9ceb2c9 2026-10-04 08:23:33 +02:00 Compare
clawbot merged commit fb4481b4f7 into next 2026-10-04 08:25:41 +02:00
clawbot deleted branch issue-74-refuse-existing-vault 2026-10-04 08:25:42 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/secret#82