Skip to main content
Glama

imogen — это самостоятельно размещаемая библиотека фотографий и видео для домашнего сервера. Она хранит ваши фотографии на оборудовании, которым вы управляете, и открывает их для всего, что вы захотите построить: веб-интерфейса, REST API, TypeScript SDK для мобильного приложения и MCP-эндпоинта, чтобы ваши ИИ-ассистенты тоже могли искать в библиотеке.

  • Выровненная лента — фотографии сохраняют пропорции съёмки и сгруппированы по дням

  • Всё, что выдаёт камера — HEIC, RAW, JPEG, видео, Live Photos

  • Устанавливается — веб-интерфейс является PWA и работает офлайн

  • Два способа войти — локальные учётные записи или единый вход через Authentik, Keycloak, Google или любую другую систему, поддерживающую OIDC

  • Основа для разработки — OpenAPI, SDK и OAuth 2.1-сервер, так что стороннее приложение — полноценный участник, а не второстепенная надстройка

  • Люди — опциональная группировка лиц, полностью работающая на вашем сервере

  • Хранилище — фотографии, для просмотра которых нужна кодовая фраза и которые скрыты от всего остального

  • Общий доступ — публикация альбома или отдельной фотографии по ссылке, с необязательным паролем, сроком действия и включением или отключением скачивания

  • Администрирование — приглашайте людей, приостанавливайте учётные записи, следите за очередью обработки, отключайте приложения и видите всё, что сейчас публично

  • Готовность к ИИ-агентам — подключите Claude или Grok к своей библиотеке по URL


Запуск

curl -O https://raw.githubusercontent.com/ergofobe/imogen-server/main/docker-compose.yml
docker compose up -d

Откройте http://localhost:3000. Первая созданная вами учётная запись становится администратором.

На этом установка заканчивается. imogen требует Postgres, и compose-файл поднимает его; не нужны ни брокер сообщений, ни кэш, ни дополнительный сервис.

Конфигурация

Всё настраивается переменными окружения, проверяемыми при запуске: сервер отказывается стартовать при неверной конфигурации, а не падает позже под нагрузкой.

Переменная

По умолчанию

Что делает

IMOGEN_PUBLIC_URL

http://localhost:3000

URL, по которому пользователи обращаются к imogen. На его основе формируются OAuth-ссылки и ссылки для общего доступа, поэтому за обратным прокси он должен быть указан верно.

DATABASE_URL

—

Строка подключения к Postgres. Обязательна.

IMOGEN_DATA_DIR

/data

Директория, где хранятся фотографии. Делайте резервные копии.

IMOGEN_SECRET

генерируется

Подписывает сессии. Если не задан, генерируется и сохраняется при первом запуске.

IMOGEN_ALLOW_SIGNUP

true

Разрешает ли кто угодно создавать учётную запись. Первая учётная запись допускается всегда. Это лишь начальное значение — администратор может изменить его в приложении, и его настройка имеет приоритет.

IMOGEN_TRASH_RETENTION_DAYS

30

Как долго удалённые фотографии можно восстановить. Также начальное значение, которое администратор может изменить.

IMOGEN_JOB_CONCURRENCY

4

Сколько фотографий обрабатывается одновременно. Увеличьте на машине со свободными ядрами.

Администрирование

Первая созданная учётная запись становится администратором. На её странице «Настройки» есть ссылка на /admin, где управляются учётные записи, приглашения, очередь обработки, подключённые приложения, хранилище и ссылки для общего доступа.

Раздел не просто закрыт для остальных — он отвечает обычной 404, точно такой же, как для несуществующего пути, так что его невозможно найти, даже если специально искать. Всё, что сканирует в поисках панели администрирования, не получает никакой информации.

Чтобы добавить кого-то на закрытый сервер, создайте приглашение и отправьте человеку ссылку. Ссылка показывается один раз и хранится только в виде хэша; если она потеряна, отзовите её и создайте новую.

Единый вход

Подключите imogen к любому OIDC-провайдеру. Укажите у провайдера redirect URI: https://photos.example.com/api/v1/auth/oidc/callback.

