diff --git a/packages/plugins/plugin-audit/src/audit-log-read-visibility.ts b/packages/plugins/plugin-audit/src/audit-log-read-visibility.ts index c5fa8085808..ded1067b9ba 100644 --- a/packages/plugins/plugin-audit/src/audit-log-read-visibility.ts +++ b/packages/plugins/plugin-audit/src/audit-log-read-visibility.ts @@ -54,15 +54,19 @@ * `config_change` row stored before its writer recorded the settings row, * an auth event that carried no session id, and an `action_invocation` row * of an action invoked with no subject record. - * There is no record to judge, and none carries a record's field values - * (the per-writer measurement in `audit-log-field-redaction.ts`), so they - * are OUTSIDE this gate's class and are served under the ledger's own grant + * There is no record to judge, so they are OUTSIDE this gate's class and + * are served under the ledger's own grant * exactly as before — through a predicate on the ledger's own columns * ({@link ledgerRowAboutNoRecordWhere}), so a broad read is served whole. This is where the ledger differs from the activity * stream, which excludes every row naming no parent: the stream has no * platform producer of such rows, while these have three, and the shipped * `config_changes` list view reads `import` rows (and the older * `config_change` rows) through the data door. + * The field-level redaction does not key on `record_id`: a `config_change` + * row naming a settings store as `object_name` carries that setting's + * `value` (readable for a non-secret key) and `digest`, and both are + * dropped for a reader whose field-level security masks the store's + * `value` (`audit-log-field-redaction.ts`), with or without a `record_id`. * - **A record action that names no record** — a `create` / `read` / `update` * / `delete` row with no `record_id` (the CRUD mirror stamps `null` when it * cannot derive the id): about a record the gate cannot name, so excluded diff --git a/packages/plugins/plugin-audit/src/audit-plugin.ts b/packages/plugins/plugin-audit/src/audit-plugin.ts index 8650c31030f..5a198b5e4e5 100644 --- a/packages/plugins/plugin-audit/src/audit-plugin.ts +++ b/packages/plugins/plugin-audit/src/audit-plugin.ts @@ -275,8 +275,10 @@ export class AuditPlugin implements Plugin { namespace: 'sys', objects: [SysAuditLog, SysActivity, SysComment, SysCommentReaction], // The ledger row's record page: its fields, and every ledger row about - // the same record beside it, so a `config_change` row's digest sits next - // to the mirror's row that carries the value (`sys-audit-log.page.ts`). + // the same record beside it, so a `config_change` row sits next to the + // mirror's rows about the same settings row (`sys-audit-log.page.ts`). + // A non-secret key's `config_change` row carries its readable before + // and after itself; a secret-valued key's carries only the digest. pages: [SysAuditLogDetailPage], // ADR-0029 D7 — contribute the one Audit Log entry into the Setup app's // `group_diagnostics` slot. The plugin owns sys_audit_log (K2), so the diff --git a/packages/plugins/plugin-audit/src/sys-audit-log.page.ts b/packages/plugins/plugin-audit/src/sys-audit-log.page.ts index 32b54bc5f88..53f6e3e6712 100644 --- a/packages/plugins/plugin-audit/src/sys-audit-log.page.ts +++ b/packages/plugins/plugin-audit/src/sys-audit-log.page.ts @@ -11,16 +11,17 @@ import type { Page } from '@objectstack/spec/ui'; * * ## Why the page exists: the other rows about the same record * - * A settings write records a `config_change` row whose `new_value` is a - * DIGEST, never the value (`service-settings`' `config-change-audit.ts`), so - * the row alone cannot answer "what was it changed to". The CRUD mirror + * A settings write records a `config_change` row whose `old_value` and + * `new_value` carry the setting's readable before and after for a key that + * is not secret-valued, and only its digest for a secret-valued key + * (`service-settings`' `config-change-audit.ts`). The CRUD mirror * (`audit-writers.ts`) writes its own row about the same write, on the stored - * settings row, and that row carries the change: a non-secret value's old and - * new state, a secret's only as the mirror's ledger view masks it. Both rows - * name the same settings row as `record_id` (the `config_change` writer - * records the id of the row the write landed on), so this page lists every - * ledger row whose `record_id` is this row's: the `config_change` row and the - * mirror's rows sit side by side, and the value is one row down. + * settings row: a non-secret value's old and new state, a secret's only as + * the mirror's ledger view masks it. Both rows name the same settings row as + * `record_id` (the `config_change` writer records the id of the row the write + * landed on), so this page lists every ledger row whose `record_id` is this + * row's: the `config_change` row and the mirror's rows sit side by side, the + * history of that settings row in one list. * * The list is the ledger itself, keyed on the pointer's id half on both sides * (`relationshipField` and `relationshipValueField` both `record_id`): the