4.9 KiB
Kiosk — the store, and what it replaces
Decided in conversation on 2026-08-18. Direction, not a work order: none of it is built, and the parts that touch third parties are months out. Recorded so the shape is not re-argued.
The commercial reasoning is BUSINESS-MODEL.md in the officer-oas repository; this file is the technical
half.
One brand, two halves
Kiosk is what a person sees. kiosk.officer.dev is the public hub: a searchable catalogue of
everything in the Officer ecosystem — plugins, apps, the plugins that have companion apps, the apps that
have plugins. Nothing happens there. It stores metadata and exposes an API, so a platform can embed it.
Underneath, two separate plugins with separate repositories:
| ships to | holds | is | |
|---|---|---|---|
| the storefront | every instance | no database, no sidecar | free infrastructure |
| the apps engine | whoever distributes builds | 4 tables, a sidecar, artefacts, Apple credentials | the paid product |
They are split on that last column rather than on any technical boundary. Bundled, every server would carry app-distribution machinery it will never run, and the commercial half would live inside the plugin that can never be uninstalled. That is the wrong seam.
Brand is not appName. label is what a person reads; appName is the directory name and the route.
Both can be "Kiosk" to a human while being distinct identifiers underneath — the same way email's label is
"Email".
/marketplace goes away
Replaced by /kiosk, and the screen moves out of the platform into the storefront plugin, which mounts
exactly the way every other plugin does.
The install machinery does NOT move. src/servers/plugins/ — discover, mount, install, schema,
ecosystem — is how any plugin gets mounted, including Kiosk itself, so it cannot live inside one. It is
already API-shaped (/api/plugins: what is on this machine, what is installed, and the four verbs that
change it), which is what makes this a UI move rather than a refactor.
So: the platform keeps the mechanism, the plugin owns the shop window.
Because a first-party plugin mounts at /<appName> (mountPrefix, verified), the storefront must be
appName: 'kiosk'. That settles the identifier by way of the route.
The storefront is a privileged plugin
Not a normal one, and the differences are deliberate:
- installed on every instance, as a setup step rather than a marketplace action — you cannot install the installer with the installer;
- not listed among installed plugins;
- no uninstall UI;
- and removing it must not brick the server. A user can always stop or delete the PM2 service by hand, so the platform has to survive the storefront being absent. The install verbs are the platform's and stay reachable without it; that is what makes this true rather than hoped.
Two calls to action, one catalogue
The hub is a discovery surface for people who do not have a server yet — someone browses it and asks their admin for something. That is a funnel, and it means the two halves end differently:
- a plugin can be installed — by an admin, on a server, now;
- an app cannot be installed from the hub at all. It needs a server running the apps plugin, an account, a registered device, and somebody paying Apple.
Same catalogue, two endings. Worth designing the entry shape around, rather than discovering it when the Install button does nothing for half the listings.
The pairing is declared data, not inference. "Plugins that have apps" cannot be derived from names that happen to match; a manifest field declares the companion. It is the smallest piece of work here and the whole browsing experience hangs off it. Safe to put in the public catalogue — names, not artefacts.
Open
- What the apps plugin is called. It cannot be
kiosk, since the storefront takes that with the route.kiosk-appsis legible and obviously paired;buildsdescribes what it manages. Currently sitting atplugins/kiosk(andgitea.officer.dev/plugins/kiosk) from the night it was started — that name is wrong and pending a decision, which is cheap now and expensive after either ships. - Whether
marketplace.officer.devbecomeskiosk.officer.dev. Precedent says rename the screen and leave the machine endpoint alone: every installed server has the current URL baked in as its default, so a rename drops them to the bundled catalogue until they update. - Everything about third parties — review, pinning, confining a plugin's sidecar. See
TODO.md, "Plugin ecosystem — before it opens to third parties". Months away and not a prerequisite for the rename.