IMOGEN_OIDC_ISSUER: https://auth.example.com/application/o/imogen/
IMOGEN_OIDC_CLIENT_ID: ...
IMOGEN_OIDC_CLIENT_SECRET: ...
IMOGEN_OIDC_LABEL: Sign in with Authentik
IMOGEN_OIDC_ADMIN_VALUE: imogen-admins   # members of this group become administrators
IMOGEN_OIDC_ACCOUNT_URL: ''              # optional; guessed for Authentik and Keycloak

Существующие локальные учётные записи связываются по подтверждённому адресу электронной почты, поэтому включение SSO никого не оставит без доступа.

Провайдер владеет именем и адресом электронной почты учётных записей, которыми управляет: imogen перечитывает их при каждом входе, показывает в настройках в режиме только для чтения и даёт ссылку на страницу учётной записи у провайдера. Задайте IMOGEN_OIDC_ACCOUNT_URL, если эта ссылка должна вести не туда, куда предполагает imogen.

Когда переменная IMOGEN_OIDC_ADMIN_VALUE задана, статус администратора определяется её значением — в том числе снимается, когда человек покидает группу. Если оставить её незаданной, imogen не вмешивается в роли, и администратор, назначенный локально, остаётся администратором.

За обратным прокси

imogen отдаёт обычный HTTP и предполагает, что за ним находится прокси, завершающий TLS. Передавайте X-Forwarded-For, чтобы сессии записывали корректный адрес, разрешите большие тела запросов для загрузки видео и задайте IMOGEN_PUBLIC_URL равным внешнему URL.

photos.example.com {
    reverse_proxy localhost:3000
    request_body { max_size 8GB }
}

Related MCP server: Google Photos MCP

Люди

imogen умеет находить лица и группировать фотографии, на которых есть каждый человек, так что вы можете один раз назвать человека и затем просматривать все снимки с ним. Детекция и распознавание выполняются на вашем сервере; ни одна фотография никуда не отправляется.

Функция выключена, пока вы её не включите, на странице «Люди». При включении загружается около 190 МБ моделей распознавания и в фоновом режиме сканируется существующая библиотека.

  • Фотографии в хранилище не сканируются никогда, а при переносе фотографии в хранилище уже найденные на ней лица забываются.

  • Никто не получает имени, пока вы его не дадите. Безымянные группы показываются, чтобы вы могли дать им имена, и те и другие можно скрыть.

  • Группировка скорее разделит одного человека на две группы, чем объединит двух людей в одну. Выделите несколько групп и укажите, что это один и тот же человек.

Примечание о моделях. imogen использует SCRFD и ArcFace из InsightFace, которые лицензированы для некоммерческих исследовательских целей. imogen не поставляет модели: ваш сервер загружает их при включении функции, поэтому решение о лицензии остаётся за вами. Если это не подходит вашей ситуации, оставьте функцию выключенной.

Хранилище

Некоторые фотографии не должны быть в одном неосторожном скролле от посторонних глаз. Перенесите их в хранилище, и они полностью исчезнут из библиотеки: их не будет ни в ленте, ни в поиске, ни в ваших альбомах, ни в опубликованной ссылке, ни в том, что может увидеть ИИ-ассистент.

Чтобы открыть хранилище, нужна кодовая фраза, которую нужно ввести снова, даже если вы уже вошли в систему.

Несколько решений, о которых стоит знать:

  • Кодовая фраза — это не пароль вашей учётной записи. Учётные записи единого входа не имеют локального пароля, и, что важнее, уже выполненного входа недостаточно: если ваш ноутбук открыт, это не должно открывать и хранилище.

  • Открыть его может только браузерная сессия. API-токен или MCP-коннектор могут обладать совершенно действительными учётными данными, но у них нет способа попасть внутрь. Это сделано намеренно, а не по недосмотру.

  • Оно закрывается само через пятнадцать минут или немедленно, когда вы попросите.

  • Никто не сможет сбросить его за вас. Пути восстановления не существует — в этом и суть.

