Embeddings are the quiet tax of every "AI-native" product. You generate them, you store them, you index them, you search them — and at every step, the easy path is to pick one vendor, hand them your text, and let them keep the vectors. Six months in, you have a million embeddings in someone else's database, billed per query, with no clean exit.
Eigen Mesh is built to avoid that trap. This post walks through the three choices that make that work: where the embeddings live, who can see them, and how you swap the model underneath.
Where embeddings live: Postgres, not a vector DB
Eigen Mesh stores embeddings in the same Postgres instance as the rest of your data, using the pgvector extension. There is no separate vector database. No Pinecone, no Weaviate, no Chroma server running next to the app. One database, one backup, one thing to operate.
This works because pgvector is no longer a toy. As of Postgres 16 and current pgvector releases, approximate nearest neighbor (ANN) search over a few hundred thousand vectors is comfortably fast on a small instance, and it scales into the millions on real hardware with HNSW indexes. For a personal second brain — even a busy one — that's well within budget.
The win is operational. You don't run a second persistence layer. You don't write code to keep two systems consistent. Your embeddings are inside a transaction boundary with the rest of the thought row, so an enrichment that fails halfway doesn't leave you with a thought but no vector. RLS applies to the embedding rows the same way it applies to everything else.
Who can see them: nobody but the database
This is the part most products get quiet about. Embeddings are useful for search, but they're also a leakage surface. A raw embedding vector can be reversed into approximate text; a query embedding can be logged; a vector returned to a client can be cached somewhere you don't control.
Eigen Mesh treats embeddings as a database-only artifact. Concretely:
- Tool results contain text and scores only. The MCP
retrieve_thoughtsresponse includes the normalized thought text, category, score, and metadata. It does not include the embedding vector. - Query embeddings are computed in-process for SQL distance only. When you call
retrieve_thoughtswith a query, Eigen Mesh embeds the query, runs the ANN search inside Postgres, and discards the query vector. The vector is never serialized into a tool response. - LLM prompts never see embeddings. The in-app Chat agent calls
retrieve_thoughts, gets text back, and feeds that text to the LLM. The model sees words, not floats. - Embeddings are not exported. There is no API surface that returns a raw vector. If you want to leave, you export your text; you re-embed on the other side.
The policy is documented at /developers/embeddings-boundary. The short version: vectors are a search index, not a data product.
Swapping the model: BYOK gateways
The embedding model is the part most likely to lock you in. If your schema, your distance metric, and your retrieval tuning are all built around one vendor's text-embedding-3-large, switching is a re-embed of everything and a re-tune of every threshold.
Eigen Mesh makes the swap easier by not caring which model you use, as long as it speaks the OpenAI-compatible /api/v1/embeddings shape. You configure:
LLM_BASE_URL— the gateway origin.LLM_API_KEY— the key, or a dummy if your gateway doesn't need one.LLM_RULE_EMBEDDING— a routing rule UUID when the gateway supports per-tenant routing.
That's it. The app calls the endpoint, stores the returned vector in pgvector, and uses it for ANN. Swap the model, re-embed, move on.
This is where the LocalLLaMA setup is interesting. If you run a local embedding model behind a shim — nomic-embed-text in Ollama, bge-m3 in vLLM, anything that exposes the OpenAI shape — you can point Eigen Mesh at it and keep all embeddings on your hardware. No outbound calls, no per-token cost, no vendor reading your notes. The same applies to the chat model used for enrichment and rerank; the app treats them uniformly through the gateway.
For managed users, the default gateway handles this transparently. For self-hosters, it's the difference between "second brain in the cloud" and "second brain on the box under your desk."
Why not a dedicated vector DB
This comes up in every architecture review. The honest answer:
- Operational cost. A second database is a second backup, second set of credentials, second monitoring target, second upgrade window. For a single-tenant second brain, that's overhead without payoff.
- Consistency. Keeping vectors co-located with the row they describe means the same transaction that inserts a thought inserts its embedding. No drift, no reconciliation job.
- Latency. Hybrid retrieval in Eigen Mesh joins vector distance, FTS rank, and precomputed graph artifacts in one query. Doing that across two databases means either shipping vectors across the wire or denormalizing into one — at which point you've reinvented pgvector.
- Scale. pgvector scales further than most people give it credit for. If you outgrow it, the abstraction is thin — re-embed into something else and update the retrieval layer. The schema doesn't bake in vendor assumptions.
The trade is that you give up some ANN throughput relative to a purpose-built index. For a memory product, that's the right trade. For a billion-vector search corpus, it isn't. Different problem.
What this means in practice
Three concrete things you can do that you can't do with most "AI memory" products:
- Run fully offline. Local chat model, local embedding model, local Postgres. No outbound calls. The product still works.
- Switch embedding models without re-architecting. Change
LLM_BASE_URL, re-embed. The retrieval pipeline doesn't change. - Audit what leaves your deployment. MCP responses are text and scores. LLM prompts are text. Embeddings are Postgres rows. There is no hidden vector shipping to a vendor.
The trade we made
We picked Postgres + pgvector because it's the boring choice that ages well. One database to operate. One backup. One security model. Embeddings treated as a search index, not a product surface. A model-agnostic gateway so you can leave.
It's not the fastest possible vector store. It's not the most exotic. It is the one you'll still be happy about in three years.
Try it
If you want to poke at the actual schema, the storage layer is documented at /developers/architecture and the retrieval pipeline at /developers/how-memory-works. If you'd rather just drop a thought in and see what happens, start on managed with 100 free credits — no card, no model setup.