# ODEI OS Installable Apps

Contract for sister deployments and partner apps that run **on top of ODEI OS** and appear on the Home launcher.

## Partner readiness checklist

1. **`JWT_SECRET_ODEI_OS`** — set a dedicated secret in production (falls back to `JWT_SECRET_CHECKOUT` / `JWT_SECRET`). Registered in Core deployment env.
2. **Partner SDK** — load [`/odei-os/sdk.js`](/odei-os/sdk.js) in the app; it posts `odei:ready` and exposes `window.OdeiOs.exchange(token)`, `close()`, `toast(msg)`, `navigate(path)`. Cross-origin apps set `window.OdeiOsHubOrigin = "https://your-hub-origin"` (or pass hub origin as the second `exchange` argument).
3. **Bridge** — typed messages in `lib/odei-os-app-bridge.ts`: `odei:ready` | `odei:close` | `odei:navigate` | `odei:toast` | `odei:resize`. Parent shell listens in `OsAppWindow`.
4. **CORS** — `POST` / `OPTIONS` `/api/odei-os/session/exchange` allow Origins that match an approved app `launchUrl` origin, optional `allowedOrigins`, or the same site.
5. **Sandbox** — external iframe apps get `allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox`; same-origin uses a milder sandbox (scripts / same-origin / forms / downloads).
6. **Folders** — first-party tiles live under Home folders (`edu`, `money`, `play`, `builders`, `system`) on **phone, tablet, and desktop**. Partner apps with `requiresHub=developers` merge into Home **Builders**. Users may ungroup / hide / create / reset folders (layout v4; remote sync migrates flat v3→folders).
7. **`payments:create`** — exchange returns `claims.payments` with Partner API docs + intents path. Launch tokens do **not** mint payment secrets.
8. **Consent** — scoped launches prompt once per app (local grant) before issuing a launch token.
9. **Nonce** — session exchange consumes the launch nonce once (replay rejected).

## Registry

| Field | Notes |
|-------|--------|
| `slug` | Unique id (URL-safe) |
| `launchUrl` | Same-origin `/path` or `https://…` |
| `openMode` | `window` \| `same_tab` \| `new_tab` \| `iframe` \| `telegram` |
| `embedAllowed` | Required for external `iframe` / `window` |
| `scopes` | `profile:read`, `org:read`, `wallet:balance:read`, `payments:create` |
| `status` | `draft` \| `pending_review` \| `approved` \| `disabled` |
| `enabled` | Master kill switch |
| `allowedOrigins` | Extra CORS origins for session exchange (MAC + developer upsert) |
| `version` | Optional partner version string (writable via MAC / developer request) |
| `homepageUrl` / `privacyUrl` | Optional partner links (writable via MAC / developer request) |

**Public tiles** = `status === "approved" && enabled === true`.

**Env:** set `JWT_SECRET_ODEI_OS` in production (see `.env.example`). Falls back to `JWT_SECRET_CHECKOUT` / `JWT_SECRET`.

**Catalogue:** `lib/odei-os-catalogue.ts` (`ODEI_OS_INSTALLABLE_CATALOGUE`) + `GET /api/public/odei-os/apps` → `catalogue`.

## APIs

| Method | Path | Who |
|--------|------|-----|
| GET | `/api/public/odei-os/apps` | Launcher merge |
| GET/PATCH | `/api/master/odei-os/apps` | MAC CRUD / approve |
| GET/POST | `/api/developers/odei-os/apps/request` | Developer request listing |
| POST | `/api/odei-os/launch-token` | Issue short-lived handoff JWT |
| POST / OPTIONS | `/api/odei-os/session/exchange` | App exchanges token → claims (CORS for approved origins) |

## Open mode rules

- External URLs default to **`new_tab`**.
- `iframe` / `window` for external hosts only when `embedAllowed` is true (otherwise forced to `new_tab`).
- Same-origin paths may use `window` (ODEI iframe with `odeiEmbed=1`) or `same_tab`.

## Auth handoff

1. User taps an approved app with scopes.
2. ODEI calls `POST /api/odei-os/launch-token` with `appId` / `appSlug`.
3. Redirect / open: `launchUrl?odei_os_token=…&return_to=/`.
4. App calls `POST /api/odei-os/session/exchange` with `{ token }` (via SDK `OdeiOs.exchange`) and receives `claims` (profile / org / wallet / payments note per scopes).

Token TTL ≈ 180 seconds. Audience = app slug. Signed with `JWT_SECRET_ODEI_OS` (or fallbacks).

## Publishing gate

1. Developer (signed into `/developers/dashboard`) submits a listing request → `pending_review`, `enabled: false`.
2. Master opens `/admin/master#odei-os-installable-apps` → Approve.
3. Tile merges onto Home (and Builders folder when `requiresHub=developers`).

## Discovery

- Human page: [`/odei-os/apps`](/odei-os/apps)
- Architecture overview: [ODEI_OS.md](./ODEI_OS.md)
- Onchain UX fabric (first-party, not Reown): [ODEI_ONCHAIN_UX_FABRIC.md](./ODEI_ONCHAIN_UX_FABRIC.md)