Перемещение фотографии в хранилище также удаляет её из всех альбомов, ведь альбомом можно поделиться.

Подключение ИИ-ассистента

imogen говорит на MCP, поэтому ассистент может искать по вашей библиотеке, просматривать фотографии и управлять альбомами — с вашего разрешения и не более того.

Claude.ai или Grok: добавьте коннектор, указывающий на https://photos.example.com/mcp. Ничего не нужно вводить вручную: клиент сам обнаруживает imogen, регистрируется и отправляет вас на экран согласия, где точно указано, на что именно он запрашивает разрешение. Отозвать его можно в любой момент в настройках.

Локальный агент (Claude Code или любое другое решение, работающее с MCP через stdio):

bun add -g @imogen/mcp
imogen-mcp login --server https://photos.example.com
{ "mcpServers": { "imogen": { "command": "imogen-mcp" } } }

Что умеет ассистент

Инструмент

Разрешение

search_photos · get_photo · get_photo_image · get_library_stats

library:read

list_albums · get_album

albums:read

create_album · add_to_album

albums:write

search_by_person · list_people

library:read

Каждый инструмент ограничен рамками подключённой учётной записи. Нет ни одного инструмента, который что-либо удаляет, и ничто из хранилища не видно ни одному из них. Найти можно только людей, которым вы дали имя, — безымянные группы и скрытые люди недоступны.

Создание на его основе

Документация по API доступна на /api/v1/docs, описание в формате OpenAPI 3.1 — на /api/v1/openapi.json.

В imogen-sdk есть клиенты для пяти языков — TypeScript, Rust, Python, Swift и Kotlin. На TypeScript:

bun add @imogen/sdk
import { ImogenClient } from '@imogen/sdk'

const imogen = new ImogenClient({ baseUrl: 'https://photos.example.com', token })

const page = await imogen.assets.list({ q: 'harbour', limit: 50 })
for await (const asset of imogen.assets.iterate()) console.log(asset.originalFilename)

// Picks its protocol by size: one request for photos, a resumable session for video.
await imogen.assets.uploadMany(files, {
  onFileComplete: (outcome, done, total) => console.log(`${done}/${total}`),
})

Создание мобильного приложения

Каждый SDK включает OAuth-клиент, необходимый нативному приложению: Swift- и Kotlin-версии существуют именно для этого. Ничего не зашито жёстко: приложение регистрируется само, поэтому работает с любым imogen-сервером, который укажет пользователь.

import { OAuthClient } from '@imogen/sdk'

const oauth = new OAuthClient('https://photos.example.com')
const client = await oauth.register('My Photo App', ['myapp://oauth'])
const pending = await oauth.beginAuthorization(client.client_id, 'myapp://oauth')

// Open pending.authorizationUrl in the system browser, then on the callback:
const tokens = await oauth.completeAuthorization(pending, callbackUrl)

Загрузка идемпотентна по содержимому: повторная отправка фотографии, которая уже есть на сервере, возвращает существующий ресурс вместо сохранения второй копии, поэтому цикл синхронизации может быть простым и при этом корректным. Передайте deviceAssetId, и клиент будет знать, что уже отправлено, не ведя собственного журнала.

Сопряжение вместо запроса адреса сервера

Описанный выше процесс по-прежнему требует, чтобы приложение знало, к какому серверу обращаться, а самостоятельно размещённая библиотека находится по адресу, который выбрал её владелец. Вводить этот адрес на телефонной клавиатуре — худший момент при установке такого приложения, поэтому это делает браузер.

Настройки → Устройства → Сопрячь устройство — здесь создаётся одноразовый билет и отображается в виде QR-кода, который содержит и URL сервера, и код. Приложение считывает квадрат и делает остальное:

val invitation = parsePairingUri(scanned) ?: return
val oauth = OAuthClient(invitation.serverUrl)
val paired = oauth.pair(invitation.code, "imogen for Android", "imogen://oauth", Build.MODEL)

