
LockIn
io.github.Sky-walkerXPublished by GitHub account Sky-walkerXv1.0.0Updated Oct 24, 2026
A notebook your coding agents write to: notes that track the code's commit, and handoffs.
Overview
LockIn is a notebook MCP server where coding agents save, search and read notes tied to code commits, plus handoffs between agents.
- What you can do
- Browse subjects, chapters and the Inbox, and search notes by keyword or meaning
- Read a single note, task or subtask in full, or a subject's whole plan with tasks and subtasks
- Save new notes under a subject or chapter, append to notes, and edit or retitle notes the same agent recorded
- Confirm, flag stale, supersede or link replacement notes anchored to a repo, commit and files
- Upload images through a one-time link and get markdown that shows them in a note
- Save and pick up handoffs between agents, capture session summaries, and manage chapters, tasks and subtasks
- When to use it
- Useful when you work with Claude Code, Codex, Cursor or another MCP client across sessions and want what each session learned kept as notes you can file, review and query later, including passing unfinished work from one agent to the next.
- Requirements
- A LockIn instance: the hosted endpoint, or a self-hosted deployment needing Docker with Compose, Postgres and an AUTH_SECRET. Agents authenticate with a per-agent token created in Settings and sent as an Authorization Bearer header; apps that sign in use OAuth instead. HTTPS is needed on an internet-reachable host.
Installation
In SourceWeft
- Open LockIn in the dashboard and add it to a workspace.
- Connect the required credentials. They are stored privately and never shown on public pages.
- Enable the server for the chats that should use its tools.
Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.
Use with an agent
Paste this prompt into Claude Code, Codex, Cursor or another agent — or into SourceWeft chat. The agent reads this server's install guide, shows you its publisher, endpoint and sign-in, and adds it to your client once you agree. Keys never go into a command.
Read https://sourceweft.com/mcp/io.github.Sky-walkerX%2Flockin/install.md and connect the MCP server it describes to my client. Before changing any config, show me its publisher, endpoint and sign-in method and wait for my OK. Never put an API key in a command or in a file I would commit; ask me where it should come from.Other MCP clients
Add this to your client's mcpServers config. The client completes OAuth sign-in on first connect.
{
"mcpServers": {
"lockin": {
"type": "http",
"url": "https://lockin.namankhandelwal.dev/api/mcp"
}
}
}README
[LockIn. The notebook your AI tools write to.]
[GitHub stars] [CI] [Last commit] [Open issues] [License: AGPL-3.0]
[MCP: 19 tools] [Self-hosted with Docker Compose] [Next.js 16] [Postgres 17] [TypeScript]
Try it · Self-host · Connect your agents · Ask your notes · Develop
You learn things inside Claude, Codex and Cursor every day, and most of it is gone when the session ends. LockIn keeps it. Your agents save notes into it over MCP, you file them by subject, and any model you choose answers questions from them.
The launch film, 45 seconds, with sound:
https://github.com/user-attachments/assets/3ae46572-283e-49fd-a3dc-e178dd269e40
How it fits together
[Claude Code saves a note to the Project-X section over MCP. The note appears in the notebook and is stamped as witnessed. In a later session Codex searches Project-X over MCP and picks up from that note.]
Monday: Claude Code saves a note to Project-X, and you witness it. Thursday: Codex picks up where it left off.
What's in it
Self-hosting
You need Docker with Compose.
Open http://localhost:3000 and create an account. The port answers only on the machine itself; for a server, see HTTPS below. The first start pulls the released images, waits for Postgres and applies the database migrations before the app starts. Images are published for amd64 and arm64; where there's none for your version, Compose builds from the checkout instead.
What runs:
To add text extraction and server-side embeddings, set INGEST_SECRET (any long random string) and INGEST_URL=http://ingest:8080 in .env, then:
The ingest image carries its embedding model, so it's about 1 GB. Without it, keyword search works, and the browser does embeddings itself. It reads two documents at a time and stops one that takes over 140 seconds; a third waits 20 seconds for a slot and is then told the service is busy. PARSE_CONCURRENCY in .env raises the first number, on a machine with a core and a few hundred MB to spare per document.
To update, pull the new release. The compose file names the version it goes with, so the images always match the checkout. Migrations run on start:
To run the code in your checkout instead of a release, build it: docker compose up -d --build.
docker compose ps shows whether the app is healthy; the same check is at /api/health (200 when the app can reach its database) for an uptime monitor.
Backups
Everything lives in two volumes: db (Postgres) and uploads (note images and PDFs). Back up both, and take a backup before updating.
Keep AUTH_SECRET with the backups. A restored copy with a different secret signs everyone out.
Settings in .env:
HTTPS
On a machine the internet can reach, LockIn needs HTTPS: without it sessions and agent tokens cross the network in the clear, and browsers won't run a local model. The https profile adds Caddy, which gets a certificate from Let's Encrypt and renews it. Point your domain at the machine, open ports 80 and 443, and set in .env:
The proxy's settings are in Caddyfile: a year of Strict-Transport-Security, and a 60 MB cap on a request's body.
With a proxy of your own on the machine (nginx, Traefik) leave the profile off, set PUBLIC_URL, point the proxy at 127.0.0.1:3000 and have it send Strict-Transport-Security. That's the one security header LockIn leaves out, since only the proxy knows the site is served over HTTPS; the rest (a content security policy, no framing by other sites, no content sniffing) it sends itself.
Port 3000 isn't open to the network, so the proxy can't be gone around. To use LockIn from other machines without a proxy, on a home network say, set LOCKIN_BIND=0.0.0.0 and TRUSTED_PROXY_HOPS=0. Docker publishes that port past most host firewalls, and everything, passwords and agent tokens included, then travels unencrypted.
Accounts
Anyone who can open the address can sign up, unless you set SIGNUP=closed: make your own account first, then close it.
With SMTP_HOST set, LockIn sends two emails. A new account gets a link to confirm its address and can't sign in until it's opened, and "Forgot password" sends a link that sets a new one. The links are built from PUBLIC_URL, so set it to the address people really use. Without SMTP there's no email: accounts sign in straight away, and you reset a forgotten password yourself:
Changing or resetting a password signs that account out everywhere. Resetting one, by the emailed link or with the script, also disconnects every agent and connected app, in case someone else had been in: a token they made would outlast the new password. Connect them again in Settings. Settings has "Change password" and "Delete account", which removes the notebook, its files and every agent's access.
A notebook you run for yourself has no privacy or terms page. If other people sign up on yours, set OPERATOR_EMAIL, and OPERATOR_NAME if you like: /privacy and /terms appear, linked from the landing and sign-up pages, and name you as the one to write to. OPERATOR_HOSTING is a sentence of your own, shown on the privacy page, on where the data is kept and how long backups are. Both pages describe what LockIn does and make a few promises in your name (that you don't read people's notes, that you'll give notice before closing), so read them before turning them on.
"Continue with Google" makes a new account, or opens the existing one with that address if the address was confirmed. It won't attach to an account made with a password whose address was never confirmed, so nobody can claim an address ahead of its owner.
Limits
LockIn counts a few things so one caller can't wear the instance out, and answers 429 with Retry-After past the limit:
Limits per network go by the visitor's address, which LockIn only knows from the X-Forwarded-For header its proxy sets. That's another reason to run it behind one. With a CDN in front of the proxy as well, set TRUSTED_PROXY_HOPS=2 and have the proxy keep the CDN's header (Caddyfile says how), or everyone behind the CDN is counted as one visitor. With no proxy at all, TRUSTED_PROXY_HOPS=0: a visitor who reaches LockIn directly writes that header themselves, so it isn't believed and those limits count all visitors together.
A request's fields are capped too (a note's body at a million characters, a title at 300). The request body itself is not: let the proxy cap it, at 60 MB or more so PDF uploads still fit (the https profile does; client_max_body_size in nginx).
How big a notebook can get
Search by meaning scores a question against every passage of the account's notebook, inside Postgres. Measured on a laptop with the bundled database: 12 ms for an ordinary notebook (a few hundred passages), 0.1 s for 300 notes and three textbooks (3,900 passages), 0.65 s for 2,000 notes and twenty textbooks (26,000 passages). It grows in a straight line with one account's passages and doesn't depend on how many accounts there are. Past that size the stored vectors want pgvector, which isn't used yet.
A subject's page sends all of its notes at once: fine for hundreds of notes in a subject, slow in the thousands (2,000 notes is about 10 MB). Split a subject that large.
Connect your agents (MCP)
LockIn runs an MCP server at /api/mcp (Streamable HTTP). In LockIn, open Settings → Connect your agents, create a token named after the agent, and copy the setup it shows. For Claude Code:
Settings also shows the Codex (~/.codex/config.toml) and Cursor (~/.cursor/mcp.json) versions.
Tools:
Agents can't delete anything.
When an agent reads a note anchored to code, get_note gives it the git diff to run, so it can tell whether the code has moved on before it relies on the note. Notes carry their commit's date, so when two notes disagree you can see which describes the later code: the note page shows both commits and dates on a replacement.
Claude Code plugin
The plugin in integrations/claude-code files each session into your notebook when Claude Code compacts it and when it ends, and adds /lockin:handoff and /lockin:pickup for passing unfinished work from one agent to another. It uses the lockin server added above:
The capture at the end is a short background run of Claude on Haiku that summarises the session. It counts against your Claude usage; LOCKIN_CAPTURE_ON_EXIT=0 turns it off. The plugin's README has the details.
Each agent gets its own token. Its notes are stamped with the token's name, and you can revoke one without touching the others. Only a hash of each token is stored. A token can make 60 requests a minute; past that it gets 429 with Retry-After.
Claude.ai, ChatGPT and other apps that sign in don't need a token. Add a custom connector with the URL https://your-lockin/api/mcp; the app sends you to LockIn to sign in and allow it. LockIn is its own OAuth 2.1 server for this (PKCE, Client ID Metadata Documents and dynamic registration, rotating refresh tokens), so it works the same self-hosted. Connected apps are listed in Settings, where you can disconnect them.
Ask (⌘J)
Ask runs the model in your browser, on your machine, or at a provider with your own key. LockIn's server builds the prompt from your notes and stores the transcript, but never sees your endpoint or API key: the browser calls the model directly. Keep several connections and pick one at the top of Ask.
-
Your own key: Anthropic, OpenAI, Gemini or OpenRouter. The key stays in this browser. Questions, and the notes used to answer them, go from the browser to that provider. Note autocomplete only runs on local and in-browser models.
-
In browser: WebLLM on WebGPU. Pick a model in Ask's settings and download it once; the browser caches it. Needs recent Chrome or Edge.
-
Local server: any OpenAI-compatible server (Ollama, LM Studio, llama.cpp, vLLM). The browser calls it directly, so it has to allow LockIn's origin:
Running both on localhost needs no configuration.
When your notes outgrow one prompt, Ask stops trimming and starts searching: notes are chunked and embedded (by the ingest service, or in the browser), and a question gets an outline of the subject plus the passages that score as relevant. Embedding in the browser downloads the model once from Hugging Face (about 130 MB, then cached); the code that runs it is served by LockIn itself.
Every note and saved resource has a page number in the notebook. The model sees each one labelled with its page and cites it like [p. 12]; in the reply that's a link to the note. Under each answer, From your notebook lists the passages it used and the pages it cited. A citation of a passage opens the note scrolled to it, with the passage marked.
Development
Environment for development (.env for the Prisma CLI and .env.local for the app):
Optional: GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, SIGNUP and SMTP_* (Accounts, above), INGEST_URL / INGEST_SECRET, and SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY to store uploads in Supabase Storage instead of ./storage.
DIRECT_URL must not be a copy of DATABASE_URL on Supabase: pgbouncer in transaction mode can't run DDL, and migrations hang instead of failing.
AUTH_SECRET is not optional. NextAuth's own routes fall back to an internal secret, so sign-in appears to work without it, but every API route reads the JWT with getToken and returns 401.
Schema changes go through migrations: edit prisma/schema.prisma, then pnpm exec prisma migrate dev --name what-changed. A database that was set up with db push before migrations existed is marked at the baseline once (pnpm exec prisma migrate resolve --applied 0001_baseline) and migrates normally after that.
The Prisma client is generated into app/generated/prisma/ (not committed) by pnpm build, or on its own with pnpm exec prisma generate.
Data model
User ──< Subject ──< Milestone ──< Task ──< Subtask, with Subject ──< Resource, User ──< ApiToken, User ──< OAuthGrant ──< OAuthToken (apps that signed in), User ──< EmailToken (confirmation and reset links) and the chat and embedding tables beside them.
A note is stored as a Milestone row (its title and notes markdown), and a subject's Plan tasks can hang off it. The Inbox is a Subject with isInbox. Milestone.source records who wrote a note ("web", or an agent's name), and witnessedAt records when the user signed off on an agent's note.
Design
PRODUCT.md is the product brief and DESIGN.md the design system (the Lab Book look: cloth spine, grid paper, clean reading pages).
Contributing
See CONTRIBUTING.md. Report security issues privately, as SECURITY.md describes.
License
AGPL-3.0-or-later. You can use, change and self-host LockIn freely. If you run a modified version as a service for others, you have to publish your changes.
Source: README.md at commit 236bae0
Tools
0Version history
1- v1.0.0LatestOct 14, 2026
Score
85% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 daysRequired25 pts
- Auth declaredRequired10 pts
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts