3.6 KiB
AwoooP RLS Canary Wave 1.2
This wave targets:
awooop_projects
Status: applied in production on 2026-05-12.
Production state:
- API image gate:
192.168.0.110:5000/awoooi/api:7d92f0acd705451d99b4413ab9748482e3675c00 awooop_projects:rls=true force=true policies=4- Operator Console tenants endpoint:
total=2 - Direct table reads:
- no
app.project_id:[] app.project_id='awoooi':['awoooi']app.project_id='ewoooc':['ewoooc']
- no
awooop_operator_list_projects()still returns both reviewed projects.
Safety Model
awooop_projects is special. Runtime checks such as MCP Gate 1 and budget
lookup should be tenant-scoped, but Operator Console needs a cross-tenant
project list.
Wave 1.2 keeps normal table access tenant-scoped and adds an explicit platform read function:
public.awooop_operator_list_projects()
The function is SECURITY DEFINER, has a fixed search_path, returns only the
Operator Console project-list columns, and grants execute only to awooop_app.
App Changes
platform_operator_service.list_tenants()reads fromawooop_operator_list_projects().budget_service._get_tenant_budget_limit(project_id)now opensget_db_context(project_id), so tenant budget reads match RLS context.
Apply
Before applying, verify the API deployment has the code that calls
awooop_operator_list_projects():
ssh wooo@192.168.0.120 \
'sudo kubectl -n awoooi-prod get deploy awoooi-api -o wide'
The image tag must be 7d92f0ac or later. If the deployment is older, do not
enable RLS; Operator Console will only see the current tenant row.
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 \
-f scripts/ops/awooop-rls-canary-wave1-2-projects.sql
The SQL aborts if:
- table is missing,
project_idis missing,- any
project_idis NULL, - row count exceeds the reviewed canary cap of 20 rows.
Verification
Expected after apply:
app.project_id='awoooi': direct table read sees onlyawoooi.app.project_id='ewoooc': direct table read sees onlyewoooc.awooop_operator_list_projects(): returns both projects for Operator Console.- tenant budget lookup can read the matching tenant row.
- global RLS preflight remains blocked only by later-wave tables.
Production verification from 2026-05-12:
/api/v1/platform/tenantsfrom API pod: HTTP 200,total=2./api/v1/healthfrom API pod: HTTP 200,status=healthy.scripts/ops/awooop-rls-preflight.sh --exact-counts:PASS=7 WARN=1 BLOCKED=1awooop_projects rls=true force=true policies=4- remaining blocker tables:
audit_logs,awooop_outbound_message,awooop_run_state,incidents,knowledge_entries,playbooks.
- Direct app-role behavior:
projects_no_context=[]projects_awoooi_context=['awoooi']projects_ewoooc_context=['ewoooc']operator_function_awoooi_context=['awoooi', 'ewoooc']
Apply / Rollback Note
An earlier production apply attempt was rolled back immediately because the
live API image was still ff30c61c..., before the Operator Console code path
had deployed. The symptom was /api/v1/platform/tenants returning only the
awoooi row. Rollback restored total=2.
After Gitea CD rolled out 7d92f0ac..., the same SQL was re-applied and the
post-apply verification above passed. Keep this deployment-order gate for any
future changes to cross-tenant read helpers.
Rollback
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 \
-f scripts/ops/awooop-rls-canary-wave1-2-projects-rollback.sql
Rollback disables RLS and removes the Wave1.2 policies on awooop_projects.
It intentionally keeps awooop_operator_list_projects() for deployed API
compatibility.