Через камеру передаётся билет, а не токен. Он одноразовый, живёт пять минут и даёт ровно один код авторизации, привязанный к PKCE-челленджу, который никогда не покидал устройство, поэтому фотографии чужого экрана недостаточно. Полученный грант — обычный и отображается среди подключённых приложений, как и любой другой.

На той же странице билет доступен и в виде ссылки — для телефона, уже открывшего веб-интерфейс: по нажатию на неё сразу открывается приложение.

Разработка

git clone https://github.com/ergofobe/imogen-server
cd imogen-server
bun install

docker compose -f docker/compose.dev.yml up -d     # Postgres
export DATABASE_URL='postgres://imogen:imogen@localhost:5432/imogen'
bun run db:migrate

bun run dev        # API on :3000
bun run dev:web    # web on :5173, proxying to the API
bun test          # needs the dev Postgres running
bun run typecheck
bun run lint

Тесты выполняются против реального Postgres и реального HTTP-сервера, а не моков. Части, которые важно сделать правильно, — потоки OAuth, курсорная пагинация, конвейер обработки медиа — это как раз те части, в которых моки позволили бы ошибиться.

Структура

Пакет

Назначение

packages/server

Приложение на Hono: маршруты, аутентификация, конвейер обработки медиа, воркеры задач.

packages/web

React-PWA. Оно использует @imogen/sdk как любой сторонний клиент, благодаря чему SDK проверяется на практике.

packages/mcp

Мост через stdio для локальных агентов.

Клиентские библиотеки живут в собственном репозитории, imogen-sdk — TypeScript, Rust, Python, Swift и Kotlin, проверяемые по одному общему набору контрактных фикстур. @imogen/shared, Zod-схемы, по которым этот сервер производит валидацию и из которых генерирует свой документ OpenAPI, тоже живут там: это API-контракт, и контракту место вместе с клиентами, которые обязаны ему следовать.

packages/server/src/api/sdk-contract.test.ts — это сторона такой договорённости. Он поднимает настоящее приложение и прогоняет его через опубликованный TypeScript-клиент; это единственное место, где можно показать, что две половины согласуются.

Дизайн-документ находится в docs/superpowers/specs.


Пока не реализовано

Семантический поиск — поиск фотографии по её описанию — не реализован. Схема резервирует векторный столбец для активов, а поисковый индекс уже на месте, так что эта возможность может появиться без миграции, но сегодня поиск охватывает имена файлов, описания, места, метаданные камеры и людей, которым вы дали имена.

Также отсутствуют: обратное геокодирование (координаты показываются как координаты), транскодирование видео и хранилище S3. Драйвер хранилища — это интерфейс, так что добавление S3 станет локальным изменением, когда оно кому-то понадобится.

Группировка лиц работает, но имеет шероховатости, о которых стоит знать. Она хорошо распознаёт фронтальные, достаточно освещённые лица; профили, солнцезащитные очки, смазанность из-за движения и маленькие дети — чьи лица меняются быстрее, чем может уследить сохранённый усреднённый шаблон, — вот где она вас разочарует. Она скорее ошибается в сторону разделения одного человека на две группы, чем слияния двух людей, исходя из того, что первое исправляется одним кликом, а второе подшивает чьи-то фотографии под именем другого человека.

Лицензия

AGPL-3.0-or-later. Если вы запускаете модифицированный imogen как сервис, поделитесь изменениями.

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    D
    maintenance
    An MCP server for Immich self-hosted photo management that provides AI-accessible tools for browsing, searching, organizing, and managing photo libraries with duplicate detection and safe deletion workflows.
    43
    1
    -
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to work with a user's Google Photos through Google's picker, letting users choose exactly what the agent sees while providing tools for uploading media, creating and sharing albums, writing captions and enrichments, searching app-created media, and managing multiple Google accounts.
    29
    44 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to search, list, preview, and export photos from Apple Photos, Google Photos Picker sessions, and local Google Takeout archives using native search and metadata inspection.
    2 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP clients to search, filter, and manage a private local photo library with CLIP-based text and similarity search, EXIF/GPS metadata, duplicate detection, albums, and folder indexing, returning ranked results and optional contact sheets.
    MIT