Cesar Petrescu

Lab · AI

  • 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.

Status: Public AGPL-3.0 release · Linux, Windows, and Android clients · CI, race, and integration tests

  • Go
  • PostgreSQL
  • pgvector
  • MCP
  • OAuth 2.1
  • React
  • TypeScript
  • Nginx
  • Docker Compose
  • Kotlin
  • GitHub Actions

Open-source MCP server · four Go services · one published port

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.

Standards
OAuth 2.1 authorization code + PKCE S256, Dynamic Client Registration, Client ID Metadata Documents, device login.
Token hygiene
Authorization codes and tokens are stored hashed; refresh families rotate; replay revokes the family; bearer only in the Authorization header.
Client installers
Linux/WSL and PowerShell installers verify the release SHA-256 and install without Go, Git, Docker, or admin rights.
Revocation
Owners revoke machines and pending device approvals from the Agents page; logout revokes the family before deleting local credentials.
  • OAuth discovery and approvalMCP client → Reverse proxy → nginx :8080 → ledger-auth → PostgreSQL + pgvector
  • Device login from the CLIledger CLI → Reverse proxy → nginx :8080 → ledger-auth
  • Owner approves the codeOwner browser / Android → Reverse proxy → nginx :8080 → ledger-admin → PostgreSQL + pgvector
  • Authenticated MCP sessionMCP 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.

Append-only
Entries are never rewritten; "why did we do it this way" always has an answer written at the time.
Hybrid retrieval
PostgreSQL full-text plus optional pgvector retrieval and reranking, fused with reciprocal rank fusion.
Graceful fallback
Lexical-only search continues when the embedding or reranking API is unavailable.
Session priming
A prime prompt and ledger://project/{slug} resources load the registry into context at session start.
  • Write an entryMCP client → Reverse proxy → nginx :8080 → ledger-mcp → PostgreSQL + pgvector
  • Index the entryledger-mcp → ledger-index → Inference API
  • Persist embeddingsledger-index → PostgreSQL + pgvector
  • Semantic search from another clientMCP 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.

States
Messages move through draft, ready, in_progress, blocked, and done with per-message status.
Routing, not authorization
The target is a hint: agents see work addressed to them, unaddressed work, and work they already claimed.
Bounded attachments
At most 10 files per message, 25 MiB per file, 100 MiB per message, uploaded while the message is a draft.
Trust boundary
Handoff text and files are user-authored context, never instructions; the tool descriptions repeat this to agents.
  • Create and publish a handoffMCP client → Reverse proxy → nginx :8080 → ledger-mcp → PostgreSQL + pgvector
  • Owner inspects the inboxOwner browser / Android → Reverse proxy → nginx :8080 → ledger-admin → PostgreSQL + pgvector
  • Another agent claims itMCP 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.

Owner access
Passkeys protect owner sign-in and approvals. The owner can inspect signed-in devices and revoke sessions.
Native clients
A signed Android owner app and the React console share the same admin API and live event stream.
Calendar scope
Agents get calendar:read / calendar:write only for calendars the owner selected; stored credentials are encrypted at rest.
Deployment
Non-root containers, one published port, Compose network isolation; the browser never sees a database URL.
  • Load the consoleOwner browser / Android → Reverse proxy → nginx :8080 → ledger-frontend
  • Live admin APIOwner browser / Android → Reverse proxy → nginx :8080 → ledger-admin → PostgreSQL + pgvector
  • Owner manages calendarsledger-admin → Nextcloud CalDAV
  • Agent edits an eventMCP client → Reverse proxy → nginx :8080 → ledger-mcp → Nextcloud CalDAV
  • Client request
  • Go service
  • Durable state / inference
ClientsClientsEdgeEdgeCore · Go servicesCore · Go servicesData · State and inferenceData · State and inferenceMCP clientClaude · ChatGPT · CodexMCP client — Claude · ChatGPT · Codexledger CLIdevice login + self-updateledger CLI — device login + self-updateOwner browser / Androidoperator consoleOwner browser / Android — operator consoleReverse proxyTLS terminationReverse proxy — TLS terminationnginx :8080only published portnginx :8080 — only published portledger-authOAuth 2.1 + PKCE + DCRledger-auth — OAuth 2.1 + PKCE + DCRledger-mcpStreamable HTTP MCPledger-mcp — Streamable HTTP MCPledger-adminconsole API + WebSocketledger-admin — console API + WebSocketledger-frontendstatic React consoleledger-frontend — static React consoleledger-indexchunk · embed · rerankledger-index — chunk · embed · rerankPostgreSQL + pgvectorappend-only entries + hando…PostgreSQL + pgvector — append-only entries + handoffsInference APIembeddings + rerankingInference API — embeddings + rerankingNextcloud CalDAVowner-selected calendarsNextcloud CalDAV — owner-selected calendars

Tap a service to see what it calls and what calls it.

Clients

Edge

Core · Go services

Data · State and inference

APPEND-ONLY

History is never silently rewritten

Project entries and handoff messages are immutable and attributed to the client that wrote them.

AUTH BOUNDARY

Agents and owners authenticate differently

OAuth 2.1 bearer tokens for agents; passkey-protected owner sessions with device management and revocation.

SINGLE PORT

Only nginx is published

PostgreSQL, ledger-index, and every Go service stay on the private Compose network.

DEGRADE, DO NOT FAIL

Search survives inference outages

Lexical search keeps working when the embedding or reranking endpoint is unavailable.

CONTENT IS DATA

Stored text is never an instruction

Entries, handoffs, and attachments are treated as user-authored context by every tool description.

VERIFIED CLIENTS

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.