Files
awoooi/docs/runbooks/AWOOOP-RLS-CANARY-WAVE1-3.md
Your Name de16c88418
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
chore(rls): 套用 outbound message canary
2026-05-12 21:55:23 +08:00

109 lines
3.4 KiB
Markdown

# AwoooP RLS Canary Wave 1.3
This wave targets one outbound evidence table:
- `awooop_outbound_message`
Status: applied in production on 2026-05-12.
Production state:
- `awooop_outbound_message`: `rls=true force=true policies=1`
- Operator Console Run detail smoke:
- run: `d385b7fe-8666-58ec-9072-9ac917adb6cf`
- `project_id=awoooi`
- HTTP 200
- `outbound_messages=1`
## Why This Table
`awooop_outbound_message` stores outbound notification evidence for Run
Timeline. It is less risky than `awooop_run_state` for this wave because it is
not the worker lease/state-machine source of truth.
Latest live evidence before staging:
```text
project_id=awoooi rows=290
send_status=sent rows=290
null_project_id_rows=0
```
`awooop_run_state` remains blocked because it is used by worker lease,
state-machine transitions, approvals, and Operator Console list/detail paths.
It needs a separate query-path and cross-tenant Operator Console design pass.
## Runtime Paths
Write paths:
- `TelegramGateway._mirror_outbound_message()` opens `get_db_context(project_id)`
and then calls `record_outbound_message()`.
- `ChannelHub._interim_feedback_task()` opens `get_db_context(project_id)` and
then calls `record_outbound_message()`.
- `record_outbound_message()` creates a shadow run if needed and inserts the
outbound row with the same `project_id`.
Read path:
- `platform_operator_service.get_run_detail()` reads outbound rows for the
selected `run_id`. Operator Console links from the runs list include
`project_id`, so the DB context matches the selected tenant.
## Apply
```bash
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 \
-f scripts/ops/awooop-rls-canary-wave1-3-outbound-message.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 1000 rows.
## Verification
Expected after apply:
- no `app.project_id`: direct reads return no rows.
- `app.project_id='awoooi'`: current outbound rows remain visible.
- `app.project_id='ewoooc'`: no `awoooi` outbound rows are visible.
- rollback-only insert under `app.project_id='awoooi'` is allowed and rolled
back.
- rollback-only insert under mismatched context is blocked by RLS.
- `/api/v1/platform/runs/{run_id}/detail?project_id=awoooi` still returns the
selected run timeline and outbound evidence.
- global RLS preflight remains blocked only by later-wave tables.
Production verification from 2026-05-12:
- `/api/v1/health` from API pod: HTTP 200, `status=healthy`.
- `/api/v1/platform/runs/d385b7fe-8666-58ec-9072-9ac917adb6cf/detail?project_id=awoooi`:
- HTTP 200
- `counts.outbound_messages=1`
- `scripts/ops/awooop-rls-preflight.sh --exact-counts`:
- `PASS=7 WARN=1 BLOCKED=1`
- `awooop_outbound_message rls=true force=true policies=1`
- remaining blocker tables: `audit_logs`, `awooop_run_state`,
`incidents`, `knowledge_entries`, `playbooks`.
- Direct app-role behavior:
- `outbound_no_context=0`
- `outbound_awoooi_context=290`
- `outbound_ewoooc_context=0`
- `insert_awoooi_context_awoooi_row=allowed`
- `insert_ewoooc_context_awoooi_row=blocked:InsufficientPrivilegeError`
- `outbound_after_probe=290`
## Rollback
```bash
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 \
-f scripts/ops/awooop-rls-canary-wave1-3-outbound-message-rollback.sql
```
Rollback removes the Wave1.3 policy and disables RLS on
`awooop_outbound_message`. It does not modify data.