关于 LockIn
LockIn 是面向 Claude、Codex、Cursor 等编码 Agent 的笔记本,可保存按主题整理的笔记,让不同会话接续工作。用户也可向笔记提问,答案会引用来源页面;项目支持自托管并导出 Markdown 或 JSON。
主要功能
- 01Agent 保存、搜索和读取笔记
- 02按主题整理 Markdown、PDF 和链接
- 03向笔记提问并引用来源页面
- 04提供任务、专注时间和复习计划
- 05支持自托管并导出 Markdown 或 JSON
适用场景
- 01在不同编码 Agent 会话间交接项目背景
- 02保存代码相关发现并按主题归档
- 03从笔记中检索信息并核对引用页面
- 04安排学习任务和间隔复习
MCP 相关
LockIn 在 `/api/mcp` 提供 Streamable HTTP MCP 服务,供客户端保存、搜索和读取笔记。用户可在设置中创建 Agent 令牌,并按界面提供的配置连接客户端。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://lockin.namankhandelwal.dev/api/mcpclaude mcp add --transport http sky-walkerx-lockin https://lockin.namankhandelwal.dev/api/mcp{"mcpServers":{"sky-walkerx-lockin":{"type":"http","url":"https://lockin.namankhandelwal.dev/api/mcp"}}}在 Cursor 中打开{"servers":{"sky-walkerx-lockin":{"type":"http","url":"https://lockin.namankhandelwal.dev/api/mcp"}}}在 VS Code 中打开{"mcpServers":{"sky-walkerx-lockin":{"type":"http","url":"https://lockin.namankhandelwal.dev/api/mcp"}}}{"mcpServers":{"sky-walkerx-lockin":{"serverUrl":"https://lockin.namankhandelwal.dev/api/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
详细介绍
你每天都会在 Claude、Codex 和 Cursor 中学到新东西,而大部分内容会在会话结束时消失。LockIn 会将它们保存下来。你的 Agent 通过 MCP 将笔记存入其中,你按主题归档,还可以选择任意模型来回答关于这些笔记的问题。
发布短片,时长 45 秒,含声音:
https://github.com/user-attachments/assets/3ae46572-283e-49fd-a3dc-e178dd269e40
它如何协同工作
周一:Claude Code 将笔记保存到 Project-X,你确认见证。周四:Codex 从上次中断处继续。
flowchart LR
cc["Claude Code"] -- save_note --> mcp
cx["Codex"] -- search_notes --> mcp
more["Cursor, Claude.ai, ChatGPT,<br/>any MCP client"] --> mcp
mcp(["LockIn's MCP server<br/>/api/mcp"]) --> nb[("Your notebook<br/>notes filed by subject")]
nb --> you["You<br/>witness, read, plan"]
nb --> ask["Ask<br/>a model you choose,<br/>answers cite your pages"]
包含的内容
|
Agent 在这里写入 Claude Code、Codex、Cursor 或任何 MCP 客户端都可以保存、搜索和阅读你的笔记。Agent 保存的所有内容都会注明记录者,并等待你确认。 |
向笔记提问 你可以在浏览器中(WebGPU)、自己的设备上(Ollama、LM Studio),或使用自己的密钥,通过 Anthropic、OpenAI、Gemini 或 OpenRouter 与大模型聊天。回答会引用相关笔记的页面。 |
|
按主题整理笔记 Markdown 笔记可包含代码,也可以将你正在阅读的 PDF 和链接归档到相应主题下。 |
专注阅读 打开任何笔记都可以全屏阅读。 |
|
按需制定计划 任务、专注时间、备考进度和间隔复习都位于各主题的“计划”选项卡中,不需要时不会打扰你。 |
归你所有 采用 AGPL-3.0 许可,可通过一条命令自行托管,所有内容都可导出为 Markdown 或 JSON。 |
自行托管
你需要安装 Docker 和 Compose。
git clone https://github.com/Sky-walkerX/LockIn.git && cd LockIn
cp .env.example .env
# set AUTH_SECRET in .env: openssl rand -base64 32
docker compose up -d
打开 http://localhost:3000 并创建账户。该端口仅在本机上接受连接;如需用于服务端,请参阅下文的 HTTPS。首次启动时会拉取已发布的镜像,等待 Postgres 就绪,并在应用启动前应用数据库迁移。镜像适用于 amd64 和 arm64;如果没有适用于你所用版本的镜像,Compose 会改为从检出目录构建。
运行的服务:
| 服务 | 功能 |
|---|---|
db | Postgres 17。数据存储在 db 卷中。 |
migrate | 应用迁移(prisma migrate deploy),然后退出。 |
app | 运行在 3000 端口上的 LockIn。笔记图片和 PDF 存储在 uploads 卷中。 |
ingest | 可选。提取 PDF 和网页文本,并在服务端生成嵌入向量。 |
caddy | 可选。在应用前端提供 HTTPS,详见下文。 |
要启用文本提取和服务端嵌入向量,请在 .env 中设置 INGEST_SECRET(任意较长的随机字符串)和 INGEST_URL=http://ingest:8080,然后运行:
docker compose --profile ingest up -d
ingest 镜像包含其嵌入模型,因此大小约为 1 GB。不使用它时,关键词搜索仍可正常工作,浏览器也会自行生成嵌入向量。它一次读取两份文档;处理时间超过 140 秒的文档会被停止。第三份文档会等待 20 秒以获取空位,之后会收到服务繁忙的提示。在每份文档额外占用一个核心和几百 MB 内存的机器上,可以通过 .env 中的 PARSE_CONCURRENCY 提高第一个数字。
要更新,请拉取新版。compose 文件指定了对应的版本,因此镜像始终与检出目录相匹配。启动时会运行迁移:
git pull && docker compose pull && docker compose up -d
要运行检出目录中的代码而不是已发布的版本,请进行构建:docker compose up -d --build。
docker compose ps 会显示应用是否健康;同样的检查也可通过 /api/health 进行(应用能够连接数据库时返回 200),可供正常运行时间监控工具使用。
备份
所有数据都存放在两个卷中:db(Postgres)和 uploads(笔记图片和 PDF)。请同时备份这两个卷,并在更新前备份。
# back up
docker compose exec -T db pg_dump -U lockin -Fc lockin > lockin.dump
docker compose cp app:/data/uploads ./uploads-backup
# restore, into a stack that is up
docker compose exec -T db pg_restore -U lockin -d lockin --clean --if-exists < lockin.dump
docker compose cp ./uploads-backup/. app:/data/uploads
docker compose exec -u root app chown -R lockin /data/uploads # copied files arrive owned by the host user
请将 AUTH_SECRET 与备份一同保存。恢复的副本如果使用了不同的密钥,会让所有人退出登录。
.env 中的设置:
| 变量 | |
|---|---|
AUTH_SECRET | 必填。用于签署会话和上传链接。 |
PUBLIC_URL | 用户访问 LockIn 时打开的地址。默认值为 http://localhost:3000。 |
LOCKIN_PORT | 主机端口。默认值为 3000。 |
LOCKIN_BIND | 该端口监听机器上的哪些地址。默认值为 127.0.0.1:仅限机器本身,也就是你或运行在该机器上的代理。 |
LOCKIN_VERSION | 要运行的版本,例如 1.0.0。默认值:当前检出的版本。 |
POSTGRES_PASSWORD | 内置 Postgres 的密码。 |
SIGNUP | open(默认值)允许任何能访问该地址的人创建账户。closed 会在创建第一个账户后停止创建新账户。 |
GOOGLE_CLIENT_ID、GOOGLE_CLIENT_SECRET | 可选。只有同时设置这两项时,才会显示“使用 Google 继续”。 |
SMTP_HOST、SMTP_PORT、SMTP_USER、SMTP_PASSWORD、SMTP_FROM | 可选。用于发送邮件,详见下文“账户”。 |
OPERATOR_EMAIL、OPERATOR_NAME、OPERATOR_HOSTING | 可选。用于隐私和条款页面,详见下文“账户”。 |
STORAGE_QUOTA_MB | 每个账户可存储的图片和 PDF 的兆字节数。默认值为 1024;设为 0 表示不设上限。 |
RATE_LIMITS | 设为 off 可关闭下方的限制。默认值为 on。 |
TRUSTED_PROXY_HOPS | LockIn 前方的反向代理数量。默认值为 1;如果访客直接访问该端口,则设为 0。 |
INGEST_URL、INGEST_SECRET | 可选。参见上文。 |
HTTPS
在互联网可访问的机器上,LockIn 需要使用 HTTPS:否则会话和 Agent 令牌会以明文形式通过网络传输,浏览器也不会运行本地模型。https 配置会添加 Caddy,由它从 Let's Encrypt 获取证书并自动续期。将域名指向该机器,开放 80 和 443 端口,并在 .env 中设置:
PUBLIC_URL=https://notes.example.com # no trailing slash
docker compose --profile https up -d
代理的设置位于 Caddyfile:Strict-Transport-Security 的有效期为一年,且请求正文大小上限为 60 MB。
如果机器上已有自己的代理(nginx、Traefik),请关闭该配置,设置 PUBLIC_URL,将代理指向 127.0.0.1:3000,并让代理发送 Strict-Transport-Security。LockIn 不会发送这个安全标头,因为只有代理知道网站是通过 HTTPS 提供服务的;其余标头(内容安全策略、禁止其他网站将页面嵌入框架,以及禁止内容嗅探)都由 LockIn 自行发送。
端口 3000 不会向网络开放,因此无法绕过代理。如果要在没有代理的情况下从其他机器使用 LockIn,例如在家庭网络中使用,请设置 LOCKIN_BIND=0.0.0.0 和 TRUSTED_PROXY_HOPS=0。Docker 会将该端口发布到大多数主机防火墙之外;之后所有数据,包括密码和 Agent 令牌,都会以未加密的形式传输。
账户
任何能打开该地址的人都可以注册,除非你将 SIGNUP=closed:先创建自己的账户,然后再关闭注册。
设置 SMTP_HOST 后,LockIn 会发送两封邮件。新账户会收到一条用于确认邮箱地址的链接,在确认之前无法登录;“忘记密码”会发送设置新密码的链接。链接根据 PUBLIC_URL 生成,因此请将其设为用户实际使用的地址。未配置 SMTP 时不会发送邮件:账户可以直接登录,你需要自行重置忘记的密码:
scripts/set-password.sh [email protected]
更改或重置密码会使该账户在所有设备上退出登录。通过邮件链接或脚本重置密码时,还会断开所有 Agent 和已连接的应用,以防其他人曾经访问过该账户:他们创建的令牌会在新密码设置后继续有效。请在“设置”中重新连接。设置页面提供“更改密码”和“删除账户”选项;删除账户会移除笔记本、其中的文件以及所有 Agent 的访问权限。
仅供自己使用的笔记本无需隐私政策或服务条款页面。如果其他人要在你的实例上注册,请设置 OPERATOR_EMAIL,并可按需设置 OPERATOR_NAME:系统会显示 /privacy 和 /terms 页面,并在首页和注册页面提供链接,注明应联系谁。OPERATOR_HOSTING 是你自行填写的一句话,会显示在隐私页面上,说明数据的存储位置以及备份的保留时间。这两个页面会介绍 LockIn 的功能,并代表你作出一些承诺(例如不查看他人的笔记、关闭服务前会提前通知),因此启用前请先阅读。
“使用 Google 继续”会创建新账户;如果该邮箱地址已验证,则会打开对应的现有账户。它不会关联到使用密码创建、且邮箱地址从未验证过的账户,因此无人能抢先占用他人的邮箱地址。
限制
LockIn 会对几项操作计数,避免单个调用方耗尽实例资源;超出限制时会返回 429,并附带 Retry-After:
| 限制 | |
|---|---|
| 登录尝试 | 同一网络上的同一地址,十分钟内 10 次 |
| 注册 | 同一网络每小时 10 次 |
| 密码重置和确认邮件 | 同一网络每小时分别为 10 封和 20 封;每个账户每分钟 1 封 |
| 上传 | 每个账户每分钟 60 次,且不得超过 STORAGE_QUOTA_MB |
| 文档提取 | 每个账户每小时 30 次 |
| Agent(MCP) | 每个令牌每分钟 60 次请求 |
网络级限制依据访客地址计算,LockIn 只能从代理设置的 X-Forwarded-For 标头中获取该地址。这也是将其放在代理后面运行的另一个原因。如果代理前面还有 CDN,请设置 TRUSTED_PROXY_HOPS=2,并让代理保留 CDN 的标头(Caddyfile 中说明了如何配置),否则 CDN 后面的所有人都会被算作同一位访客。如果完全不使用代理,请设置 TRUSTED_PROXY_HOPS=0:直接访问 LockIn 的访客可以自行填写该标头,因此系统不会信任它,所有访客都会合并计算这些限制。
请求字段也有限制(笔记正文最多一百万个字符,标题最多 300 个字符)。请求正文大小本身不受限制:请在代理中设置上限,至少为 60 MB,确保 PDF 上传仍可使用(https 配置文件已设置;nginx 中对应的配置项为 client_max_body_size)。
笔记本可以有多大
语义搜索会在 Postgres 中,将问题与账户笔记本中的每个段落进行比对。在搭载捆绑数据库的笔记本电脑上测得:普通笔记本(数百个段落)耗时 12 ms;包含 300 条笔记和 3 本教材(3,900 个段落)耗时 0.1 s;包含 2,000 条笔记和 20 本教材(26,000 个段落)耗时 0.65 s。耗时随单个账户的段落数量线性增长,不受账户总数影响。超过这个规模后,存储的向量需要使用 pgvector,但目前还未使用。
科目页面会一次性发送该科目的所有笔记:科目内有数百条笔记时没问题,但达到数千条时会变慢(2,000 条笔记约为 10 MB)。如果某个科目有这么多笔记,请将其拆分。
连接你的 Agent(MCP)
LockIn 在 /api/mcp 上运行 MCP 服务(Streamable HTTP)。在 LockIn 中,打开 Settings → Connect your agents,创建一个以 Agent 命名的令牌,然后复制页面显示的配置。对于 Claude Code:
claude mcp add --transport http lockin https://your-lockin/api/mcp \
--header "Authorization: Bearer lk_pat_…"
Settings 还会显示 Codex(~/.codex/config.toml)和 Cursor(~/.cursor/mcp.json)的配置版本。
工具:
| 工具 | |
|---|---|
list_subjects | 列出主题及其各自的笔记数量,以及收件箱。 |
search_notes | 摄取服务运行时按含义搜索,也始终支持按关键词搜索。笔记和资源会附带其所在页面。 |
get_note | 根据 ID 获取一篇笔记、一个任务或一个子任务的完整内容。笔记会附带其计划:任务、任务笔记及嵌套子任务。 |
get_plan | 获取某个主题的完整计划:每篇笔记的任务和子任务及其完成状态,以及未归属于任何笔记的任务。 |
save_note | 在指定主题下新建笔记。如果未指定主题,或指定的主题不存在,则笔记会放入收件箱。代码相关笔记可以注明所描述的仓库、提交(及其日期)和文件。回复会列出看起来相关的笔记,因此 Agent 可以说明其笔记是否取代了其中某篇。 |
append_to_note | 在笔记末尾添加内容,绝不会替换任何内容。对于该 Agent 记录的笔记,会直接续写;对于其他人记录的笔记,则会添加在带日期的“From Codex”行下方。 |
edit_note | 重命名笔记,或替换其中一段完全匹配的文本。只能编辑该 Agent 记录的笔记;其他人的笔记只能追加内容。笔记会重新标记为“待见证”。 |
confirm_note | 确认笔记仍与代码一致:将其锚点移至 Agent 的提交,并清除任何过期标记。 |
link_replacement | 声明该 Agent 保存的笔记取代了一篇已过时的旧笔记。新笔记会再次等待你的见证,其“见证”按钮会标明它取代的页面。 |
flag_stale | 将笔记标记为可能已过时,并注明原因,供你查看。 |
supersede_note | 新建一篇笔记,取代一篇已过时的笔记。只有在你见证新笔记后,旧页面才会退役;之后搜索和“提问”中都不会再显示它。 |
save_handoff | 为下一个 Agent 留下交接信息:目标、工作进展、已排除的方案、下一步,以及应优先阅读的页面。 |
pickup_handoff | 接手某个主题或仓库的待处理交接信息,以及其中标注的页面文本。这同时也是一个 MCP 提示词:pickup。 |
capture_session | 将会话摘要按每个会话一篇笔记的形式保存。Claude Code 插件会在会话压缩和结束时调用此工具。 |
move_note | 将笔记归档到另一个主题下,或移至收件箱。其任务也会一并移动。 |
add_task | 在笔记下添加任务,或添加到主题中未归属于笔记的任务列表。 |
add_subtask | 在某个任务下,或在其某个顶层子任务下添加子任务。 |
update_plan_item | 将任务或子任务标记为已完成或未完成、重命名,或为其添加笔记。完成周期性任务后,会安排下一次任务。 |
Agent 无法删除任何内容。
当 Agent 读取锚定到代码的笔记时,get_note 会提供要运行的 git diff,这样它就能在依赖笔记内容之前判断代码是否已有变更。笔记会记录其提交日期,因此当两篇笔记内容不一致时,你可以看出哪一篇描述的是较新的代码:替代笔记页面会显示两个提交及其日期。
Claude Code 插件
integrations/claude-code 中的插件会在 Claude Code 压缩会话以及会话结束时,将会话归档到你的笔记本中,并添加 /lockin:handoff 和 /lockin:pickup,用于在 Agent 之间交接未完成的工作。它使用上文添加的 lockin 服务:
claude plugin marketplace add Sky-walkerX/LockIn
claude plugin install lockin@lockin
会话结束时的捕获会在后台短暂运行 Claude 的 Haiku 模型来总结会话。这会计入你的 Claude 用量;设置 LOCKIN_CAPTURE_ON_EXIT=0 可将其关闭。插件的 README 中有详细说明。
每个 Agent 都有自己的令牌。它的笔记会标注令牌名称,你可以撤销某个令牌,而不影响其他令牌。系统只存储每个令牌的哈希值。每个令牌每分钟最多可发起 60 次请求;超过后会收到带有 Retry-After 的 429 响应。
Claude.ai、ChatGPT 和其他需要登录的应用不需要令牌。添加自定义连接器,使用 URL https://your-lockin/api/mcp;应用会将你带到 LockIn 登录并授权。LockIn 为此自带 OAuth 2.1 服务(PKCE、Client ID Metadata Documents 和动态注册、轮换刷新令牌),因此在可自托管环境中也能以相同方式运行。已连接的应用会列在“设置”中,你可以在那里断开连接。
询问(⌘J)
“询问”会在你的浏览器、本机或使用你自己的密钥的服务商处运行模型。LockIn 的服务会根据你的笔记构建提示词并保存对话记录,但永远不会看到你的端点或 API 密钥:浏览器会直接调用模型。你可以保留多个连接,并在“询问”页面顶部选择一个。
-
**使用自己的密钥:**Anthropic、OpenAI、Gemini 或 OpenRouter。密钥保留在此浏览器中。问题以及用于回答问题的笔记会从浏览器发送给相应服务商。只有本地模型和浏览器内模型支持笔记自动补全。
-
**在浏览器中运行:**通过 WebGPU 使用 WebLLM。在“询问”的设置中选择模型并下载一次;浏览器会缓存它。需要较新版本的 Chrome 或 Edge。
-
**本地服务:**任何兼容 OpenAI 的服务(Ollama、LM Studio、llama.cpp、vLLM)。浏览器会直接调用它,因此该服务必须允许 LockIn 的来源:
OLLAMA_ORIGINS=https://your-lockin ollama serve
# macOS app: launchctl setenv OLLAMA_ORIGINS "https://your-lockin", then restart Ollama
如果两者都在 localhost 上运行,则无需配置。
当笔记多到无法放进一个提示词时,“询问”会停止截断笔记,转而开始搜索:笔记会被分块并生成嵌入(由摄取服务或浏览器完成),每个问题都会获得主题大纲,以及评分为相关的段落。在浏览器中生成嵌入时,会从 Hugging Face 下载一次模型(约 130 MB,之后会缓存);运行模型的代码由 LockIn 自身提供。
笔记本中的每条笔记和已保存资源都有页码。模型会看到带有页码标签的每条内容,并像 [p. 12] 这样引用;在回复中,这会成为指向笔记的链接。每个回答下方的 来自你的笔记本 会列出使用过的段落及引用的页码。点击段落引用会打开笔记并滚动到对应位置,同时标记该段落。
开发
pnpm install
pnpm dev # http://localhost:3000
pnpm test # vitest
pnpm lint # eslint
pnpm build # prisma generate && next build
pnpm db:migrate # prisma migrate deploy
开发环境(Prisma CLI 使用 .env,应用使用 .env.local):
DATABASE_URL= # Postgres, runtime. Supabase: transaction pooler (:6543, pgbouncer=true)
DIRECT_URL= # Postgres, migrations. Supabase: session pooler (:5432, no pgbouncer params)
AUTH_SECRET=
NEXTAUTH_SECRET=
NEXTAUTH_URL= # e.g. http://localhost:3000
可选:GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET、SIGNUP 和 SMTP_*(见上文“账户”),以及 INGEST_URL / INGEST_SECRET 和 SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY,用于将上传内容存储在 Supabase Storage,而不是 ./storage。
在 Supabase 上,DIRECT_URL 不能是 DATABASE_URL 的副本:事务模式下的 pgbouncer 无法运行 DDL,迁移会卡住,而不是失败。
AUTH_SECRET 不是可选项。NextAuth 自带的路由会回退到内部密钥,因此即使没有设置它,登录看起来也能正常工作,但每个 API 路由都会通过 getToken 读取 JWT 并返回 401。
架构变更通过迁移完成:编辑 prisma/schema.prisma,然后运行 pnpm exec prisma migrate dev --name what-changed。如果数据库是在迁移功能出现之前通过 db push 创建的,则只需在首次使用时将其标记为基线(pnpm exec prisma migrate resolve --applied 0001_baseline),之后即可正常迁移。
Prisma 客户端由 pnpm build 生成到 app/generated/prisma/(不提交到版本库),也可以单独运行 pnpm exec prisma generate 生成。
数据模型
User ──< Subject ──< Milestone ──< Task ──< Subtask,此外还有 Subject ──< Resource、User ──< ApiToken、User ──< OAuthGrant ──< OAuthToken(已登录的应用)、User ──< EmailToken(确认和重置链接),以及与它们并列的聊天和嵌入表。
笔记存储为一条 Milestone 记录(其标题和 notes Markdown 内容),主题的 Plan 任务可以挂在其下。收件箱是一个 isInbox 为真的 Subject。Milestone.source 记录笔记的撰写者(“web” 或 Agent 名称),witnessedAt 记录用户确认 Agent 笔记的时间。
设计
PRODUCT.md 是产品简介,DESIGN.md 是设计系统(实验室日志风格:布面书脊、方格纸、简洁的阅读页面)。
贡献
请参阅 CONTRIBUTING.md。如 SECURITY.md 所述,请私下报告安全问题。
许可证
AGPL-3.0-or-later。你可以自由使用、修改并自托管 LockIn。如果你将修改后的版本作为服务提供给他人,则必须公开你的修改。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。
