<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: lobrzut</title>
    <description>The latest articles on DEV Community by lobrzut (@lobrzut).</description>
    <link>https://dev.to/lobrzut</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3989520%2F691a5e25-9a3a-449b-b466-627336ced00d.jpeg</url>
      <title>DEV Community: lobrzut</title>
      <link>https://dev.to/lobrzut</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lobrzut"/>
    <language>en</language>
    <item>
      <title>Your agent forgets you every session. I built the memory layer instead of another wrapper.</title>
      <dc:creator>lobrzut</dc:creator>
      <pubDate>Tue, 25 Aug 2026 15:03:49 +0000</pubDate>
      <link>https://dev.to/lobrzut/your-agent-forgets-you-every-session-i-built-the-memory-layer-instead-of-another-wrapper-fjn</link>
      <guid>https://dev.to/lobrzut/your-agent-forgets-you-every-session-i-built-the-memory-layer-instead-of-another-wrapper-fjn</guid>
      <description>&lt;p&gt;I've been running this on my own machines for six months, every working day, before it had a name I liked or an installer anyone else could use. That's the only qualification I have for writing this.&lt;/p&gt;

&lt;p&gt;Every coding agent I use is competent and amnesiac. Claude Code knows my repo layout for exactly as long as the session lasts. Cursor holds a conversation until I reload the window. Claude Desktop keeps its own history in its own store, which Cursor cannot read, which Claude Code cannot read either. The decisions of the last half-year — why I picked AGPL, why the index lives outside the encrypted vault, which approach already failed and why — live in six separate places, none of which talk to each other, and all of which belong to a vendor.&lt;/p&gt;

&lt;p&gt;The industry answer to this is vendor memory: ChatGPT remembers you, Claude remembers you, each inside its own walls. That is a retention feature, not a record you own. You cannot export it programmatically into a shape another agent reads, and it does not survive you switching tools.&lt;/p&gt;

&lt;p&gt;So I built the other thing. Pomnia is a memory layer that sits underneath whatever agent you happen to be using, speaks MCP, and stores its corpus on hardware you control. It isn't an agent, a chat UI or a wrapper, and it has no opinion about which model you use.&lt;/p&gt;

&lt;p&gt;If the name is new to you but the project is not: this was &lt;strong&gt;Brain AI Hub&lt;/strong&gt; when I wrote about it here in June. Same project, renamed — that post points at a repository I have since frozen.&lt;/p&gt;

&lt;p&gt;This is a write-up of how it actually works, including the parts that aren't encrypted and the parts that don't exist yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "portable memory layer" means concretely
&lt;/h2&gt;

&lt;p&gt;Three components, and the split between them is the whole design:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pomnia Desktop&lt;/strong&gt; (Electron, Windows and macOS, Linux AppImage/deb) is the writer. It reads conversations out of four assistants already on the machine — Claude Code's JSONL under &lt;code&gt;~/.claude&lt;/code&gt;, Cursor's &lt;code&gt;state.vscdb&lt;/code&gt;, Claude Desktop and Antigravity — snapshots the profiles of three more (VS Code, Windsurf, Continue) without extracting chats from them, and imports exports from Claude.ai, ChatGPT, Gemini and Grok, plus PDF/DOCX/EPUB documents. It distils them into notes and writes those into a vault folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;brain-core&lt;/strong&gt; is the same engine with a different entry point: one Node process that speaks MCP over HTTP, indexes a vault, and serves it to any agent holding a bearer token. No web app, no database server, no queue. This is what you run on a Linux box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The vault&lt;/strong&gt; is a plain folder. Copy it to another machine and open it there. That is the portability claim, and it is a folder-copy claim, not a sync-service claim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzx1w0c6eanjtmynvxajs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzx1w0c6eanjtmynvxajs.png" alt="Who reads what: the desktop collects and distils, the server only serves, and every agent asks the same corpus" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline: distill, chunk, embed, hybrid search
&lt;/h2&gt;

