Skip to content

Migration story for stored cleartext datasource credentials: operator-initiated re-homing into sys_secret (blocked by the managed-schemaMode refusal) #8155

Closed

Description

@huangyiirene

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 existing SecretBinder.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.

Activity

  1. added
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    on Aug 12, 2026
  2. added theissue type on Aug 12, 2026
  3. hotlong commented on Aug 12, 2026

    @hotlong
    Contributor

    Triage: lands in the services lane (Setup action over the existing SecretBinder.bind() path in service-datasource) ⇒ domain:services, type Feature, pm:blocked on the Blocked-by: #8153 line already in the body. The maintainer's #7990 Option-A ruling already puts the migration in scope, and the recommended shape (b) — operator-initiated, per-datasource, ref durably written before the inline key is removed — is designed to stay clear of the escalate-if-destructive clause, so no new decision is needed here; what gates it is purely the #8153 contract question (now escalated as needs-user-decision), because running the migration first would move rows from one invalid state to another.

    For the unlock re-check when #8153 resolves: re-verify that the _diagnostics.valid:false inventory still exists (the #8154 redaction work is under an explicit constraint not to break it) and that the idempotency/no-second-secret-row constraint from #8103's measurements is in the dispatch order verbatim.


    Generated by Claude Code

  4. hotlong commented on Aug 14, 2026

    @hotlong
    Contributor

    Unlock scan + grade: upstream Blocked-by: #8153 cleared — #8153 closed (PR #8588 merged 2026-08-14T00:53Z: external.credentialsRef, and only it, now schema-legal on schemaMode 'managed'), so the shape this migration writes is no longer self-invalidating. Rather than requeueing, escalated to needs-user-decision: a stored-data migration shape is on the human floor regardless of prism agreement. The half that IS settled: the 2026-08-13 Option-A ruling on #7990 put the existing-rows migration in scope of the programme, not optional — what needs the maintainer is the shape below.

    Proposed shape (the card's recommendation, carried unchanged): operator-initiated, per-datasource Setup action "Move credential to the secret store" — read stored cleartext, bind via the existing SecretBinder.bind(), write external.credentialsRef, delete the inline key only after the ref is durably written. Non-destructive by construction; no discovery phase needed (/meta _diagnostics already enumerates affected rows).

    Four-prism block:

    1. Platform long-term coherence — completes the [security] sys_metadata.metadata is a general cleartext sink: any authored artefact whose schema permits an inline credential lands it there (datasource config.password, connector authentication) #7990 programme's stored-rows half with mechanisms the platform already has (SecretBinder, sys_secret); no new concept, no schema change beyond what Every wizard-created datasource with a password is badged invalid today — the service writes external.credentialsRef onto rows whose schemaMode defaults to 'managed', which the schema refuses #8153 landed.
    2. Measured business pull — any deployed instance that created datasources before the [services half of #7990] Close the datasource/connector credential write/read paths: scrub getDatasource().config, fix the false "credential-stripped" claim, and write the stored-cleartext-rows migration story #8081/fix(service-datasource): stop serving stored credentials on the datasource read path, and fix the false "credential-stripped" claim (#8081) #8126 fixes holds cleartext credentials at rest today; the ruling itself declares this in scope.
    3. AI-agent error-resistance — operator-initiated + delete-only-after-durable-write is the fail-safe ordering; a batch/automatic variant would be the error-prone one. The proposed shape is the resistant one.
    4. Startup scope discipline — one Setup action reusing existing binder machinery, small and bounded; the alternative (a manual runbook) saves little and leaves cleartext lingering indefinitely.

    Recommendation: approve shape (b) as carried (all four prisms point the same way; it stays in the inbox only because stored-data migration shapes are a named human-floor category). One ordering constraint to confirm alongside: #8154 (metadata read-path redaction, unblocked this round) must keep _diagnostics intact — recorded on both cards.

    Premise re-check lines: git log --oneline -3 -- packages/spec/src/data/datasource.zod.ts · state of #8154 · git grep -n "credentialsRef" origin/main -- packages/services/service-datasource/src | head.

    本评论来自分诊座位 Routine。


    Generated by Claude Code

  5. hotlong commented on Aug 14, 2026

    @hotlong
    Contributor

    Maintainer ruling (2026-08-14, verbatim: 「同意你的建议」, approving item 1 of the eight-item decision-box list: 「#8155:批 shape (b),target:v17 进席 A(耦合注记 #8154)?」): shape (b) approved as carried — operator-initiated per-datasource Setup action, bind via existing SecretBinder.bind(), write external.credentialsRef, delete the inline key only after the ref is durably written; shape (c) stays the fallback for rows (b) cannot bind. needs-user-decision → pm:queue, target:v17; enters the v17 bug-focus Seat A (#8667). Coupling constraint for the implementer: #8154 (metadata read-path redaction, same seat) must NOT strip _diagnostics — that badge is this card's discovery mechanism.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedpm:dispatchedsecuritytarget:v17

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions