Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .changeset/olive-pots-tease.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
---
'@objectstack/service-settings': minor
---

Add a **report-only** classifier for `sys_secret` orphans (#8103), plus the reachability
measurements a sweep would depend on.

`classifySysSecretRows()` is a pure, read-only function over caller-supplied snapshots: it
never writes, never deletes and never decrypts, and its `SecretRowSnapshot` type
deliberately has no `ciphertext` member. It reports which `sys_secret` rows the settings
subsystem still references (`in_force`), which are unreferenced and attributable to a
declared encrypted specifier (`orphaned`), and which it cannot attribute at all
(`unattributable`).

That third verdict exists because re-measuring #8063's reachability argument **falsified**
one of its three facts: `sys_setting.value_enc` is *not* the only column that holds a
`sys_secret` handle. The store has three producers — `SettingsService`, the engine's
`secret`-field channel (which stores `secret:<id>` on any business row, including
tenant-authored objects), and the datasource credential binder (`sys_secret:<id>` at
`external.credentialsRef`). Two are invisible from this package and the engine's set of
holders is not statically enumerable, so "unreferenced by `sys_setting`" is not
"unreferenced". Rows that cannot be attributed are reported, never classified as orphans,
and the report carries explicit caveats naming its own blind spots.

The classifier also pins the two directional guards the card names: a row re-wrapped in
place by `rotateKey()` keeps its handle and is reported `in_force` (rotation metadata never
decides a verdict), and a legacy inline `value_enc` contributes no handle to the referenced
set while flagging any `sys_secret` row sharing its `(namespace, key)`.

No deletion ships with this change — the vehicle for removing orphans remains an open
maintainer decision.
19 changes: 19 additions & 0 deletions packages/services/service-settings/src/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,6 +66,25 @@ export {
redactSecretValues,
dropEchoedSecretMasks,
} from './settings-secret-redaction.js';
// #8103 — REPORT-ONLY classification of `sys_secret` rows against the settings
// subsystem's references. Published because the operator-facing vehicle for it
// (admin command / opt-in script) is still an open maintainer decision and will
// live outside this package; the classifier is the part that is safe to settle
// now. ⛔ Contains no deletion and must not grow one — and note the verdict
// vocabulary's third value: `sys_secret` has three producers, so "unreferenced
// by `sys_setting`" is NOT "unreferenced". See the module header.
export {
classifySysSecretRows,
collectEncryptedSpecifierRefs,
isSecretHandle,
SECRET_HANDLE_PREFIX,
type ClassifiedSecretRow,
type EncryptedSpecifierRef,
type SecretRowSnapshot,
type SecretRowVerdict,
type SettingRowSnapshot,
type SysSecretOrphanReport,
} from './sys-secret-orphan-report.js';
export {
settingsObjects,
settingsPluginManifestHeader,
Expand Down
Loading
Loading