auth: records carry an account identity; set-identity and import set it, replace keeps it
Two oauth records imported from a consumer's auth store serve account_id: null, and a quota consumer that labels accounts per provider collapses identity-less accounts of one provider into a single unlabelled row. The read surface already serves a stored RecordIdentity (the login path captures it); this adds the write side for records that arrive by import, without touching secret material.
ck auth import --account-id <id> [--email] [--org-name] | --clear-identityon every import source.ck auth set-identity <credential-id> --account-id … | --clear— identity-only mutation: decrypts, attaches the label, re-seals the SAMEOAuthCredential(pinned field-for-field with non-empty scopes and client_id), keeps handles, state andstale_pending, bumpsrecord_versiononce, audited. Exists becauseimport --replacereads a source file and would overwrite a vault-custodied token family with stale material.import --replacewithout identity flags PRESERVES the existing record's identity at the store layer (both the offline path and the connected admin op), so an unattended re-import from a rotating source keeps the label;--clear-identitydrops it; a corrupt existing record is still repaired, with the incoming identity only.RecordIdentity::validateruns at every record write sink (create, CAS overwrite, identity-policy overwrite, set-identity): non-empty, no control chars, ≤256 bytes; email-without-account_id is normalised away per the existingwith_identityrule.ck auth usableshowsaccount=<id|none>per oauth row; runbook updated.
Gate: bash scripts/gate.sh passes (workspace floor 484, debug profile); core 316, cli_admin 31+1 ignored, clippy, fmt. Mutations red-checked: preservation removed → sticky test red; store-level validation removed → connected-route test red; scopes cleared / client_id dropped in set-identity → OAuth equality red; stale_pending cleared → invariant red.
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.
Summary by cubic
Adds account identity metadata to imported OAuth records and a set-identity command. Token-only import --replace now preserves existing identity instead of dropping it, while explicit identity flags override or clear it; set-identity changes metadata without replacing token material.
ck auth importaccepts--account-id,--email,--org-name, and--clear-identity; email and organization name require an account ID.- Identity conflicts are refused: replace won't preserve a label when the incoming account ID conflicts, and a supplied
--account-idthat contradicts the incoming token's own claim is refused on create. set-identityre-seals the same OAuth credential, keeps handles, lifecycle state, andstale_pending, incrementsrecord_version, and writes an audit event.- Identity validation runs at every record write, normalizes legacy email-only identities, and limits fields to non-empty values without control characters or over 256 bytes.
- A separate admin operation makes older daemons refuse identity-aware replacement instead of silently dropping the label; the legacy
admin.storewire shape stays byte-frozen. ck auth usabledisplaysaccount=<id|none>and renders invalid stored identity metadata as<invalid>.