# AwoooP RLS Manual Script Review Manual scripts are not API runtime, but they can still break after fail-closed RLS if they connect directly with `DATABASE_URL` and do not set `app.project_id`. Run: ```bash python3 scripts/ops/awooop-rls-manual-script-audit.py --show-pass ``` Use strict mode for CI or pre-apply review: ```bash python3 scripts/ops/awooop-rls-manual-script-audit.py --strict-review ``` ## 2026-05-12 Result ```text AwoooP RLS manual script audit: BLOCKED=0 REVIEW=5 PASS=13 ``` `BLOCKED=0` means no hardcoded PostgreSQL URL with inline credentials was found in the scanned manual scripts. `REVIEW=5` are intentional operator paths: - `apps/api/scripts/awooop_phase1_batch1_backfill.py`: RLS/project_id bootstrap backfill; use migration/operator role. - `apps/api/scripts/run_migration.py`: DDL migration script; use migration/operator role. - `scripts/ops/awooop-rls-role-bootstrap.sql`: role bootstrap; requires postgres/CREATEROLE operator. - `scripts/ops/awooop_rls_preflight.py`: read-only preflight inside API pod. - `scripts/sync_dev_db.py`: dev schema sync; requires `DEV_DATABASE_URL` and must not target production. The common tenant-data manual scripts now set `app.project_id` or use `get_db_context()`. ## Operator Rule - Direct tenant table access must set `app.project_id` on every connection, or use an approved migration/operator role with BYPASSRLS. - Direct `asyncpg` scripts should use session-level context: ```python await conn.execute("SELECT set_config('app.project_id', $1, FALSE)", project_id) ``` - SQLAlchemy runtime/service code should use `get_db_context()` or `get_db()`. - Do not add PostgreSQL URLs with inline credentials to scripts, comments, or examples.