Files
awoooi/docs/runbooks/AWOOOP-RLS-CANARY-WAVE1-2.md
2026-05-12 21:41:14 +08:00

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']
  • 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 from awooop_operator_list_projects().
  • budget_service._get_tenant_budget_limit(project_id) now opens get_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_id is missing,
  • any project_id is NULL,
  • row count exceeds the reviewed canary cap of 20 rows.

Verification

Expected after apply:

  • app.project_id='awoooi': direct table read sees only awoooi.
  • app.project_id='ewoooc': direct table read sees only ewoooc.
  • 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/tenants from API pod: HTTP 200, total=2.
  • /api/v1/health from API pod: HTTP 200, status=healthy.
  • scripts/ops/awooop-rls-preflight.sh --exact-counts:
    • PASS=7 WARN=1 BLOCKED=1
    • awooop_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.