Tenant and System Bootstrap Reset
Dev-cycle reset from shell scripts through to system-admin UI

Table of Contents

Background

During development iteration it is necessary to re-run the provisioning wizards without recreating the entire database. Three distinct reset levels are needed:

  • Party-level reset (ores_iam_reset_tenant_fn) — soft-deletes admin-created data (counterparties, org units, books, report definitions) and flips Operational parties back to Inactive so the PartyProvisioningWizard fires on next login. This function already exists.
  • Tenant bootstrap reset — same as party-level reset, plus re-enables system.bootstrap_mode for the tenant so the TenantProvisioningWizard also fires on next login.
  • System bootstrap reset — purges all non-system tenants, removes the system admin account, and re-enables system.bootstrap_mode for the system tenant so the SystemProvisionerWizard fires on next startup.

Shell scripts (reset_tenant.sh, reset_system.sh) and their underlying SQL functions (ores_iam_reset_tenant_bootstrap_fn, ores_iam_reset_system_fn) were implemented on branch feature/dq-publish-pattern, but revealed two issues when smoke-tested:

  1. The change_reason_code system.admin_reset used in the functions does not exist in ores_dq_change_reasons_tbl.
  2. The functions are not SECURITY DEFINER, so they cannot be invoked by a service DB user that lacks DML rights on ores_variability_system_settings_tbl — a service isolation violation waiting to happen when we add the NATS handler.

This plan addresses both issues and completes the reset feature end-to-end: from a correctly seeded change reason and hardened SQL functions through to a NATS handler in ores.iam.core and a UI action in the system admin area.

Architecture

Why IAM owns the reset operation

The tenant lifecycle functions (provision, terminate, deprovision, purge, reset) all live in ores.iam.core and are called from the IAM service. Bootstrap reset is a tenant lifecycle operation, so it belongs in the same service.

The SQL functions write across service boundaries (IAM tables and ores_variability_system_settings_tbl). This is handled the same way as the existing ores_iam_create_initial_admin_fn provisioner: the functions are declared SECURITY DEFINER SET search_path = public, pg_temp so they execute with DDL-owner privileges. The IAM service DB user only needs EXECUTE on the function — no cross-service DML grant is required.

Change reason code

system.admin_reset is seeded as a new system change reason in the DQ change reasons population script. Using system.initial_load would be semantically incorrect (it means first-ever creation, not re-trigger by an administrator). A dedicated code gives a clear audit trail.

NATS subjects

Two new subjects in the iam.v1.system namespace:

Subject Handler
iam.v1.system.reset-tenant reset_tenant_handler
iam.v1.system.reset reset_system_handler

Only a user with the SuperAdmin role may invoke these subjects. The handler validates the role before executing the SQL function.

UI placement

  • Reset Tenant — context menu on the tenant row in the system admin tenant list (the existing TenantsWindow or equivalent). Opens a confirmation dialog, then runs the NATS call through the workflow steps widget pattern so progress is visible.
  • Reset System — a dedicated action in the system settings / admin area, behind a second confirmation (type "yes" style) given its destructive scope.

Implementation Plan

Phase 1 — Seed system.admin_reset change reason [DONE]

Tasks

  • Add system.admin_reset to the system change reasons population script (projects/ores.sql/populate/dq/). The reason should be seeded for the system tenant only; other tenants inherit system change reasons via the shared-governance lookup in ores_dq_validate_change_reason_fn.
  • Apply to the running database.
  • Verify the reason is visible:

    SELECT code FROM ores_dq_change_reasons_tbl
    WHERE code = 'system.admin_reset'
      AND tenant_id = ores_utility_system_tenant_id_fn()
      AND valid_to = ores_utility_infinity_timestamp_fn();
    

Phase 2 — Harden SQL functions [DONE]