&lt;p&gt;Raw chat logs are terrible retrieval material. They are mostly ceremony — greetings, tool call noise, three attempts at the same thing. Embedding them wholesale gives you an index that returns the shape of a conversation rather than its conclusion.&lt;/p&gt;

&lt;p&gt;So the first stage is &lt;strong&gt;distill&lt;/strong&gt;: a local LLM through Ollama turns a conversation into a structured note with fixed sections — Summary, Decisions, Root causes, Solutions, Files touched, Commands run, Errors seen, Facts, Open questions. The note keeps the language of the source; my vault is bilingual Polish and English and nothing translates anything.&lt;/p&gt;

&lt;p&gt;Distillation runs on the client, deliberately. brain-core does not distil at all. The LLM stage needs a model, a GPU and a passphrase-holder, and none of those belong on a headless server that exists to serve many agents.&lt;/p&gt;

&lt;p&gt;Then &lt;strong&gt;chunk and embed&lt;/strong&gt;. Chunking is byte-identical between the desktop build and the server, and both apply nomic's &lt;code&gt;search_document:&lt;/code&gt; / &lt;code&gt;search_query:&lt;/code&gt; prefixes, so a &lt;code&gt;library.db&lt;/code&gt; built by one is readable by the other as long as the embedding model matches. Change &lt;code&gt;--embed-model&lt;/code&gt; and the index is invalid — and an incremental reindex will not fix it, because file contents did not change so the indexer skips every file and reports success. You have to delete &lt;code&gt;library.db&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then &lt;strong&gt;hybrid search&lt;/strong&gt;. &lt;code&gt;search_library&lt;/code&gt; combines semantic similarity with keyword hits and returns chunks with source path, page and score. Query in either language. Notes that failed the quality gate during distillation carry a penalty and rank below full-value versions of the same conversation, which matters more than it sounds: without it, a garbage summary of a good session outranks the good one because it is shorter and denser.&lt;/p&gt;

&lt;p&gt;Indexing is incremental — &lt;code&gt;indexDir&lt;/code&gt; skips unchanged files on mtime and size, then content hash, so nothing re-embeds without reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search that says how sure it is
&lt;/h2&gt;

&lt;p&gt;For a long time &lt;code&gt;search_library&lt;/code&gt; returned its top five whether or not the vault knew anything. With nothing relevant indexed it returned the five &lt;em&gt;least&lt;/em&gt; irrelevant chunks — in exactly the shape of a real answer. An agent handed five loosely related fragments writes a plausible answer out of them, so the memory layer was itself a source of the thing it exists to prevent.&lt;/p&gt;

&lt;p&gt;Every response now carries a verdict in prose, because a model has no calibration for &lt;code&gt;score: 0.32&lt;/code&gt; but will read a sentence: &lt;strong&gt;strong&lt;/strong&gt;, &lt;strong&gt;lexical&lt;/strong&gt; ("matched on words, not on meaning — leads to verify"), or &lt;strong&gt;none&lt;/strong&gt; ("nothing covers this, do not build an answer out of these").&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjsl3792pjrl3efrd0oil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjsl3792pjrl3efrd0oil.png" alt="The same shape either way, against three verdicts in prose" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing is suppressed, only labelled. Measured against my own vault, questions whose answer is in there score about 0.21 on semantic similarity; questions about things that could not be in there score about 0.06 — and two genuine hits in that measurement sat below the floor, carried entirely by their words. Dropping them to look decisive would have lost real answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What six months of this actually changed
&lt;/h2&gt;

&lt;p&gt;Three things, and none of them are features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I stopped re-explaining myself.&lt;/strong&gt; This is the boring one and it is the one that matters. A new session used to open with me establishing the same context — the repo layout, the constraint I settled on in March, the approach that already failed and why. Now the agent asks the corpus before it answers. My vault today holds 2575 distilled notes and sessions, indexed into 3879 chunks, built from conversations I've already had and would otherwise have had to repeat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It got cheaper.&lt;/strong&gt; Not because of a pricing trick — because re-establishing context costs tokens every single time, and I stopped paying that toll several times a day. If you work with agents seriously you already know the shape of that bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I got something I can point at.&lt;/strong&gt; This is the one I didn't expect to care about. My assistants' memory used to be a thing that happened to me somewhere else, governed by someone else's retention policy. Now it is a folder. I can open it, grep it, back it up, copy it to another machine, delete a note I regret. That is a smaller claim than "AI memory" and a much more useful one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honesty section, which is the point
&lt;/h2&gt;

