Skip to main content
Glama

imogen は、ホームラボ向けのセルフホスト型写真・ビデオライブラリです。あなたが管理するハードウェアに写真を保存し、構築したいあらゆるものに写真を公開できます。ウェブインターフェース、REST API、モバイルアプリ向けの TypeScript SDK、そして AI アシスタントがライブラリを検索できるようにする MCP エンドポイントなどです。

  • ジャスティファイドタイムライン — 写真は撮影時の縦横比を保ったまま、日付ごとにグループ表示

  • カメラが生み出すすべてに対応 — HEIC、RAW、JPEG、ビデオ、Live Photos

  • インストール可能 — ウェブインターフェースは PWA で、オフラインでも動作

  • 2 種類のサインイン — ローカルアカウント、または Authentik、Keycloak、Google、その他 OIDC に対応したあらゆるサービスによるシングルサインオン

  • その上に構築できる設計 — OpenAPI、SDK、OAuth 2.1 サーバーを備え、サードパーティ製アプリが後付けではなく第一級の市民として扱われる

  • People(人物) — オプションの顔グループ化。すべて自分のサーバー上で実行

  • ヴォールト(金庫) — 閲覧にパスフレーズが必要な写真を、他のすべてから隠す

  • 共有 — アルバムや写真 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 ファイルが Postgres を起動します。メッセージブローカーもキャッシュも、実行するサイドカーもありません。

設定

すべては環境変数で設定し、起動時に検証されます。サーバーは不適切な設定では起動を拒否し、後で負荷がかかってから失敗することはありません。

Variable

Default

What it does

IMOGEN_PUBLIC_URL

http://localhost:3000

ユーザーが imogen にアクセスする URL。OAuth と共有リンクはここから生成されるため、リバースプロキシの背後では正しい値を設定する必要があります。

DATABASE_URL

Postgres 接続文字列。必須。

IMOGEN_DATA_DIR

/data

写真の保存場所。バックアップしてください。

IMOGEN_SECRET

generated

セッションの署名に使用。未設定の場合、初回起動時に生成され永続化されます。

IMOGEN_ALLOW_SIGNUP

true

誰でもアカウントを作成できるかどうか。最初のアカウントは常に許可されます。これは初期値にすぎません。管理者はアプリ内で変更でき、管理者が設定した値が優先されます。

IMOGEN_TRASH_RETENTION_DAYS

30

削除した写真を復元できる期間。これも管理者が変更できる初期値です。

IMOGEN_JOB_CONCURRENCY

4

同時に処理する写真の数。余裕のあるコアがあるマシンでは増やせます。

管理

最初に作成されたアカウントが管理者になります。その Settings(設定)ページには /admin へのリンクがあり、アカウント、招待、処理キュー、接続済みアプリケーション、ストレージ、共有リンクを管理できます。

この領域は単に他のユーザーに閉じられているだけではありません。サーバーが未知のパスに対して返すのと同じ、素の 404 を返します。そのため、探しても見つけることができません。管理パネルをスキャンするどんなツールにも、何も情報は渡りません。

閉じたサーバーに誰かを追加するには、招待を作成してリンクを送ります。リンクは一度だけ表示され、ハッシュとしてのみ保存されます。リンクを失くした場合は、それを失効させて新しいリンクを作成してください。

シングルサインオン

imogen を任意の OIDC プロバイダーに向けます。プロバイダー側のリダイレクト 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_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: immich-mcp

People(人物)

imogen は顔を検出し、各人物が写っている写真をグループ化できます。一度名前を付ければ、その人物が写っているすべての写真を閲覧できます。検出と認識は自分のサーバー上で実行され、写真が外部に送信されることはありません。

この機能は People(人物)ページで有効にするまで オフ のままです。有効にすると、約 190 MB の認識モデルをダウンロードし、バックグラウンドで既存ライブラリをスキャンします。

  • ヴォールト内の写真はスキャンされず、写真をヴォールトに入れると、その写真で見つかった顔は忘れられます。

  • あなたが名前を付けるまで、誰にも名前は付きません。名前のないグループは名前を付けられるように表示され、名前の付いた・付いていないどちらのグループも非表示にできます。

  • グループ化は、2 人の人物を 1 つにまとめるよりも、1 人の人物を 2 つのグループに分ける方向に倒れます。複数選択して「同じ人物」と指定できます。

モデルに関する注意。 imogen は InsightFace の SCRFD と ArcFace を使用しています。これらは非営利の研究用途向けにライセンスされています。imogen はモデルを同梱しません。機能を有効にするとサーバーがモデルをダウンロードするため、ライセンスの判断はあなた自身に委ねられます。状況に合わない場合は、この機能をオフのままにしてください。


ヴォールト

うっかりスクロールしただけで目に入ってほしくない写真もあります。それらをヴォールトに移動すると、ライブラリから完全に消えます。タイムラインにも、検索にも、アルバムにも、共有リンクにも、AI アシスタントが見られるものにも表示されません。

開くにはパスフレーズが必要で、すでにサインインしていても再度入力します。

