add photos, an immich-backed library behind its own sidecar

officer-photos owns the whole Immich contract: the instance URL and the API
key live there and nowhere else, and the platform side is an auth-gated
forwarder holding no credentials. The route surface is an allow-list keyed on
the first path segment, so admin, auth, api-keys, sessions, jobs, system-config
and libraries are unreachable by construction rather than by enumeration.

The UI mirrors Immich's own sidebar — timeline, explore, map, search, albums,
people, favorites, sharing, archive, trash — because the point of a sidecar
screen is to reproduce what the upstream already ships, then extend it. The
timeline reads Immich's columnar time-bucket format directly; selection lives
in the URL per docs/navigation-audit.md.

Two things worth knowing for anyone touching this later:

- `duration` is an integer count of milliseconds in Immich 3.0. It was an
  HH:MM:SS.mmm string before, and every stale example still shows that form.
- the map container is sized with h-full/w-full, never `absolute inset-0`.
  maplibre's stylesheet sets `position: relative; overflow: hidden` on the
  element it is given, and an unlayered vendor rule beats Tailwind 4's layered
  `.absolute` regardless of source order — so the div collapses to height 0 and
  clips its own canvas away. Nothing errors: the GL context is healthy, tiles
  download and pixels are drawn into a buffer nobody ever composites.

maplibre-gl is pinned to 5.x deliberately; 6.0 resolves a separate worker file
from import.meta.url, which Officer's index.html fallback answers with HTML.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-03 00:18:33 +00:00
co-authored by Claude Opus 5
parent 4f4e0c5dbc
commit 035a1ba8f6
42 changed files with 3429 additions and 1 deletions
+7
View File
@@ -88,6 +88,13 @@ module.exports = {
args: 'run src/servers/sidecar/invoiceshelf/index.ts',
watch: false,
},
// The photo library. Wraps a self-hosted Immich and holds its API key; the platform sees none of it.
{
name: 'officer-photos',
script: 'bun',
args: 'run src/servers/sidecar/photos/index.ts',
watch: false,
},
// The bitcoin wallet. Holds seed material (sealed under an owner passphrase) and node credentials, so
// it is the one sidecar whose restart has a security-relevant side effect: every wallet relocks.
// The one place anything leaves this machine to tell the owner something: push (APNs + FCM) and the