# Photos — the extraction record Extracted from the platform on **2026-08-18**, the fifth plugin out (after offscale, music, email, vault). `plugins/EXTRACTING-A-PLUGIN.md` is the runbook; this file is what it cost. ## The clean case Offscale's shape rather than music's or vault's: **nothing stayed behind, and nothing had to be redesigned.** Worth recording why, because it is the cheapest extraction so far and the reasons are structural rather than luck. - **No reverse dependency.** Nothing in `src/` imported anything of photos'. The whole platform surface was one commented router import, one barrel line, and the ordinary wiring (route, screen, registry, dock rule, catalogue entry). Compare vault, whose extraction had to invent an account-lifecycle hook because three core auth handlers called into it. - **Configuration was already right.** The URL and API key live in `photos_config` — a per-user registry with one active row, encrypted at rest, driven from the app. Vault had to move `VAULTWARDEN_URL` out of `.env` and invent a settings panel; here the sidecar already read the database and its own comment already said why nothing reads `process.env`: an `IMMICH_API_KEY` there would sit in the environment of every pm2 process, including the one with the largest attack surface and no code to use it. - **The frontend was already panels.** Two of them, `photos-nav` and `photos-view`, plus a layout. The screen was a `WorkspaceView` around exactly those, so `web/panels.ts` and `web/layout.ts` are the screen's own contents with the wrapper removed. ## What moved | Was | Is now | | ------------------------------------------ | ------------------- | | `src/servers/sidecar/photos/*` (5 files) | `sidecar/*` | | `src/servers/api/photos/router.ts` | `api/router.ts` | | `src/databases/officer_db/src/photos/*` | `db/*` | | `src/workspaces/officerdev/src/apps/Photos/*` (25 files) | `web/*` | | `src/apps/officer-web/Screens/Dashboard/Photos/*` | `web/layout.ts` | The prefix in `api/router.ts` is now DERIVED from `mountPrefix` rather than the literal `/api/photos`, which was right only by coincidence — see the comment there, and `plugins/music/api/router.ts` for the same fix and the same reasoning. ## What the platform lost Deleted, not commented: the sidecar, the api router, the db directory, the Photos app, the screen, the `App.tsx` routes, the Dashboard barrel line, the `usePageTitle` rule, the `AppRegistry` wiring, the `officerdev` barrel re-exports, the permission registry entry, both database barrels, the `hono.ts` comment, and the `app-store/catalogue.ts` entry. **The catalogue deletion is required, not tidying** — the trap the runbook records as having bitten twice. `permissionAvailability()` derives from `sidecar_installs`, which a plugin never gets a row in, so a leftover entry makes the permission permanently `unavailable`, puts `/photos` into `deniedRoutes` and withholds the dock tile, on a server where the plugin is installed and working. ## Two things paid down on the way **Palette classes.** The runbook says to pay these down as part of the extraction rather than exporting colour debt into a repository nobody revisits, and it puts Photos at 47. It was **6** — someone had already done most of it. All six were state colours wearing palette numbers (`text-emerald-400` for the app's own tile, `text-emerald-500`/`text-amber-500` on a connection check) and are now `success` and `warning` from the closed tone set, so they follow the theme. **Test fixtures.** Photos was the canonical app-store fixture across four test files, chosen for being `existing|provisioned` with `members: 'accounts'`. Repointed at `jellyfin`, which has the identical shape, rather than deleted — the assertions are about the installer, not about photos. ## Still open - **Immich is still provisioned by the app store**, not by this plugin. `modes: ['existing', 'provisioned']` and a compose template lived in the catalogue entry that had to be deleted, so the `provisioned` half has no home right now: you point it at an instance you already run. Docker provisioning for plugins is a design still in progress, and this is one of the features waiting for it. - **No `assets/icon.png`.** Falls back to the `Images` glyph, which is what the catalogue entry used.