# Code Remote Manager 공개 출시 전 종합 감사

- 감사일: 2026-07-17
- 대상 브랜치: `main`
- 감사 시작 HEAD: `d75fc17`
- 감사 방식: 저장소·빌드 산출물·현재 listener/Tailscale 상태의 읽기 전용 검사, 공식 정책 재확인
- 수정 원칙: 감사 중 추적 파일·애플리케이션 설정을 수정하거나 커밋하지 않음
- 문서 성격: 명시된 감사 스냅샷의 증거이며 실시간 저장소 상태가 아님
- 현재 문서 체계: [문서 안내](../README.md), [활성 체크리스트](../PRE_RELEASE_CHECKLIST.md)

## 1. 최종 결론

현재 공개 출시는 모든 채널에서 **NO-GO**다. 자동 테스트와 PWA·Android·Windows
빌드 명령은 성공하지만, 다음 P0가 출시를 차단한다.

- Tailscale peer가 사용할 수 있는 앱 수준 인증이 없고 고위험 Codex 권한을 요청할 수 있다.
- 메시지 재전송 멱등성이 없어 동일 명령이 두 번 실행될 수 있다.
- Windows installer에 Backend production dependency가 포함되지 않아 설치 형태 기동이 실패한다.
- Windows 설정 앱이 기존 계정·workspace 설정을 백업·병합하지 않고 덮어쓸 수 있다.
- Windows installer와 Android AAB가 서명되지 않았다.
- Android AAB의 bundled web asset이 현재 PWA build와 일치하지 않는다.
- 공개 개인정보처리방침·약관·지원 URL과 앱 내부 링크가 준비되지 않았다.
- Google/Apple 심사용 개인정보 없는 demo mode 또는 안전한 review environment가 없다.
- iOS 프로젝트, Xcode build, 서명, Privacy Manifest가 없다.

감사 당시 작업 트리는 `64 modified`, `12 untracked`, `staged 0`이므로 특정 소스와
산출물을 하나의 재현 가능한 release candidate로 고정할 수도 없다. 이 감사에서
기존 사용자 변경을 reset, checkout, revert, clean 또는 덮어쓰기 하지 않았다.

## 2. 배포 채널별 GO/NO-GO

| 채널 | 판정 | 핵심 근거 |
| --- | --- | --- |
| Windows 호스트 | **NO-GO** | 설치 형태 Backend dependency 누락, 기존 설정 덮어쓰기 위험, unsigned installer |
| Tailscale 원격접속 | **공개 NO-GO** | 현재 topology는 안전하지만 앱 인증·Tailnet policy 검증·멱등성 없음 |
| Google Play | **NO-GO** | unsigned/stale AAB, 정책 URL·Data Safety·App Access·AI 신고 기능·demo 부재 |
| Apple App Store | **NO-GO** | iOS 프로젝트·서명·manifest·Purpose String·review environment 부재 |

현재 머신에서 확인한 topology는 다음과 같다.

- Backend `43110`: loopback listener만 존재
- PWA `43111`: loopback listener만 존재
- Tailscale Serve: PWA 포트 `43111`만 대상
- Backend 포트 `43110` Serve mapping: 없음
- Funnel: 비활성

이것은 현재 머신의 read-only snapshot일 뿐 설치기와 Backend가 경계를 fail-closed로
강제한다는 뜻은 아니다. Tailscale의 초기 tailnet policy는 모든 tailnet 기기 간
접근을 허용한다. 공유 Tailnet에서는 모바일 기기만 PWA service에 접근하도록
grant/ACL을 제한해야 한다.

