Encrypted (sealed) RAG
You have probably paused before dragging a file into someone else's web app. No specific reason, just the half-second of registering that once it lands over there, somebody over there can open it. That half-second is what this page is about. A managed search index cannot avoid reading your documents, because to find the right chunk it has to embed it and to embed it it has to see the plaintext, so by default your text and its vectors sit in our database in the clear. Fine for a public handbook. Not fine for a signed agreement or a medical record, where "the vendor's database got dumped" becomes a headline with your customer's name in it. A sealed store encrypts the text and the vectors at rest under a key tied to that one store, decrypts only the handful of chunks a query returns, and hands those back over TLS. Dump the database and you get ciphertext. What follows is the exact version of what that protects and what it doesn't, because on this subject the limits matter more than the pitch.
What sealing changes
A normal store keeps chunk text and embedding vectors in plaintext, so anyone who can read the database, whether that is a stolen backup or an operator with access, can read your documents. A sealed store encrypts the chunk text and the vectors at rest under a key tied to that store, using AES-256-GCM, before anything is written. A query decrypts only the handful of chunks it returns, in memory, and hands plaintext back to you over TLS. Dump the database and all you get is ciphertext.
The key layout is the ordinary envelope arrangement, which is worth spelling out because the guarantees follow directly from it. Each store gets its own random data key. Chunks are sealed under that key, and the key itself is stored only in wrapped form, encrypted under a higher key that never sits next to the data. Nothing shares a key with anything else, so a compromise scoped to one store stays scoped to one store, and the blast radius of any single leak is one tenant rather than the index.
Turning it on is one field at create time. Upload, attach and file_search stay byte-for-byte the same calls. Your application code does not change at all.
curl https://api.fightclub.pro/v1/vector_stores \
-H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"acme-contracts","embedding_model":"text-embedding-3-small",
"encryption":"managed"}'
# => { "id":"vs_...", "encryption":"managed", "vector_sealing":"full" }Sealed stores accept text-based files only today: text, Markdown, plain text and the like (JSON, XML, YAML), up to 25 MB. A sealed PDF, DOCX or image fails ingest with a clear error, so those documents go on a plaintext store for now. Export a contract or record to text or Markdown before sealing it.
Who holds the key: managed vs byok
This is the choice that decides your threat model, so it is worth getting right.
- ·
encryption: "managed"keeps the per-store key on our side, wrapped under our server key. A stolen disk or leaked backup is unreadable. We can still technically decrypt to serve your queries, so this is "encrypted at rest", not "the vendor cannot read it". It satisfies most compliance requirements for at-rest encryption with a per-tenant key. - ·
encryption: "byok"means you bring the key, as a passphrase presented per session. We store only a wrapped copy and never the passphrase, so while the store is locked it is ciphertext even to us. This is the choice when your requirement is that the vendor must not be able to read the data.
byok stores are locked until you present the passphrase. Unlock with POST /v1/vector_stores/:id/unlock and lock again with /lock. While locked, ingest and search fail closed: no plaintext comes out, ever.
What gets sealed: full vs source
By default (vector_sealing: "full") the embedding vectors are encrypted as well as the text. The reason is the thing almost everyone gets wrong about embeddings. A vector looks like inert numeric exhaust, so the instinct is that encrypting the text is the whole job and the coordinates are harmless leftovers. They are not. An embedding was produced deterministically from your sentence, and that means someone holding the vectors can run text through the same public model, compare, and steer towards sentences that land in the same place. What comes out is not a perfect transcript, though it is close enough to recover names, subjects and the gist of a clause. Encrypting the text while leaving the vectors readable protects the filing cabinet and publishes the index cards.
Full sealing closes that, so a database dump leaks nothing readable.
You can opt down to vector_sealing: "source", which keeps the vectors in plaintext for slightly faster ranking and seals only the document text. That guards the literal files but not their meaning, since the plaintext vectors still leak semantic content. Full is the default for exactly that reason; only choose source if you understand the trade and want the speed.
Large sealed corpora: the IVF mode
A sealed store normally keeps its vectors in a warm in-memory index for fast search, which is fine at the scale a single customer holds. If you have a genuinely large sealed corpus and do not want it all sitting decrypted in memory, set vector_index: "ivf".
In that mode we cluster the vectors and store plaintext cluster centroids. A query first finds the nearest clusters from the centroids, then decrypts only those clusters, so the whole corpus never has to be decrypted at once. The cost is that the centroids reveal how your documents group together at rest, which is coarse structure, not content. It is opt-in for that reason. If even that grouping cannot leak, the confidential deployment below is the answer.
GraphRAG on a sealed store
Encryption and GraphRAG compose. With both on, the entity names and relationship labels are sealed under your key, and only the facts a given query returns are decrypted. The shape of the graph (which node connects to which) stays visible at rest; the names attached to those nodes do not. So a dump shows that two things are related without revealing what they are.
The honest limits
Sealing is at-rest protection plus "we cannot read it without your key" for byok. It is not zero-knowledge, and it would be dishonest to sell it as such.
There is always a moment where your plaintext is sitting in our server's memory. You cannot embed ciphertext or search ciphertext, so to answer you at all we have to decrypt, in RAM, for the length of the request. Sealing protects the database on disk and the backup on tape. It does not make the data invisible to the machine doing the work. If that moment is unacceptable for your data, no cipher fixes it. The only real answer is moving the machine itself into hardware you control, which is what the confidential deployment below is for.
- ·
Plaintext exists in memory briefly while we parse, embed and answer. You cannot embed or search ciphertext, so there is always a transient decrypted moment in RAM.
- ·
A graph leaks its structure at rest even when the labels are sealed.
- ·
With
managed, we hold the key, so we can technically decrypt while serving you. Usebyokwhen that is unacceptable.
When even that is not enough: confidential deployment
For a threat model where no plaintext may touch our infrastructure at all, even transiently in memory, the answer is a confidential deployment. Bring-your-own-RAG runs the sealed engine inside your own confidential-compute environment (your SEV-SNP or TDX hardware, your key) so we never touch the data. There is also a customer-managed-key variant we host against your cloud KMS. Both are available for regulated deployments on request rather than self-serve, because they are wired to your specific environment.
Picking a mode
A short decision path. Most stores need none of this; reach for it when the data is genuinely sensitive.
- ·
Public or low-sensitivity content: no encryption. Fast and cheap.
- ·
Sensitive data, compliance wants at-rest encryption, you trust us to operate it:
encryption: "managed", full sealing (the default). - ·
The vendor must not be able to read it:
encryption: "byok"with a passphrase, lock when idle. - ·
Large sealed corpus you do not want decrypted in RAM: add
vector_index: "ivf"(accepts a coarse grouping leak). - ·
No plaintext may ever touch our infrastructure: confidential deployment / BYO-RAG, talk to us.