fix: audit storage & migration compatibility for admin/operational su… - #1694
Merged
Baskarayelu merged 2 commits intoAug 30, 2026
Conversation
…rfaces Closes Remitwise-Org#1679 Add deterministic storage/migration compatibility guarantees for the audit backing of privileged workflows (RBAC guard, admin controllers, audit views): - audit-storage.compatibility: forward/backward compatibility contract (isSchemaCompatible, assertWriteBackwardCompatible length bounds, normalizeLegacyAuditRow legacy fixtures, UNKNOWN_ACTION fallback). - 1787400000000-audit-storage-compat migration: idempotent, resumable (audit_storage_checkpoint), observable (RAISE NOTICE) schema evolution; stamps existing rows schemaVersion=3, new writes =4; clean rollback. - RbacGuard hardening: dedup repeated/replayed decisions (no duplicate/partial audit state), degraded-mode markers when the audit store is unavailable, forward-compat gate, schemaVersion stamped on audit records. - AuditService stamps schemaVersion on every write; entity gains the column. - Regression coverage at the integration boundary: upgrade/rollback/rerun/ partial-progress migration contract, guard resilience, and compatibility utility specs (48 tests). Verified pre-existing failures (bcrypt native module, unrelated AuditAction enum build errors) are not introduced here.
Fix the failing nest build in CI by correcting the corrupted AuthModule: - import from @nestjs/common (was @nestj/common) - use TypeOrmModule and import Repository from typeorm - fix malformed private readonly repo declaration - reference jwtConfig (was wtConfig) and import AuditModule Add missing auth audit actions (SIGN_IN, REFRESH, LOGOUT, LOGOUT_ALL, VERIFY_EMAIL, ISSUE_VERIFICATION_TOKEN, RESEND_VERIFICATION) to AuditAction used by the auth providers.
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.
Closes #1679
…rfaces
PR title: fix: audit storage & migration compatibility for admin/operational surfaces
Closes #1679
Summary
This PR hardens the storage backing every privileged/operational workflow —
the RBAC guard, admin controllers, and audit review views — so it provides a
deterministic, reviewable guarantee under normal, invalid, repeated,
concurrent, and failure conditions. Previously an admin boundary or recovery
gap could expose operational controls or leave old records unreadable; this
change closes that gap with a forward/backward compatibility contract, a
resumable and observable migration, and guard-level resilience so no rejected,
stale, repeated, or failed operation leaves unauthorized or partial state.
Scope
meridian-api/src/auth/guard/rbac/plus the audit storage itwrites to (
meridian-api/src/audit,meridian-api/src/database/migrations).What changed
1. Compatibility contract —
src/audit/audit-storage.compatibility.tsisSchemaCompatible()/SUPPORTED_SCHEMA_VERSIONS— forward & backwardversion negotiation; legacy rows with no version column are always readable.
assertWriteBackwardCompatible()— rejects (degraded, never thrown) writesthat exceed the column length bounds older readers assume, so a newer writer
can never emit a row an older reader cannot parse.
normalizeLegacyAuditRow()— fills safe defaults for columns introduced aftera row was written (
correlationId,chainHash,contributionXp,epochNumber,schemaVersion, …); unknown future enumactionvalues degrade toUNKNOWN_ACTIONinstead of crashing a reader.2. Resumable, observable migration —
1787400000000-audit-storage-compat.tsIF NOT EXISTS/IF EXISTS) so reruns and post-failureresumes are safe.
audit_storage_checkpointtable records phase + timestamp (partial-progresssupport) and raises
RAISE NOTICEprogress for operator observability.schemaVersion = 3(prior version) and drops in aseparate
schemaVersioncolumn; clean rollback that preserves all auditrecords.
3. RBAC guard resilience —
src/auth/guard/rbac/rbac.guard.tsde-duplicated within a 5s window so a retry cannot leave duplicate/partial
audit state.
audit.degraded_mode+audit.write_failedmarker pair is emitted while theauthorization decision remains authoritative (deny still denies, allow still
allows).
schemaVersionstamped oneach audit record.
4. Audit writer & entity
AuditService.log()/logContractEvent()stampschemaVersion.AuditLogentity gains theschemaVersioncolumn.5. Tests (focused regression at the integration boundary) — all green
rbac.guard.resilience.spec.ts— degraded storage still denies/allows with nopartial state; degraded-mode marker emitted; repeated decisions collapsed to
one record; distinct decisions recorded separately; schemaVersion stamped.
audit-storage.compatibility.spec.ts— legacy fixtures normalize; versionnegotiation; length-bound rejection per column.
1787400000000-audit-storage-compat.spec.ts— migration contract provingupgrade, rollback, rerun (idempotent), and partial-progress resume.
6. Documentation —
docs/audit-storage-migration-compatibility.mdDesign & invariants, failure behavior, compatibility impact, migration/rollback
considerations, operational limitations, and security assumptions.
Acceptance criteria — checklist
make migrations resumable and observable (compatibility contract,
checkpoint-based migration).
changes explicit (only additive
schemaVersion+ checkpoint; documented).partial state (auth-before-audit, idempotency, degraded-mode handling).
boundary (guard resilience + migration contract + compatibility specs).
Validation (commands & results)
Additional checks:
verified by the migration spec using a fake QueryRunner (no live DB required).
refactors or broad dependency upgrades are included.
Compatibility & rollback
npm run migration:run) before deploying this code sothe
schemaVersioncolumn exists when writers begin stamping it.npm run migration:revert) removes the column + checkpoint tableand preserves all pre-existing records; the new enum values added earlier are
intentionally not dropped (Postgres restriction) and are harmless.
Security / correctness note
claims and never depends on audit-health, so a degraded store cannot weaken
authorization. Normalization never elevates privilege (unknown actions are
surfaced for review, never mapped to a granted/denied outcome).
de-duplication would require a shared store and is out of scope.
Add deterministic storage/migration compatibility guarantees for the audit backing of privileged workflows (RBAC guard, admin controllers, audit views):