- [Tailscale 기본 ACL 예시](https://tailscale.com/docs/reference/examples/acls)
- [Tailscale Serve](https://tailscale.com/docs/features/tailscale-serve)
- [Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel)

## 3. 발견사항

P0는 출시 차단, P1은 공개 안정판 전 해결, P2는 문서화 후 후속 처리다.

### P0 — 출시 차단

| 발견사항 | 근거 | 재현·확인 | 영향 | 권장 수정 |
| --- | --- | --- | --- | --- |
| Origin 검사를 인증처럼 사용하는 무인증 제어면 | `apps/backend/src/http.js:66-72`, `apps/backend/src/index.js:57-319` | Origin 없는 GET/POST 요청 | account·Chat·diff 열람, 명령·승인·중단 | 단말 pairing token, 폐기·회전, 전 API 인증/인가, CSRF·재전송 방어 |
| 무인증 peer가 `never` + `danger-full-access` 요청 가능 | `apps/backend/src/sessions.js:1291-1301`, `codex-runtime.js:883-897` | message API에 고위험 option 전달 | quota 소비와 무승인 로컬 명령 | Backend permission ceiling과 로컬 명시적 고위험 허용 설정 |
| Backend loopback이 설정 검증에서 강제되지 않음 | `apps/backend/src/config.js:36-58`, `index.js:322-324` | wildcard bindHost 설정 가능 | Backend 직접 노출 | IPv4/IPv6 loopback 외 값이면 기동 실패 |
| 설치기가 기존 Serve/Funnel/backend-port mapping을 검사하지 않음 | `windows-host/SetupApp/MainForm.cs:333-338` | 위험 mapping을 둔 상태에서 설치 | 이전 노출이 남아도 성공 표시 | 설치 전후 safe projection 검사와 위험 상태 중단 안내 |
| Windows installer가 Backend dependency를 누락 | `windows-installer/build-installer.ps1:60-72`, `apps/backend/src/workspace-artifacts.js:5` | source-only packaged backend import가 `parse5` module missing으로 종료 | 설치 후 Backend 기동 불가 | Backend bundle 또는 production dependencies 포함, 설치 directory `/health` smoke gate |
| 업그레이드 시 기존 설정을 덮어쓸 수 있음 | `windows-host/SetupApp/MainForm.cs:272-316`, `build-installer.ps1:83-84` | 기존 다계정 설정 후 setup 저장 | 계정·workspace 구성 손실 | schema version, 원자적 backup·merge·restore, upgrade test |
| message POST가 멱등하지 않음 | `apps/backend/src/index.js:225-230`, `sessions.test.js:2504-2540` | 동일 message를 응답 유실 후 재전송 | 동일 명령이 queue에서 두 번 실행 | 영속 `clientMessageId`와 disposition replay |
| 첨부가 extension/request MIME만 신뢰하고 quota·GC 없음 | `apps/backend/src/sessions.js:587-616`, `index.js:272-280` | HTML을 이미지 이름과 active MIME으로 업로드 | same-origin active content, disk DoS | magic-byte 검사, server MIME, `nosniff`, quota, DELETE, abandoned-upload GC |
| release candidate가 고정되지 않음 | `git status --short`, manifest/version 비교 | 64 modified, 12 untracked, staged 0 | source와 binary provenance 불명 | user changes review 후 단일 version/build/tag RC |
| Windows installer와 AAB가 unsigned, Android web asset stale | Authenticode, `jarsigner`, SHA-256 비교 | `NotSigned`, unsigned AAB, asset hash mismatch | Windows 신뢰 경고, Play 제출 불가, 오래된 UI | sync → build → signing → hash/manifest 검증을 단일 pipeline으로 고정 |
| 공개 정책·지원 URL 및 앱 내부 링크 부재 | `distribution/PRIVACY.txt`, `TERMS.txt`, DNS/HTTPS 확인 | 예정 privacy/support host가 resolve되지 않음 | Apple/Google 제출 차단 | 법적 정보 확정 후 public HTTPS HTML과 앱 내부 link |
| reviewer access/demo 부재 | 현재 native/PWA flow | reviewer가 개인 Windows PC와 Tailnet 없이 접근 불가 | 심사 불가·거절 | synthetic fake-engine demo, static sample QR/URL, 영문 review steps |
| Google AI content 신고 기능 부재 | Assistant response UI와 Google 정책 | response별 report/flag 경로 없음 | Play 정책 거절 | in-app report/flag, 처리·보존·응답 절차 |
| Apple chatbot 규칙 적용 근거와 대응 부재 | Apple Guideline 4.7 | filter/report/response/blocking 대응 없음 | iOS 심사 위험 | 적용 범위 확인, 필요한 moderation/reporting과 Review Notes |
| iOS project 전체 부재 | `ios/`, `@capacitor/ios`, plist/manifest/Xcode 검색 | IPA build 불가 | App Store 제출 불가 | Android와 분리해 macOS/Xcode 기반 프로젝트 추진 |
| local-data 설명이 실제 동작과 불일치하고 전체 reset 없음 | `apps/pwa/src/shared/i18n.js:93-96`, `connection-mode.js:17-20` | 연결 해제 뒤 localStorage·Cache Storage·OS notification 확인 | draft와 account/chat/project metadata 잔존 | mobile 전체 reset과 PC data deletion을 분리 구현·설명 |

### P1 — 공개 안정판 전 해결

| 발견사항 | 영향 | 권장 수정 |
| --- | --- | --- |
| npm `latest`, toolchain floor·`packageManager`·`engines`·`.nvmrc` 부재 | 재현 build와 공급망 검증 곤란 | Node/npm/Java/.NET/Gradle version pin, `npm ci`, CI |
| root LICENSE, CHANGELOG, SUPPORT, SECURITY, NOTICE, complete SBOM 부재 | 재배포 license와 지원 책임 불명 | npm/Android/.NET transitive SBOM·NOTICE와 법률 검토 |
| raw error logging과 불완전 host regex redaction | token 변형·공백/UNC path가 log에 남을 수 있음 | 모든 sink 직전 공용 structured sanitizer와 retention |
| session/preferences/attachment에 통합 TTL·삭제 정책 없음 | 삭제 기대와 실제 잔존 불일치 | data별 retention, GC, deletion receipt |
| Android permission/SDK 및 16KB page-size 미검증 | Play permission·SDK·device compatibility 위험 | final merged manifest, SDK Index, network capture, 16KB 검사 |
| `RECORD_AUDIO`, Camera, notification/boot/wakelock disclosure 미완료 | permission prompt와 실제 사용 불일치 가능 | feature 직전 prominent disclosure와 runtime permission |
| safe auto-update·rollback·backup/restore 없음 | update 실패 시 복구 곤란 | signed channel, atomic migration, previous-version rollback |
| PWA short name `Codex`, scaffold icon과 불완전 비제휴 표면 | OpenAI 공식 제품으로 오해 가능 | 독립 product name/icon/disclaimer 일관화 |
| age, countries, languages, monetization, export/encryption, accessibility 미확정 | store metadata 불완전 | operator decisions와 real-device validation |

### P2 — 후속 처리

| 발견사항 | 권장 수정 |
| --- | --- |
| PWA main JS 614 kB, Vite 500 kB warning | feature-based lazy loading/code splitting |
| Android release `minifyEnabled=false` | 안정성 확인 후 R8/ProGuard와 mapping 보존 |
| recent-app snapshot/notification preview 보호 부재 | 민감 화면·알림 내용 숨김 option |
| ROADMAP/checklist native 상태와 실제 artifact 상태 불일치 | RC 확정 후 document authority와 날짜 정리 |

## 4. 데이터 흐름 및 스토어 신고 매트릭스

“개발자 중앙 Relay가 없다”와 “스토어상 데이터 수집 없음”은 같은 뜻이 아니다.
Google은 사용자 기기 밖으로 전송되는 데이터를 기본적으로 collection으로 보고,
SDK와 앱이 제어하는 WebView 전송도 포함한다.

| 데이터 | 발생 → 처리 → 전송 | 저장·보존 | 삭제·제어 | 신고 후보 |
| --- | --- | --- | --- | --- |
| Prompt | phone → Tailscale HTTPS → PC Backend → Codex/OpenAI | phone draft, PC session cache, Codex history | 성공 전송 시 draft 삭제; 전체 reset/history 삭제 불완전 | Apple User Content/Text, Google Messages/Other User Content |
| Assistant response | OpenAI/Codex → PC → SSE → phone | PC cache와 Codex history | CRM cache와 durable history 삭제를 구분 | User Content |
| Reasoning summary | Codex-provided summary만 PC→phone | session/history에 포함 가능 | Chat data와 동일 | Other User Content |
| Account 표시·usage | PC Codex account/status → phone | account ID와 usage snapshot | CRM profile 제거는 OpenAI account deletion 아님 | Email, User ID, Product Interaction |
| Authentication | device code/URL 표시, OpenAI에서 승인 | auth/token은 PC account-scoped Codex home | CRM profile removal과 OpenAI deletion 분리 | Authentication Information |
| Chat/thread metadata | Codex history + CRM projection → phone | title, project label, ref, pin 등 | local-delete와 durable Codex deletion 다름 | User Content, Identifiers |
| Workspace/project label | PC filesystem → basename/opaque ID | PC allowlist, phone context/notification | workspace removal과 reset 필요 | Files/Other User Content |
| Changed files/diff | allowlisted workspace에서 요청 시 PC→phone | event/cache에 포함 가능 | cache와 original project deletion 분리 | Files and Documents/User Content |
| Images | picker → PC attachment → Codex/OpenAI | PC attachment에 무기한 잔존 가능 | per-file server delete와 abandoned GC 없음 | Photos/User Content, optional |
| Voice | microphone → Web Speech provider 가능 → transcript | raw audio 저장 미발견, transcript는 draft/history | OS permission·stop·draft deletion | Audio Data 조건부, provider/network 확인 필요 |
| Notifications | session/usage → phone localStorage·OS notification | type별 local retention | on/off 있으나 full delivered cleanup 없음 | App Activity/Identifiers 가능 |
| Device/network metadata | saved origin, connection state, PC hostname, Tailscale metadata | local state와 Tailscale 측 metadata 가능 | disconnect는 origin만 제거 | Device/Network/Other Data 검토 |
| Local cache/draft | phone localStorage와 PC session store | 통합 TTL 없음 | full reset 없음 | local-only는 제외 가능, off-device 부분은 신고 |
| Error/log/crash | Backend/host error → PC local log | size bound만 있고 time retention 미정 | full deletion UI 없음 | Diagnostics; developer telemetry SDK는 미발견 |
| Capacitor/SDK | Camera QR, ML Kit/CameraX, Local Notifications, Network/App/StatusBar | SDK별 상이 | OS permission/app data deletion | final AAB SDK Index·network capture 기준 |

확인된 방어:

- JSON/SSE 최종 safe projection과 sanitizer
- hidden reasoning 대신 Codex reasoning summary만 projection
- workspace opaque ID와 allowlist 내부 relative change path
- restored queue의 자동 실행 금지와 FIFO dequeue
- runtime client와 local attachment path 비직렬화
- service worker의 `/api/` cache 제외
- Android `allowBackup=false`, `usesCleartextTraffic=false`

## 5. 법률·정책 문서 초안

아래 문구는 implementation과 operator 정보 확정 전 게시하면 안 된다. 최종 공개 전
변호사 또는 적격 법률 전문가의 검토가 필요하다.

### 5.1 통합 개인정보처리방침 초안

> 시행일: `[운영자 입력 필요]`
> 운영자: `[법적 이름·사업자 형태·주소 입력 필요]`
> 개인정보 문의: `[이메일 입력 필요]`
>
> Code Remote Manager는 사용자가 자신의 Windows PC에서 실행하는 Codex 환경을
> 자신의 모바일 기기에서 제어하도록 돕는 독립 애플리케이션입니다. 현재 버전은
> 운영자가 제공하는 중앙 Relay나 클라우드 코드 실행 서버를 사용하지 않습니다.
>
> 사용자가 입력한 prompt, 이미지, 음성 transcript, project 정보와 변경 내용은
> Tailscale을 통해 사용자의 PC로 전송되고 사용자가 연결한 Codex/OpenAI account로
> 처리될 수 있습니다. Account 표시 정보, usage, Chat/thread metadata와 Assistant
> response는 사용자의 PC에서 모바일 기기로 전달될 수 있습니다.
>
> 앱은 모바일 기기에 연결 주소, UI 설정, 미전송 draft, notification·Chat·project
> metadata를 저장할 수 있습니다. PC에는 session cache, attachment, 설정, log와
> Codex history가 남을 수 있습니다. Data별 보존 기간은 `[운영자 입력 필요]`입니다.
>
> Tailscale은 device connection 관련 network metadata를 처리할 수 있고 OpenAI는
> 사용자가 연결한 account에 따라 prompt와 response를 처리합니다. Voice 기능은
> 실행 환경의 Web Speech provider가 audio를 처리할 수 있으므로 최종 platform별
> provider와 transmission 여부를 확인해 고지합니다.
>
> 현재 감사에서 광고, 광고 ID, developer analytics 또는 crash-reporting SDK는
> 발견되지 않았습니다. Final artifact에서 다시 확인하며 향후 추가할 경우 사전
> 고지와 필요한 동의를 받습니다.
>
> 앱에서 Codex profile을 제거해도 OpenAI account나 OpenAI가 보유한 data가 삭제되지
> 않습니다. OpenAI account deletion은 OpenAI에서, Tailscale account deletion은
> Tailscale에서 수행해야 합니다. Mobile app removal은 PC의 Codex history, project,
> CRM cache·log를 삭제하지 않습니다.
>
> 사용자는 `[mobile local-data reset 경로—구현 필요]`와 `[PC CRM data deletion
> 경로—구현 필요]`를 통해 각 위치의 데이터를 별도로 삭제할 수 있어야 합니다.
>
> 이 정책은 store policy 설명이며 각 출시 국가의 privacy law 준수를 대신하지
> 않습니다. 출시 국가, 국외 이전, 미성년자, 보존 근거와 정보주체 권리는
> `[법률 검토 필요]`입니다.

### 5.2 이용약관/EULA 초안

> Code Remote Manager는 사용자 자신의 Windows PC와 Codex/OpenAI account를 remote
> control하기 위한 사용자별 단일 운영자 도구입니다. 사용자는 자신이 소유하거나
> 적법하게 사용할 권한이 있는 PC·account·project에서만 앱을 사용해야 합니다.
>
> 사용자는 OpenAI/Codex 및 Tailscale의 별도 약관, 요금과 사용 제한을 준수해야
> 합니다. 본 앱은 OpenAI 또는 Tailscale의 공식 제품이 아니며 제휴·승인 관계가
> 없습니다.
>
> 사용자는 Tailnet access policy, device security, workspace allowlist, approval
> policy와 backup을 안전하게 구성할 책임이 있습니다. Public multi-user Backend,
> central Relay, third-party PC control은 현재 허용된 product boundary가 아닙니다.
>
> Availability는 user PC, network, Tailscale과 OpenAI/Codex 상태에 의존합니다.
> Warranty disclaimer, liability limit, indemnification, termination, governing law와
> jurisdiction은 `[운영자 입력 및 법률 검토 필요]`입니다.
>
> Apple 배포 시 Apple Standard EULA를 store license 기반으로 사용하고 이 공통
> 약관을 app usage terms로 제공하는 방식을 우선 검토합니다. Custom EULA 필요성은
> monetization과 출시 국가 확정 후 법률 검토합니다.

### 5.3 데이터 및 계정 삭제 안내 초안

1. Mobile local data: `[Settings > Privacy > Clear all data on this device — 구현 필요]`
2. PC CRM data: session cache, attachment, app preferences와 log 삭제 경로 `[구현 필요]`
3. Codex profile removal: CRM 연결만 제거하며 OpenAI account를 삭제하지 않음
4. OpenAI account/Codex history: OpenAI가 제공하는 account·data management 사용
5. Tailscale account/device: Tailscale admin surface에서 제거
6. App removal: mobile data 일부만 제거하며 PC project/history/cache/log가 남을 수 있음
7. 삭제 완료 기간과 법적 보존 예외: `[운영자 입력 및 법률 검토 필요]`

현재 앱은 developer-provided app account를 만들지 않고 사용자의 OpenAI/Codex
account를 연결한다. Google의 app-account 정의상 account deletion requirement는
적용되지 않을 가능성이 높지만 모든 developer는 Data Safety deletion questions에
답해야 한다.

### 5.4 지원 및 문의 정책 초안

> Support email: `[운영자 입력 필요]`
> Privacy contact: `[운영자 입력 필요]`
> Security report: `[운영자 입력 필요]`
> Support language/time zone/response target: `[운영자 입력 필요]`
>
> Support 범위는 설치, 연결, local-data reset, 오류 진단과 release update입니다.
> OpenAI account·billing·Codex outage와 Tailscale account·network issue는 각 provider의
> 지원 범위입니다. Security report에는 credential, token, full log 또는 실제 project
> 파일을 첨부하지 않도록 안내합니다.

### 5.5 오픈소스·제3자 서비스 고지 초안

> Code Remote Manager는 Node.js, React, Capacitor, Android/ML Kit/CameraX 및 기타
> open-source component를 포함합니다. 전체 version, copyright와 license는
> `[complete transitive NOTICE/SBOM 생성 필요]`에서 제공합니다.
>
> 앱은 사용자 선택에 따라 OpenAI/Codex 및 Tailscale을 사용합니다. 각 provider의
> terms와 privacy policy가 별도로 적용됩니다. Code Remote Manager와 운영자는
> OpenAI 또는 Tailscale의 공식 partner·승인 제품이 아닙니다.

### 5.6 보안 및 로컬 실행 구조 설명 초안

> Backend는 사용자 Windows PC의 loopback에서만 수신하며 외부에 직접 공개하지
> 않습니다. PWA만 Tailscale Serve로 Tailnet 내부에 제공하고 Funnel은 사용하지
> 않습니다. 사용자가 선택한 workspace allowlist 밖의 path는 app feature로
> 노출하지 않습니다.
>
> 이 구조는 현재 implementation target입니다. 출시 전 Backend loopback enforcement,
> device authentication, Tailnet policy verification, high-risk permission ceiling,
> idempotency와 log redaction을 추가해야 합니다.
>
> Shared Tailnet의 default allow-all policy를 그대로 사용하지 말고 mobile device만
> 해당 PWA service에 접근하도록 grant/ACL을 제한해야 합니다.

### 5.7 Apple App Privacy 답변 초안

Final IPA와 network capture 전의 보수적 답변이다.

| 질문 | 임시 답변 |
| --- | --- |
| Tracking | No — ads/cross-app tracking SDK 미발견, final IPA 재검증 필요 |
| Contact Info | Email: Collected/Linked/App Functionality 후보 |
| Identifiers | OpenAI/Codex user/account ID: Collected/Linked/App Functionality 후보 |
| User Content | prompt, response, diff/file, image: Collected/Linked/App Functionality |
| Audio Data | Web Speech off-device processing이면 Yes/Optional, iOS 구현 확인 필요 |
| Usage Data | Product Interaction 및 quota/status: Yes 후보/App Functionality |
| Diagnostics | developer server transmission 미발견; local-only이면 collection 제외 가능 |
| Advertising | 사용 안 함 |
| Account deletion | CRM app account 없음; OpenAI profile unlink와 OpenAI deletion 구분 |
| Sign in with Apple | specific third-party service client exemption 가능성, Apple 확인 필요 |

Capacitor는 Apple privacy manifest 및 binary dependency signature 대상 SDK 목록에
포함된다.

- [Apple App Privacy](https://developer.apple.com/help/app-store-connect/manage-app-information/manage-app-privacy/)
- [Apple App Review Guidelines](https://developer.apple.com/app-store/review/guidelines/)
- [Apple third-party SDK requirements](https://developer.apple.com/support/third-party-SDK-requirements/)
- [Apple account deletion](https://developer.apple.com/support/offering-account-deletion-in-your-app/)

### 5.8 Google Play Data Safety 답변 초안

| 질문 | 임시 답변 |
| --- | --- |
| User data collected | Yes — phone 밖의 user PC/OpenAI로 전송 |
| User data shared | user-initiated transfer exception 적용 여부 최종 검토; 불확실하면 Yes |
| Encryption in transit | Yes 후보 — final release capture로 Tailscale HTTPS/OpenAI TLS 확인 |
| App account creation | No — developer-provided identity 없음 |
| Data deletion | 모든 developer 답변 필요; 현재 full reset 부재로 출시 전 기능 필요 |
| Purpose | App Functionality |
| Required/optional | text prompt required, image/audio optional |
| Data types | email/user ID, messages/content, photos, files/docs, app activity; audio/diagnostics 조건부 |
| Ads/tracking/sale | 현재 미사용·미발견, final AAB 재검증 |
| SDK | Capacitor, barcode/ML Kit/CameraX, local notification 등 final SDK Index 확인 |

Android target SDK는 36이다. 2026-07-17 현재 API 35 floor를 충족하며 2026-08-31부터
요구되는 API 36도 충족한다.

- [Google User Data](https://support.google.com/googleplay/android-developer/answer/10144311?hl=en)
- [Google Data Safety](https://support.google.com/googleplay/android-developer/answer/10787469?hl=en)
- [Google app account deletion](https://support.google.com/googleplay/android-developer/answer/13327111?hl=en)
- [Google target API](https://support.google.com/googleplay/android-developer/answer/11926878?hl=en)
- [Google AI-generated content](https://support.google.com/googleplay/android-developer/answer/13985936?hl=en)

### 5.9 Apple Review Notes 초안

영문 제출본이 필요하다. 아래 문구는 demo 구현 전 제출하면 안 된다.

> Code Remote Manager is an independent client for controlling a user-owned
> Windows PC running the user's own Codex/OpenAI account. It is not affiliated
> with or endorsed by OpenAI or Tailscale.
>
> Production operation requires a Windows host and a private Tailscale
> connection. The Backend listens only on loopback; only the PWA is made
> available inside the Tailnet through Tailscale Serve. Funnel is not used.
>
> For review, select “Review Demo” and scan the supplied static sample QR code.
> The demo uses synthetic accounts, projects, conversations and diffs through
> a fake engine. It does not connect to a reviewer’s OpenAI account, Tailnet or
> private PC and cannot execute real commands.
>
> Demo steps: `[install → sample QR → synthetic Chat → streaming → approval/
> deny → Changes → notification → reset 절차 입력 필요]`.

### 5.10 Google Play App Access 초안

영문 제출본이 필요하다. 아래 문구는 demo 구현 전 제출하면 안 된다.

> 1. Launch the app and select “Review Demo.”
> 2. Open the static QR image at `[public HTTPS URL required]` and scan it.
> 3. No personal OpenAI, Tailscale or Windows credentials are required.
> 4. The demo provides synthetic Chat history, streaming, approvals, queue,
>    diff, image partial failure, usage and notifications.
> 5. The demo cannot execute commands or access external files.
> 6. Reset with `[Review Demo > Reset demo data]`.

- [Google App Access](https://support.google.com/googleplay/android-developer/answer/15748846?hl=en)

## 6. 수동 실기기 검증 체크리스트

자동 test 통과와 별개로 아래는 모두 **수동 검증 필요**다.

- [ ] clean Windows PC 최초 설치·자동 시작·Codex 최초 login
- [ ] 여러 Codex account 추가·전환·삭제와 실제 quota 소비 account
- [ ] unified Chat/Desktop history, old Chat open/resume
- [ ] 다른 execution account handoff 시 visible conversation만 전달
- [ ] SSE streaming과 externally running thread watch
- [ ] approval/deny/interrupt와 duplicate runtime approval
- [ ] FIFO queue, failed head retention, restart 후 explicit resume
- [ ] 동일 client message 재전송 시 exactly-once execution
- [ ] Changes/diff allowlist·symlink·large-file boundary
- [ ] multi-image partial failure, MIME mismatch, delete와 abandoned GC
- [ ] WebView speech support와 actual audio network destination
- [ ] notification/usage/lock-screen content
- [ ] Backend/PC restart가 work를 자동 실행하지 않는지
- [ ] update failure, migration, rollback, uninstall, backup/restore
- [ ] Wi-Fi↔LTE와 Tailscale reconnect 중 duplicate transmission
- [ ] 390×844, 100dvh, composer placement, 16 px input, 48 px control
- [ ] screen lock/background/process kill recovery
- [ ] recent-app snapshot과 OS notification privacy
- [ ] shared Tailnet peer unauthorized API rejection
- [ ] Serve PWA-only, no Backend mapping/Funnel
- [ ] token/path/error redaction across HTTP/SSE/session/log/screen/notification
- [ ] Android 64-bit/16KB page size, merged permissions, Play SDK Index
- [ ] fake-engine demo를 clean reviewer device에서 완주
- [ ] store screenshots에서 actual account/path/code/token 제거
- [ ] VoiceOver/TalkBack, keyboard, contrast, dark mode, rotation

## 7. 운영자 입력 필요

- individual developer 또는 legal entity
- public legal name과 address
- support, privacy, security-report email
- privacy/terms/support를 게시할 public HTTPS domain
- release countries와 supported languages
- minimum age와 child-directed 여부
- free/paid/subscription/in-app purchase 여부
- final release의 analytics/crash reporting 사용 여부
- data category별 retention과 deletion completion target
- governing law와 jurisdiction
- external legal review와 penetration-test budget

현재 code에는 developer-provided app account가 없고 repository handoff에는 Android-first,
iOS-later로 기록되어 있으므로 두 항목은 미확정 질문으로 반복하지 않는다. 계획이
바뀌면 갱신해야 한다.

## 8. 위험이 낮은 순서의 수정 계획

1. Operator/legal metadata와 Android-first boundary를 확정한다.
2. Current user changes를 review해 single RC/version/build/tag로 고정한다.
3. Product name/icon/version과 public policy/support URL·in-app links를 통일한다.
4. Toolchain/dependency pinning, SBOM/NOTICE와 reproducible CI를 만든다.
5. Windows installer dependency와 installed-layout smoke를 수정한다.
6. Windows config migration/backup/rollback과 Authenticode signing을 추가한다.
7. Android sync→bundle→sign→manifest/SDK/hash pipeline을 고정한다.
8. Loopback/Serve/Funnel boundary를 fail-closed로 만든다.
9. Attachment MIME/quota/GC, log sanitizer, retention/full reset을 구현한다.
10. Device pairing authentication, permission ceiling, message/approval idempotency를 구현한다.
11. Synthetic review demo와 AI response reporting을 구현한다.
12. Full real-device E2E 후 Android release를 판단한다.
13. Android가 안정된 뒤 iOS project를 별도 macOS/Xcode 작업으로 추진한다.

## 9. 실행 검증과 결과

| 명령·검사 | 결과 |
| --- | --- |
| `npm.cmd test` | tools/Windows 23/23, Backend 313/313, PWA 602 pass + 2 skip/604, fail 0 |
| `npm.cmd --workspace apps/pwa exec vite build` | 성공, 417 modules, main JS 614 kB warning |
| `npm.cmd audit` | npm vulnerabilities 0; Android/.NET audit를 대신하지 않음 |
| `git diff --check` | exit 0; LF→CRLF warning만 있음 |
| `gradlew bundleRelease` | 성공, 302 tasks, 1 executed/301 up-to-date |
| Android artifact | AAB 존재, unsigned, current PWA asset과 hash mismatch |
| `npm.cmd run build:windows-installer` | installer EXE 생성 성공 |
| Windows Authenticode | `NotSigned`, 약 55 MB |
| Installed-layout Backend smoke | exit 1, `parse5` dependency missing 재현 |
| Tailscale read-only status | Serve 43111 있음, 43110 없음, Funnel false |
| Listener | 43110·43111 모두 현재 loopback only |
| Toolchain | Node 22.17.0, npm 11.12.1, Java 21.0.11, Gradle 8.14.3, .NET SDK 9.0.304 |
| Git (감사 당시) | main, `d75fc17`, modified 64, untracked 12, staged 0 |
| iOS | project/Xcode 조건 부재; 검증하지 않음 |
| Real Codex/device E2E | 실행하지 않음; 수동 checklist로 유지 |

## 10. 다음 단계 승인 경계

이 보고서는 상태 감사와 초안이다. 다음 단계에서는 P0를 한꺼번에 수정하지 말고
각 항목을 독립적인 설계·TDD patch로 나눈다. 특히 authentication, permission ceiling,
idempotency, installer migration은 product/security boundary를 바꾸므로 구현 전 design
승인이 필요하다. 실제 다음 작업 순서는
[활성 체크리스트](../PRE_RELEASE_CHECKLIST.md)를 따른다.

## 11. 문서 정리 후속 메모

2026-07-17 문서 정리에서 문서 권위와 현재 실행 지시를 다음과 같이 분리했다.

- 누적 세션 기록이던 기존 체크리스트와 2026-07-10 handoff를 당시 archive로 이동했다.
  활성 문서 정리 후 해당 사본은 삭제됐으며 내용은 Git 기록에 남아 있다.
- [문서 안내](../README.md)와 짧은 [활성 체크리스트](../PRE_RELEASE_CHECKLIST.md)를
  만들고 ROADMAP, 스토어 초안과 installer 배포 문서의 상태 표시를 맞췄다.
- 감사 당시 변경은 이후 `f4fde50`에 기록됐고 문서 정리 시작 직전 clean worktree를
  확인했다. 이는 감사 당시 dirty-source 상태만 후속 설명하는 것이며 단일 버전,
  서명, artifact manifest 또는 재현 가능한 release candidate를 증명하지 않는다.

이 정리로 P2의 문서 권위 혼선은 해소됐지만 기술 P0와 모든 채널의 **NO-GO**
판정은 변경되지 않았다. 감사의 빌드·도구·네트워크 결과도 새로 실행한 결과로
간주하면 안 된다.

## 12. Windows installed-layout 후속 조치

2026-07-17 후속 P0-B 패치에서 감사 당시 재현된 Backend dependency 누락을 수정했다.

- Backend source와 실제 production dependency graph(`parse5`, `entities`)를 하나의
  installer staging layout에 복사한다.
- 저장소 `node_modules`가 상위 경로에 없는 임시 layout에서 import와 host 경유
  `/api/health`를 검증한다.
- 동일한 layout smoke가 실패하면 Inno Setup compilation을 시작하지 않는다.
- installer는 검증한 layout 하나만 입력으로 사용하며 npm 고지의 손상된 package
  heading 생성도 회귀 테스트와 함께 수정했다.
- 집중 테스트, 전체 자동 suite, PWA build, npm audit, diff check와 installer build가
  통과했다.

실행 중인 개발 서버가 native build dependency를 잠그고 있어 전체 `npm ci` rehearsal은
중단됐으며 서버를 임의로 종료하지 않았다. 의존성 tree는 잠금 파일 기준 `npm install`로
복구한 뒤 다시 검증했다. 따라서 이 결과는 clean-machine 설치·upgrade·uninstall 증거가
아니다. 생성 산출물도 unsigned ignored verification artifact이며 release candidate가 아니다.
Windows의 config migration, signing 및 나머지 P0가 남아 있으므로 모든 채널의 **NO-GO**
판정은 유지한다.

## 13. Windows configuration migration 후속 조치

2026-07-17 후속 P0-C 패치에서 감사 당시 확인한 기존 설정 덮어쓰기 위험을 수정했다.
감사 본문의 당시 결과는 변경하지 않으며 아래 내용만 후속 증거로 추가한다.

- 현재 schema는 정수 `1`이다. `schemaVersion`이 없는 기존 형식은 legacy 입력으로
  업그레이드하고, 명시된 미지원 version과 손상된 JSON은 fail-closed 처리한다.
- Setup은 schema marker와 감지한 Codex CLI, 필수 local/current origin, 사용자가 명시적으로
  선택한 workspace 추가만 소유한다. 기존 관리형 계정, `historyOnly` source, label과 ID,
  workspace allowlist, `projectsRoot`, 제거되거나 이동한 프로젝트 참조, 사용자 데이터 참조,
  알 수 없는 필드는 보존한다.
- candidate를 durable pending 파일에 쓰고 flush한 뒤 실제 Backend loader로 검증한다. 기존
  config가 있으면 인접한 `config-backups` 아래 backup을 만든 후 atomic replace하며, 최종 검증
  또는 replace 실패 시 원본을 복원한다. 성공 backup은 최신 3개를 유지한다.
- 최초 생성 실패, pending만 남은 중단, journal 이후 중단, replace 도중 중단을 회귀 테스트로
  검증했다. 유효하지 않은 최초 생성 후보는 활성 위치에서 격리한다.
- installed-layout smoke는 staging된 Setup 실행 파일로 synthetic legacy config를 upgrade하고,
  staging된 Backend loader로 결과를 다시 읽어 계정·history source·workspace·unknown field와
  backup 보존을 확인한다. 이 smoke가 실패하면 Inno Setup compilation을 시작하지 않는다.
- uninstall source는 프로그램과 startup entry만 제거하고 설정, backup, 인증 파일과 사용자
  데이터를 유지한다.

집중 테스트와 전체 `npm.cmd test`, PWA production build, `npm.cmd audit`, `git diff --check`,
실제 installer build가 통과했다. 다만 설치 검증은 격리한 synthetic layout을 사용했으며 새
Windows 사용자 환경에서의 실제 install·upgrade·startup·restart·uninstall rehearsal은 아니다.
생성된 installer도 unsigned ignored verification artifact이고 release candidate가 아니다.
P0-A 인증·권한 설계, signing 및 다른 P0가 남아 있으므로 모든 채널의 **NO-GO** 판정은
유지한다.

## 14. App authentication과 permission ceiling 후속 조치

2026-07-17 후속 P0-A 패치에서 감사 당시 확인한 무인증 제어면과 브라우저 요청 권한의
무제한 전달을 수정했다. 감사 본문의 당시 결과는 변경하지 않으며 아래 내용만 후속 증거로
추가한다.

- Windows Setup은 PC에서만 5분 유효한 single-use QR과 12자 fallback code를 생성한다. 코드
  실패는 5회로 제한하고, 성공·만료·잠금 후 같은 pairing attempt를 재사용할 수 없다.
- 기기마다 별도의 180일 credential을 발급한다. 브라우저에는 `HttpOnly` cookie로 저장하고,
  PC에는 credential hash만 저장한다. 기기별 조회·회전·폐기와 Backend 재시작 복구가 테스트된다.
- `GET /health`, `POST /auth/pair`, CORS preflight를 제외한 account·Chat·session·approval·Changes·
  usage 및 두 SSE 경로는 credential을 요구한다. 브라우저 mutation은 exact Origin과 CSRF marker,
  SSE는 exact Origin도 요구한다. Origin만 맞고 credential이 없는 요청은 거부한다.
- PWA는 브라우저 재시작과 설치 후 cookie를 재사용하고, 폐기·만료 시 pairing 화면으로 돌아간다.
  native 첫 QR scan은 PC origin 저장과 pairing을 한 번에 처리하며 SSE reconnect도 credential을
  포함한다.
- schema-v1 config에 로컬 `permissionCeiling`을 추가한다. 신규·legacy upgrade의 설치 기본값은
  전체 built-in profile과 `never`를 허용하지만, 채팅 기본값은 기존 `:workspace`/`on-request`를
  유지한다. 이후 사용자가 PC에서 낮춘 ceiling은 upgrade가 보존하며 브라우저 API로 높일 수 없다.
  알 수 없거나 ceiling을 넘는 값은 Codex turn 시작 전에 거부한다.
- pairing state는 retained `dataDir` 아래에 두고 uninstall 시 설정·backup·인증·사용자 데이터를
  보존한다. 인증 실패는 이 데이터를 삭제하지 않으며 credential이나 실제 로컬 경로를 log 또는
  사용자 오류에 포함하지 않는다.
- Tailnet grant/ACL은 어떤 peer가 PWA Serve에 도달할지 제한하는 1차 경계이고 앱 pairing은 도달한
  요청을 인증하는 별도 경계다. Funnel과 Backend 직접 Serve는 계속 금지한다. accepted message와
  approval의 중복 전달 방지는 P0-D로 분리했다.

집중 인증·권한 테스트, 전체 `npm.cmd test`, PWA production build, `npm.cmd audit`, installed-layout
pairing/authentication smoke, `git diff --check`, 실제 installer build가 통과했다. 다만 실제 분실 기기
운영 rehearsal, 승인된 least-privilege Tailnet policy, clean Windows-user install·upgrade·startup·
restart·uninstall, signing은 아직 검증하지 않았다. 생성 installer는 unsigned ignored verification
artifact이며 release candidate가 아니다. P0-D/P0-E/P0-F와 나머지 채널 gate가 남아 있으므로 모든
채널의 **NO-GO** 판정은 유지한다.

## 15. 메시지·승인 idempotency 후속 조치

2026-07-17 후속 P0-D 패치에서 감사 당시 확인한 메시지·승인 중복 실행 위험을 수정했다. 감사 본문의
당시 결과는 변경하지 않으며 아래 내용만 후속 증거로 추가한다.

- PWA는 메시지마다 UUID v4를 만들고, 동일 payload의 불확실한 네트워크 재시도에만 같은 키를
  유지한다. 성공, 확정적 오류, payload 변경 및 새 사용자 동작에는 새 키를 사용한다.
- Backend는 메시지, queue steer/delete, approval decision, runtime approval notification 범위를
  hash한 receipt만 `dataDir`에 저장한다. 본문, credential, cookie, 실제 로컬 경로는 저장하지 않는다.
- receipt는 7일, 최대 4,096개로 제한한다. 만료되지 않은 기록을 임의 제거하지 않으며 손상, 용량
  초과 또는 쓰기 실패 시 runtime을 호출하지 않고 fail-closed 처리한다.
- 동일 키·payload와 동일 승인은 기존 disposition을 반환한다. 다른 payload, 상충하는 승인,
  steer/delete 충돌은 `409`로 거부한다.
- running FIFO queue, account handoff, 새 Chat 생성 재시도, Backend restart, restored queue의 명시적
  진행 규칙을 회귀 테스트로 보존했다. SSE 재연결 자체는 mutation을 만들지 않는다.
- 인증·Origin·CSRF를 적용한 임시 HTTP Backend에서 response loss, SSE 재연결 및 Backend restart 후
  accepted message가 한 번뿐임을 확인했다. fake engine과 실제 Codex runtime smoke에서도 accepted와
  completed turn이 각각 한 번이었다.

검증 한계: 실제 Codex smoke는 임시 Chat과 안전한 read-only prompt를 사용해 소량의 quota와 로컬
thread history를 소비했다. 물리 모바일 기기에서 실제 패킷 차단을 재현하지 않았고, P0-E attachment
lifecycle·전체 log sanitizer·삭제 기능, signing, clean-machine 및 Tailnet policy gate는 여전히 남아
있다. 따라서 모든 공개 채널의 **NO-GO** 판정은 유지한다.

## 16. 첨부파일·로그·삭제 후속 조치

2026-07-17 후속 P0-E 패치에서 감사 당시 확인한 첨부파일 수명, 안전한 로그, 데이터 삭제 경계 문제를
수정했다. 감사 본문의 당시 결과는 변경하지 않으며 아래 내용만 후속 증거로 추가한다.

- Backend는 브라우저의 확장자와 MIME을 신뢰하지 않고 magic byte로 PNG, JPEG, GIF, WebP를
  판별한다. 파일당 8 MiB, 메시지당 8개 및 총 32 MiB로 제한하고 응답에는 `nosniff`를 적용한다.
- 여러 파일 중 일부 업로드가 실패해도 성공한 파일은 유지한다. 전송·queue·실행 중 참조는 조기
  삭제하지 않으며, 개별 삭제와 Backend restart 후 opaque 참조 복구를 지원한다.
- 전송되지 않은 첨부파일은 24시간 후 시작 시 최대 128개, 완료·실패 session cache는 30일 후 최대
  64개씩 정리한다. preference는 명시적 초기화 전까지 유지하고 production log는 7일, 일별 1 MiB로
  제한한다. cleanup 또는 저장 실패는 rollback하거나 fail-closed 처리한다.
- persisted session, SSE, HTTP 오류, runtime stderr와 Backend 운영 로그는 공통 safe projection을
  사용한다. packaged host도 최종 파일 기록 전에 credential, cookie, token, 인증 파일명과 실제 로컬
  경로를 제거하고 크기와 기간을 제한한다.
- 모바일 로컬 초기화는 해당 기기의 pairing을 먼저 해제한 뒤 앱 소유 storage/cache만 지운다. PC
  초기화는 managed Windows host에서만 실행되고 active·queued work를 차단한 뒤 고정된 CRM runtime
  data, attachment, preference, pairing, usage와 log만 staging 삭제하고 Backend만 다시 시작한다.
- Codex Chat/history, 설정, 인증정보, CRM config와 backup, OpenAI/Tailscale 계정, uninstall 보존
  정책과 P0-D receipt는 삭제하지 않는다.

인증된 fake-engine HTTP 통합시험에서 위조 이미지 거부, server MIME, `nosniff`, restart 복구, accepted
attachment 1회와 개별 삭제를 확인했다. 실제 Codex valid-image smoke는 started/completed 각 1회,
assistant output, image decode 오류 없음과 공개 로컬 경로 없음이 확인됐다. 첫 실제 smoke의 손상된
시험 fixture는 유효한 이미지 증거로 인정하지 않았고 정상 PNG로 다시 검증했으므로 임시 Chat 두 개가
로컬 history에 남을 수 있다.

기존 모바일 로컬 초기화 버튼은 native와 휴대폰 너비 PWA 설정에 표시하고 넓은 PC 화면에서는
숨기도록 수정했다. 집중 초기화 테스트와 전체 필수 검증까지 통과해 P0-E를 완료했다. signing,
clean-machine, Tailnet policy 및 store 작업은 별도 P0-F 이후 gate이며 모든 공개 채널의 **NO-GO**
판정은 유지한다.
