feat: add APN relay MVP and experimental push bridge
This commit is contained in:
parent
8ac2fbbd12
commit
f276a8db42
17 changed files with 1196 additions and 1 deletions
173
specs/apn-relay-mvp-layout.md
Normal file
173
specs/apn-relay-mvp-layout.md
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
# APN Relay MVP Layout
|
||||
|
||||
This is the minimum setup to get reliable mobile background notifications working with OpenCode.
|
||||
|
||||
## Part 1: APN Relay spec and routes
|
||||
|
||||
### Goal
|
||||
|
||||
- Receive event posts from OpenCode.
|
||||
- Look up device tokens by shared secret.
|
||||
- Send APNs notifications to iOS devices.
|
||||
- Keep the service small and easy to run in Docker.
|
||||
|
||||
### Stack
|
||||
|
||||
- Runtime: Bun
|
||||
- Web framework: Hono
|
||||
- Database: PlanetScale MySQL (via Drizzle ORM)
|
||||
- Deployment artifact: Docker image (`packages/apn-relay`)
|
||||
|
||||
### Minimal data model
|
||||
|
||||
- `device_registration`
|
||||
- `id`
|
||||
- `secret_hash` (hash of shared secret)
|
||||
- `device_token` (APNs token)
|
||||
- `bundle_id`
|
||||
- `apns_env` (`sandbox` or `production`)
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
- `delivery_log` (optional but recommended)
|
||||
- `id`
|
||||
- `secret_hash`
|
||||
- `event_type`
|
||||
- `session_id`
|
||||
- `status` (`sent` or `failed`)
|
||||
- `error`
|
||||
- `created_at`
|
||||
|
||||
### API routes
|
||||
|
||||
#### `GET /health`
|
||||
|
||||
- Response: `{ ok: true }`
|
||||
|
||||
#### `POST /v1/device/register`
|
||||
|
||||
- Purpose: upsert device token for a shared secret.
|
||||
- Body:
|
||||
- `secret` (string)
|
||||
- `deviceToken` (string)
|
||||
- `bundleId` (string)
|
||||
- `apnsEnv` (`sandbox` or `production`)
|
||||
- Response: `{ ok: true }`
|
||||
|
||||
#### `POST /v1/device/unregister`
|
||||
|
||||
- Purpose: remove token mapping for a shared secret.
|
||||
- Body:
|
||||
- `secret` (string)
|
||||
- `deviceToken` (string)
|
||||
- Response: `{ ok: true }`
|
||||
|
||||
#### `POST /v1/event`
|
||||
|
||||
- Purpose: receive event from OpenCode and push to all devices for that secret.
|
||||
- Body:
|
||||
- `secret` (string)
|
||||
- `eventType` (`complete` or `permission` or `error`)
|
||||
- `sessionID` (string)
|
||||
- `title` (optional string)
|
||||
- `body` (optional string)
|
||||
- Response:
|
||||
- `{ ok: true, sent: number, failed: number }`
|
||||
|
||||
### APNs behavior for MVP
|
||||
|
||||
- Use APNs auth key (`.p8`) with JWT auth.
|
||||
- Default to user-visible alert pushes for reliability.
|
||||
- `apns-push-type: alert`
|
||||
- `apns-priority: 10`
|
||||
- Payload includes `eventType` and `sessionID` in `data`.
|
||||
- Keep advanced silent/background tuning out of scope for MVP.
|
||||
|
||||
### Env vars
|
||||
|
||||
- `PORT`
|
||||
- `DATABASE_URL`
|
||||
- `APNS_TEAM_ID`
|
||||
- `APNS_KEY_ID`
|
||||
- `APNS_PRIVATE_KEY`
|
||||
- `APNS_DEFAULT_BUNDLE_ID` (fallback)
|
||||
|
||||
## Part 2: Mobile app setup (`packages/mobile-voice`)
|
||||
|
||||
### Goal
|
||||
|
||||
- Pair app with OpenCode server using QR data.
|
||||
- Register APNs token in relay using shared secret.
|
||||
- Keep existing foreground SSE behavior.
|
||||
- Receive APNs when app is backgrounded or terminated.
|
||||
|
||||
### Pairing flow (simple)
|
||||
|
||||
1. User runs OpenCode serve with relay enabled.
|
||||
2. OpenCode prints a QR code that includes:
|
||||
- `hosts` (array of server URLs)
|
||||
- `relayURL`
|
||||
- `relaySecret`
|
||||
3. User scans QR in mobile app.
|
||||
4. App saves `relaySecret` in secure storage and server profile metadata.
|
||||
|
||||
### Token registration flow
|
||||
|
||||
1. App gets APNs token (`Notifications.getDevicePushTokenAsync()`).
|
||||
2. App calls `POST {relayURL}/v1/device/register` with secret and token.
|
||||
3. App re-registers on token change and on app startup.
|
||||
|
||||
### Prompt and monitoring flow
|
||||
|
||||
1. App sends prompt to OpenCode (`POST /session/:id/prompt_async`).
|
||||
2. If app stays foregrounded, existing SSE monitor still updates UI quickly.
|
||||
3. If app goes backgrounded, APNs notification from relay carries state updates.
|
||||
|
||||
### Mobile changes
|
||||
|
||||
- Replace Expo push relay integration with APNs relay integration.
|
||||
- Keep local notification behavior for handling incoming payload data.
|
||||
- Store `relaySecret` with secure storage, not plain AsyncStorage.
|
||||
- Remove session-specific monitor start/stop calls for MVP.
|
||||
|
||||
## Part 3: OpenCode serve setup and modifications (`packages/opencode`)
|
||||
|
||||
### Goal
|
||||
|
||||
- Watch all sessions for the current OpenCode server.
|
||||
- Detect target events in OpenCode server.
|
||||
- Forward those events to APN relay using shared secret.
|
||||
|
||||
### Serve config and terminal UX
|
||||
|
||||
- Add serve options:
|
||||
- `--relay-url`
|
||||
- `--relay-secret` (optional; generate random if missing)
|
||||
- Default relay URL: `https://relay.opencode.ai`
|
||||
- If relay is configured, print QR payload in terminal:
|
||||
- `hosts` (local LAN and configured host, including Tailscale IP when present)
|
||||
- `relayURL`
|
||||
- `relaySecret`
|
||||
|
||||
### New experimental routes
|
||||
|
||||
- No required monitor routes for MVP.
|
||||
- Optional debug route:
|
||||
- `POST /experimental/push/test`
|
||||
- Purpose: force-send a test event to relay to validate config.
|
||||
|
||||
### Event forwarding behavior
|
||||
|
||||
- Subscribe to existing OpenCode events.
|
||||
- For all sessions under the running OpenCode server:
|
||||
- On `permission.asked` -> send `eventType=permission`
|
||||
- On `session.error` -> send `eventType=error`
|
||||
- On `session.status` idle (or `session.idle`) -> send `eventType=complete`
|
||||
- Include `sessionID` in every relay request so the mobile app can label the event.
|
||||
- Best effort posting only for MVP (log failures, no complex retry queue yet).
|
||||
|
||||
### Out of scope for this MVP
|
||||
|
||||
- Certificate-based trust between OpenCode and relay.
|
||||
- Complex key rotation UX.
|
||||
- Multi-tenant dashboard auth model.
|
||||
- Guaranteed delivery semantics.
|
||||
Loading…
Add table
Add a link
Reference in a new issue