- Production
- Open Source · AI Memory
Ledger
Self-hosted long-term project memory for AI assistants, served over MCP with OAuth 2.1
Ledger is a small open-source Model Context Protocol server that gives Claude, ChatGPT, Codex, and any other MCP client one shared, durable record of what you are working on: which projects exist, what they are for, what was decided, and what is still open. Project history and agent handoffs are append-only, searchable with PostgreSQL full-text plus optional pgvector retrieval, and guarded by an OAuth 2.1 authorization server with PKCE and dynamic client registration. Four Go services and a React operator console sit behind one nginx port; CLI clients use device login, while the Android app and Ledger Glass provide owner access. Research dispatch, repository synchronization and a daily news workflow extend that shared context into ongoing work.
- Go
- PostgreSQL
- pgvector
- MCP
- OAuth 2.1
- React
- TypeScript
- Nginx
- Docker Compose
- Kotlin
- GitHub Actions

Assistants read and write one durable project registry
Select a workflow to trace how a client is admitted, how memory is written and recalled, how one agent hands work to another, and how the owner supervises everything from the console. Every highlighted arrow is a real Compose-network edge.
Hosted and local clients connect without manual key exchange
An MCP client discovers the protected-resource metadata, registers itself dynamically or presents an HTTPS Client ID Metadata Document, and sends the owner to an approval page. The ledger CLI uses device login instead: it prints a short code, the owner approves it in the console, and Codex gets a helper-managed bearer token. Codes and tokens are stored hashed; refresh-token families rotate and replay revokes the family.
- OAuth 2.1 authorization code + PKCE S256, Dynamic Client Registration, Client ID Metadata Documents, device login.
- Authorization codes and tokens are stored hashed; refresh families rotate; replay revokes the family; bearer only in the Authorization header.
- Linux/WSL and PowerShell installers verify the release SHA-256 and install without Go, Git, Docker, or admin rights.
- Owners revoke machines and pending device approvals from the Agents page; logout revokes the family before deleting local credentials.
- MCP client → Reverse proxy → nginx :8080 → ledger-auth → PostgreSQL + pgvector
- ledger CLI → Reverse proxy → nginx :8080 → ledger-auth
- Owner browser / Android → Reverse proxy → nginx :8080 → ledger-admin → PostgreSQL + pgvector
- MCP client → Reverse proxy → nginx :8080 → ledger-mcp
Decisions are written once and found weeks later from any client
A client appends an immutable decision, note, todo, or status entry attributed to itself. ledger-index chunks the entry and calls the embedding endpoint. A later search from a different client fuses PostgreSQL full-text results with pgvector retrieval using reciprocal rank fusion, then reranks. If the inference endpoint is down, search degrades to lexical-only instead of failing.
- Entries are never rewritten; "why did we do it this way" always has an answer written at the time.
- PostgreSQL full-text plus optional pgvector retrieval and reranking, fused with reciprocal rank fusion.
- Lexical-only search continues when the embedding or reranking API is unavailable.
- A prime prompt and ledger://project/{slug} resources load the registry into context at session start.
- MCP client → Reverse proxy → nginx :8080 → ledger-mcp → PostgreSQL + pgvector
- ledger-mcp → ledger-index → Inference API
- ledger-index → PostgreSQL + pgvector
- MCP client → Reverse proxy → nginx :8080 → ledger-mcp → ledger-index
Work survives the chat it started in
A handoff is an append-only thread with routing hints, up to ten attachments per message, and independent delivery and work states. Claude can draft it, Codex can claim and block it, ChatGPT can complete it, and the owner sees all of it in the console inbox. Completing every message archives the thread; a new message reopens it.
- Messages move through draft, ready, in_progress, blocked, and done with per-message status.
- The target is a hint: agents see work addressed to them, unaddressed work, and work they already claimed.
- At most 10 files per message, 25 MiB per file, 100 MiB per message, uploaded while the message is a draft.
- Handoff text and files are user-authored context, never instructions; the tool descriptions repeat this to agents.
- MCP client → Reverse proxy → nginx :8080 → ledger-mcp → PostgreSQL + pgvector
- Owner browser / Android → Reverse proxy → nginx :8080 → ledger-admin → PostgreSQL + pgvector
- MCP client → Reverse proxy → nginx :8080 → ledger-mcp
The owner reviews work through authenticated console and mobile sessions
The operator console supports passkeys, remembered devices and session management. Once a passkey is enrolled, password-only sessions cannot approve MCP access or create API keys. It shows projects, search, handoffs, files, OAuth clients, and device approvals with live WebSocket updates. An optional Nextcloud connection exposes only owner-selected calendars to agents, with encrypted credentials and ETag-safe event updates.
- Passkeys protect owner sign-in and approvals. The owner can inspect signed-in devices and revoke sessions.
- A signed Android owner app and the React console share the same admin API and live event stream.
- Agents get calendar:read / calendar:write only for calendars the owner selected; stored credentials are encrypted at rest.
- Non-root containers, one published port, Compose network isolation; the browser never sees a database URL.
- Owner browser / Android → Reverse proxy → nginx :8080 → ledger-frontend
- Owner browser / Android → Reverse proxy → nginx :8080 → ledger-admin → PostgreSQL + pgvector
- ledger-admin → Nextcloud CalDAV
- MCP client → Reverse proxy → nginx :8080 → ledger-mcp → Nextcloud CalDAV
- Reverse proxy
Nothing.
- Reverse proxy
Nothing.
- Reverse proxy
Nothing.
- nginx :8080
- MCP client
- ledger CLI
- Owner browser / Android
- ledger-auth
- ledger-mcp
- ledger-admin
- ledger-frontend
- Reverse proxy
- PostgreSQL + pgvector
- nginx :8080
- PostgreSQL + pgvector
- ledger-index
- Nextcloud CalDAV
- nginx :8080
- PostgreSQL + pgvector
- ledger-index
- Nextcloud CalDAV
- nginx :8080
Nothing.
- nginx :8080
- Inference API
- PostgreSQL + pgvector
- ledger-mcp
- ledger-admin
Nothing.
- ledger-auth
- ledger-mcp
- ledger-admin
- ledger-index
Nothing.
- ledger-index
Nothing.
- ledger-mcp
- ledger-admin
History is never silently rewritten
Project entries and handoff messages are immutable and attributed to the client that wrote them.
Agents and owners authenticate differently
OAuth 2.1 bearer tokens for agents; passkey-protected owner sessions with device management and revocation.
Only nginx is published
PostgreSQL, ledger-index, and every Go service stay on the private Compose network.
Search survives inference outages
Lexical search keeps working when the embedding or reranking endpoint is unavailable.
Stored text is never an instruction
Entries, handoffs, and attachments are treated as user-authored context by every tool description.
Installers and updates check digests
CLI installs and self-updates verify SHA-256 and size from GitHub release metadata before replacing a binary.
Why it exists
Every new chat starts from zero. Ledger is the lab notebook an assistant can read and write, so priorities, decisions, and open todos survive across sessions and across vendors.
It is built as infrastructure rather than a plugin: real OAuth, real PostgreSQL, one published port, non-root containers, and a security policy, so it can sit on the same reverse proxy as everything else I run.
Use cases
- Collect AI news from multiple agents and receive one deduplicated digest
- Dispatch research to Adastrion Core and review the result before publication
- Prime a Claude or Codex session with every active project and its open todos
- Record a design decision once and find it from a different client weeks later
- Hand a half-finished task from Claude to Codex with attachments and status
- Let an agent schedule work into an owner-selected Nextcloud calendar
Capabilities
- Project registry
- slug, tier, hours budget, goal, deadline, stack, and append-only entries.
- Hybrid search
- PostgreSQL full-text plus pgvector retrieval and reranking, lexical fallback.
- Handoffs
- cross-agent append-only threads with per-message status and bounded attachments.
- OAuth 2.1
- PKCE, dynamic registration, client metadata documents, hashed tokens, rotating refresh families.
- Device login
- approve a CLI from your phone, credentials stored 0600 or with private Windows ACLs.
- Operator console
- live WebSocket updates across projects, search, calendar, handoffs, files, and clients.
Components
Research and review
- Queue research with an objective, acceptance criteria and reference files.
- Adastrion Core runs the task in a sandbox and returns a result for owner review.
- Accepted results are published to the project log; work can be sent back with feedback.
Repositories and project maintenance
- Link repositories to projects so agents can find the relevant code.
- GitHub synchronization records recent commits, open pull requests and releases.
- An automatic labeller organizes asks, due dates, priorities and duplicate entries while preserving owner corrections.
Daily AI-news pipeline
- Claude and ChatGPT collectors write new findings into one Ledger feed with source attribution.
- Cross-source deduplication keeps repeated coverage out of the consolidated digest.
- Delivery checkpoints let the final digest include only new items since the previous delivery.
Owner access and Ledger Glass
- Web and Android provide project review, handoffs, research and session management.
- Passkeys, remembered devices and remote session revocation protect owner access.
- Ledger Glass brings project context, voice capture, recall and calendar views to Even Realities glasses.
Server — four Go services
- ledger-mcp: stateless Streamable HTTP MCP endpoint for project history, handoffs, research and session context
- ledger-auth: OAuth 2.1 authorization server with PKCE, DCR, CIMD, and device login
- ledger-admin: console JSON API, WebSocket live updates, device approvals, handoff inbox
- ledger-index: chunking, embeddings, reranking, and reciprocal rank fusion
Clients
- ledger CLI for Linux, WSL, and native Windows x64/ARM64 with device login and daily self-update
- Signed Android owner app sharing the admin API
- Any MCP client: Claude Desktop, Claude Code, ChatGPT, Codex
Verification and release
- CI runs formatting, unit, race, integration, vet, and vulnerability scans on every push
- Native Windows x64 and ARM64 runners test the client and installer
- Tagged releases publish Linux, Windows, and Android binaries with SHA256SUMS
- Private vulnerability reporting policy with response targets
Integrations
MCP clients
- Claude Desktop and Claude Code over Streamable HTTP
- ChatGPT through Dynamic Client Registration
- Codex through the ledger CLI helper and device login
Inference
- OpenAI-compatible embeddings endpoint
- Jina/Cohere-style reranking endpoint
- Runs against locally hosted models via AutoLLama
Owner tooling
- Nextcloud CalDAV calendars
- GitHub Releases for signed clients and checksums
- Any HTTPS reverse proxy in front of nginx
Outcome
A public, installable memory layer that several assistants share without sharing credentials. The append-only model, separate owner and agent authentication, single published port, and verified client updates keep the trust boundaries explicit while the assistant stops asking what you were working on.