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

4.3 KiB

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.