&lt;p&gt;You don't find out that your search index is plaintext by designing a product. You find it out by living inside one.&lt;/p&gt;

&lt;p&gt;Pomnia's vault blobs are encrypted with AES-256-GCM, random IV per blob, integrity tag, key derived by scrypt at N=2¹⁷ from a passphrase that is never stored. That is real and you can read &lt;code&gt;src/core/crypto.ts&lt;/code&gt; to check it.&lt;/p&gt;

&lt;p&gt;Here's what is &lt;strong&gt;not&lt;/strong&gt; encrypted, and I'd rather you learn it from me than from &lt;code&gt;strings&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vault sidecars are plaintext.&lt;/strong&gt; &lt;code&gt;skills/&lt;/code&gt;, &lt;code&gt;USER.md&lt;/code&gt;, &lt;code&gt;sessions/&lt;/code&gt;, the distilled notes. They sit in the vault folder as ordinary Markdown, on purpose, because folder-first is the property that makes them yours — you can open them in Obsidian, grep them, put them in git. The crypto layer ignores them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The search index is plaintext.&lt;/strong&gt; &lt;code&gt;library.db&lt;/code&gt; holds the full text of every chunk, not just vectors, because hybrid search and citations need the text. It lives in AppData (or &lt;code&gt;/var/lib/pomnia&lt;/code&gt; on a server) as a machine-local rebuildable cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Locking the vault seals the key, not the index.&lt;/strong&gt; Lock clears the open-vault path. It does not wipe AppData. An unlocked-then-locked machine still has a readable index on disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwh4vg4vyhswxlbf3w75v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwh4vg4vyhswxlbf3w75v.png" alt="What the passphrase seals, and what stays local plaintext" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the accurate sentence is: &lt;em&gt;the passphrase seals the archive of blobs; the knowledge surface your agents read is local plaintext — protect the folder and the disk.&lt;/em&gt; Full-disk encryption and folder ACLs are doing real work here and I'm not going to pretend otherwise.&lt;/p&gt;

&lt;p&gt;I chose messaging over shipping SQLCipher before launch, and that was a deliberate call rather than an oversight. Encrypting the index costs a key lifecycle, a reindex UX, and breakage on every agent path that reads a sidecar directly, and it buys nothing against the threat most single-user local-first setups actually face. If the stolen-laptop or shared-PC case turns out to hurt real users, wipe-on-lock is the cheap fix and a vault-keyed index is the expensive one. Neither is written yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The night the vault would not open
&lt;/h2&gt;

&lt;p&gt;Worth telling because it's the least flattering thing here, and because it's why I trust the durability code now.&lt;/p&gt;

&lt;p&gt;I was running a backup when the machine lost power. On the way back up, the vault refused the passphrase — &lt;em&gt;bad magic, not a Pomnia blob&lt;/em&gt;. &lt;code&gt;manifest.cvb&lt;/code&gt;, the index of every snapshot in the vault, had come back as 55 088 bytes of zeros. Not corrupted in an interesting way. Zeros.&lt;/p&gt;

&lt;p&gt;The cause was a line I'd written myself and would have defended: write to a temporary file, then &lt;code&gt;rename()&lt;/code&gt; over the target. That's the standard atomic-write recipe, and it's genuinely atomic — against &lt;em&gt;readers&lt;/em&gt;. It says nothing about durability. The rename is journalled, the file's contents sit in the page cache, and a power cut between the two leaves you with a perfectly-named file full of nothing.&lt;/p&gt;

