Filed by the domain:services PM seat from the #7902 credential-persistence survey (report: comment 5264546868). Unassigned, no pm:queue, no domain:*. For triage to grade and route.
This is a verification card, not a defect card. It may correctly close as no-change. It exists because two shipped surfaces assert a protection that the survey could not confirm from the storage side, and an unverified security claim is worth exactly one measurement.
The two claims
| # | Object · column | What it declares | What the storage side shows | Evidence |
|---|
| ②-c | sys_scim_provider.scim_token | field label says "(hash)" | plain Field.text, in field group Secret, apiMethods: ['list'] | sys-scim-provider.object.ts:107-114 |
| ②-d | sys_oauth_application.client_secret | description claims"hashed/encrypted at rest" | plain Field.text | sys-oauth-application.object.ts:300-306 |
In both cases better-auth owns the write, so the protection — if it exists — happens upstream of the column and is invisible to a survey that reads declarations. The survey deliberately recorded these as unverified rather than clean, which is the correct call and the reason this card exists.
The measurement this card is asking for
For each of the two, answer one question with evidence:
Does the value that actually lands in the column come out of a one-way hash / an encryption step — or is the declaration describing an intention that the write path does not perform?
Concretely: follow better-auth's write path for each column and read what it stores; or write one through the real stack and read the row back. A round-trip that reproduces the stored value from the plaintext proves it is not hashed.
Then one of two outcomes:
- Claims hold ⇒ close as no-change, and (cheaply) make the declaration cite where the hashing happens so the next survey does not re-open this. A claim that is true but unciteable will be re-measured by every future audit.
- Claims are false ⇒ this re-grades to P1 and joins the ①/② family: a token/secret sitting in cleartext behind a description that says otherwise is strictly worse than one that is honestly plain, because it silences exactly the review that would have caught it.
Why the "(hash)" label is not itself evidence
The survey's structural finding is that declarations near a credential are not load-bearing unless something enforces them. Two data points from the same sweep:
So the question is not whether the label is plausible — it is whether anything makes it true.
Explicitly NOT claimed
- No leak is asserted. If the claims hold, both surfaces are fine, and that is a likely outcome.
- No accusation about better-auth. It may well hash both; the point is that this repo currently cannot show it, and both columns are API-reachable if it doesn't.
- Not an ADR-0100 case —
Field.password() has zero occurrences repo-wide.
Routing note — why no domain:*
Both columns are declared in packages/platform-objects (domain:metadata), the write paths are better-auth under plugin-auth (domain:identity), and any remediation could land in either. The measurement itself is lane-neutral. Flagged rather than guessed.
Evidence
Measured 2026-08-12 by the dev on #7902 (read-only survey, pr: null), which recorded both as unverified rather than clean. Full verdict table in comment 5264546868 on #7902.
Source
Split out of #7902 (survey items ②-c and ②-d, packed as one card because they are one question asked twice). Siblings: #7986, #7987, #7990, #8009.
Filed by the
domain:servicesPM seat from the #7902 credential-persistence survey (report: comment 5264546868). Unassigned, nopm:queue, nodomain:*. For triage to grade and route.This is a verification card, not a defect card. It may correctly close as no-change. It exists because two shipped surfaces assert a protection that the survey could not confirm from the storage side, and an unverified security claim is worth exactly one measurement.
The two claims
sys_scim_provider.scim_tokenField.text, in field groupSecret,apiMethods: ['list']sys-scim-provider.object.ts:107-114sys_oauth_application.client_secretField.textsys-oauth-application.object.ts:300-306In both cases better-auth owns the write, so the protection — if it exists — happens upstream of the column and is invisible to a survey that reads declarations. The survey deliberately recorded these as unverified rather than clean, which is the correct call and the reason this card exists.
The measurement this card is asking for
For each of the two, answer one question with evidence:
Concretely: follow better-auth's write path for each column and read what it stores; or write one through the real stack and read the row back. A round-trip that reproduces the stored value from the plaintext proves it is not hashed.
Then one of two outcomes:
Why the "(hash)" label is not itself evidence
The survey's structural finding is that declarations near a credential are not load-bearing unless something enforces them. Two data points from the same sweep:
datasource-admin-service.ts:179carries a "credential-stripped" comment — reverse-checked, and no scrub ofconfigexists anywhere in that package (this became [security]sys_metadata.metadatais a general cleartext sink: any authored artefact whose schema permits an inline credential lands it there (datasourceconfig.password, connectorauthentication) #7990's ①-c).sys_api_key.keytook a real mechanism instead of a claim:internal: true, soomitInternalFieldsomits it (api-key-ui-lifecycle (secondary): thekeycolumn (SHA-256 hash) serializes over the data API, contradicting its own "never exposed to clients" description #7728). Neither of the two columns here carries that flag.So the question is not whether the label is plausible — it is whether anything makes it true.
Explicitly NOT claimed
Field.password()has zero occurrences repo-wide.Routing note — why no
domain:*Both columns are declared in
packages/platform-objects(domain:metadata), the write paths are better-auth underplugin-auth(domain:identity), and any remediation could land in either. The measurement itself is lane-neutral. Flagged rather than guessed.Evidence
Measured 2026-08-12 by the dev on #7902 (read-only survey,
pr: null), which recorded both as unverified rather than clean. Full verdict table in comment 5264546868 on #7902.Source
Split out of #7902 (survey items ②-c and ②-d, packed as one card because they are one question asked twice). Siblings: #7986, #7987, #7990, #8009.