Minesweeper X under wine on a headless display, as a plugin. It was /api/minesweeper, a Dashboard screen, an officerdev panel, a permission registry entry and a hand-written websocket provider in server.tsx. The socket is why it had to live in the platform: Bun's route table is fixed at serve(), so nothing installed later could add one. That ceiling is gone, and this is the first plugin through the per-plugin relay. Three things changed rather than moved: The sidecar owns the session. The platform used to spawn bin/session.sh detached, so the game outlived every restart, nothing supervised it, and a crashed run left wine processes holding the prefix — the next start hung rather than failed. PM2 supervises a sidecar, and stop() uses 'wineserver -k' scoped by WINEPREFIX instead of 'pkill -f wineserver', which would have killed every other wine prefix on the machine. x11vnc moved from 127.0.0.1:5907 to a unix socket in a mode-0700 directory. -nopw was only ever safe because of the machine, not the platform: any local account could open that port and take the display. A fixed port is squattable too. There is now no listener to authenticate against. The wine prefix is confined to C:. This is the one that mattered. The platform classified Minesweeper as kind:'execution' — never grantable — because a fresh prefix maps the whole filesystem to Z: and symlinks the profile into the owner's real home, and the game has Open and Save dialogs. A plugin permission has no kind and is always grantable, so extracting it without this would have converted a sealed capability into a file picker running as the owner. Re-asserted on every start, because wine restores z: whenever it updates a prefix. Not solved: one display and one game, so two grantees share a board. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
11 lines
585 B
TypeScript
11 lines
585 B
TypeScript
// `panels.ts`, not `.tsx` — discovery checks for that exact name.
|
|
import { Bomb } from 'lucide-react';
|
|
import type { AppRegistryMeta } from 'officerdev';
|
|
import { MinesweeperPanel } from './MinesweeperPanel';
|
|
|
|
// Minesweeper X on a virtual display, streamed over RFB. The client is ./rfb.ts — a few hundred lines
|
|
// rather than a vendored noVNC, because we control the server and need two encodings out of ten.
|
|
export const appRegistryMetas: AppRegistryMeta[] = [
|
|
{ key: 'minesweeper', name: 'Minesweeper', icon: Bomb, component: MinesweeperPanel, availableOnPanel: false },
|
|
];
|