feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
400
docs/v3/MOBILE_APP_COMPLETION_SSOT.md
Normal file
400
docs/v3/MOBILE_APP_COMPLETION_SSOT.md
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
# D3RO Voice 모바일 앱 완성 정본 (SSOT)
|
||||
|
||||
> 상태: ACTIVE
|
||||
> 최초 감사 기준: 2026-08-21 / `main` @ `a9c9a1c`
|
||||
> 마지막 release-readiness 스냅샷: 2026-08-29 (Play Console live 재확인, release identity `1.1.0`/`1010001`, production AAB 대기)
|
||||
> 제품 모바일 런타임: `apps/mobile-rn`
|
||||
> 데이터·권한 정본: `server/supabase/migrations` + Supabase Auth/Storage/Edge Functions
|
||||
> 제품화 재개 핸드오프: [`MOBILE_PRODUCTIZATION_HANDOFF_2026-08-21.md`](./MOBILE_PRODUCTIZATION_HANDOFF_2026-08-21.md)
|
||||
> 이 문서는 이전 `docs/v3/00-mobile-master-plan.md`의 미래 로드맵을 대체하는 구현·검증 정본이다.
|
||||
|
||||
## 0. 2026-08-29 출시 게이트 스냅샷
|
||||
|
||||
| 게이트 | 현재 증거 | 판정 |
|
||||
|---|---|---|
|
||||
| release identity | `release/product-version.json`: version `1.1.0`, Android versionCode / iOS build `1010001` | source SSOT GREEN; 실제 store artifact 미검증 |
|
||||
| Play 앱 | app ID `4976102237698469110`, package `com.d3ro.voice`, 상태 `임시` | 앱 생성 GREEN, 제출 RED |
|
||||
| Play 설정 | 0/11, 첫 AAB 0건 | RED |
|
||||
| App Signing | Play SHA-256 `01:00:19:21:DB:4F:33:40:85:CE:21:E4:B8:DE:CC:BD:71:DA:87:67:C5:6E:3B:59:83:2A:A1:C8:29:EA:0D:AB` | Play identity 확인; upload certificate 대조는 첫 AAB 후 |
|
||||
| upload key | external PKCS12·user-only ACL·Credential Manager·private-key readback GREEN; SHA-256 `4F:AC:69:24:...:15:2B:54` | local key GREEN; CI secret·복구 백업·AAB signer/Console 대조는 RED |
|
||||
| release evidence key | Ed25519 external private/public pair roundtrip GREEN; public SSOT·keyId `2797d3e6...4a890b7f` | local pair GREEN; CI private-key secret·복구 백업·실제 signed evidence는 RED |
|
||||
| App Links | source `assetlinks.json` 3개는 Play SHA-256으로 갱신 | live는 구 certificate라 배포·public 재검증 전까지 RED |
|
||||
| Firebase | Console `u/0`, `u/1` 모두 D3RO project 없음 | 사용자 승인 후 project·Android app 생성 필요; RED |
|
||||
| AdMob | App/Banner/Rewarded ID·SSV SSOT 확정 | `검토 필요`·`광고 게재 제한`·store 미연결·결제 프로필 미완료; RED |
|
||||
| production inputs | production Firebase config·AdMob/upload/evidence CI secret 주입 증거·AAB 없음 | RED |
|
||||
| closed test | 0/12명, 연속 14일 미시작 | RED |
|
||||
| production access | disabled | RED |
|
||||
|
||||
이 스냅샷의 RED는 내부 구현을 미완성으로 되돌리는 뜻이 아니다. 다만 source/test GREEN을 Play production GREEN으로 확대 해석하지 않는다. Data safety, App content, production access 답변은 실제 Console 문구를 사용자가 검토·승인하기 전에 저장·제출하지 않는다.
|
||||
|
||||
## 0.1 완료 계약
|
||||
|
||||
이 작업에서 "완료"는 화면이 존재하거나 타입 검사가 통과했다는 뜻이 아니다. 아래 조건을 모두 만족해야 한다.
|
||||
|
||||
1. `apps/mobile-rn`의 모든 사용자 노출 버튼·메뉴·딥링크·공유 진입점이 실제 동작한다.
|
||||
2. 데스크톱과 Web의 서버 공유 기능은 동일한 Supabase 사용자·구독·데이터에서 읽고 쓴다.
|
||||
3. 모바일에서 불가능한 데스크톱 OS 전용 동작은 모바일 네이티브 동등 기능으로 제공한다.
|
||||
4. 인증·결제·광고 보상·동기화·삭제는 클라이언트 성공 알림만으로 성공 처리하지 않는다.
|
||||
5. Free tier 광고는 실제 광고 SDK 완료 콜백과 서버의 중복 방지 보상 기록을 통과해야 한다.
|
||||
6. 정상·실패·취소·재시도·오프라인·중복 요청·권한 거부·세션 만료 경로를 자동화한다.
|
||||
7. Android 에뮬레이터에서 설치된 APK를 대상으로 전체 E2E를 수행하고 증거를 남긴다.
|
||||
8. 실행 코드에 미구현 placeholder, 가짜 성공 타이머, 개발 우회 로그인, 무반응 Pressable을 남기지 않는다.
|
||||
9. 각 작업은 코드, 자동 테스트, 에뮬레이터 증거를 모두 확보한 뒤에만 `[x]`로 바꾼다.
|
||||
|
||||
## 1. 정본 경계와 보존 규칙
|
||||
|
||||
- `apps/mobile-rn`: Android/iOS 제품 모바일 앱. 모든 신규 구현은 여기에 한다.
|
||||
- `apps/mobile`: Expo 51 기반 과거 스켈레톤. 참고만 하며 제품 기능을 이쪽에 이중 구현하지 않는다.
|
||||
- `apps/web`: 서버 공유 기능과 사용자 데이터 UX의 비교 기준.
|
||||
- `apps/desktop`: 데스크톱 기능 목록과 로컬 전용 기능의 의미 기준.
|
||||
- `packages/api-client`: Supabase 타입과 공용 API 호출의 정본. 모바일에서 임의 테이블 타입을 중복 작성하지 않는다.
|
||||
- `packages/core`: 기능·구독·광고·오류 도메인 타입의 정본.
|
||||
- `packages/ui-native`: 모바일 디자인 토큰과 공용 컴포넌트 정본.
|
||||
- 기존 dirty worktree와 `main`의 미푸시 커밋 2개를 보존한다. 관련 없는 바이너리·Web·스크립트 변경을 되돌리거나 함께 정리하지 않는다.
|
||||
- 비밀값은 저장소에 하드코딩하지 않는다. 빌드 시 주입하고, 누락 시 기능을 숨겨 성공처럼 보이게 하지 말고 진단 가능한 오류로 닫는다.
|
||||
|
||||
## 2. 2026-08-21 초기 감사 결과
|
||||
|
||||
> 아래 표는 구현 전 기준선이다. 현재 판정은 바로 다음 `2.1`과 4절 체크리스트,
|
||||
> 7절 실행 증거를 따른다. 기준선의 실패 문구를 현재 상태로 읽으면 안 된다.
|
||||
|
||||
| 영역 | 현재 사실 | 판정 |
|
||||
|---|---|---|
|
||||
| 앱 기반 | RN CLI 0.85 / React 19, Android 네이티브 프로젝트와 서명 APK가 존재 | 기반만 존재 |
|
||||
| 화면 | Login, Dash, History, Record, Talk, Settings, ProPaywall 7개 화면 | 기능 동등성 미달 |
|
||||
| Supabase | `SUPABASE_URL`과 anon key가 빈 문자열이며 dummy fallback으로 크래시만 회피 | 실제 인증·데이터 불가 |
|
||||
| 세션 라우팅 | 앱은 항상 Login을 첫 화면으로 열며 인증 상태에 따른 root 전환이 없음 | 실패 |
|
||||
| 회원가입 | 회원가입 문구는 Pressable이지만 handler와 화면이 없음 | 실패 |
|
||||
| OAuth | 브라우저 호출 코드는 있으나 설정 주입·콜백 수명주기·취소/오류 검증이 불완전 | 미검증 |
|
||||
| 계정 관리 | 이메일 표시와 로그아웃 확인만 존재. 프로필 수정·비밀번호·연결 계정·삭제 없음 | 실패 |
|
||||
| 녹음 | recorder hook이 placeholder이며 stop 결과가 항상 `null` | 실패 |
|
||||
| 파일 전사 | document picker, content URI 처리, 업로드 진행/취소 화면 없음 | 없음 |
|
||||
| 히스토리 | 최대 50개 조회만 수행. 필터는 데이터에 적용되지 않고 상세/편집/삭제/공유 없음 | 실패 |
|
||||
| AI 대화 | 사용자 말풍선만 로컬 state에 추가하고 서버 LLM 응답을 호출하지 않음 | 실패 |
|
||||
| 설정 | 언어·자동 교정·햅틱·STT 스위치가 고정값이며 저장되지 않음 | 실패 |
|
||||
| 결제 | 1.8초 대기 후 "결제 완료" 알림을 띄움 | 가짜 성공, 출시 차단 |
|
||||
| 구매 복원 | 1.2초 대기 후 성공 알림을 띄움 | 가짜 성공, 출시 차단 |
|
||||
| 광고 보상 | 2.5초 대기 후 로컬 토큰을 50 증가시킴 | 가짜 보상, 출시 차단 |
|
||||
| 광고 인프라 | 데스크톱 mediation 타입·adapter·테스트가 있으나 다수 adapter가 합성 bid/creative를 반환. 모바일 광고 SDK 없음 | 기반 참고만 가능 |
|
||||
| 백엔드 | Auth/RLS/Storage/STT/LLM/팀/Push/Stripe/Payple/Admin 함수와 스키마가 존재 | 재사용 가능, 모바일 IAP·광고 원장 없음 |
|
||||
| 신원 체계 | Supabase Auth/Postgres, 별도 .NET JWT/SQLite, 데스크톱 로컬 license가 서로 다른 사용자·권한을 가짐 | 단일 SSOT 필요 |
|
||||
| STT 실패 | Supabase와 .NET STT가 provider 전체 실패 때 합성 transcript를 성공으로 반환 가능 | 가짜 성공, 출시 차단 |
|
||||
| 배포 APK | 공개 APK가 `application-debuggable`이며 arm64 단일 ABI. 보유 AVD는 x86_64 | 에뮬레이터 설치 불가 |
|
||||
| 테스트 | 모바일 Jest smoke 1개 외에 기능 단위·통합·기기 E2E가 없음 | 실패 |
|
||||
| Android 권한 | 제품 manifest에는 INTERNET 외 녹음·알림·미디어/foreground service 권한이 없음 | 실패 |
|
||||
|
||||
### 2.1 현재 실행 스냅샷
|
||||
|
||||
| 묶음 | 현재 판정 | 확인된 증거 / 남은 게이트 |
|
||||
|---|---|---|
|
||||
| Auth·계정 | 구현·자동화 GREEN, 외부 OAuth·privacy purge 기기 E2E 대기 | auth/signup/invite 경계는 최신 exact APK `.11`에서 PASS. Keychain 세션, 중앙 `purgeAllAccountLocalData`, cold no-session·SIGNED_OUT·refresh session loss·A→B 전환·실패 재시도 경계는 전체 모바일 57 suites/353 tests에 포함돼 GREEN. 실제 token-expiry 강제 로그아웃·logout/delete/provider 연결/해제 기기 E2E와 Google/GitHub/Apple 동의→callback은 남음 |
|
||||
| 온보딩·테마 | 구현·기기 journey GREEN | 신규/기존 분기, skip/replay, 계정별 preference merge, system/light/dark, reduced motion, 알림·마이크 권한 상태 구현. production preference `0→skip→1`, Android PID 유지, 5.8초 내 Main, navigation warning 0을 실측 |
|
||||
| 녹음·전사·Talk | 핵심 경로 GREEN | API 34 microphone FGS, 지속 알림, pause/resume/discard, 실제 통화 audio-focus, 프로세스 kill 후 WAV 복구, 파일 import, cloud STT fail-closed, on-device Whisper, Claude SSE, Android TTS를 통합 APK에서 확인. STT provider 호출 전 원자 quota reservation·실패 환불은 20-way 동시성으로 검증. Android `ACTION_SEND` 수신은 허용 MIME·크기·provider 예외·MP4 AAC 추출을 fail-closed로 구현했고 API 34 instrumentation 4/4(CSPRNG 1 + share 3)와 별도 UID 외부 ContentProvider APK→`ACTION_SEND`→Record→durable queue→cancel cleanup E2E가 GREEN |
|
||||
| 데이터 portability | 구현·공유 E2E GREEN | 00021 atomic export/restore, 손상/타 사용자/충돌 rollback, legacy import를 local integration으로 확인. `portable_exports`→FileProvider `shared_exports` 경계와 PDF Print/DOCX chooser·30초 cleanup을 실제 기기에서 확인 |
|
||||
| History·회의 | 실제 데이터·회의 생성 계약 GREEN | Supabase history pagination/search/favorite/CAS, Web↔mobile history E2E, meetings/memos/documents RLS/CAS, meeting recording lifecycle 구현. 00025의 제목·참석자·4개 언어·소유 템플릿 원자 생성은 2계정 43 assertions와 competing-payload PT409로 검증. speaker/audio timestamp와 최종 교차 앱 E2E는 남음 |
|
||||
| Teams·Push | 팀 GREEN, 외부 Push 대기 | production 17 assertions와 local 36 assertions로 원자 팀/초대/RLS/role/device token 계약 확인. durable push outbox 00015~00020과 logout device `1→0` 확인. 실제 FCM 수신은 Firebase/FCM 자격 증명·cron 배포가 필요 |
|
||||
| Billing·Ads | test 광고와 production AdMob identity/SSV 준비, store·실제 지급 E2E RED | Google Play restore는 실제 store list→모든 token 서버 검증→entitlement snapshot 일치 후에만 성공. Google 공식 test adaptive banner와 rewarded `AdActivity` 완주를 실측했다. production app `ca-app-pub-1039714767792854~6427959892`, banner `/9840591290`, rewarded `/2255790918`, `50 cloud_ai_tokens`, SSV URL, Edge v13, durable receipt 원장은 SSOT에 반영했다. 그러나 live는 `검토 필요`, `광고 게재 제한`, store 미연결, 결제 프로필 미완료다 |
|
||||
| Admin | 서버·Web·모바일 E2E와 production DB·Edge 배포 GREEN | .NET 21/21, 00023 관리 integration 25 assertions, Edge/JWT 47 assertions와 TLS Chromium E2E GREEN. 모바일 API 34에서 ordinary/manager/admin/super_admin 및 stale 강등을 55 assertions로 실측: Free test 광고, Pro 광고 미요청, 관리자 메뉴/API 차단, 구독 수정, 역할 경계·변경, 삭제 확인·취소, stale JWT 403와 민감 state purge가 모두 PASS. fixture 잔존 0. production 00023 및 admin Edge 4개 배포 후 익명 GET은 모두 `401 authentication_required` + `Cache-Control: no-store`로 재검증 |
|
||||
| 품질·배포 | exact-APK 에뮬레이터 gate GREEN, production 배포 RED | mobile ESLint 0, typecheck GREEN, 전체 Jest 57 suites/353 tests GREEN은 2026-08-24 실행 증거다. universal non-debuggable TEST-DEMO `0.0.0-e2e.20260821.11`/`202608221`을 API 34에 fresh install해 cold launch·auth/signup/invite를 검증했지만 production artifact가 아니다. 2026-08-29 release identity `1.1.0`/`1010001`, local upload key, Ed25519 evidence keypair, AdMob identity는 확정했지만 production Firebase·CI secret 주입·AAB·Play track·Fold6 Play 설치 증거는 없다 |
|
||||
|
||||
## 3. 플랫폼 동등성 원칙
|
||||
|
||||
| 데스크톱 기능 | 모바일 동등 기능 |
|
||||
|---|---|
|
||||
| 전역 단축키 녹음 | 앱 바로가기, 알림 action, 홈 화면 quick action, 앱 내 큰 녹음 CTA |
|
||||
| OS 현재 앱에 텍스트 삽입 | 복사, Android 공유 시트, 선택한 앱으로 공유 |
|
||||
| 로컬 Whisper/Ollama | 모바일 기기 지원 여부를 감지하고, 기본은 인증된 Cloud STT/LLM. 결과·쿼터·오류 계약은 동일 |
|
||||
| 파일 드롭 | Android Storage Access Framework 문서 선택과 다른 앱의 `ACTION_SEND`/`ACTION_VIEW` 수신 |
|
||||
| 데스크톱 SQLite | Supabase SSOT + 암호화된 모바일 캐시·업로드 큐 |
|
||||
| 시스템 오디오 회의 녹음 | 마이크 foreground service 녹음. Android 정책상 캡처 가능한 미디어 projection은 별도 명시 동의 후만 사용 |
|
||||
| 데스크톱 설정 모달 | 모바일 설정 navigation stack과 계정·기기·데이터 관리 화면 |
|
||||
|
||||
## 4. 전체 기능 매트릭스와 작업 등록
|
||||
|
||||
상태 표기: `[ ]` 미완료, `[~]` 구현 중, `[x]` 코드+자동화+에뮬레이터 증거 완료, `[!]` 외부 콘솔/계정 증거 대기.
|
||||
|
||||
### G — 거버넌스·기반
|
||||
|
||||
- [x] G-001 제품 모바일 런타임을 `apps/mobile-rn`으로 확정하고 본 문서에 기록
|
||||
- [ ] G-002 `apps/mobile` 참조 코드에서 재사용할 기능을 선별하고 이중 런타임 의존 제거
|
||||
- [x] G-003 모바일 환경 설정 스키마 작성: Supabase URL/anon key, API origin, OAuth redirect, AdMob app/unit IDs, Play product IDs
|
||||
- [x] G-004 debug/test/prod 설정 분리와 시작 시 구성 진단 화면 구현
|
||||
- [x] G-005 하드코딩된 release signing 자격 증명을 로컬/CI secret 주입으로 이동
|
||||
- [ ] G-006 공용 API·DB 타입을 `packages/api-client`에서 소비하고 화면별 임시 타입 제거
|
||||
- [ ] G-007 오류 코드·사용자 메시지·관찰 가능 로그를 공용 규약으로 통일
|
||||
- [ ] G-008 legacy bundle/APK 생성물을 소스 변경과 분리하고 재현 가능한 build script 확정
|
||||
- [ ] G-009 Supabase Auth/profile/subscription을 단일 사용자·entitlement SSOT로 확정하고 .NET 계정은 제거 또는 명시적 매핑
|
||||
- [ ] G-010 Android application ID, iOS bundle ID, OAuth client, 딥링크 scheme을 하나의 제품 식별자로 통일
|
||||
- [ ] G-011 노출된 signing/Forgejo/OAuth/JWT 자격 증명을 폐기·회전하고 CI secret으로 이동
|
||||
- [x] G-012 .NET 고정 관리자 seed·정적 salt SHA-256 비밀번호·fallback JWT secret 제거
|
||||
|
||||
### A — 인증·회원가입·본인 계정 관리
|
||||
|
||||
- [ ] A-001 auth bootstrap splash와 session restore 구현
|
||||
- [ ] A-002 인증 상태에 따라 Login/Main/Onboarding을 원자적으로 전환
|
||||
- [ ] A-003 이메일 로그인 입력 검증·rate limit 메시지·세션 저장
|
||||
- [ ] A-004 이메일 회원가입 화면과 약관 동의, 중복 계정 오류, 이메일 확인 대기 화면
|
||||
- [ ] A-005 이메일 확인 딥링크와 확인 후 session 전환
|
||||
- [ ] A-006 비밀번호 찾기 메일 요청·recovery 딥링크·새 비밀번호 설정
|
||||
- [ ] A-007 Google OAuth PKCE 시작/취소/콜백/코드 교환/재실행 검증
|
||||
- [ ] A-008 GitHub OAuth PKCE 시작/취소/콜백/코드 교환 검증
|
||||
- [ ] A-009 Apple 로그인은 iOS에서 네이티브 정책 준수, Android에서는 지원 여부를 명확히 표시
|
||||
- [ ] A-010 OAuth identity 충돌·동일 이메일 연결·provider 오류 처리
|
||||
- [ ] A-011 프로필 이름·아바타·locale 조회/편집과 optimistic rollback
|
||||
- [ ] A-012 본인 이메일·provider·가입일·마지막 로그인·구독 상태 확인 화면
|
||||
- [ ] A-013 비밀번호 변경과 재인증 요구 처리
|
||||
- [~] A-014 연결된 로그인 provider 목록과 연결/해제 안전장치 — 코드·Jest GREEN, 실제 provider 동의/연결/해제 E2E 대기
|
||||
- [~] A-015 모든 로컬 민감 캐시를 지우는 로그아웃 및 navigation reset — Keychain·recorder·durable queue purge 코드·Jest GREEN, 최신 APK 재검증 대기
|
||||
- [~] A-016 계정 삭제: 재인증, 명시 확인, 서버 cascade/보존 정책, 완료 후 로컬 purge — 서버 성공 후 로컬 purge 코드·Jest GREEN, 최신 APK 재검증 대기
|
||||
- [~] A-017 access token 만료·refresh 실패·강제 로그아웃 E2E — 중앙 privacy purge와 session-loss/race/failure-retry Jest GREEN, 실제 token-expiry 기기 E2E 대기
|
||||
- [ ] A-018 release 빌드에서 dev bypass와 dummy auth 성공 경로 완전 제거
|
||||
|
||||
### O — 온보딩·튜토리얼·테마·접근성
|
||||
|
||||
- [ ] O-001 첫 실행 welcome과 기존 사용자/신규 사용자 분기
|
||||
- [ ] O-002 로그인 전 개인정보·클라우드 처리·녹음 권한 설명
|
||||
- [ ] O-003 마이크 권한 요청/거부/다시 묻지 않음/설정 이동
|
||||
- [ ] O-004 알림 권한은 가치 설명 뒤 요청하고 거부 시 기능 저하 안내
|
||||
- [ ] O-005 첫 녹음→전사→공유를 안내하는 단계별 튜토리얼
|
||||
- [ ] O-006 주요 탭과 파일 가져오기·동기화·결제 관리 튜토리얼
|
||||
- [ ] O-007 튜토리얼 다시 보기·건너뛰기·완료 상태 동기화
|
||||
- [ ] O-008 system/light/dark 테마와 D3RO 토큰 기반 렌더링
|
||||
- [ ] O-009 테마·언어·햅틱·자동 교정·STT 옵션 저장 및 기기간 동기화
|
||||
- [ ] O-010 글자 확대, TalkBack label/role, focus order, 48dp 터치 영역, 색 대비 검증
|
||||
- [ ] O-011 reduced motion과 애니메이션 비활성 경로
|
||||
- [ ] O-012 한국어/영어 필수 전체 번역과 하드코딩 문자열 제거
|
||||
|
||||
### D — 데이터 가져오기·동기화·오프라인
|
||||
|
||||
- [ ] D-001 `user_settings`, `devices`, `audio_files`, `processing_jobs` 스키마·RLS·Realtime 확정
|
||||
- [ ] D-002 모바일 기기 등록, last seen, push token, 앱 버전 갱신
|
||||
- [ ] D-003 연결 기기 목록·현재 기기 표시·원격 연결 해제
|
||||
- [ ] D-004 데스크톱/Web에서 생성한 history/meeting/document/dictionary/knowledge/team 데이터 조회
|
||||
- [ ] D-005 모바일 변경을 데스크톱/Web에 Realtime 또는 재조회로 반영
|
||||
- [ ] D-006 최근 데이터 암호화 캐시와 로그인 사용자별 격리
|
||||
- [ ] D-007 오프라인 생성·편집 queue, 재시도 backoff, 앱 재시작 복구
|
||||
- [ ] D-008 충돌 정책: 서버 revision/updated_at 기반 감지와 사용자 선택 또는 안전 병합
|
||||
- [ ] D-009 pull-to-refresh, background refresh, stale/offline 표시
|
||||
- [ ] D-010 기존 데스크톱 내보내기 파일 가져오기와 형식 검증
|
||||
- [ ] D-011 Web/모바일 JSON·CSV·TXT 호환 가져오기와 중복 idempotency
|
||||
- [ ] D-012 데이터 전체 내보내기, 계정 데이터 다운로드, 공유
|
||||
- [ ] D-013 부분 실패·중복 import·다른 사용자 데이터·손상 파일 fail-closed 테스트
|
||||
- [ ] D-014 앱 삭제/재설치·다른 기기 로그인 후 클라우드 복원 E2E
|
||||
|
||||
### R — 녹음·모바일 파일·전사·공유
|
||||
|
||||
- [ ] R-001 Android `RECORD_AUDIO` runtime permission과 manifest 선언
|
||||
- [ ] R-002 실제 RN recorder 모듈로 시작/일시정지/재개/중지/취소 구현
|
||||
- [ ] R-003 오디오 레벨 meter와 timer가 실제 recorder state에서 파생
|
||||
- [ ] R-004 앱 background 전환·전화/오디오 focus loss·프로세스 종료 복구
|
||||
- [ ] R-005 Android foreground service와 지속 알림으로 장시간 녹음
|
||||
- [ ] R-006 저장 공간 부족·마이크 사용 중·권한 철회·장치 오류 처리
|
||||
- [ ] R-007 녹음 파일 메타데이터, content type, duration, checksum 생성
|
||||
- [ ] R-008 Supabase Storage 사용자 경로 업로드와 RLS 검증
|
||||
- [ ] R-009 업로드 진행률·취소·재시도·resume 가능한 queue
|
||||
- [x] R-010 STT 함수 호출, 인증/쿼터/413/415/429/5xx 오류 계약 처리 — provider 호출 전 원자 reservation, 20-way 동시성·overage·환불·ACL integration GREEN
|
||||
- [ ] R-011 전사 job 상태 Realtime/poll fallback과 앱 재시작 복원
|
||||
- [ ] R-012 완료 transcript/history/audio_files를 한 transaction 계약으로 연결
|
||||
- [ ] R-013 Android 파일 선택기로 m4a/mp3/wav/aac/flac/ogg/mp4/mov/webm 선택
|
||||
- [ ] R-014 `content://`, cloud provider, 한글/공백 파일명, MIME 불일치 안전 처리
|
||||
- [ ] R-015 대용량 파일 크기·시간 제한, 청크 업로드, 진행률, 취소, 재개
|
||||
- [x] R-016 다른 앱 공유 시트 `ACTION_SEND`/`ACTION_VIEW` 오디오·비디오 수신 — native instrumentation 3/3와 별도 UID Android ContentProvider APK의 실제 `ACTION_SEND`→Record→durable queue→cancel cleanup E2E GREEN
|
||||
- [ ] R-017 transcript/요약/회의록을 텍스트·Markdown·파일로 공유
|
||||
- [ ] R-018 원본 오디오 공유는 사용자가 명시 선택한 경우에만 임시 URI 권한으로 제공
|
||||
- [ ] R-019 전사 결과 복사·편집·저장·재처리·삭제
|
||||
- [ ] R-020 녹음/파일 전사 정상·실패·취소·오프라인·재시작 E2E
|
||||
- [x] R-021 Supabase/.NET STT provider 전체 실패 시 합성 transcript 성공을 제거하고 fail-closed — 502/503만 반환, 합성 성공 경로 0, Deno/.NET 계약 테스트 GREEN
|
||||
- [x] R-022 .NET STT endpoint 인증·사용자 quota를 강제하고 익명 user fallback 제거 — 사용자 경로는 Supabase JWT+원자 quota SSOT, .NET provider endpoint는 32-byte service token 전용. anonymous 401, direct authenticated legacy 410, exact internal token만 provider orchestration
|
||||
|
||||
### H — 히스토리·회의·문서
|
||||
|
||||
- [ ] H-001 History 사용자 범위 pagination과 날짜 그룹
|
||||
- [ ] H-002 전체/즐겨찾기/처리중 filter를 실제 query/state에 적용
|
||||
- [ ] H-003 검색, 날짜/모드/상태 정렬, empty/error/retry UI
|
||||
- [ ] H-004 히스토리 상세: 원문·교정문·메타데이터·오디오 재생
|
||||
- [ ] H-005 즐겨찾기, 제목 변경, 편집, 재처리, 삭제, 복구 가능 확인
|
||||
- [x] H-006 회의 생성: 제목·참석자·언어·템플릿
|
||||
- [ ] H-007 회의 중 녹음·메모·부분 전사 timeline
|
||||
- [ ] H-008 회의 종료와 전체 transcript 병합·처리 상태
|
||||
- [ ] H-009 요약·결정사항·액션 아이템·회의록 생성
|
||||
- [ ] H-010 transcript/summary/documents 탭 조회·편집·동시 수정 충돌
|
||||
- [ ] H-011 오디오 seek와 transcript timestamp 연동
|
||||
- [ ] H-012 PDF/DOCX/TXT/Markdown 내보내기와 Android 공유
|
||||
- [ ] H-013 회의 목록 search/filter/sort/delete/share
|
||||
- [ ] H-014 화자 구분 상태·화자명 편집·미지원 fallback
|
||||
- [ ] H-015 Web↔모바일 동일 회의 교차 편집 E2E
|
||||
|
||||
### F — 데스크톱/Web 전체 기능의 모바일 화면
|
||||
|
||||
- [ ] F-001 Dashboard 통계·사용량·처리 job·구독·동기화 상태를 실제 데이터로 표시
|
||||
- [ ] F-002 Dictionary 목록/검색/추가/편집/삭제/import/export/sync
|
||||
- [ ] F-003 Custom instructions 목록/활성화/CRUD/reorder/sync
|
||||
- [ ] F-004 Voice commands 목록/활성화/CRUD와 모바일 실행 가능한 action 구분
|
||||
- [ ] F-005 Actions 실행, 권한 확인, 실행 결과·실패 기록
|
||||
- [ ] F-006 AI text chat server 호출, streaming, 취소, retry, session history
|
||||
- [ ] F-007 Push-to-talk→STT→LLM→TTS 음성 대화와 재생/중단
|
||||
- [ ] F-008 custom instruction과 대화 backend/model/voice 선택
|
||||
- [ ] F-009 Knowledge 문서 목록/검색/추가/삭제와 RAG query
|
||||
- [ ] F-010 모바일 파일에서 지식 문서 가져오기와 처리 상태
|
||||
- [x] F-011 Templates 목록/선택/CRUD와 회의·전사 문서 생성 연동
|
||||
- [x] F-012 Memos 목록/CRUD/search/share/sync
|
||||
- [ ] F-013 연결 상태·백엔드 건강·quota를 실제 probe 결과로 표시
|
||||
- [ ] F-014 데스크톱 전용 기능은 이유와 모바일 대안을 명시하고 무반응 UI를 두지 않음
|
||||
|
||||
### T — 팀·관리·알림
|
||||
|
||||
- [ ] T-001 팀 목록/생성/이름 변경/삭제와 role 권한
|
||||
- [ ] T-002 이메일/링크 초대, 딥링크 수락, 만료·중복·다른 계정 처리
|
||||
- [ ] T-003 팀원 목록, admin/member/viewer 권한, 제거·나가기
|
||||
- [ ] T-004 팀 회의·문서 공유와 RLS 교차 사용자 음성/문서 접근 차단
|
||||
- [ ] T-005 팀 코멘트·활동 feed·알림
|
||||
- [x] T-006 일반 사용자의 본인 데이터 관리와 관리자 전용 기능을 분리 — ordinary UI에서 Admin 숨김·관리 API request 0, manager/admin/super 분기 API 34 E2E PASS
|
||||
- [x] T-007 admin/manager role 사용자는 사용자·구독·결제 감사 정보를 모바일에서도 안전 조회 — manager read/subscription update, stale JWT 403와 target/modal purge API 34 E2E PASS
|
||||
- [x] T-008 role 변경·구독 수정 등 파괴적 관리 동작은 재확인·감사 로그·서버 권한 검증 — admin role 범위·delete confirm/cancel, super manager→admin, RPC readback과 cleanup 0 E2E PASS
|
||||
- [ ] T-009 push token 등록/교체/로그아웃 삭제와 중복 방지
|
||||
- [ ] T-010 전사 완료·팀 초대·댓글·결제 상태 push 수신과 해당 화면 딥링크
|
||||
- [ ] T-011 알림 카테고리 설정·OS 권한 미허용 fallback
|
||||
- [ ] T-012 foreground/background/terminated 상태 알림 E2E
|
||||
- [x] T-013 Admin API 장애 때 realistic mock 운영 데이터를 반환하지 않고 명시 오류·stale 상태 표시 — stale-role 403 실측에서 `admin-error` 노출 및 기존 민감 state 제거
|
||||
|
||||
### M — 구독·구매·결제·Free tier 광고
|
||||
|
||||
- [x] M-001 서버 구독 상태와 quota를 읽는 entitlement provider 구현
|
||||
- [x] M-002 Web Payple/Stripe 구독을 모바일에서 자동 인식하고 중복 구매 경고
|
||||
- [x] M-003 Android Google Play Billing 라이브러리와 product ID 구성
|
||||
- [x] M-004 Play product/offer 조회와 스토어가 반환한 현지 가격·기간 표시
|
||||
- [x] M-005 Pro/Pro+ 구매 시작, 사용자 취소, pending, already-owned, 오류 처리
|
||||
- [x] M-006 purchase acknowledgement와 앱 재시작 시 미완료 purchase 복구
|
||||
- [x] M-007 구매 token을 클라이언트에서 신뢰하지 않는 서버 검증 함수
|
||||
- [x] M-008 검증 결과를 idempotent purchase 원장과 subscriptions entitlement에 반영
|
||||
- [x] M-009 Real-time Developer Notifications 또는 정기 검증으로 갱신·취소·환불·보류 반영
|
||||
- [x] M-010 구매 복원은 실제 스토어 조회+서버 검증 후에만 성공 표시
|
||||
- [x] M-011 구독 관리 버튼을 Google Play 구독 관리 화면으로 연결
|
||||
- [x] M-012 현재 플랜·만료/갱신일·결제 provider·사용량·결제 내역 표시
|
||||
- [~] M-013 가격·기능표를 서버/스토어 SSOT로 통일하고 hardcoded 가짜 가격 제거 — Web billing catalog·desktop checkout server readback·모바일 Play product 가격 사용, production Play/Payple/Stripe 교차 검증 대기
|
||||
- [x] M-014 Google Mobile Ads SDK와 test/prod app ID·unit ID 분리
|
||||
- [x] M-015 Free entitlement에만 adaptive banner를 안정적인 앱 레이아웃 영역에 표시
|
||||
- [x] M-016 Pro/Pro+ entitlement에서는 광고 request 자체를 하지 않음
|
||||
- [x] M-017 rewarded ad load/show/earned/closed/failure state machine
|
||||
- [x] M-018 광고를 끝까지 보지 않거나 SDK 오류 시 보상 금지
|
||||
- [x] M-019 서버 side verification 또는 signed reward nonce와 광고 보상 원장
|
||||
- [x] M-020 동일 광고·nonce·사용자 중복 보상 차단과 cooldown/rate limit
|
||||
- [x] M-021 서버 확정 잔액을 재조회한 뒤 UI에 반영하고 로컬 임의 증가 제거
|
||||
- [x] M-022 광고 동의/연령/지역별 privacy options와 consent 상태 저장
|
||||
- [x] M-023 광고 차단·네트워크 없음·no fill에서 핵심 Free 기능은 안전 fallback
|
||||
- [x] M-024 debug 에뮬레이터에서 Google test banner/rewarded ad E2E
|
||||
- [ ] M-025 Play Billing license tester에서 구매·복원·취소/pending E2E
|
||||
- [x] M-026 결제/광고 receipt·token·개인정보를 로그와 analytics에 노출하지 않음
|
||||
- [ ] M-027 Payple webhook 요청 인증·서명 검증과 replay 방지
|
||||
- [x] M-028 Stripe `pro_plus` 상품 계약과 DB tier를 통일하고 subscriptions/profiles drift 방지
|
||||
- [x] M-029 데스크톱 임의 checkout URL·무조건 성공 payment verify·legacy license 우회 제거 — 인증된 Stripe/Payple Edge-only 계약, trusted URL·server readback·deadline·idempotency fail-closed와 전체 desktop 590 tests GREEN
|
||||
|
||||
### Q — 자동화·Android 에뮬레이터·출시 게이트
|
||||
|
||||
- [x] Q-001 모바일 typecheck, lint, Jest 기본 gate 정비
|
||||
- [x] Q-002 auth/account service 단위·통합 테스트
|
||||
- [x] Q-003 recorder/upload/job/file import service 단위·통합 테스트
|
||||
- [x] Q-004 sync/cache/conflict/retry 테스트
|
||||
- [x] Q-005 subscription/IAP verification/idempotency 테스트
|
||||
- [x] Q-006 ad entitlement/reward/cooldown/fraud 테스트
|
||||
- [x] Q-007 Supabase migration을 새 로컬 DB에 전부 적용
|
||||
- [x] Q-008 RLS cross-user/anonymous/admin 권한 SQL 테스트
|
||||
- [x] Q-009 Edge Function auth/quota/content type/size/idempotency 테스트
|
||||
- [~] Q-010 Android debug·release APK clean build — universal debug와 non-debuggable E2E 양 ABI clean build GREEN, production signed release APK/AAB는 외부 secret gate
|
||||
- [~] Q-011 JS bundle이 포함된 release APK의 offline cold start — embedded bundle non-debuggable E2E APK cold start GREEN, production signed release artifact는 대기
|
||||
- [ ] Q-012 에뮬레이터 API 35+ 생성·부팅·초기화 script
|
||||
- [ ] Q-013 fresh install→onboarding→signup/login→logout E2E
|
||||
- [ ] Q-014 OAuth provider redirect→callback→session→Main E2E
|
||||
- [x] Q-015 녹음 권한→실제 오디오 fixture 주입/녹음→전사 결과 E2E
|
||||
- [x] Q-016 파일 picker→fixture 선택→upload→전사→상세→공유 E2E
|
||||
- [x] Q-017 Web seeded 데이터→모바일 조회/편집→Web 확인 sync E2E
|
||||
- [x] Q-018 Free banner/rewarded test ad와 Pro 광고 미요청 E2E — official Google test ad 실측, ordinary Free 광고 노출과 manager/admin Pro 광고 비노출·미요청 API 34 PASS
|
||||
- [ ] Q-019 Play Billing test purchase/restore/manage E2E
|
||||
- [ ] Q-020 모든 탭·설정·팀·관리·실패 복구 journey E2E
|
||||
- [ ] Q-021 회전·폰트 확대·TalkBack·dark/light·작은/큰 화면 visual gate
|
||||
- [ ] Q-022 airplane mode·process kill·token expiry·server 5xx·중복 tap 회복 E2E
|
||||
- [~] Q-023 앱 로그 crash/ANR/React error/민감정보 0건 확인 — exact `.9` auth/signup/invite/small-screen 및 admin role journeys에서 fatal/ANR/React fatal 0, production provider journey 민감정보 로그 감사 대기
|
||||
- [x] Q-024 APK package/version/signature/SHA-256와 설치된 앱 버전 증거
|
||||
- [x] Q-025 최종 증거 표에 명령·결과·artifact·스크린샷/영상 경로 기록
|
||||
- [ ] Q-026 Node 24 LTS/JDK 17/Gradle 환경을 로컬·CI에서 통일
|
||||
- [~] Q-027 debug x86_64+arm64, release arm64 APK/AAB ABI 정책과 에뮬레이터 호환성 검증 — universal E2E APK 정책 GREEN, 실제 production arm64 APK/AAB CI 검증 대기
|
||||
- [~] Q-028 공식 배포가 `assembleRelease` 비-debuggable artifact만 허용하도록 fail-closed — signed evidence·hash/version/package/cert/prod AdMob·create-only 경계와 negative self-test GREEN, 실제 production AAB/CI/secrets 대기
|
||||
|
||||
## 5. 구현 순서와 게이트
|
||||
|
||||
1. Gate 0 — G 기반, 실제 Supabase 구성, auth root, 테스트 harness
|
||||
2. Gate 1 — A 계정 전체 + O 온보딩/권한/테마
|
||||
3. Gate 2 — R 녹음·파일 전사 + H 히스토리 상세·공유
|
||||
4. Gate 3 — D 동기화/가져오기/오프라인 + F 전체 기능 화면
|
||||
5. Gate 4 — H 회의/문서 + T 팀/관리/알림
|
||||
6. Gate 5 — M 실제 Play Billing·광고·서버 검증
|
||||
7. Gate 6 — Q 전체 자동화와 Android 에뮬레이터 clean-room E2E
|
||||
|
||||
각 Gate는 다음 Gate로 가기 전 typecheck/unit/integration와 해당 에뮬레이터 journey를 통과한다. 뒤 단계에서 회귀가 발견되면 해당 작업을 다시 `[ ]`로 되돌리고 수정·재검증한다.
|
||||
|
||||
## 6. 외부 시스템 준비 항목
|
||||
|
||||
아래 항목은 구현을 멈추는 이유가 아니다. Google test ID, 로컬 Supabase, fixture account로 코드와 자동화를 먼저 완성한다. 다만 production 완료 표시는 실제 콘솔 증거가 있어야 한다.
|
||||
|
||||
- [~] EXT-001 Supabase production URL/anon key, redirect allow-list, OAuth provider secrets — production Auth와 Google provider entry는 GREEN, GitHub/Apple provider secret 및 실제 mobile consent callback은 남음
|
||||
- [!] EXT-002 Play app-signing SHA-256은 확인했지만 Android OAuth/Firebase client의 package·SHA-1/SHA-256 등록과 live App Links 배포가 남음
|
||||
- [!] EXT-003 Google Play Console 앱 `4976102237698469110` / package `com.d3ro.voice`는 생성. Pro/Pro+ base plan/offer/product IDs와 track 설정은 남음
|
||||
- [!] EXT-004 Play Billing license tester와 테스트 결제 수단
|
||||
- [!] EXT-005 Google service account, Android Publisher API, RTDN Pub/Sub
|
||||
- [~] EXT-006 AdMob app `ca-app-pub-1039714767792854~6427959892`, banner `/9840591290`, rewarded `/2255790918`, SSV 설정 — identity/URL SSOT는 확정. `검토 필요`·`광고 게재 제한`·결제 프로필·store 미연결·test-device 실제 지급 E2E 대기
|
||||
- [x] EXT-007 개인정보 처리방침·이용약관·계정 삭제 공개 URL — NAS host/runtime에 동일 해시로 좁게 배포, `/privacy/`, `/terms/`, `/delete-account/` 외부 HTTPS 200·정확한 title·삭제 요청 링크 확인. 모바일 Settings와 Paywall에서 canonical URL 진입
|
||||
- [!] EXT-008 local upload key·Ed25519 evidence keypair는 ACL/readback/roundtrip GREEN. production Firebase/AdMob/signing/evidence secret의 CI 보관·보호 환경 승인·복구 백업 및 `1.1.0`/`1010001` production AAB workflow는 대기
|
||||
- [!] EXT-011 Firebase Console `u/0`, `u/1` 모두 D3RO project 0개 — 사용자 action-time 승인 후 production project·Android app·Play fingerprint·FCM 생성/연결
|
||||
- [!] EXT-009 Fold6 physical-device install/share/OAuth/purchase 증거 — exact `.11` APK Taildrop 전송 완료, 사용자 설치·실행·공유/OAuth/구매 확인 필요
|
||||
- [!] EXT-010 Play closed test 12명·연속 14일 완료와 production access 승인 — 2026-08-29 현재 0명, access disabled
|
||||
|
||||
## 7. 검증 증거 표
|
||||
|
||||
| Gate | 명령/시나리오 | 결과 | 증거 경로 | 확인 시각 |
|
||||
|---|---|---|---|---|
|
||||
| Historical baseline | `git status --short --branch` | 2026-08-21 당시 `main`이 remote보다 2 commit 앞서고 dirty 변경이 다수였다. 현재 release SHA로 재사용하지 않는 역사 증거다 | 본 문서 1절 | 2026-08-21 |
|
||||
| Mobile lint | `npm --prefix apps/mobile-rn run lint` | GREEN, warning/error 0 (`--max-warnings=0`) | `apps/mobile-rn/.eslintrc.js`, `package.json` | 2026-08-21 |
|
||||
| Mobile typecheck | `npm --prefix apps/mobile-rn run typecheck` | GREEN | `apps/mobile-rn/tsconfig.json` | 2026-08-21 |
|
||||
| Mobile unit | `npm --prefix apps/mobile-rn test` | 전체 통합 tree 57 suites / 353 tests GREEN; 중앙 account-local privacy purge, cold/session-loss/A→B auth 경계, secure logout, identity safety, mobile admin, rewarded/banner retry·template/memo·meeting creation·recording language·generation idempotency 포함 | `apps/mobile-rn/__tests__` | 2026-08-24 |
|
||||
| Supabase local | migration-up + shadow replay 00001~00028 및 domain integration | 기존 domain integration에 더해 AdMob receipt 원장의 unknown/ineligible/cooldown replay, account-delete unlink, ACL, 교차 사용자·동시 요청을 검증했다. mobile-platform 85/85, reward race 2회 연속, SSV Deno 6/6, db lint error 0, shadow diff empty, fixture cleanup 0 | `server/supabase/migrations/20260821000028_ad_reward_receipt_replay_protection.sql`, `server/supabase/tests`, `server/supabase/functions/_shared/admob-ssv.test.ts` | 2026-08-24 |
|
||||
| Android build | `:app:assembleDebug -PreactNativeArchitectures=arm64-v8a,x86_64` | universal BUILD SUCCESSFUL; 두 ABI의 screens/gesture/Nitro/Whisper SO 완전성 확인 후 x86_64 clean cold launch GREEN, SHA-256 `E4647A4C041174DF2B2F094EF7BB61B4B01DD10A379DB32CCBE4105231E04A7C` | `apps/mobile-rn/android/app/build/outputs/apk/debug/app-debug.apk`, `scratch/demo/d3ro-mobile-dual-abi-installed.png` | 2026-08-21 |
|
||||
| Latest Android E2E test artifact | JDK 17 `:app:assembleE2e -PreactNativeArchitectures=arm64-v8a,x86_64` + artifact/build-config verifiers | BUILD/verifier GREEN; `com.d3ro.voice`, version `0.0.0-e2e.20260821.11`/`202608221`, non-debuggable, embedded bundle 3,808,068 B·Whisper model 77,691,713 B/SHA 검증, Google test AdMob ID, arm64+x86_64. APK 146,739,935 B, SHA-256 `74035B69DB0A564846DF5ED4B85CA5D0E2289D5BB7D73DEE02C35DFD57380C2B`. API 34 exact-APK fresh install와 cold launch GREEN. production artifact가 아니다 | `apps/mobile-rn/android/app/build/outputs/apk/e2e/app-e2e.apk`, `scripts/ci/verify-android-artifact.mjs`, `scripts/ci/verify-mobile-build-config.mjs` | 2026-08-21 |
|
||||
| Public Forgejo distribution | 최신 E2E TEST-DEMO 고유 asset upload + anonymous redownload hash 대조 | PENDING; `.11` SHA-256 `74035B69…380C2B` 공개 업로드·anonymous redownload 검증은 수행하지 않음 | Forgejo release asset | 2026-08-21 |
|
||||
| Mobile release boundary | `npm run release:mobile:boundary:test` + source-contract/security/syntax/YAML checks | GREEN; release mode·expected version/package/cert/prod AdMob·Ed25519 signed evidence와 artifact hash 일치, immutable snapshot, hardlink/reparse/path-swap/static APK 우회·overwrite·test AdMob/debug signer/nonrelease 거부를 검증. 실제 production APK/AAB에는 아직 실행하지 않음 | `scripts/ci/mobile-release-evidence-lib.mjs`, `scripts/ci/verify-mobile-release-boundary.mjs`, `.github/workflows/release.yml` | 2026-08-21 |
|
||||
| Production Android release gate | release/config/artifact/provenance self-tests + Play/Firebase/AdMob live 재확인 | release identity `1.1.0`/`1010001`, Play app ID `4976102237698469110`, package `com.d3ro.voice`, app-signing SHA-256, local upload/evidence key, AdMob identity를 확인했다. Play는 `임시`, 설정 0/11, AAB 0건, closed tester 0/12명, production access disabled다. Firebase project·CI secret 주입·AAB가 없으므로 현재 판정은 RED다 | `release/product-version.json`, `release/android-release-identity.json`, `scripts/ci/verify-mobile-release-config.mjs`, `scripts/ci/verify-android-artifact.mjs`, live consoles | 2026-08-29 |
|
||||
| App Links release identity | source/live exact certificate 대조 | `site/public`, `apps/web/public`, `apps/api-server/wwwroot`의 source 3개는 Play app-signing SHA-256으로 갱신. live는 아직 폐기된 과거 certificate를 반환하므로 deploy·public 재검증 전까지 RED | `*/.well-known/assetlinks.json`, `scripts/ci/verify-android-app-links.mjs`, live `d3ro.chanpaca.net` | 2026-08-29 |
|
||||
| Public legal and deletion resources | scoped NAS host/container deploy + anonymous HTTPS render | 개인정보처리방침, 이용약관, 앱 외부 계정 삭제 요청 경로를 기존 사이트 전체와 분리해 배포했다. host와 running container 4개 파일 SHA 일치; `/privacy/`, `/terms/`, `/delete-account/` 모두 외부 HTTPS 200·정확한 title, privacy→deletion 링크 visible. 실서버 본문 운영자는 `YUN CHAN`, Cloudflare email-protection 복호화 연락처는 `yunchan8804@gmail.com`, `TWENTYOZ` 잔존은 0건이다. Google Play 정책상 필요한 앱 식별·개발자 문의·수집/공유·보관/삭제·외부 삭제 요청 경로를 포함하고 모바일 Settings/Paywall에도 canonical link를 노출 | `site/public/legal.css`, `site/public/privacy/index.html`, `site/public/terms/index.html`, `site/public/delete-account/index.html`, `apps/mobile-rn/src/screens/SettingsScreen.tsx`, live `d3ro.chanpaca.net` | 2026-08-21 |
|
||||
| Android upload signing identity | create-only 3072-bit RSA PKCS12 + Windows credential vault + certificate readback | 2026-08-21에 생성한 local upload-key 후보 SHA-256은 `4F:AC:69:24:82:1C:50:DA:AB:ED:76:49:32:A5:3C:48:6F:8C:6C:5F:34:B9:F1:8D:B9:20:AA:40:99:15:2B:54`다. 다만 production CI secret·오프라인 복구 백업·실제 AAB signer 증거는 없다. Play Console upload certificate는 첫 AAB 업로드 후 표시되며, 현재 확인한 Play app-signing SHA-256과 분리해 대조해야 한다 | `release/android-release-identity.json`, `scripts/gen-keystore.js`, Windows Credential Manager | 2026-08-29 |
|
||||
| Release evidence signing identity | Ed25519 keypair creation + public-key fingerprint + sign/verify roundtrip | private key는 `C:\Users\encep\.d3ro\release\d3ro-mobile-evidence-ed25519-private.pem` user-only ACL, public key는 `release/mobile-release-evidence-public.pem`이다. keyId `2797d3e63affd2348941bc2871b57e520c958f7f5da1a4bbe8aa46904a890b7f`, pair/roundtrip GREEN. CI private-key secret·오프라인 복구 백업·실제 signed evidence는 대기 | `release/mobile-release-evidence-public.pem`, `release/android-release-identity.json`, `scripts/ci/verify-release-evidence-key-pair.mjs` | 2026-08-29 |
|
||||
| Firebase project readiness | Firebase Console `u/0`, `u/1` live 확인 | 두 계정 모두 D3RO project가 없다. production project·Android app·`google-services.json`·FCM은 사용자 승인 후 새로 준비해야 한다 | Firebase Console, `apps/mobile-rn/android/app` | 2026-08-29 |
|
||||
| AdMob production identity | live console + release identity exact match | app `ca-app-pub-1039714767792854~6427959892`, banner `ca-app-pub-1039714767792854/9840591290`, rewarded `ca-app-pub-1039714767792854/2255790918`는 일치한다. 그러나 `검토 필요`, `광고 게재 제한`, store 미연결, 결제 프로필 미완료라 production 게재 GREEN이 아니다 | `release/android-release-identity.json`, AdMob live console | 2026-08-29 |
|
||||
| Production Supabase deployment | linked migration/function deploy + live AdMob probes | remote migration `00001`~`00028`이 local과 일치하고 `admob-ssv` v13이 ACTIVE다. rewarded unit secret 존재, unsigned missing-query는 200, unknown key는 503, AdMob URL verification은 GREEN이다. 00028 전후 schema dump는 각각 SHA-256 `92CAA70E…A1DF`, `7545ADD2…EB4E`로 보존했다. Stripe/Play/Resend/STT/FCM provider gates와 실제 Google-signed reward 지급 E2E는 남음 | `scratch/production-backups/admob-ssv-before-20260821000028-20260824.sql`, `scratch/production-backups/admob-ssv-after-20260821000028-20260824.sql`, `server/supabase/migrations/20260821000028_ad_reward_receipt_replay_protection.sql`, `server/supabase/functions/admob-ssv` | 2026-08-24 |
|
||||
| Production DB credential | linked schema dump + Supabase CLI source audit | CLI `db dump --dry-run`이 credential을 stdout에 출력하지만, CLI 공식 소스 대조 결과 primary DB password가 아니라 API가 매 작업마다 생성하고 만료시키는 `cli_login_postgres` 임시 역할 password다. primary rotation은 불필요하며 저장소·Supabase trace 잔존 0. 해당 dry-run 명령은 credential 출력 때문에 재사용 금지 | Supabase CLI `internal/utils/flags/db_url.go`, `scratch/prod-public-schema-before-mobile-20260821.sql` | 2026-08-21 |
|
||||
| Physical Fold6 | exact `.11` APK Taildrop→`z-fold6` | TRANSFERRED over active direct Tailnet; `D3RO-Voice-0.0.0-e2e.20260821.11-TEST-DEMO-74035b69db0a.apk`, 146,739,935 B, SHA-256 `74035B69DB0A564846DF5ED4B85CA5D0E2289D5BB7D73DEE02C35DFD57380C2B`. 실제 기기 설치·실행·OAuth·구매는 사용자 확인 PENDING | Fold6 Taildrop/runtime | 2026-08-21 |
|
||||
| Mobile secure auth/account | secure storage·logout·recording cleanup·identity linking Jest | Keychain strict response 검증, legacy migration, local purge, remote sign-out 실패 fallback, recorder/queue cleanup, provider link/unlink 안전장치 GREEN. 최신 APK logout/delete/provider E2E는 PENDING | `apps/mobile-rn/src/lib/secure-auth-storage.ts`, `apps/mobile-rn/src/lib/logout.ts`, `apps/mobile-rn/__tests__` | 2026-08-21 |
|
||||
| Mobile admin E2E | ordinary/manager/admin/super_admin/stale-manager API 34 journeys | 55 assertions PASS. Free test 광고 표시·관리 request 0, Pro 광고 미요청, manager 구독 credits/memo 수정, admin bounded role+delete confirm/cancel, super manager→admin, stale JWT 즉시 403와 target/modal purge. auth/profile/operation/audit fixture 0 | `apps/mobile-rn/.maestro-output/admin-local/evidence-role-e2e-pass.json`, `admin-local-ordinary-free-ad-visible.png`, `admin-local-admin-bounded-role-options.png`, `admin-local-stale-manager-forbidden-purged.png` | 2026-08-21 |
|
||||
| Android incoming share | main/androidTest Kotlin compile + API 34 instrumentation + external provider E2E | 허용 MIME만 수신, UI thread 밖 provider I/O, size/name/type cap, provider 예외·unsupported/empty/oversize·비 AAC MP4 fail-closed 구현. slow provider 비차단, provider `SecurityException`, unsupported video one-shot failure 3/3 GREEN. 별도 UID ContentProvider APK의 WAV를 `ACTION_SEND`해 Record 라우팅, app-owned cache copy, retry queue, cancel cleanup까지 GREEN; canonical cache path와 RN cache alias 불일치도 실제 E2E에서 발견·교정 | `apps/mobile-rn/android/app/src/main`, `apps/mobile-rn/android/app/src/androidTest`, `apps/mobile-rn/.maestro/incoming-share-local-check.yaml`, `scripts/ci/run-mobile-incoming-share-local-e2e.ps1` | 2026-08-21 |
|
||||
| Emulator recorder | 권한→FGS 녹음→HOME→통화 focus loss→resume→SIGKILL→cold recovery→discard | GREEN, 실제 WAV 성장/header/journal/notification cleanup 확인 | API 34 `emulator-5554` logcat + 앱 DB/cache inspection | 2026-08-21 |
|
||||
| Emulator STT/Talk | 실제 Korean WAV/import→STT→history/share, PTT WAV→STT→Claude SSE→TTS/stop | GREEN; cloud provider 미구성은 503 fail-closed, local Whisper fallback은 인증 우회 없이 동작 | API 34 `emulator-5554`, public Supabase rows | 2026-08-21 |
|
||||
| Auth live | production `/auth/v1/settings`, Google authorize canonical redirect, disposable email login/logout/delete | Email·Google provider entry GREEN; settings는 Google=true/GitHub=false/Apple=false, Google authorize 302→`accounts.google.com`, Supabase canonical callback·state/client ID present. full Google consent→mobile callback와 GitHub/Apple은 PENDING | production Supabase + API 34 emulator | 2026-08-21 |
|
||||
| Billing test | restore/provider/entitlement unit+hook integration | 3 suites / 28 tests GREEN; sideload는 `storeInstallRequired` fail-closed. Play license test는 EXTERNAL | `apps/mobile-rn/__tests__/billing*.test*`, `entitlement.test.ts` | 2026-08-21 |
|
||||
| Ads test | Free Google test banner→rewarded 완주 + production 콘솔/SSV 연결 | 공식 `Test Ad` banner/`AdActivity`/`Reward granted` 실측과 미구성 시 fail-closed를 유지한다. production ID, `50 cloud_ai_tokens`, secret, verified SSV URL, Edge v13, receipt exactly-once는 운영 반영했다. production 광고를 클릭하지 않았으며 test-device 실제 Google-signed reward E2E는 남음 | `scratch/demo/d3ro-mobile-test-ad.png`, `d3ro-mobile-rewarded-test-ad.png`, `server/supabase/functions/admob-ssv`, AdMob live console | 2026-08-24 |
|
||||
| Admin live-local | login→JWT proxy→role/subscription/audit + stale-role | .NET 21/21, migration 25, Edge/JWT 47 assertions, TLS Chromium console/5xx 0 | `scratch/admin-browser-e2e.png` | 2026-08-21 |
|
||||
| STT fail-closed quota | Deno/.NET contracts + 00026 SQL/REST concurrency | provider 전체 실패 502/503, direct legacy user 410, internal exact service token only. 20 concurrent remaining-base 요청에서 exactly 1 reserve, replay single charge, 3 overage exact, provider failure refund, authenticated ledger/RPC denial, fixture 0 | `server/supabase/functions/stt-proxy`, `apps/api-server/Controllers/SttController.cs`, `server/supabase/migrations/20260821000026_stt_quota_reservations.sql`, `apps/mobile-rn/__tests__/stt-quota.local.integration.mjs` | 2026-08-21 |
|
||||
| Desktop payment·ads | authenticated Edge-only checkout/subscription readback + no-fill·위조 event·reward·settlement 경계 | 임의 provider/tier/URL, 무조건 성공 verify, legacy local license 우회를 제거하고 timeout·retry idempotency를 fail-closed로 검증. 광고 20/20, 전체 desktop 590 tests, typecheck/build/scoped lint GREEN; 실제 production provider 결제 E2E는 외부 게이트 | `apps/desktop/src/main/ipc/payment-handlers.ts`, `apps/desktop/tests/unit/payment-handlers.spec.ts`, `scratch/demo/d3ro-desktop-ads-fail-closed.png` | 2026-08-21 |
|
||||
| Credential gate | repository runtime secret scan + release-keystore audit + API fail-closed bootstrap | 평문 keystore password를 포함하던 untracked 생성 스크립트를 발견해 환경변수·PKCS12·create-only로 교체하고 scanner에 keytool literal negative case 추가. 노출된 키는 live direct APK cert `06:EE:…:E4:81`과 동일해 `d3ro-release-key.COMPROMISED-20260821.keystore`로 보존 격리했으며 active path에서 제거. scanner self-test/전체 scan, .NET 26/26, script syntax GREEN | `scripts/gen-keystore.js`, `scripts/ci/check-no-hardcoded-secrets.mjs`, `.github/workflows/ci.yml` | 2026-08-21 |
|
||||
| Exact-APK cold launch | fresh uninstall/install→`am start -W`→top activity/logcat | GREEN; exact `.11` SHA-256 `74035B69…380C2B` fresh install, `LaunchState:COLD`, total 0.872초, `MainActivity` top-resumed. fresh auth·signup·invite PASS, fatal/ANR/React fatal 0 | API 34 `emulator-5556`, installed package inspection + Maestro + logcat | 2026-08-21 |
|
||||
| Exact-APK Maestro E2E | exact `.11` TEST-DEMO fresh install + auth/signup/invite, 직전 `.9` small-screen journey | `.11` GREEN: fresh-install-auth 30s, fresh-install-signup 40s, invite warm listener+cold `getInitialURL` 14s. 동일 UI `.9`의 320x568dp signup 2m39s PASS. `.11` 전체 구간 fatal/ANR/React fatal 0 | `apps/mobile-rn/.maestro/*.v11.junit.xml`, `apps/mobile-rn/.maestro-output`, `apps/mobile-rn/.maestro-output/final-20260821-9` | 2026-08-21 |
|
||||
| Android instrumentation | API 34 `connectedDebugAndroidTest` | 4 tests, failures 0, errors 0: CSPRNG 1 + incoming share 3 GREEN | `apps/mobile-rn/android/app/build/outputs/androidTest-results/connected/debug/TEST-Pixel_7_API_34(AVD) - 14-_app-.xml`, `apps/mobile-rn/android/app/build/reports/androidTests/connected/debug` | 2026-08-21 |
|
||||
|
||||
## 8. 종료 감사
|
||||
|
||||
최종 종료 전 아래 검색과 대조를 수행한다.
|
||||
|
||||
- `apps/mobile-rn/src`에서 `TODO`, `placeholder`, mock 성공, 무반응 handler, 고정 사용자 데이터가 0건인지 확인한다.
|
||||
- 데스크톱 navigation·service 목록, Web route 목록, 본 문서 F/H/T 항목을 다시 대조한다.
|
||||
- 모든 `Pressable`, `PhysicalButton`, switch, 링크가 정상·disabled·오류 상태를 갖는지 검사한다.
|
||||
- 모든 Supabase query에 사용자/RLS 경계와 pagination/error 처리가 있는지 검사한다.
|
||||
- 결제·광고 보상은 서버 검증 전에 entitlement/토큰을 바꾸는 경로가 0건인지 검사한다.
|
||||
- 에뮬레이터를 초기화한 clean install과 기존 사용자 upgrade install을 모두 검증한다.
|
||||
- 실제 설치 APK의 version/signature/hash와 화면에 표시된 버전을 대조한다.
|
||||
Loading…
Add table
Add a link
Reference in a new issue