&lt;p&gt;I wrote a repair tool that rebuilds the manifest by reading the snapshots that survived. It got 137 of 141 back. Four were gone. No tool was going to fix that.&lt;/p&gt;

&lt;p&gt;Then I fixed the actual bug: &lt;code&gt;fsync&lt;/code&gt; before the rename, a &lt;code&gt;manifest.cvb.prev&lt;/code&gt; kept one generation back, a fallback that reads it when the primary is unreadable, and an error message that tells you how many snapshots survived and what command to run — instead of &lt;em&gt;bad magic&lt;/em&gt;, which is true and useless. There is a test that simulates the interrupted write, because a durability fix without a reproduction is a hope.&lt;/p&gt;

&lt;p&gt;If you're evaluating anything that holds data you care about, that's the question worth asking: not whether the author claims atomic writes, but whether they know the difference between atomic and durable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it up
&lt;/h2&gt;

&lt;p&gt;Server, on a Linux box with systemd and Node 22 at &lt;code&gt;/usr/bin/node&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://pomnia.ai/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It resolves &lt;code&gt;pomnia-brain-core-*-linux-x64.tar.gz&lt;/code&gt; from &lt;code&gt;releases/latest&lt;/code&gt;, verifies sha256 against the published &lt;code&gt;.sha256&lt;/code&gt; asset, then hands off to &lt;code&gt;deploy/install.sh&lt;/code&gt; under sudo. &lt;code&gt;POMNIA_BOOTSTRAP_DRY_RUN=1&lt;/code&gt; stops after unpack so you can read the thing before it touches your system, which is the correct instinct with any &lt;code&gt;curl | sh&lt;/code&gt;. Add &lt;code&gt;--with-ollama&lt;/code&gt; to answer the embedding-model prompt up front.&lt;/p&gt;

&lt;p&gt;The installer creates a system user, writes the unit, starts the service, and &lt;strong&gt;checks that it answers before telling you it worked&lt;/strong&gt;. It prints an admin password and a bearer token once. On a fresh install the host claims the empty vault and becomes its writer, so an agent can save into it immediately with no desktop involved.&lt;/p&gt;

&lt;p&gt;Then point an agent at it. Cursor, &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pomnia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://192.168.1.10:7865/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer btk_…"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code uses &lt;code&gt;~/.claude.json&lt;/code&gt; with &lt;code&gt;"type": "http"&lt;/code&gt;. VS Code 1.103+ uses the same shape but the top-level key is &lt;code&gt;servers&lt;/code&gt;, not &lt;code&gt;mcpServers&lt;/code&gt;. Pomnia Desktop's Connect tab generates all of these for you, which is less impressive than it is useful, because the top-level key differing between clients is exactly the kind of thing that costs an hour.&lt;/p&gt;

&lt;p&gt;The tools an agent then sees: &lt;code&gt;search_library&lt;/code&gt;, &lt;code&gt;save_conversation&lt;/code&gt;, &lt;code&gt;checkpoint_session&lt;/code&gt;, &lt;code&gt;get_user_profile&lt;/code&gt;, &lt;code&gt;memory&lt;/code&gt;, &lt;code&gt;library_status&lt;/code&gt;, &lt;code&gt;list_skills&lt;/code&gt;, &lt;code&gt;list_cli_skills&lt;/code&gt;, &lt;code&gt;get_skill&lt;/code&gt;. Nine, and that's the whole surface. Two of them do most of the work — &lt;code&gt;get_user_profile&lt;/code&gt; at session start so the agent knows who it is talking to, and &lt;code&gt;search_library&lt;/code&gt; before it answers anything that might already have been decided.&lt;/p&gt;

