Split out of #8081 scope item 3 by the domain:services PM seat. The maintainer's Option-A ruling on #7990 put this in scope of the programme — "The existing-rows migration is in scope of the programme, not optional" — and #8081's dispatch order asked for the story, not its execution. This card carries the proposal the dev produced. ⛔ Not graded by me; no domain:* label.
Blocked-by:#8153
An inventory already exists, and it is free
/meta re-parses every stored row and badges it _diagnostics: {valid:false, errors:[…]} carrying the full refusal message. Operators can already enumerate the affected rows — the migration needs no discovery phase, which removes the usual first half of a migration card.
⚠️ That also creates a coupling worth stating: #8154 (redacting the /meta read path) must not strip or break _diagnostics, or this inventory disappears.
Recommended shape — (b) admin tooling, per-datasource, operator-initiated
A Setup action — "Move credential to the secret store" — that reads the stored cleartext, binds it through the existingSecretBinder.bind(), writes external.credentialsRef, and removes the inline key only after the ref is durably written.
Is it destructive? No, and deliberately so. No credential value is discarded — it is re-homed from cleartext into sys_secret. The inline key is deleted only once the encrypted row exists. It is per-datasource and operator-initiated, so there is no sweep over a table. That matters: the ruling's "escalate if destructive" clause is what this shape is designed to stay clear of.
Rejected, with reasons
⚠️ Sequencing — this is blocked, and running it early makes things worse
The migration's entire output is external.credentialsRef on an existing row. Per #8153, writing that onto a row whose schemaMode defaults to 'managed' is exactly the shape that fails re-parse.
⇒ Running this before #8153 is settled would convert every affected managed datasource from "invalid because it holds cleartext" to "invalid because it holds a credentialsRef" — a migration that moves rows from one invalid state to another while appearing to succeed. #8153 first.
Constraints
Provenance: #8081 dev report (comment 5269923121, §③), #7990 maintainer ruling (comment 5266068845), my ACCEPT receipt (#8081 comment 5270102954). Related: #8103 (the same destructive-sweep boundary, for sys_secret orphans), #8154, #8152.
Split out of #8081 scope item 3 by the
domain:servicesPM seat. The maintainer's Option-A ruling on #7990 put this in scope of the programme — "The existing-rows migration is in scope of the programme, not optional" — and #8081's dispatch order asked for the story, not its execution. This card carries the proposal the dev produced. ⛔ Not graded by me; nodomain:*label.Blocked-by:#8153An inventory already exists, and it is free
/metare-parses every stored row and badges it_diagnostics: {valid:false, errors:[…]}carrying the full refusal message. Operators can already enumerate the affected rows — the migration needs no discovery phase, which removes the usual first half of a migration card./metaread path) must not strip or break_diagnostics, or this inventory disappears.Recommended shape — (b) admin tooling, per-datasource, operator-initiated
A Setup action — "Move credential to the secret store" — that reads the stored cleartext, binds it through the existing
SecretBinder.bind(), writesexternal.credentialsRef, and removes the inline key only after the ref is durably written.Is it destructive? No, and deliberately so. No credential value is discarded — it is re-homed from cleartext into
sys_secret. The inline key is deleted only once the encrypted row exists. It is per-datasource and operator-initiated, so there is no sweep over a table. That matters: the ruling's "escalate if destructive" clause is what this shape is designed to stay clear of.Rejected, with reasons
sys_setting.value_encstill points at the old handle, so a leaked credential stays live after a 200 #8030 for the same reason. The ruling's escalate-if-destructive clause points straight at it.The migration's entire output is
external.credentialsRefon an existing row. Per #8153, writing that onto a row whoseschemaModedefaults to'managed'is exactly the shape that fails re-parse.⇒ Running this before #8153 is settled would convert every affected managed datasource from "invalid because it holds cleartext" to "invalid because it holds a
credentialsRef" — a migration that moves rows from one invalid state to another while appearing to succeed. #8153 first.Constraints
sys_secretrow for an already-migrated datasource (the orphan-accumulation failure mode measured in sys_secret: rotations that happened BEFORE #8030's fix left decryptable orphans on deployed instances, and nothing reaps them #8103 and guarded in PR fix(plugin-webhooks): move webhook custom headers onto the encrypted channel (#7986) #8114'sheadersPatch).Provenance: #8081 dev report (comment 5269923121, §③), #7990 maintainer ruling (comment 5266068845), my ACCEPT receipt (#8081 comment 5270102954). Related: #8103 (the same destructive-sweep boundary, for
sys_secretorphans), #8154, #8152.