Files
photos/PLUGIN.md
T
pastilhasandClaude Opus 5 2dad1907f8 photos, extracted from the platform into its own repository
The fifth plugin out, and the cheapest so far — offscale's shape rather than
music's or vault's. Nothing stayed behind and nothing had to be redesigned,
for three structural reasons recorded in PLUGIN.md:

  • No reverse dependency. Nothing in src/ imported anything of photos'.
  • Configuration was already right. The URL and API key live in photos_config,
    a per-user registry encrypted at rest and 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.
  • The frontend was already panels. The screen was a WorkspaceView around
    exactly photos-nav and photos-view, so web/panels.ts and web/layout.ts are
    the screen's contents with the wrapper removed.

The router's prefix is now derived from mountPrefix rather than the literal
'/api/photos', which was correct only by coincidence — a third-party publisher
mounts at /api/p/<publisher>/photos and the literal would forward the wrong
path to the sidecar.

Palette debt paid down as the runbook requires: 6 classes (it says 47; someone
had already done most of it), all of them state colours wearing palette
numbers, now `success` and `warning` from the closed tone set so they follow
the theme.

Immich itself is still provisioned by the app store rather than by this plugin
— the `provisioned` half of its old catalogue entry has no home until Docker
provisioning for plugins exists. Point it at an instance you already run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 19:10:39 +00:00

70 lines
4.3 KiB
Markdown

# 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.