知っておくべきいくつかの設計判断:

  • パスフレーズはアカウントのパスワードではありません。 シングルサインオンのアカウントにはローカルパスワードがありません。そもそも、サインイン済みのセッションだけでは不十分であるべきです。ノートパソコンが開いたまま見つかっても、これまで開かれては困ります。

  • 開けるのはブラウザセッションだけです。 API トークンや MCP コネクタは完全に有効な資格情報を持っていても、入る手段がありません。これは仕様によるもので、漏れではありません。

  • 15 分後に自動で閉じます。または、あなたが求めればすぐに閉じます。

  • 誰もあなたの代わりにリセットできません。 復旧経路はなく、それが意図するところです。

写真をヴォールトに移動すると、すべてのアルバムからも削除されます。アルバムは共有できるものだからです。


AI アシスタントを接続する

imogen は MCP に対応しているため、アシスタントはあなたのライブラリの検索、写真の閲覧、アルバムの管理ができます。ただし、あなたが許可した場合に限り、それ以外のことはできません。

Claude.ai または Grok: https://photos.example.com/mcp を指すコネクタを追加してください。手作業で貼り付けるものは何もありません。クライアントが imogen を発見し、自分自身を登録し、何を求めているかを正確に示す同意画面へあなたを送ります。設定からいつでも取り消せます。

ローカルエージェント(Claude Code、または stdio 経由で MCP に対応するもの):

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

アシスタントができること

Tool

Permission

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 には 5 言語のクライアントがあります。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)

アップロードはコンテンツ単位で冪等です。サーバーがすでに持っている写真を再送信すると、2 番目のコピーを保存する代わりに既存のアセットを返します。そのため、同期ループはシンプルでありながら正しく動作します。deviceAssetId を渡せば、クライアントは独自の台帳を保持しなくても、送信済みの内容を把握できます。

ホスト名を尋ねる代わりのペアリング

上記のフローでも、アプリはどのサーバーと通信するかを知る必要があります。セルフホストのライブラリは、所有者が選んだ任意のアドレスにあります。それをスマートフォンのキーボードで打ち込むのは、こうしたアプリをインストールする上で最悪の瞬間なので、代わりにブラウザが行います。

Settings(設定)→ Devices(デバイス)Pair a device(デバイスをペアリング) で、一回限りのチケットが作成され、サーバー URL とコードの両方を含む QR コードとして表示されます。アプリがその正方形を読み取り、残りを実行します:

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

カメラを通過するのはトークンではなくチケットです。一回限りで、有効期限は 5 分間、得られるのは 1 つの認可コードだけです。そのコードはデバイスから出たことのない 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 フロー、カーソルページネーション、メディアパイプライン)こそ、モックだと間違えやすい部分です。

構成

Package

What it is

packages/server

Hono アプリ: ルート、認証、メディアパイプライン、ジョブワーカー。

packages/web

React PWA。サードパーティクライアントと同様に @imogen/sdk を利用しており、SDK の品質を保っています。

packages/mcp

ローカルエージェント向けの stdio ブリッジ。

クライアントライブラリは専用のリポジトリ、imogen-sdk にあります。TypeScript、Rust、Python、Swift、Kotlin の各実装は、共通のコントラクトフィクスチャ一式に対してチェックされます。@imogen/shared — このサーバーが検証に使用し、OpenAPI ドキュメントの生成元となる Zod スキーマ — もそこに置かれています。それが API コントラクトであり、コントラクトはそれに従わなければならないクライアントと共にあるのです。

packages/server/src/api/sdk-contract.test.ts は、この取り決めのこちら側です。実際のアプリを起動し、公開済みの TypeScript クライアント経由でそれを操作します。両者が一致することを示せるのは、この場所だけです。

設計ドキュメントは docs/superpowers/specs にあります。


まだ実装されていない機能

セマンティック検索(写真を言葉で説明して見つける機能)は実装されていません。スキーマにはアセット用のベクター列が確保され、検索インデックスも配置済みなので、マイグレーションなしで追加できます。ただし、現時点の検索が対象とするのは、ファイル名、説明、場所、カメラのメタデータ、そしてあなたが名前を付けた人物だけです。

ほかにも、逆ジオコーディング(座標は座標として表示されます)、動画のトランスコード、S3 ストレージがありません。ストレージドライバーはインターフェースなので、S3 が必要になった場合の変更は影響が限定的です。

顔のグループ化は機能しますが、知っておくべき荒削りな点があります。正面を向き、適度な照明が当たった顔はうまく読み取れます。横顔、サングラス、モーションブラー、そして幼い子ども(顔の変化が保存済みの平均値の追従よりも速い)は、期待を裏切る場面です。この機能は、2人の人物を1つのグループにまとめる方向ではなく、1人の人物を2つのグループに分ける方向に誤ります。前者はクリック1つで修正できますが、後者は誰かの写真を別の人の名前の下に収めてしまうからです。

ライセンス

AGPL-3.0-or-later。改変した imogen をサービスとして運用する場合は、その改変を共有してください。

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ergofobe/imogen-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server