feat(privacy+dist): telemetry consent gate + app store distribution prep (#4)
* fix(security): fail closed on biometric init error H-03: setting isAuthenticated: true on initialization failure was a security bypass — any crash during biometric setup granted full access. Fail closed instead; user sees auth prompt on next open. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(security): use Crypto.randomUUID for connection IDs H-04: Math.random() is not cryptographically random. Connection IDs are used as SecureStore key suffixes; switch to expo-crypto randomUUID for a secure source. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(deps): pin expo-crypto to ~15.0.9 15.0.10 does not exist on npm; ~15.0.9 is the latest stable in the 15.x series compatible with Expo SDK 54. * feat: add OpenCode Connect coming-soon waitlist card Adds a discoverable 'OpenCode Connect — Coming Soon' card to the add-connection quick-connect screen. Users can enter their email and tap 'Join Waitlist' to send a pre-filled mailto. No backend required. * fix(cua): detect actual screen dimensions and fix JSON parsing - Get real screen size via `wm size` instead of hardcoding 1080x2400; emulator is 1080x1920 so y-coordinates were systematically off - Extract first JSON object via regex when model returns multiple objects - Use AZURE_OPENAI_MODEL env var for deployment name (defaults gpt-5.4) - Add AZURE_DEV_AI_* path for Azure AI Foundry endpoints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(security): SHA-pin upload-google-play and sanitize notification bodies M-02: Pin r0adkll/upload-google-play to commit SHA e738b9d (v1.1.5) to prevent supply-chain hijack via tag mutation. M-03: Sanitize all push notification bodies — strip control chars, truncate to 200 chars. Prevents server-supplied strings (error messages, file paths from permission patterns, session titles) from leaking unbounded text into the OS notification drawer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(privacy): add telemetry consent gate for Sentry crash reporting Sentry was always-on, violating F-Droid anti-feature policy and user trust norms. Now gated behind explicit opt-in: - First-launch consent modal (TelemetryConsentModal) shows once on fresh install; user can Allow or Decline. - Consent state persisted in expo-secure-store (survives restarts). - Settings > Privacy section: crash reporting toggle + privacy policy link. - initSentry() called only after consent granted — not on app start. Closes #3 (partial) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(config): add real icons and complete iOS/Android app.json config - Add 1024×1024 app icon, 432×432 adaptive icon foreground, 200×200 splash - iOS: push notification entitlement (aps-environment: production), speech/ microphone/camera/photo usage descriptions for future features, disable ITSAppUsesNonExemptEncryption - Android: adaptive icon with dark background (#0F172A), versionCode: 1 - expo-notifications plugin wired in app.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(dist): add iOS CI workflow, README rewrite, CONTRIBUTING, and LICENSE - publish-app-store.yml: EAS Build + TestFlight submission; runs on tag/release/ workflow_dispatch; bumps ios.buildNumber from github.run_number - README: full rewrite — features, install badges, connection guide, contributing - CONTRIBUTING.md: contribution guide for OSS contributors - LICENSE: MIT Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(dist): add store listings, strategy, privacy policy, F-Droid/IzzyOnDroid templates - distribution/strategy.md: monetization strategy (free client + opencode Cloud) - distribution/play-listing.md: Google Play store copy (name, description, tags) - distribution/app-store-listing.md: App Store listing copy - distribution/privacy-policy.{md,html}: GDPR-compliant privacy policy - distribution/PLAY_CONSOLE_SETUP.md: Play Console setup runbook - distribution/ios-enrollment-runbook.md: Apple Developer Program enrollment steps - distribution/SIGNING-KEY-FINGERPRINTS.md: keystore fingerprint for reproducible builds - distribution/fdroid-submission/: F-Droid metadata template - distribution/izzyondroid-submission/: IzzyOnDroid submission template - distribution/whatsnew/: Play Store release notes (en-US) - distribution/whatsnew-ios/: TestFlight release notes - distribution/play-graphics/: Play Store screenshot placeholders - distribution/app-store-graphics/: App Store screenshot placeholders Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(telemetry): handle SecureStore failure + Android back button - add .catch() on loadTelemetryConsent() so SecureStore rejection shows the consent modal instead of blocking startup forever - add onRequestClose={onDecline} to Modal so Android back button records the decline rather than silently dismissing - fix catch block in telemetry.ts to not clobber _resolved when SecureStore read fails mid-session Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): run gradlew clean to prevent stale modules.json duplicate Sentry Gradle plugin writes modules.json to src/main/assets; cached build intermediates contain an old copy → mergeReleaseAssets fails with 'Duplicate resources'. Running clean before assembleRelease clears the intermediate state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(ci): remove android build output cache causing duplicate modules.json Caching android/app/build/intermediates and android/app/.cxx causes two issues: 1. Stale modules.json in intermediates → Duplicate resources error 2. .cxx CMake artifacts reference absolute paths → ninja clean fails Keeping only Gradle distribution cache (~/.gradle) which is safe. Expo prebuild regenerates android sources fresh each run anyway. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
349
distribution/app-store-listing.md
Normal file
349
distribution/app-store-listing.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# App Store Listing — OpenCode (iOS)
|
||||
|
||||
Copy-paste reference for App Store Connect. Use this once the Apple Developer Program enrollment is approved and the app is created in App Store Connect.
|
||||
|
||||
---
|
||||
|
||||
## App Information
|
||||
|
||||
### App Name (max 30 chars)
|
||||
|
||||
```
|
||||
OpenCode
|
||||
```
|
||||
(8 chars)
|
||||
|
||||
### Subtitle (max 30 chars)
|
||||
|
||||
```
|
||||
AI Coding Agent, In Your Pocket
|
||||
```
|
||||
(31 chars — trim to:)
|
||||
|
||||
```
|
||||
AI Coding Agent in Your Pocket
|
||||
```
|
||||
(30 chars exactly)
|
||||
|
||||
### Promotional Text (max 170 chars — appears above description, can be updated without a new review)
|
||||
|
||||
```
|
||||
Drive your self-hosted AI coding agent from anywhere. Connect to opencode on your workstation and review, approve, and guide AI-generated code changes in real time.
|
||||
```
|
||||
(165 chars)
|
||||
|
||||
### Description (max 4000 chars)
|
||||
|
||||
```
|
||||
OpenCode is an open-source mobile client for the opencode AI coding agent (github.com/sst/opencode). Connect to your self-hosted opencode server and drive AI-powered coding sessions directly from your iPhone or iPad.
|
||||
|
||||
KEY FEATURES
|
||||
|
||||
• Multiple connection types — local network (Wi-Fi), secure tunnels (Cloudflare Tunnel, ngrok), or cloud-hosted opencode instances
|
||||
• Biometric unlock — Face ID / Touch ID to keep your sessions private
|
||||
• Real-time streaming chat — watch your AI agent think and respond live as it works through your code
|
||||
• File diff viewer — see exactly what code changes the agent proposes before you accept them
|
||||
• Multi-session management — start, resume, and switch between coding sessions
|
||||
• Tool call approval — review and approve file writes, shell commands, and other actions before the agent executes them on your code
|
||||
|
||||
WHO IT'S FOR
|
||||
|
||||
• Developers who run opencode on their workstation or a server and want to check in from their phone
|
||||
• Engineers away from their desk who want to guide long-running AI coding sessions
|
||||
• Teams who self-host AI dev tools and want a polished mobile companion
|
||||
|
||||
WHAT IT IS NOT
|
||||
|
||||
• Not an AI model — you bring your own opencode server (which connects to Claude, GPT-4, Gemini, or local models via your API keys)
|
||||
• Not a code editor — pairs with your existing IDE and terminal workflow
|
||||
• Not a subscription service — the app is free and open source
|
||||
|
||||
OPEN SOURCE
|
||||
|
||||
OpenCode Mobile is MIT licensed. Source code, issue tracker, and community discussion:
|
||||
https://github.com/dzianisv/opencode-mobile
|
||||
|
||||
PRIVACY
|
||||
|
||||
OpenCode Mobile does not collect your code, prompts, or AI responses. All AI traffic goes directly from the app to YOUR opencode server — never through our infrastructure. We use Sentry for crash reporting only (no personally identifiable information, no message content). See our privacy policy for full details.
|
||||
|
||||
SELF-HOSTED FIRST
|
||||
|
||||
OpenCode requires you to run an opencode server:
|
||||
1. Install: npm install -g opencode-ai
|
||||
2. Run: opencode serve
|
||||
3. Connect the app to the server URL
|
||||
|
||||
If you cannot self-host, contact support@vibebrowser.app and we will provide a temporary review endpoint.
|
||||
|
||||
SUPPORT
|
||||
|
||||
Email: support@vibebrowser.app
|
||||
Issues: https://github.com/dzianisv/opencode-mobile/issues
|
||||
```
|
||||
|
||||
(Character count: ~1,750 — well under 4,000 limit. Add more feature detail or FAQ if desired.)
|
||||
|
||||
---
|
||||
|
||||
## Keywords (max 100 chars, comma-separated)
|
||||
|
||||
```
|
||||
opencode,AI coding,developer,LLM,Claude,GPT,coding agent,self-hosted,mobile dev,terminal
|
||||
```
|
||||
(89 chars)
|
||||
|
||||
Alternative / supplemental terms to rotate in A/B: `code review`, `AI assistant`, `remote dev`, `SSH`, `copilot`
|
||||
|
||||
---
|
||||
|
||||
## URLs
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Support URL | https://www.vibebrowser.app/ |
|
||||
| Marketing URL | https://github.com/dzianisv/opencode-mobile |
|
||||
| Privacy Policy URL | https://opencode.vibebrowser.app/privacy |
|
||||
|
||||
---
|
||||
|
||||
## App Category
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Primary Category | Developer Tools |
|
||||
| Secondary Category | Productivity |
|
||||
|
||||
---
|
||||
|
||||
## Pricing & Availability
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Price | Free |
|
||||
| In-App Purchases | None |
|
||||
| Availability | All territories (no regional restrictions) |
|
||||
|
||||
---
|
||||
|
||||
## Age Rating Questionnaire
|
||||
|
||||
Apple uses a questionnaire to assign an age rating. Answer as follows for OpenCode Mobile:
|
||||
|
||||
| Question | Answer | Notes |
|
||||
|---|---|---|
|
||||
| Cartoon or fantasy violence | None | Dev tool — no violence content |
|
||||
| Realistic violence | None | |
|
||||
| Prolonged graphic violence | None | |
|
||||
| Sexual content or nudity | None | |
|
||||
| Profanity or crude humor | None | |
|
||||
| Mature or suggestive themes | None | |
|
||||
| Horror/fear-inducing content | None | |
|
||||
| Medical/treatment information | None | |
|
||||
| Alcohol, tobacco, drugs | None | |
|
||||
| Gambling | None | |
|
||||
| Contests | None | |
|
||||
| Unrestricted web access | No | App connects to user-specified servers only, no general browser |
|
||||
| Sharing location data | No | |
|
||||
| User-generated content | No | Chat is between user and their own AI backend, not user-to-user |
|
||||
|
||||
**Expected rating: 4+** (no objectionable content; developer tool)
|
||||
|
||||
---
|
||||
|
||||
## Privacy Nutrition Label (App Privacy)
|
||||
|
||||
Apple requires you to declare what data the app collects. Fill the App Privacy section in App Store Connect as follows.
|
||||
|
||||
### Data Not Collected
|
||||
|
||||
OpenCode Mobile does NOT collect any of the following:
|
||||
- Name, Email address, Phone number, Physical address, Other contact info
|
||||
- Health/fitness data
|
||||
- Financial info, payment info
|
||||
- Precise or coarse location
|
||||
- Contacts
|
||||
- Emails or text messages
|
||||
- Photos or videos
|
||||
- Audio data
|
||||
- Gameplay content
|
||||
- Customer support data
|
||||
- Browsing history, search history
|
||||
- Sensitive info
|
||||
- User content (code, prompts, AI responses are not sent to our servers)
|
||||
- Identifiers (User ID, Device ID — Sentry uses an installation-scoped anonymous ID, see below)
|
||||
|
||||
### Data Linked to You: None
|
||||
|
||||
### Data Not Linked to You
|
||||
|
||||
| Data Type | Category | Purpose | Optional? |
|
||||
|---|---|---|---|
|
||||
| Crash Data | Diagnostics | App functionality | No — always on (see note) |
|
||||
| Performance Data | Diagnostics | App functionality | No — always on (see note) |
|
||||
| Other Diagnostic Data | Diagnostics | App functionality | No |
|
||||
|
||||
**Explanation**: Sentry crash reporting sends device model, OS version, app version, and stack traces. Sentry assigns an anonymous installation ID (not linked to any Apple ID or personal information). No user-generated content (code, prompts, responses) is ever sent.
|
||||
|
||||
**Sentry opt-in status**: As of v0.2.3, Sentry is **always on** when a DSN is configured. If you add a settings toggle for Sentry consent (planned), change Optional? to "Yes" and add a note that users who decline are in the "Data Not Collected" category. In App Store Connect, once opt-in is implemented, this section can be removed or marked optional.
|
||||
|
||||
**"Are you or your third-party partners using this data to track users?"**: No
|
||||
|
||||
**App Tracking Transparency (ATT)**: OpenCode Mobile does **not** use the ATT framework (`AppTrackingTransparency`) and does **not** access the IDFA (Identifier for Advertisers). No ad networks or cross-app tracking SDKs are present. No ATT permission prompt is shown to users.
|
||||
|
||||
---
|
||||
|
||||
## Export Compliance
|
||||
|
||||
Apple asks about encryption during submission. Answer:
|
||||
|
||||
| Question | Answer |
|
||||
|---|---|
|
||||
| Does the app use encryption beyond what is in the operating system? | No |
|
||||
| Does the app qualify for any exemptions? | N/A |
|
||||
|
||||
**Rationale**: OpenCode Mobile uses only standard HTTPS/TLS provided by iOS networking APIs (URLSession via React Native's fetch). It does not implement any custom cryptographic algorithms. Per US Export Administration Regulations (EAR), apps using only standard OS-provided encryption and that do not modify the encryption are exempt from ERN requirements. Source: https://developer.apple.com/documentation/security/complying_with_encryption_export_regulations
|
||||
|
||||
**Action**: In App Store Connect > App Information > Export Compliance, select "No" when asked if the app uses non-exempt encryption.
|
||||
|
||||
---
|
||||
|
||||
## App Review Notes — ATS Justification
|
||||
|
||||
The app's `Info.plist` sets `NSAllowsArbitraryLoads: true` in `NSAppTransportSecurity`. Apple App Store review may ask for justification. Paste the following in the **App Review Notes** field in App Store Connect, or in the reviewer communication:
|
||||
|
||||
---
|
||||
|
||||
**Justification for NSAllowsArbitraryLoads:**
|
||||
|
||||
OpenCode is a developer tool that connects to user-self-hosted opencode CLI servers. These servers typically run on `localhost` or a LAN address (e.g. `http://192.168.1.100:4096`) over plain HTTP. Requiring TLS would prevent the app from functioning as designed for the dev-tool use case, because:
|
||||
|
||||
1. The opencode server (`opencode serve`) serves over plain HTTP by default. Requiring the user to configure TLS certificates for a localhost developer tool is an unreasonable burden.
|
||||
2. The connection targets are the user's **own** machines, not third-party services. There is no user-to-user data exchange and no third-party server communication through this code path.
|
||||
3. `NSAllowsArbitraryLoadsInWebContent` is explicitly set to `false` — we only relax ATS for the app's own network calls to the user-configured backend.
|
||||
|
||||
This is exactly the use case Apple's own documentation acknowledges when describing developer tools and enterprise apps that connect to custom internal servers. Reference: https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity
|
||||
|
||||
We do not weaken security for any user-facing content delivered by third parties. If the reviewer requires additional justification or a demonstration, we are happy to provide a screen recording or a live server endpoint.
|
||||
|
||||
---
|
||||
|
||||
## App Review Information
|
||||
|
||||
### Sign-in Required
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Sign-in required? | No — the app does not have user accounts |
|
||||
|
||||
### App Access Notes
|
||||
|
||||
Paste the following in the "Notes" field of the App Review Information section:
|
||||
|
||||
```
|
||||
OpenCode Mobile requires the reviewer to connect to an opencode server.
|
||||
|
||||
OPTION A — We provide a hosted review endpoint:
|
||||
Contact support@vibebrowser.app before the review window and we will email a temporary server URL valid for 72 hours.
|
||||
|
||||
OPTION B — Self-host (5 minutes):
|
||||
1. Install Node.js 20+ on any macOS/Linux machine
|
||||
2. Run: npm install -g opencode-ai
|
||||
3. Run: opencode serve --hostname 0.0.0.0
|
||||
4. The terminal prints a URL like: http://192.168.x.x:4096
|
||||
5. In the app: tap "Add Connection" → enter that URL → tap "Connect"
|
||||
6. Tap "New Session" → type any prompt (e.g. "list files in current directory")
|
||||
7. Observe streaming response and tool call approval flow
|
||||
|
||||
Note: The --hostname 0.0.0.0 flag makes the server listen on all interfaces
|
||||
so the iOS device (or simulator) on the same Wi-Fi network can reach it.
|
||||
Without it, the server only accepts connections from localhost.
|
||||
|
||||
The app has no hidden features or paywalls. All functionality is accessible after connecting to a server.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Graphic Assets — Required Before Submission
|
||||
|
||||
All icons and screenshots must be provided before submitting for review.
|
||||
|
||||
### App Icon
|
||||
|
||||
| Asset | Size | Format | Notes |
|
||||
|---|---|---|---|
|
||||
| App Icon | 1024×1024 px | PNG, no alpha channel | Used for App Store; Xcode fans out all other sizes |
|
||||
|
||||
The 1024×1024 icon must NOT have rounded corners (Apple applies them). No transparency.
|
||||
|
||||
Current status: `assets/icon.json` is a placeholder — **real PNG required before submission**.
|
||||
|
||||
### iPhone Screenshots (REQUIRED)
|
||||
|
||||
Minimum 1 screenshot per device class. Recommended: 3–5 showing key flows.
|
||||
|
||||
| Device | Resolution | Size name in App Store Connect |
|
||||
|---|---|---|
|
||||
| iPhone 6.7" (iPhone 16 Pro Max / 15 Plus) | 1320×2868 or 1290×2796 | 6.7" Super Retina XDR Display |
|
||||
| iPhone 6.5" (iPhone 14 Plus / 11 Pro Max) | 1242×2688 | 6.5" Super Retina XDR Display |
|
||||
| iPhone 5.5" (iPhone 8 Plus) | 1242×2208 | 5.5" Retina HD Display |
|
||||
|
||||
Note: As of 2024, Apple only requires 6.7" and 6.5" for new submissions. 5.5" is optional but recommended for coverage.
|
||||
|
||||
Suggested screenshot subjects:
|
||||
1. Connection setup screen (add server URL)
|
||||
2. Active chat session — streaming AI response
|
||||
3. File diff view — seeing a code change
|
||||
4. Tool approval dialog
|
||||
5. Session list / multi-session view
|
||||
6. Biometric unlock (if possible to screenshot without triggering auth)
|
||||
|
||||
### iPad Screenshots (REQUIRED for Universal apps)
|
||||
|
||||
Since `supportsTablet: true`, iPad screenshots are required.
|
||||
|
||||
| Device | Resolution | Size name in App Store Connect |
|
||||
|---|---|---|
|
||||
| iPad 12.9" (iPad Pro 6th gen) | 2048×2732 | 12.9" iPad Pro (6th gen) |
|
||||
| iPad 11" (iPad Pro M4) | 1668×2388 | 11" iPad Pro (M4) |
|
||||
|
||||
Minimum 1 per device class required. iPad screenshots can be the same content as iPhone.
|
||||
|
||||
### Preview Videos (Optional)
|
||||
|
||||
- Max 30 seconds
|
||||
- Same resolution as screenshots for each device class
|
||||
- MP4 or MOV
|
||||
- No audio required
|
||||
|
||||
---
|
||||
|
||||
## TestFlight Beta App Description (shown to TestFlight testers)
|
||||
|
||||
```
|
||||
OpenCode Mobile — iOS beta
|
||||
|
||||
Drive your self-hosted opencode AI coding agent from your iPhone or iPad. Connect to opencode running on your workstation (or any server) and guide AI coding sessions remotely.
|
||||
|
||||
This is an early beta. Please report issues via the TestFlight feedback button or email support@vibebrowser.app.
|
||||
|
||||
To use: you need opencode running somewhere accessible (local Wi-Fi, Tailscale, Cloudflare Tunnel, etc). Run `opencode serve` and enter the server URL in the app.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pending Before First Submission
|
||||
|
||||
- [ ] Apple Developer Program enrollment approved (D-U-N-S 142059652, VIBE TECHNOLOGIES LLC)
|
||||
- [ ] App Store Connect app record created (bundle ID: ai.opencode.mobile)
|
||||
- [ ] App icon 1024×1024 PNG (no alpha, no rounded corners)
|
||||
- [ ] iPhone screenshots (6.7" minimum; 6.5" strongly recommended)
|
||||
- [ ] iPad screenshots (12.9" minimum)
|
||||
- [ ] Privacy policy live at https://opencode.vibebrowser.app/privacy
|
||||
- [ ] App Store Connect API key created (for CI — Key ID, Issuer ID, .p8 file)
|
||||
- [ ] Apple Distribution certificate + provisioning profile (or use EAS managed signing)
|
||||
- [ ] Export compliance answered (No to custom encryption)
|
||||
- [ ] App privacy nutrition label filled
|
||||
- [ ] Age rating questionnaire completed (expected: 4+)
|
||||
- [ ] TestFlight internal group created
|
||||
- [ ] Review notes prepared with temporary server URL or self-host instructions
|
||||
Reference in New Issue
Block a user