Skip to content

plugin-audit never provisions sys_audit_log / sys_activity — Setup "System Overview" audit widgets silently render zeros #4887

Description

@xuyushun441-sys

What happens

On a freshly seeded database with plugin-audit loaded, every load of Setup →
System Overview logs a burst of:

WARN [Analytics] dataset "sys_audit_log_metrics" backing object "sys_audit_log" is unavailable
  (SELECT COUNT(*) AS "event_count" FROM "sys_audit_log" WHERE (action = 'login' AND …)
   - no such table: sys_audit_log); returning an empty result instead of failing the widget

12 of these per dashboard load in my session (login / permission_change /
config_change counters, plus the GROUP BY action and GROUP BY user_id charts).

The tables are genuinely absent:

sys_audit_log    MISSING
sys_activity     MISSING
sys_comment      EXISTS

(88 tables in the DB; Audit is in the loaded-plugin list.)

Why this is worse than log noise

The analytics layer degrades gracefully — empty result instead of a failed widget
— so the client sees nothing wrong. My browser sweep recorded
/_console/apps/com.objectstack.setup/dashboard/system_overview as completely
clean: no failed requests, no console errors, no error text.

So the dashboard renders 0 logins, 0 permission changes, 0 config changes and
looks authoritative doing it. That is the "Total Spend: 0 on a populated table"
failure mode rest-server.ts calls out by name in the analytics resolver comment
— except here it is structural rather than a wrong-database mistake, and it is
the security dashboard reading zero.

The guard that should prevent this exists and isn't taking effect

plugin-audit/src/audit-plugin.ts has provisionSystemTables() (~L168-198),
added for precisely this, with a docblock that describes today's symptom:

sys_audit_log / sys_activity / sys_comment are otherwise lazy-created on first
WRITE … A freshly provisioned env that READS one first … hits SQLite "no such
table" … The UI degrades to an empty feed, but the log is noisy and can mask
real errors.

It is wired at kernel:ready (~L80, L97) and loops [SysAuditLog, SysActivity, SysComment] calling engine.syncObjectSchema(obj.name).

Two of its three tables are missing anyway, and no AuditPlugin: could not provision … warning appears in the log — so the per-object catch never fired.
syncObjectSchema does exist on the ObjectQL engine (objectql/src/engine.ts:5628),
so the early if (typeof sync !== 'function') return; bail is the most likely
path, but I did not confirm which of the two silent exits is taken — that belongs
to the fix. sys_comment existing while the other two don't suggests it gets
created by some other path rather than by this loop succeeding.

Worth noting the early return is silent: if provisioning is skipped wholesale,
nothing says so.

Suggested direction

  • Find why the kernel:ready provisioning doesn't reach syncObjectSchema for
    these objects, and make both bail-outs audible (a logger.warn on the
    typeof sync !== 'function' return) so a future skip isn't silent again.
  • Consider whether a shipped system dataset whose backing table may not exist
    should surface as "not available" in the widget rather than a confident 0
    an empty audit chart and a zero-events audit chart mean very different things
    to whoever is reading a security dashboard.

Repro

  1. os dev --ui --seed-admin -d file:/tmp/fresh.db in examples/app-showcase
  2. Sign in, open /_console/apps/com.objectstack.setup/dashboard/system_overview
  3. Server log shows the no such table: sys_audit_log warnings; the dashboard
    shows zeros with no error
  4. sqlite3 /tmp/fresh.db ".tables" | grep -E 'sys_audit_log|sys_activity' → nothing

Observed on main @ 0e96e46, SqlDriver(better-sqlite3), single-tenant.
Found while browser-sweeping showcase + Studio for #4879.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions