You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
rejectCredentialAggregation keys off the secret/password TYPES, so an internal-flagged column is not covered — the same type-vs-flag gap #7728 just fixed on the read path #7922
Split out of #7728 (PR #7920) by the domain:metadata seat. The dev recorded it as an out-of-scope observation rather than riding it into a credential PR — correct call. Filing it so the observation is a card, not a sentence in a merged PR body.
The gap
#7728 minted internal: true — "the declared value is never returned on the generic data path" — because ADR-0100's third credential channel (auth-subsystem one-way hashes living in ordinary text columns) had no read protection at all, and structurally so: collectMaskedReadFields collects by field TYPE, so a text column is collected by nothing regardless of managedBy.
rejectCredentialAggregation (packages/objectql/src/engine.ts) has the same shape of blind spot: it keys off collectCredentialFields, i.e. the secret / password types. An internal-flagged text column is invisible to it.
⇒ The read path now understands "protected by flag". The aggregation guard still only understands "protected by type".
Why it is filed as a card and not a bug
No reachable route exposes this today — the dev checked: there is no /data/:object/aggregate, and analytics requires a declared dataset. So this is a latent inconsistency, not a live disclosure, and it is filed at that grade deliberately.
⚠️It matters more for the next adopter than for the one that exists.sys_api_key.key is a SHA-256 hash — aggregating it leaks little. sys_session.token (#7823) is a live bearer credential, replay-proven working. If a route ever opens, the two are not equivalent risks, and the flag's promise ("never returned on the generic data path") reads as though it already covers this.
Scope
Fold internal into the aggregation guard's notion of a protected column — most likely by having rejectCredentialAggregation consult collectInternalReadFields alongside collectCredentialFields, so both the type-keyed and flag-keyed sets are refused.
⚠️Check the direction of the guard before assuming.collectCredentialFields deliberately ignores managedBy (it is the unconditional collector), while collectInternalReadFields has no managedBy exemption by design. They should compose cleanly, but that is an assumption to measure, not to inherit from this card.
Controls a fix must not break
An unflagged column must still aggregate normally — a guard that refuses too much silently breaks analytics, and that failure is quiet.
The read-path behaviour from fix(spec,objectql): internal field flag — stop sys_api_key.key serializing on the data API (#7728) #7920 must be unchanged: internal still omits on find / findOne / the 201 create body / the by-id update body, ?select= included, and sys_api_key.key still authenticates (where: { key: <hash> }) and still mints show-once. packages/objectql/src/internal-fields.test.ts and packages/qa/dogfood/test/api-key-hash-not-serialized.dogfood.test.ts already assert all of that — reuse them as the regression floor rather than writing a second, drifting copy.
Split out of #7728 (PR #7920) by the
domain:metadataseat. The dev recorded it as an out-of-scope observation rather than riding it into a credential PR — correct call. Filing it so the observation is a card, not a sentence in a merged PR body.The gap
#7728 minted
internal: true— "the declared value is never returned on the generic data path" — because ADR-0100's third credential channel (auth-subsystem one-way hashes living in ordinarytextcolumns) had no read protection at all, and structurally so:collectMaskedReadFieldscollects by field TYPE, so atextcolumn is collected by nothing regardless ofmanagedBy.rejectCredentialAggregation(packages/objectql/src/engine.ts) has the same shape of blind spot: it keys offcollectCredentialFields, i.e. thesecret/passwordtypes. Aninternal-flaggedtextcolumn is invisible to it.⇒ The read path now understands "protected by flag". The aggregation guard still only understands "protected by type".
Why it is filed as a card and not a bug
No reachable route exposes this today — the dev checked: there is no
/data/:object/aggregate, and analytics requires a declared dataset. So this is a latent inconsistency, not a live disclosure, and it is filed at that grade deliberately.sys_api_key.keyis a SHA-256 hash — aggregating it leaks little.sys_session.token(#7823) is a live bearer credential, replay-proven working. If a route ever opens, the two are not equivalent risks, and the flag's promise ("never returned on the generic data path") reads as though it already covers this.Scope
Fold
internalinto the aggregation guard's notion of a protected column — most likely by havingrejectCredentialAggregationconsultcollectInternalReadFieldsalongsidecollectCredentialFields, so both the type-keyed and flag-keyed sets are refused.collectCredentialFieldsdeliberately ignoresmanagedBy(it is the unconditional collector), whilecollectInternalReadFieldshas nomanagedByexemption by design. They should compose cleanly, but that is an assumption to measure, not to inherit from this card.Controls a fix must not break
internalfield flag — stopsys_api_key.keyserializing on the data API (#7728) #7920 must be unchanged:internalstill omits onfind/findOne/ the 201 create body / the by-id update body,?select=included, andsys_api_key.keystill authenticates (where: { key: <hash> }) and still mints show-once.packages/objectql/src/internal-fields.test.tsandpackages/qa/dogfood/test/api-key-hash-not-serialized.dogfood.test.tsalready assert all of that — reuse them as the regression floor rather than writing a second, drifting copy.Evidence trail
keycolumn (SHA-256 hash) serializes over the data API, contradicting its own "never exposed to clients" description #7728 / PR fix(spec,objectql):internalfield flag — stopsys_api_key.keyserializing on the data API (#7728) #7920 — whereinternalwas minted, and the measurement that channel 3 had no read protectionsys_session.token— a live session credential — serializes over the data API (ADR-0100 channel 3 has no read protection) #7823 —sys_session.token, the live-credential adopter this would matter forinternalis registeredliveinpackages/spec/liveness/field.json