Summary
pond creds add's interactive wizard never prompts for a scope, so it always writes a scope-less (catch-all) credential set. When a catch-all [creds.default] already exists, adding any second set fails validation:
Error: the resulting config would be invalid; nothing written
Caused by:
[creds.default] and [creds.work] are both scope-less; at most one catch-all set is allowed - add a `scope` to one
Net effect: once you have a [creds.default], the wizard can only replace it - you cannot add a second credential set (e.g. for a different bucket/scope) interactively. You have to hand-edit config.toml or use the non-interactive --scope path.
Repro
- Have a config with a scope-less
[creds.default].
- Run
pond creds add, accept a new name (e.g. work), enter an access key + secret.
- It exits non-zero with the "at most one catch-all set is allowed" error; nothing is written.
Root cause
src/main.rs creds add wizard collects name -> (replace-confirm) -> Access key ID -> Secret access key, then calls set_creds_set(.., scope.as_deref(), ..) with scope = None (the wizard never collects a scope). set_creds_set already supports a scope param and the validation in Config::load_str already enforces "at most one catch-all set" - only the interactive prompt is missing.
Suggested fix (small, ~10-15 lines)
When adding a new set (not replacing) and a catch-all set already exists, prompt for an optional scope:
cliclack::input("Scope (S3 URL prefix; blank = catch-all)")
Treat blank as None and pass it through to set_creds_set. Alternatively, catch the "two catch-all" validation error and re-prompt for a scope. The first (only-prompt-when-needed) keeps the common first-set case noise-free.
Notes
- Pre-existing UX gap; surfaced by the CLI e2e harness (
ops/e2e/run.py), where the creds add wizard test now intentionally exercises the replace-default path and documents this limitation.
- Low severity: the wizard still works for the first set and for key rotation (replace); only "add a 2nd scoped set interactively" is unsupported.
Summary
pond creds add's interactive wizard never prompts for a scope, so it always writes a scope-less (catch-all) credential set. When a catch-all[creds.default]already exists, adding any second set fails validation:Net effect: once you have a
[creds.default], the wizard can only replace it - you cannot add a second credential set (e.g. for a different bucket/scope) interactively. You have to hand-editconfig.tomlor use the non-interactive--scopepath.Repro
[creds.default].pond creds add, accept a new name (e.g.work), enter an access key + secret.Root cause
src/main.rscreds addwizard collects name -> (replace-confirm) -> Access key ID -> Secret access key, then callsset_creds_set(.., scope.as_deref(), ..)withscope = None(the wizard never collects a scope).set_creds_setalready supports a scope param and the validation inConfig::load_stralready enforces "at most one catch-all set" - only the interactive prompt is missing.Suggested fix (small, ~10-15 lines)
When adding a new set (not replacing) and a catch-all set already exists, prompt for an optional scope:
Treat blank as
Noneand pass it through toset_creds_set. Alternatively, catch the "two catch-all" validation error and re-prompt for a scope. The first (only-prompt-when-needed) keeps the common first-set case noise-free.Notes
ops/e2e/run.py), where thecreds addwizard test now intentionally exercises the replace-default path and documents this limitation.