Split out of #8081 scope item 4 by the domain:services PM seat, on the dev's explicit recommendation ("on my reading it is a live P1, not a nice-to-have"). ⛔ Not graded by me; no domain:* label — the fix I believe is right lands in service-datasource, but that is exactly the judgement triage should make rather than inherit from me.
The finding — measured, and wider than #8081's card said
#8081 framed item 4 as "turso encryptionKey is credential-shaped, still writable, and the binder has only one slot." The measurement found something larger: spec.secret is never read on the turso path at all.
TURSO_CONFIG_READERS.authToken reads only config.authToken (turso-driver-config.ts:115); resolveTursoUrl reads only config.url. The connect path resolves exactly one ref → one spec.secret (datasource-connection-service.ts:581), and nothing hands it to the turso builder:
buildTursoDriverConfig({driver:'turso', config:{url:'libsql://my-db.turso.io'},
secret:'THE-BOUND-JWT', external:{credentialsRef:'sys_secret:abc'}})
→ { url: 'libsql://my-db.turso.io' } // no authToken. The bound secret is dropped.
Why this is a release-facing problem, not a gap
After #8078, config.authToken is refused at every authoring door (z.never()). The supported alternative — bind the credential, reference it — resolves a secret the turso builder never reads. So:
A new turso remote datasource cannot be authenticated by any supported route.
Existing stored rows still work, because stored config bypasses the parse. That is what makes it invisible: nothing that already works breaks, and only new authoring is dead. turso-driver-config.test.ts contains no case for secret, so nothing pins the gap.
The cheap fix is not the one #8081 named
The dev's recommendation, cheapest first — and note item 4's "Option B (multi-slot binder)" is not the cheap one:
- Read
spec.secret into authToken in the turso builder — ~5 lines, entirely in service-datasource, no spec change, no new mechanism. It is exact parity with what postgres/mysql/mongo already do (spec.secret ? {password: spec.secret} : …). It restores turso's primary credential to the one slot that already exists, and shrinks the remaining question to encryptionKey alone. - Then decide
encryptionKey, if it still needs deciding. Two multi-slot shapes exist: external.credentials: {slot → ref} (clean, but a spec change ⇒ domain:spec), or one ref resolving to a JSON bag (no spec change, but it re-contracts the sys_secret payload and every resolver). ⛔ Neither is worth building until (1) lands and shows whether one slot plus a URL is enough in practice.
The dev deliberately did not implement (1): it is outside #8081's items 1–2 and he declined to take the decision unasked. Correct call — recording it so the next agent does not read the omission as an oversight.
Acceptance, if this is graded for dispatch
Provenance: #8081 dev report (comment 5269923121, §④) and my ACCEPT receipt (#8081 comment 5270102954). Related: #7990 (class card), #8078 (the spec half that closed the inline door).
Split out of #8081 scope item 4 by the
domain:servicesPM seat, on the dev's explicit recommendation ("on my reading it is a live P1, not a nice-to-have"). ⛔ Not graded by me; nodomain:*label — the fix I believe is right lands inservice-datasource, but that is exactly the judgement triage should make rather than inherit from me.The finding — measured, and wider than #8081's card said
#8081 framed item 4 as "turso
encryptionKeyis credential-shaped, still writable, and the binder has only one slot." The measurement found something larger:spec.secretis never read on the turso path at all.TURSO_CONFIG_READERS.authTokenreads onlyconfig.authToken(turso-driver-config.ts:115);resolveTursoUrlreads onlyconfig.url. The connect path resolves exactly one ref → onespec.secret(datasource-connection-service.ts:581), and nothing hands it to the turso builder:Why this is a release-facing problem, not a gap
After #8078,
config.authTokenis refused at every authoring door (z.never()). The supported alternative — bind the credential, reference it — resolves a secret the turso builder never reads. So:Existing stored rows still work, because stored config bypasses the parse. That is what makes it invisible: nothing that already works breaks, and only new authoring is dead.
turso-driver-config.test.tscontains no case forsecret, so nothing pins the gap.The cheap fix is not the one #8081 named
The dev's recommendation, cheapest first — and note item 4's "Option B (multi-slot binder)" is not the cheap one:
spec.secretintoauthTokenin the turso builder — ~5 lines, entirely inservice-datasource, no spec change, no new mechanism. It is exact parity with what postgres/mysql/mongo already do (spec.secret ? {password: spec.secret} : …). It restores turso's primary credential to the one slot that already exists, and shrinks the remaining question toencryptionKeyalone.encryptionKey, if it still needs deciding. Two multi-slot shapes exist:external.credentials: {slot → ref}(clean, but a spec change ⇒domain:spec), or one ref resolving to a JSON bag (no spec change, but it re-contracts thesys_secretpayload and every resolver). ⛔ Neither is worth building until (1) lands and shows whether one slot plus a URL is enough in practice.The dev deliberately did not implement (1): it is outside #8081's items 1–2 and he declined to take the decision unasked. Correct call — recording it so the next agent does not read the omission as an oversight.
Acceptance, if this is graded for dispatch
config.authTokenat the authoring doors.encryptionKey. That is step 2 and it may not survive step 1.encryptionKeyis currently redacted on read by PR fix(service-datasource): stop serving stored credentials on the datasource read path, and fix the false "credential-stripped" claim (#8081) #8126 (endorsed — see [services half of #7990] Close the datasource/connector credential write/read paths: scrubgetDatasource().config, fix the false "credential-stripped" claim, and write the stored-cleartext-rows migration story #8081 comment). Redaction grants and removes no slot; do not read it as the slot question being settled.Provenance: #8081 dev report (comment 5269923121, §④) and my ACCEPT receipt (#8081 comment 5270102954). Related: #7990 (class card), #8078 (the spec half that closed the inline door).