&lt;p&gt;The unit does not depend on Ollama at all — no &lt;code&gt;Requires=&lt;/code&gt;, not even a &lt;code&gt;Wants=&lt;/code&gt;, because the appliance embeds in-process and never needs it. Without Ollama the server still starts and still serves skills, profile and note reads; semantic search stops and &lt;code&gt;/healthz&lt;/code&gt; reports &lt;code&gt;degraded&lt;/code&gt; rather than pretending. &lt;code&gt;/healthz&lt;/code&gt; returns 503 when the server genuinely cannot serve — an index that is empty while the vault holds notes, an unreadable vault, a database that will not open — because "process is up, every search returns nothing" used to be the state that looked healthy. An empty index over an empty vault is &lt;code&gt;degraded&lt;/code&gt; and answers 200: a fresh install has not failed, it has nothing yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sync is push-based and local.&lt;/strong&gt; The desktop syncs both ways to a server you run, keeping both copies when a file differs on each side. No git-remote, no S3, no hosted anything, no mobile client — portability past your own machines is still copying a folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One vault, one writer.&lt;/strong&gt; A second instance over the same corpus serves read-only and names who holds the lock. Real multi-tenancy needs auth and per-user isolation, which is a different product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing is signed.&lt;/strong&gt; SmartScreen warns on every new Windows hash, Gatekeeper warns on the macOS &lt;code&gt;.dmg&lt;/code&gt;. That is reputation, not a verdict, and the certificates cost money I have not spent. Do not turn off your antivirus for me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distillation still wants a chat model, and it takes the machine with it.&lt;/strong&gt; The server embeds in-process now, but distilling needs Ollama, and a large backlog on a consumer GPU runs for hours. Pointing the desktop at Ollama on another box fixes that completely, and it is what I actually run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;run_skill&lt;/code&gt;, &lt;code&gt;search_code&lt;/code&gt; and &lt;code&gt;code_status&lt;/code&gt; are stubs.&lt;/strong&gt; Handled so a client with a cached catalogue gets a clear answer. They do not work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It is 0.1.x.&lt;/strong&gt; The repo went public two months ago; the first installable release is a month old. Six months on my own machines is a real test and a narrow one: one person, one language pair, three computers. Everything above is what happened to me, not a claim about what will happen to you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why AGPL and not a cloud vault
&lt;/h2&gt;

&lt;p&gt;A hosted vault would be easier to build and much easier to sell. It would also make me the thing I'm trying to route around: another company holding the record of how you think, with its own retention policy and its own exit.&lt;/p&gt;

&lt;p&gt;AGPL-3.0-only, specifically, rather than MIT. Section 13 means anyone offering a modified Pomnia as a network service owes their users the source. MIT would let someone take this, close it, host it, and sell the exact product I refused to build. The licence doesn't forbid making money — I hold the whole copyright and can sell commercial licences to anyone AGPL doesn't suit. It forbids making money quietly on a closed fork.&lt;/p&gt;

&lt;p&gt;The name is reserved separately: fork the code, call it something else. Code can be audited; a name has to be trusted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it is
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/lobrzut/pomnia" rel="noopener noreferrer"&gt;github.com/lobrzut/pomnia&lt;/a&gt; — AGPL-3.0-only. Site and docs: &lt;a href="https://pomnia.ai" rel="noopener noreferrer"&gt;pomnia.ai&lt;/a&gt;. Releases carry sha256 for every artifact.&lt;/p&gt;

&lt;p&gt;The name is Old Polish. &lt;em&gt;Pomnieć&lt;/em&gt; — to remember.&lt;/p&gt;

