|
| 1 | +--- |
| 2 | +"@objectstack/plugin-audit": patch |
| 3 | +--- |
| 4 | + |
| 5 | +docs(plugin-audit): the published README stops documenting an `auditService` API, a row shape and an action vocabulary that do not exist (#9517) |
| 6 | + |
| 7 | +<!-- adr-0087: not-required (no-migration-prescription) Documentation only. No |
| 8 | +authorable property is added, renamed, retired or tombstoned; no schema, type or |
| 9 | +runtime behaviour changes. The only edited artifact is the package README, which |
| 10 | +ships in the package's `files` array. --> |
| 11 | + |
| 12 | +`packages/plugins/plugin-audit/README.md` is in the package's published `files` |
| 13 | +array and `private` is unset, so it is **what the npm package page renders**. It |
| 14 | +documented an API surface with no implementation anywhere in the repo, under a |
| 15 | +banner claiming SOC 2 / HIPAA / GDPR readiness. |
| 16 | + |
| 17 | +**Measured against `origin/main` before anything was rewritten**, and the drift |
| 18 | +was wider than the ledger of it: |
| 19 | + |
| 20 | +- **Every `auditService.*` method the README called is absent from the repo** — |
| 21 | + `getFailedActions`, `logAdminAction`, `logDataAccess`, and also |
| 22 | + `getRecordHistory`, `getUserActivity`, `searchLogs`, `getRecordSnapshot`, |
| 23 | + `generateReport`, `archiveLogs`, `purgeLogs`, `logDataDeletion`, |
| 24 | + `logDataExport`. Twelve methods, zero implementations. A reader following the |
| 25 | + README wrote code that could not compile. |
| 26 | +- **`PluginAudit` does not exist**, and neither does the `.configure({...})` |
| 27 | + static it was called through — no class in this repo exposes one. The export is |
| 28 | + `AuditPlugin`, a `Plugin` class registered as `kernel.use(new AuditPlugin())` |
| 29 | + and taking **no configuration at all**. The documented config object |
| 30 | + (`trackObjects`, `trackFields`, `retentionDays`, `autoArchive`, `excludeUsers`, |
| 31 | + `trackSystemEvents`) was fabricated in full. |
| 32 | +- **`IAuditService` is not in `@objectstack/spec/contracts`** — the README's |
| 33 | + "Contract Implementation" section named an interface the spec has never |
| 34 | + declared. |
| 35 | +- **The row shape was not the shipped one.** The README declared `timestamp`, |
| 36 | + `userName`, `userEmail`, `recordName`, `changes`, `sessionId`, `status` and |
| 37 | + `errorMessage`. `sys_audit_log` declares none of them. |
| 38 | +- **The action values were outside the enum.** `'insert'`, `'auth:login'`, |
| 39 | + `'security:password_reset'`, `'workflow:approval'` and `'user_role_change'` are |
| 40 | + not forms this object accepts; the namespaced-colon spelling never was one. |
| 41 | +- **The object name was wrong** — `audit_log`, not `sys_audit_log`. |
| 42 | +- **The REST namespace does not exist.** Six `/api/v1/audit/*` routes were |
| 43 | + documented; the object declares `apiMethods: ['get', 'list']` and is read over |
| 44 | + the ordinary object API. |
| 45 | + |
| 46 | +The compliance paragraph is **deleted, not softened or relocated**: a |
| 47 | +regulatory-readiness claim is a company-level statement needing an accountable |
| 48 | +owner, and it does not belong in a package README. The three external |
| 49 | +SOC 2 / GDPR / HIPAA links that existed only to support that framing are gone |
| 50 | +with it. |
| 51 | + |
| 52 | +The replacement documents only what the code can be pointed at: the real exports; |
| 53 | +the real `sys_audit_log` columns; the seven-value action enum **with the writer |
| 54 | +for each value**, so a reader can check any row of it; the credential masking on |
| 55 | +`old_value` / `new_value`; and the coverage model, which is |
| 56 | +**all objects minus an exclusion list** rather than the fabricated per-object |
| 57 | +`trackObjects` config — subtraction, because the object universe is open and an |
| 58 | +enumerated allow list would silently stop auditing everything registered after |
| 59 | +boot. |
| 60 | + |
| 61 | +Three things are now stated that the old README obscured, all of them gaps a |
| 62 | +reader could otherwise mistake for coverage: |
| 63 | + |
| 64 | +- **reads and views are not on the ledger** — no writer emits a read action; |
| 65 | +- **failed operations are not on the ledger** — there is no success/failure |
| 66 | + column, and the writers fire only on `after*` events, i.e. only on operations |
| 67 | + that succeeded, so `getFailedActions`-style "security monitoring" had no |
| 68 | + mechanism behind it in the first place; |
| 69 | +- **`ip_address` / `user_agent` are populated on auth events only** — the |
| 70 | + record-level writer does not stamp them, so a null client fingerprint on a CRUD |
| 71 | + row does not mean the request had none. |
| 72 | + |
| 73 | +Two dependency boundaries are **named with their degraded behaviour** rather than |
| 74 | +left silent, following the `access-recipes.mdx` pattern: hierarchy-relative |
| 75 | +permission scopes need `@objectstack/security-enterprise` and **fail closed to |
| 76 | +`own`** without it, so a grant written to let managers read their reports' audit |
| 77 | +rows shows them only their own on an open build; and `lifecycle.archive` needs a |
| 78 | +registered `archive` datasource, **failing closed to retention** without one — |
| 79 | +nothing is ever deleted and the table grows, which is the safe direction for a |
| 80 | +ledger but not the documented one. |
0 commit comments