Skip to main content
Glama

imogen 是一个面向家庭实验室的自托管照片和视频库。它把你的照片保存在你掌控的硬件上,并向你想构建的任何东西开放:Web 界面、REST API、用于移动应用的 TypeScript SDK,以及一个 MCP 端点,让你的 AI 助手也能搜索这个照片库。

  • 对齐时间线 —— 照片保持拍摄时的宽高比,按天分组

  • 相机产出的一切 —— HEIC、RAW、JPEG、视频、Live Photos

  • 可安装 —— Web 界面是一个 PWA,可离线工作

  • 两种登录方式 —— 本地账户,或通过 Authentik、Keycloak、Google 或其他任何支持 OIDC 的服务进行单点登录

  • 为扩展而生 —— OpenAPI、SDK 和 OAuth 2.1 服务器,让第三方应用成为头等公民,而不是事后补充

  • 人物 —— 可选的人脸分组,完全运行在你自己的服务器上

  • 保险库 —— 需要口令才能查看的照片,对其他一切隐藏

  • 共享 —— 将相册或单张照片发布为链接,可选密码、过期日期,以及下载开关

  • 管理 —— 邀请用户、冻结账户、查看处理队列、断开应用连接,并查看当前所有公开内容

  • 智能体就绪 —— 用一个 URL 将 Claude 或 Grok 连接到你的照片库


运行

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

打开 http://localhost:3000。你创建的第一个账户将成为管理员。

这就是全部安装过程。imogen 需要 Postgres,compose 文件会启动一个;没有消息代理,没有缓存,也没有需要运行的 sidecar。

配置

所有内容都是环境变量,并在启动时校验——服务器在配置错误时会拒绝启动,而不是在负载下才失败。

变量

默认值

作用

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

同时处理的照片数量。在有空闲核心的机器上可以调高。

管理

创建的第一个账户将成为管理员。它的设置页面有一个指向 /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

人物

imogen 可以识别人脸,并按每个人出现的照片进行分组,这样你只需命名一次,就可以浏览包含该人的所有照片。检测和识别都在你的服务器上运行;不会将任何照片发送到任何地方。

默认情况下,它是关闭的,直到你打开它——在“人物”页面开启。启用时需要下载约 190 MB 的识别模型,并在后台扫描你现有的照片库。

  • 保险库中的照片永远不会被扫描,将照片移入保险库会忘记其中已找到的人脸。

  • 在你命名之前,没有人有名字。未命名的分组会显示出来,以便你为它们命名;而且无论是分组还是人,都可以隐藏。

  • 分组时宁愿将同一个人拆到两个组,也不愿将两个人合并到一个组。选择多个组并告诉它这些是同一个人。

关于模型的说明。 imogen 使用 InsightFace 的 SCRFD 和 ArcFace,它们的许可证允许非商业研究用途。imogen 不附带任何模型:你的服务器在启用该功能时下载它们,因此是否接受该许可由你自己决定。如果这不适合你的情况,请关闭该功能。


保险库

有些照片不应该在一次不小心的滚动中就被看到。将它们移入保险库,它们就完全离开照片库:不在时间线中,不在搜索中,不在你的相册中,不在共享链接中,也不在任何 AI 助手能看到的内容中。

打开它需要口令,即使你已经登录,也要再次输入。

有几个设计决策值得了解:

  • 口令不是你的账户密码。 单点登录账户没有本地密码,更重要的是,一个已经登录的会话不应该足够——发现你的笔记本电脑开着,不应该也能打开这个。

  • 只有浏览器会话可以打开它。 API 令牌或 MCP 连接器可以持有完全有效的凭据,但仍然无法进入。这是有意设计的,不是疏漏。

  • 它会自动关闭,十五分钟之后,或在你要求时立即关闭。

  • 没有人能为你重置它。 没有恢复途径,这正是关键所在。

将照片移入保险库也会将其从所有相册中移除,因为相册是可共享的。


连接 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" } } }

助手可以做什么

工具

权限

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,客户端无需保留自己的记录即可知道已发送的内容。

配对,而不是输入主机名

上述流程仍然需要应用知道要连接哪台服务器,而自托管的照片库位于其所有者选择的任何地址。在手机键盘上输入这个地址是安装这类应用时最糟糕的时刻,因此改为由浏览器来完成。

设置 → 设备配对设备 会生成一次性票据,并将其呈现为二维码,二维码中同时包含服务器 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 挑战,因此拍下某人屏幕的照片是不够的。最终获得的授权是普通授权,并像其他任何授权一样出现在已连接应用中。

同一个页面还会将票据以链接形式提供,供已经在使用 Web 界面的手机使用:点按该链接会直接打开应用。


开发

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 服务器运行,而不是使用 mock。那些值得做对的部分——OAuth 流程、游标分页、媒体处理管道——恰恰是使用 mock 时会让你做错的部分。

项目结构

作用

packages/server

Hono 应用:路由、认证、媒体处理管道、任务 worker。

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 是一个范围可控的改动。

人脸分组功能可用,但有一些值得了解的粗糙之处。它能很好地识别正面的、光线适当的人脸;侧面、墨镜、运动模糊和幼儿——其面部变化速度快于存储的平均面容所能追踪的速度——则是它会让你失望的地方。它宁可把同一个人拆到两个组里,也不愿把两个人合并,理由是前者只需点击一下即可修复,而后者会把某人的照片归档到另一个人的名下。

许可证

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