&lt;p&gt;If you try the server and the install script does something stupid on your distro, that's the feedback I want most — it's the newest code in the project.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>selfhosted</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Self-hosted second brain with MCP</title>
      <dc:creator>lobrzut</dc:creator>
      <pubDate>Wed, 17 Jun 2026 17:11:21 +0000</pubDate>
      <link>https://dev.to/lobrzut/self-hosted-second-brain-with-mcp-59d4</link>
      <guid>https://dev.to/lobrzut/self-hosted-second-brain-with-mcp-59d4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update, August 2026 — this project is now Pomnia.&lt;/strong&gt; It was rewritten from Python to TypeScript, the vault is encrypted, and the server speaks MCP over HTTP. The repository this post links to is frozen. Current version: &lt;a href="https://github.com/lobrzut/pomnia" rel="noopener noreferrer"&gt;github.com/lobrzut/pomnia&lt;/a&gt; · &lt;a href="https://pomnia.ai" rel="noopener noreferrer"&gt;pomnia.ai&lt;/a&gt; · &lt;a href="https://dev.to/lobrzut/your-agent-forgets-you-every-session-i-built-the-memory-layer-instead-of-another-wrapper-fjn"&gt;the new write-up&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I run IT and cybersecurity ops by day and tinker in a homelab at night. The problem I kept hitting: useful context from Cursor and Claude Code sessions evaporates when the chat ends. Notes end up scattered. RAG demos are cloud-first. I wanted something I own.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/lobrzut/brain" rel="noopener noreferrer"&gt;Brain AI Hub&lt;/a&gt;: a portable second brain with a local LLM, markdown vault, semantic search, and MCP hooks for IDE agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local LLM&lt;/strong&gt; - Ollama (qwen2.5, nomic-embed). OpenAI-compatible API on &lt;code&gt;:11434&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge store&lt;/strong&gt; - Obsidian-style vault, PDF/EPUB library, sqlite-vec RAG, lightweight knowledge graph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent bridge&lt;/strong&gt; - three MCP servers (&lt;code&gt;brain-vault&lt;/code&gt;, &lt;code&gt;brain-library&lt;/code&gt;, &lt;code&gt;brain-rag&lt;/code&gt;) with one-click deploy to Cursor, Claude Code, VS Code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transcript pipeline&lt;/strong&gt; - distills exports from Claude/Cursor/Antigravity into vault markdown, dedupes, indexes code, runs scheduled jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard&lt;/strong&gt; - FastAPI UI on &lt;code&gt;:7860&lt;/code&gt; for services, chat, GPU/VRAM, API keys, pipeline status.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Two editions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Edition&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Windows portable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Install.bat&lt;/code&gt; then &lt;code&gt;Start.bat&lt;/code&gt; - copy the folder, run on another PC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linux server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;linux/bootstrap.sh&lt;/code&gt; on the server (see repo README) - MCP SSE gateway on &lt;code&gt;:7862&lt;/code&gt; for LAN clients&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Install scripts speak English and Polish. Set &lt;code&gt;LANG=en&lt;/code&gt; or &lt;code&gt;LANG=pl&lt;/code&gt; in &lt;code&gt;locale.env&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP in practice
&lt;/h2&gt;

&lt;p&gt;On Windows, Brain deploys stdio MCP configs from the dashboard. On Linux, point Cursor at the SSE gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"brain-rag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://192.168.1.10:7862/sse/brain-rag"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agents can search your vault, pull library chunks, and run skills without sending data to a third-party memory API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP instead of only RAG?
&lt;/h2&gt;

&lt;p&gt;RAG answers retrieval. MCP gives agents &lt;strong&gt;tools&lt;/strong&gt;: write a note, list vault files, trigger a skill, query the code index. That matches how Cursor and Claude Code actually work: function calls mid-session, not a single embedding search at prompt time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;Python, FastAPI, Ollama, sqlite-vec, PowerShell (Windows), systemd (Linux). Homelab-friendly: MikroTik/UniFi networking, WireGuard, Docker where it helps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/lobrzut/brain.git
&lt;span class="nb"&gt;cd &lt;/span&gt;brain
&lt;span class="c"&gt;# Windows: Install.bat &amp;amp;&amp;amp; Start.bat&lt;/span&gt;
&lt;span class="c"&gt;# Linux:  curl -fsSL https://raw.githubusercontent.com/lobrzut/brain/main/linux/bootstrap.sh | sudo bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://127.0.0.1:7860&lt;/code&gt;, connect MCP from the Tools tab, drop a PDF in the library, run a distill job on an old chat export.&lt;/p&gt;

&lt;p&gt;Feedback and issues welcome on &lt;a href="https://github.com/lobrzut/brain" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>rag</category>
      <category>ollama</category>
      <category>homelab</category>
    </item>
  </channel>
</rss>
