184 lines
8.8 KiB
Markdown
184 lines
8.8 KiB
Markdown
# AI 콘텐츠 신고 운영 runbook
|
|
|
|
기준일: 2026-08-24
|
|
|
|
이 문서는 D3RO Voice의 AI 결과 신고를 production에서 접수·검토·종결·정리하는
|
|
절차다. 현재는 구현과 local 검증만 완료됐으며, production 배포·운영 담당 지정·자동
|
|
purge·실제 설치본 E2E는 완료되지 않았다.
|
|
|
|
## 1. 책임과 접근 경계
|
|
|
|
출시 전에 다음 값을 실제 담당자로 교체한다.
|
|
|
|
| 역할 | 책임 | 현재 상태 |
|
|
| ----------------- | -------------------------------------------------- | -------------------------------- |
|
|
| moderation owner | 매 영업일 queue 확인, SLA와 종결 품질 관리 | `<PLACEHOLDER_MODERATION_OWNER>` |
|
|
| safety escalation | 아동 안전, 자해·폭력, 명백한 불법 콘텐츠 즉시 판단 | `<PLACEHOLDER_SAFETY_ON_CALL>` |
|
|
| privacy owner | 개인정보·계정 삭제·보관 예외 검토 | `<PLACEHOLDER_PRIVACY_OWNER>` |
|
|
| purge owner | 매일 purge 실행·실패 알림·월간 보관 감사 | `<PLACEHOLDER_PURGE_OWNER>` |
|
|
|
|
- 일반 사용자는 authenticated `content-report` Edge Function으로만 신고한다.
|
|
- `content_generation_receipts`, `content_reports`,
|
|
`content_report_review_actions`는 `anon`과 `authenticated`의 직접 접근을 모두
|
|
거부한다.
|
|
- queue 조회·상태 변경 RPC는 `profiles.role`이 `manager`, `admin`,
|
|
`super_admin`인 인증 사용자만 호출할 수 있다.
|
|
- `profiles.role`과 `profiles.tier`는 일반 사용자의 column UPDATE 권한에서 제외한다.
|
|
표시 이름, 아바타, locale만 self-service 수정이 가능하다.
|
|
- service-role key는 모바일 앱, 브라우저, 로그, 티켓, Play reviewer 안내에 넣지 않는다.
|
|
|
|
근거 마이그레이션:
|
|
|
|
- `server/supabase/migrations/20260824000029_content_reporting.sql`
|
|
- `server/supabase/migrations/20260824000030_profile_managed_field_acl.sql`
|
|
|
|
## 2. Production 배포 순서
|
|
|
|
다음 순서를 한 release window 안에서 수행한다.
|
|
|
|
1. 권위 있는 release commit과 production Supabase project ref를 기록한다.
|
|
2. migration `20260824000029`, `20260824000030`의 dry-run diff를 검토한 뒤 적용한다.
|
|
3. `llm-proxy`와 `content-report`를 같은 source SHA에서 배포한다.
|
|
4. Talk, Commands, Actions 클라이언트를 포함한 AAB를 배포한다.
|
|
5. purge job과 실패 알림을 활성화한다.
|
|
6. Free/Pro+ 테스트 계정으로 제출→queue→검토→종결→감사 원장 E2E를 수행한다.
|
|
|
|
예시 명령의 placeholder는 실행 전에 secret manager와 release record의 실제 값으로
|
|
대체한다. 이 문서의 명령 실행 자체가 production 배포 승인인 것은 아니다.
|
|
|
|
```powershell
|
|
supabase db push --linked --dry-run
|
|
supabase db push --linked
|
|
supabase functions deploy llm-proxy --project-ref '<PROJECT_REF>'
|
|
supabase functions deploy content-report --project-ref '<PROJECT_REF>'
|
|
```
|
|
|
|
배포 직후 다음을 확인한다.
|
|
|
|
- `X-D3RO-Generation-Purpose`가
|
|
`talk_response|command_response|action_response`만 허용된다.
|
|
- 성공한 provider 응답에는 UUID `X-D3RO-Generation-Id`가 있다.
|
|
- receipt에는 user/model/purpose/expiry만 있고 prompt·AI 출력은 없다.
|
|
- 신고에는 사용자가 미리 확인한 snapshot, reason, 선택 comment만 있다.
|
|
- 음성, 전체 대화, 원래 prompt는 자동 첨부되지 않는다.
|
|
- 일반 사용자 role/tier 직접 UPDATE와 ledger 직접 SELECT/INSERT가 거부된다.
|
|
|
|
## 3. Queue와 처리 SLA
|
|
|
|
운영 도구는 로그인한 manager 이상 사용자의 access token으로 다음 RPC를 호출한다.
|
|
브라우저 콘솔이나 공유 스크립트에 token을 붙여 넣지 않는다.
|
|
|
|
```ts
|
|
const { data, error } = await supabase.rpc('admin_list_content_reports_v1', {
|
|
p_status: 'pending',
|
|
p_limit: 100,
|
|
p_before: null
|
|
})
|
|
```
|
|
|
|
권장 SLA:
|
|
|
|
- 아동 성적 학대·착취 또는 즉각적인 생명 위험: 발견 즉시 escalation
|
|
- 개인정보 노출, 구체적 폭력 위협, 자해 조장: 4시간 이내 1차 검토
|
|
- 혐오·괴롭힘·성적·사기·스팸·오정보·기타: 1영업일 이내 1차 검토
|
|
- 미처리 `pending`의 최장 age가 24시간을 넘으면 moderation owner와 on-call에 알림
|
|
|
|
검토자는 snapshot과 사용자가 선택한 사유·comment만 필요한 범위에서 본다. 다른
|
|
대화, 녹음, 계정 데이터는 자동으로 확장 조회하지 않는다.
|
|
|
|
## 4. 검토와 종결
|
|
|
|
각 조치는 새 UUID idempotency key와 3~1000자의 사실 기반 note가 필요하다.
|
|
|
|
```ts
|
|
await supabase.rpc('admin_act_on_content_report_v1', {
|
|
p_report_id: reportId,
|
|
p_idempotency_key: crypto.randomUUID(),
|
|
p_action: 'begin_review',
|
|
p_note: 'Initial policy review started'
|
|
})
|
|
```
|
|
|
|
허용 전이는 다음뿐이다.
|
|
|
|
| action | 결과 상태 | 사용 조건 |
|
|
| ------------------- | ----------- | -------------------------------------------- |
|
|
| `begin_review` | `reviewing` | 담당자가 증거를 확인하기 시작함 |
|
|
| `dismiss` | `dismissed` | 정책 위반이 아니거나 증거가 불충분함 |
|
|
| `confirm_violation` | `actioned` | 정책 위반을 확인하고 별도 완화 조치를 기록함 |
|
|
|
|
`confirm_violation`은 원장 상태를 확정하지만 계정 정지나 provider 차단을 자동 실행하지
|
|
않는다. 필요한 계정·모델·prompt 정책 조치는 기존 승인된 관리자 절차에서 별도로
|
|
수행하고, 신고 note에는 비밀이나 snapshot 원문을 복제하지 말고 조치 식별자만 남긴다.
|
|
|
|
의도적인 허위·보복·반복 남용 신고는 신고 자체를 삭제해 숨기지 않는다. `dismiss`로
|
|
종결하고 반복 패턴은 별도 abuse case로 escalation한다. 선의의 신고자에게 불이익을
|
|
주지 않는다.
|
|
|
|
## 5. 보관과 purge
|
|
|
|
- generation receipt: 생성 후 30일
|
|
- `pending`, `reviewing` 신고 증거: 해결할 때까지
|
|
- `dismissed`, `actioned` 신고 증거: 해결 시점부터 180일
|
|
- 계정 삭제: reporter/reviewer identity와 moderation audit actor는 `NULL`로 분리하지만,
|
|
미해결 또는 180일 보관 창의 선택 증거는 moderation·분쟁 대응을 위해 남을 수 있다.
|
|
- general audit log에는 snapshot이나 전체 생성 결과를 복제하지 않는다.
|
|
|
|
정리는 `purge_expired_content_reporting_data_v1()`만 사용한다. 이 RPC는
|
|
service-role만 실행할 수 있다. scheduler는 secret manager에서 service credential을
|
|
주입하는 서버 측 job이어야 하며, 모바일·브라우저 job은 금지한다.
|
|
|
|
성공 응답 예시는 다음과 같다.
|
|
|
|
```json
|
|
{ "purgedReports": 3, "purgedReceipts": 121 }
|
|
```
|
|
|
|
매일 실행 결과에 실행 시각, release SHA, 숫자만 기록한다. report ID, snapshot,
|
|
comment, service credential은 scheduler log에 남기지 않는다. 24시간 동안 성공 실행이
|
|
없거나 RPC가 실패하면 purge owner에게 알리고, 72시간 이내 복구되지 않으면 privacy
|
|
owner에게 escalation한다.
|
|
|
|
## 6. 모니터링과 월간 감사
|
|
|
|
운영 모니터는 원문 없이 다음 집계만 보관한다.
|
|
|
|
- status별 수와 최장 pending age
|
|
- 시간·일 rate-limit 발생 수
|
|
- 제출 4xx/5xx와 idempotency conflict 수
|
|
- review action 성공·실패 수
|
|
- 마지막 purge 성공 시각과 삭제 건수
|
|
|
|
매월 다음을 표본 확인한다.
|
|
|
|
- 일반 사용자 role/tier self-update가 계속 거부되는가
|
|
- expired receipt가 남아 있지 않은가
|
|
- resolved report가 180일 이후 purge되는가
|
|
- pending/reviewing 증거가 time purge되지 않는가
|
|
- audit log에 snapshot·comment·prompt가 복제되지 않았는가
|
|
- manager/admin/super_admin 이외 계정이 moderation RPC를 호출하지 못하는가
|
|
|
|
## 7. Release 증거와 rollback
|
|
|
|
release evidence에는 비밀이나 신고 원문 없이 다음만 보관한다.
|
|
|
|
- source commit SHA와 migration/function deployment ID
|
|
- local SQL, Edge HTTP, Deno test 결과
|
|
- Play 설치본의 Talk/Commands/Actions 신고 왕복 캡처
|
|
- queue 도달, `begin_review`, terminal action의 report ID 해시와 상태 시각
|
|
- purge job 설정, 마지막 성공 시각, alert test
|
|
- privacy/terms 공개 URL과 해당 버전의 hash
|
|
|
|
배포 후 신고 제출이나 generation receipt가 실패하면 AI 생성 경로를 정상처럼 계속
|
|
노출하지 않는다. 영향 surface를 비활성화하거나 이전 검증 release로 rollback하고,
|
|
receipt와 report schema를 임의로 drop하거나 moderation evidence를 수동 삭제하지 않는다.
|
|
|
|
## 현재 미해결 gate
|
|
|
|
- production migration/Edge/mobile 동시 배포
|
|
- manager용 moderation UI 또는 승인된 내부 operator client
|
|
- 실제 운영 담당자와 on-call 지정
|
|
- 매일 purge scheduler와 실패 알림
|
|
- provider safety/filter 검증
|
|
- Play가 서명한 실기기 신고 E2E
|
|
- Talk/Commands/Actions 밖의 AI 생성 문서 신고 범위
|