Tasks

  • Add SECURITY DEFINER SET search_path = public, pg_temp to both ores_iam_reset_tenant_bootstrap_fn and ores_iam_reset_system_fn in their respective create/iam/ SQL files.
  • Change change_reason_code argument from system.initial_load (placeholder) to system.admin_reset in both functions.
  • Re-apply both functions to the running database.
  • Smoke-test via shell scripts:

    ./projects/ores.sql/reset_tenant.sh -t barclays_plc -y
    

Phase 3 — IAM messaging protocol [DONE]

Tasks

  • Add protocol types to ores.iam.api:
    • reset_tenant_command (field: tenant_code: string)
    • reset_tenant_result (fields: success: bool, message: string)
    • reset_system_command (no fields)
    • reset_system_result (fields: success: bool, message: string)
  • Declare NATS subjects in the IAM API protocol header: iam.v1.system.reset-tenant and iam.v1.system.reset.
  • No CMakeLists changes needed; handlers are header-only and GLOB_RECURSE picks them up automatically.

Phase 4 — IAM service handlers [DONE]

Tasks

  • Implement reset_handler in ores.iam.core (header-only, include/ores.iam.core/messaging/reset_handler.hpp):
    • reset_tenant(): decode reset_tenant_command, check iam::system:reset-tenant permission, call ores_iam_reset_tenant_bootstrap_fn($1) in system tenant context
    • reset_system(): decode reset_system_command, check iam::system:reset permission, call ores_iam_reset_system_fn()
  • Subscribe both handlers in registrar.cpp.
  • Build verified green.

Phase 5 — Qt UI [DONE]

Tasks

  • Reset Tenant action:
    • Added "Reset" toolbar button to TenantMdiWindow (enabled on selection, ArrowRotateCounterclockwise icon).
    • Confirmation dialog explains what is wiped vs. preserved.
    • Sends reset_tenant_command via QtConcurrent::run + QFutureWatcher; refreshes model and emits tenantReset on success.
    • TenantController wires tenantReset to a status bar message.
  • Reset System action:
    • Added "Reset &System…" to the System menu in AdminPlugin, separated from the Configuration submenu by a separator.
    • Two-step confirmation: explanatory dialog + typed YES required.
    • Sends reset_system_command via QtConcurrent::run + QFutureWatcher; shows information dialog on success instructing the user to restart.
  • Signals emitted on success; tenant list refreshes automatically after Reset Tenant.

Files Affected

File Change
projects/ores.sql/populate/dq/dq_change_reasons_populate.sql Add system.admin_reset reason
projects/ores.sql/create/iam/iam_tenant_bootstrap_reset_create.sql SECURITY DEFINER + correct reason code
projects/ores.sql/create/iam/iam_system_reset_create.sql SECURITY DEFINER + correct reason code
projects/ores.sql/reset_tenant.sh Already created; shell script
projects/ores.sql/reset_system.sh Already created; shell script
projects/ores.iam.api/include/ores.iam.api/messaging/reset_protocol.hpp New protocol types
projects/ores.iam.core/include/ores.iam.core/messaging/reset_handler.hpp Header-only handler (both reset_tenant + reset_system)
projects/ores.iam.core/src/messaging/registrar.cpp Subscribe both handlers
projects/ores.qt.admin/include/ores.qt/TenantMdiWindow.hpp Add resetAction_, resetSelected(), tenantReset signal
projects/ores.qt.admin/src/TenantMdiWindow.cpp Implement Reset toolbar button + NATS call
projects/ores.qt.admin/include/ores.qt/TenantController.hpp Add onTenantReset slot
projects/ores.qt.admin/src/TenantController.cpp Wire tenantReset signal
projects/ores.qt.admin/include/ores.qt/AdminPlugin.hpp Add act_reset_system_, on_reset_system()
projects/ores.qt.admin/src/AdminPlugin.cpp Implement Reset System menu item + NATS call

Date: 2026-05-17

Emacs 29.1 (Org mode 9.6.6)