# AwoooP RLS Preflight Runbook > Purpose: verify whether production is ready for PostgreSQL Row-Level Security > without enabling RLS or changing data. ## Command Default path runs the probe inside the production API pod through the 120 control-plane host. `DATABASE_URL` stays inside Kubernetes and is not printed. ```bash bash scripts/ops/awooop-rls-preflight.sh ``` Before enabling RLS, run exact backfill counts: ```bash bash scripts/ops/awooop-rls-preflight.sh --exact-counts ``` Useful variants: ```bash bash scripts/ops/awooop-rls-preflight.sh --json bash scripts/ops/awooop-rls-preflight.sh --local AWOOOP_RLS_SSH_TARGET=wooo@192.168.0.120 bash scripts/ops/awooop-rls-preflight.sh ``` Exit code `2` means the gate is blocked and RLS must not be enabled yet. ## Exact Count Scope After any target table has RLS enabled, `--exact-counts` runs as the production app DB user and is filtered by the current `app.project_id`. The output marks these rows with: ```text scope=rls_filtered project_context=... ``` Treat those counts as tenant-visible evidence, not global row counts. Use a reviewed postgres/operator path for global counts after RLS is enabled. ## 2026-05-12 Initial Production Result `--exact-counts` returned: - `PASS current_role_rls_enforced`: current DB user is `awoooi`, not superuser and not `BYPASSRLS`. - `PASS project_context_set_config`: `set_config('app.project_id', 'awoooi', TRUE)` works in the API pod. - `BLOCKED required_roles`: `awooop_app`, `awooop_platform_admin`, and `awooop_migration` do not exist. - `WARN role_bootstrap_authority`: current API DB user `awoooi` is not `CREATEROLE`; role bootstrap requires `postgres` or a `CREATEROLE` operator. - `WARN app_role_membership`: `awooop_app` is missing, so membership cannot be evaluated yet. - `PASS project_id_columns`: every existing target table has `project_id`. - `BLOCKED rls_enabled_forced_policy`: existing target tables are not yet RLS enabled, forced, or policied. - `PASS fail_open_policies`: production DB currently has no fail-open policy expressions. - `PASS project_id_backfill`: exact counts found zero `NULL project_id` rows in counted target tables. Current blocker summary: ```text PASS=5 WARN=2 BLOCKED=2 ``` Important exact counts from the same run: | Table | Rows | NULL project_id | | --- | ---: | ---: | | `audit_logs` | 686 | 0 | | `awooop_mcp_tool_registry` | 4 | 0 | | `awooop_outbound_message` | 235 | 0 | | `awooop_projects` | 2 | 0 | | `awooop_run_state` | 113 | 0 | | `incidents` | 1518 | 0 | | `knowledge_entries` | 2102 | 0 | | `playbooks` | 220 | 0 | ## 2026-05-12 Role Bootstrap Applied At `19:33 CST`, the manual role bootstrap was applied through the host PostgreSQL socket as `postgres`. It did not enable RLS policies. Post-bootstrap `--exact-counts` returned: - `PASS current_role_rls_enforced`: current DB user is still `awoooi`, not superuser and not `BYPASSRLS`. - `PASS project_context_set_config`: `set_config('app.project_id', 'awoooi', TRUE)` works. - `PASS required_roles`: `awooop_app`, `awooop_platform_admin`, and `awooop_migration` now exist. - `PASS app_role_membership`: current API DB user is a member of `awooop_app`. - `PASS project_id_columns`: every existing target table has `project_id`. - `BLOCKED rls_enabled_forced_policy`: target tables are still not RLS enabled, forced, or policied. - `PASS fail_open_policies`: no fail-open policy expressions detected. - `PASS project_id_backfill`: exact counts found zero `NULL project_id` rows in counted target tables. Current blocker summary: ```text PASS=7 WARN=0 BLOCKED=1 ``` Updated exact counts: | Table | Rows | NULL project_id | | --- | ---: | ---: | | `audit_logs` | 686 | 0 | | `awooop_mcp_tool_registry` | 4 | 0 | | `awooop_outbound_message` | 248 | 0 | | `awooop_projects` | 2 | 0 | | `awooop_run_state` | 126 | 0 | | `incidents` | 1524 | 0 | | `knowledge_entries` | 2103 | 0 | | `playbooks` | 220 | 0 | ## Remediation Order 1. Verify all DB access paths use `get_db()` / `get_db_context()` or otherwise set `app.project_id` before queries. 2. Apply policies first in staging or a canary DB. 3. In production, enable one batch at a time. 4. After each batch, run: ```bash bash scripts/ops/awooop-rls-preflight.sh --exact-counts ``` 5. Validate AwoooP Runs, Approvals, Monitoring, Tickets, Cost, alert ingestion, background workers, and TelegramGateway mirror paths. ## Do Not - Do not enable all policies in production before the role path is decided. - Do not rely on fail-open `IS NULL` or empty-string policies as the target state. - Do not run destructive rollback SQL unless the incident commander explicitly approves it.