Defaults and lifecycle
One default per owner and provider, explicit transitions, and what disable/delete really do.
A stored credential is active or disabled, may be its owner's default, and can be soft-deleted. These three axes drive everything resolve does, so their invariants are strict.
The default flag
is_default marks the record resolve picks for its owner and provider. The invariant: at most one active default per (provider, owner), enforced transactionally.
make-defaultswitches atomically: the previous default loses the flag in the same transaction; there is never a moment with two, or a gap with none. Onlyactiverecords qualify (400otherwise). Calling it on the current default is a version-checked no-op.- The owner's first credential for a provider becomes the default automatically — no flag needed; a second create without the flag leaves the default where it was.
createwithmake_default: truetakes the default over for a brand-new record with the same atomic switch. The409 DEFAULT_CREDENTIAL_ALREADY_EXISTSyou see documented is a concurrency guard — two such creates racing — not the normal answer; retry once and one of them holds the flag.
Disable and enable
disable pauses a record without destroying it: resolve stops seeing it immediately, and is_default clears in the same transaction. When disabling your current default you may pass replacement_default_id — another active record of the same owner and provider — to promote it atomically; without it the owner is deliberately left with no default (and resolve falls through to the next level).
enable returns the record to active but does not restore the default flag — re-promotion is an explicit make-default. A restored key silently becoming the live default again is exactly the surprise this rule prevents.
Delete
DELETE is a soft delete: instant disappearance from lists, reads, resolve, and reveal; is_default clears; the ciphertext survives a recovery window (default 5 days) until the purge job removes it physically (audited as credential.purged). Deleting your current default accepts the same replacement_default_id. Repeating the delete on your own deleted record: idempotent 204. No version required.
Optimistic locking ties it together
PATCH, make-default, disable, and enable all demand the current version; each success increments it. Two admins editing the same record cannot silently overwrite each other — the loser gets 409 CREDENTIAL_VERSION_CONFLICT and re-reads. The full convention: Errors and Conventions.
Worked example of the whole dance — two keys, a switch, a disable with replacement: cookbook scenario 04.