what the mobile app needs is now buildable against something real: GET /apps, GET /key, GET /install/:app/:platform/:version, POST /installs, plus device and build verbs. all under /api/kiosk, all behind a platform session. the install URL is built per request and never stored, because it carries the CALLING user's key - two people asking for the same build get different links, which is the point: the artefact route is public and the key is what authorises it. deliberately NOT here: the four requests that arrive with no session. installd fetching the manifest and the ipa, the device posting its udid, safari downloading a mobileconfig. a plugin cannot serve an unauthenticated route today, so those wait for the platform primitive and will land in api/public.ts - a separate file so the two surfaces cannot be confused by whoever reads either one. so /install returns a correct URL pointing at a route that does not exist yet. that is the shape the app should build against. layout is a horizontal root with the app list first, and that is load-bearing: on a phone WorkspaceRenderer collapses horizontal groups to children[0] and PluginScreen passes no mobilePanelId, so first is the only panel a phone shows. the install page is the one page that must work in mobile safari. PUBLIC_URL is read from the environment rather than officer-url.mjs, which exports only loopback URLs - ios is not on loopback, and it needs https with a cert it already trusts or it declines silently.
48 lines
2.5 KiB
Markdown
48 lines
2.5 KiB
Markdown
# Kiosk
|
|
|
|
Build, sign and install Officer's iOS and Android apps on your own registered devices. What EAS does,
|
|
self-hosted.
|
|
|
|
- **A catalogue of builds**, newest per app per platform, with an Install button that hands
|
|
`itms-services://` to iOS or a plain URL to Android.
|
|
- **A per-user key** that authorises artefact downloads. Held in memory by the app and re-read on demand, so
|
|
rotating one costs nothing.
|
|
- **Device records** — who owns which iPhone or iPad, and whether Apple knows about it yet.
|
|
|
|
## Status — read before installing
|
|
|
|
**Nothing here has run.** Ported on 2026-08-18 from the standalone service that was called OAS. The
|
|
authenticated API and the two panels exist and typecheck; no endpoint has answered a request.
|
|
|
|
### What works, in principle
|
|
|
|
`GET /apps`, `GET /apps/:appName`, `GET /key`, `POST /key/rotate`,
|
|
`GET /install/:appName/:platform/:version`, `POST /installs`, and the device and build verbs. All under
|
|
`/api/kiosk`, all requiring a platform session.
|
|
|
|
### What is missing, and why
|
|
|
|
**Installing will not work yet.** Four requests in the flow arrive with no session — iOS `installd` fetching
|
|
the manifest and the `.ipa`, the device posting its UDID, and Safari downloading a `.mobileconfig` — and a
|
|
plugin cannot serve an unauthenticated route today. `UNPROTECTED_API_PREFIXES` is hardcoded in the platform
|
|
and plugin routers mount only behind the account gate.
|
|
|
|
So `GET /install/...` returns a correct-looking URL pointing at a route that does not exist. That is
|
|
deliberate: the URL shape is what the mobile app builds against, and it stops being a dead end the day the
|
|
platform gains `unauthenticatedPaths`. Those routes then land in `api/public.ts`, kept separate from
|
|
`api/router.ts` so the two surfaces cannot be confused by anyone reading either.
|
|
|
|
**Enrolment is not ported** for the same reason — it is entirely public routes.
|
|
|
|
**Artefacts arrive by rsync**, into `$OFFICER_ROOT/data/kiosk/`. The plugin records metadata about files
|
|
already on disk; it does not accept uploads, because the platform's shared upload route caps at 5MB and no
|
|
`.ipa` will ever satisfy that.
|
|
|
|
## Notes
|
|
|
|
- `PUBLIC_URL` must be **HTTPS with a certificate iOS already trusts**. It fetches the manifest and binary
|
|
itself and declines silently otherwise, which looks exactly like a broken build.
|
|
- Table names are prefixed `kiosk_`. There are no migrations — see the note at the top of `db/schema.ts`.
|
|
- The plugin is currently `appName: kiosk`. If the plugin *storefront* takes that name, this one is renamed;
|
|
see `docs/kiosk-ecosystem.md` in the platform.
|