fix(audit): only stamp organization_id when the audit table declares it (#1532)#1538
Merged
Conversation
…it (#1532) On single-tenant stacks the SchemaRegistry's applySystemFields() injects `organization_id` only when multiTenant is true, so sys_audit_log / sys_activity have no `organization_id` column. The audit writer stamped it unconditionally, so every record write triggered an audit INSERT that failed with "table sys_audit_log has no column named organization_id". The error was swallowed ("Audit write failed"), leaving audit logging silently non-functional on the sql (better-sqlite3) driver and paying a failed SQL round-trip on every mutation. The writer now resolves each audit/activity object's registered field set via engine.getSchema() (cached) and only includes `organization_id` when the column actually exists — preserving the RLS tenant stamp in multi-tenant deployments while keeping single-tenant writes valid. tenant_id (an explicitly declared lookup) is unaffected. Adds regression tests covering both the single-tenant (column absent → omitted) and multi-tenant (column present → stamped) paths.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1532 — on the SQL (better-sqlite3) driver, every record insert/update/delete triggered an audit write that failed with:
The error was caught (
Audit write failed), so business mutations still succeeded, but no audit row was ever persisted — a silent compliance/audit-trail gap, plus a failed SQL round-trip on every mutation.Root cause
The SchemaRegistry's
applySystemFields()(packages/objectql/src/registry.ts) only auto-injects theorganization_idcolumn when multi-tenant mode is enabled:Neither
sys_audit_lognorsys_activitydeclaresorganization_idexplicitly (they rely on this auto-injection). So on single-tenant stacks the column never exists — yetaudit-writers.tsstampedorganization_idinto everyauditRowandactivityRowunconditionally, making the INSERT reference a non-existent column.The reporter's stack is single-tenant, matching the 100%-reproduction.
Fix
The audit writer now resolves each target object's registered field set via
engine.getSchema()(cached, since schemas are static after registration) and only includesorganization_idwhen the column actually exists:organization_idis stamped exactly as before, preserving the RLS tenant predicate (organization_id = current_user.organization_id) so non-admin members still see their audit rows.tenant_id(an explicitly declared lookup onsys_audit_log) is unaffected.Tests
Added
audit-writers.test.tscovering both paths:organization_idomitted,tenant_idstill written)organization_idstamped from tenant context)Both pass. (Note: the pre-existing
objects.test.tscan't resolve@objectstack/spec/systemin this environment because thespecpackage isn't built — unrelated to this change.)https://claude.ai/code/session_01UPMrWD5uBQZ29gQBNZBu5x
Generated by Claude Code