Delete can terminate the system tenant

Table of Contents

This page is a capture in the inbox bucket of the product backlog — a pre-sprint idea, not yet pulled into a sprint as a story.

1. What

The generic entity delete can terminate the system tenant. Every dedicated lifecycle function refuses that explicitly; the delete rule the generic path actually uses does not.

2. Why

Found on 2026-09-23 while documenting User Journey: Retire or reset a tenant.

The system tenant holds the shared reference data and the system administration for the whole installation, so terminating it is close to unrecoverable.

Evidence:

  1. Every dedicated function refuses it and demands system context. projects/ores.sql/create/iam/iam_tenant_terminator_create.sql:51 raises "Cannot terminate the system tenant" when the id is the system tenant, and line 56 raises unless the caller is in system tenant context. iam_tenant_deprovisioner_create.sql:65 and :70 do the same, and the purger follows the same shape.
  2. The generic path does not go through them. The generated entity delete issues a plain DELETE on ores_iam_tenants_tbl, and projects/ores.sql/create/iam/iam_tenants_create.sql:150-157 is the rule that catches it:

    create or replace rule ores_iam_tenants_delete_rule as
    on delete to "ores_iam_tenants_tbl" do instead (
        update "ores_iam_tenants_tbl"
        set valid_to = clock_timestamp(),
            status = 'terminated'
        where id = OLD.id
          and valid_to = ores_utility_infinity_timestamp_fn();
    );
    

    No system-tenant guard, and no system-context check.

  3. iam.v1.tenants.delete reaches that rule and checks only iam::tenants:delete. The dedicated iam::tenants:terminate permission is seeded and held by SuperAdmin, but no operation uses it.

So the guard exists, but in the wrong place: inside the functions the generic path does not call.

The fix is to add the same guard to the delete rule — refuse when OLD.id is ores_utility_system_tenant_id_fn(). The check constraint at line 60 (id <> max_uuid or code = 'system') is a consistency rule about the row, not a deletion guard, and does not prevent this.

3. References

  • projects/ores.sql/create/iam/iam_tenants_create.sql:150-157 — the unguarded delete rule.
  • projects/ores.sql/create/iam/iam_tenant_terminator_create.sql:51-57 — the guard that exists elsewhere.
  • projects/ores.sql/create/iam/iam_tenant_deprovisioner_create.sql:65-71 — the same guard.

4. See also

Emacs 29.3 (Org mode 9.6.15)