An AI assistant can now work inside a ShieldFive vault: list it, search it,
find duplicates, read a text file, rename and move things, upload, and move
local files in. It does this through @shieldfive/mcp, a Model Context
Protocol server that runs on your machine. The ShieldFive server still never
sees a file name or a byte of plaintext.
This post records how that works, what shipped between 2026-09-14 and 2026-09-21, and where the guarantee stops. The last part is the important one.
The problem
Every AI integration for cloud storage works the same way: the provider hands the assistant a token, the assistant calls an API, the API returns plaintext. That requires the provider to be able to decrypt. For an end-to-end encrypted vault it defeats the point, so the decryption has to happen somewhere the server is not.
What runs where
- The browser creates the access, while the vault is unlocked. You pick folders, a scope and an expiry.
- The MCP server runs locally over stdio, next to the assistant. It decrypts in memory and writes nothing decrypted to disk. One module makes network calls.
- The ShieldFive server checks scope, expiry and revocation on every request, and serves ciphertext. It stores a SHA-256 of the access token, not the token.
What a grant is
A grant is a connection string:
sf-grant-v1:<grant_id>.<token>.<secret>.<pkfp>
token is the bearer credential the server checks. secret never reaches the
server. pkfp pins the fingerprint of the owner's ML-KEM public key, so
uploads are encrypted to the right key.
From the secret the browser derives a grant key:
GK = HKDF-SHA256(secret, salt = grant_id, info = "shieldfive/v1/agent-grant/wrap")
and wraps each chosen folder's key under it with AES-GCM, with the grant, object kind and object id in the associated data. A wrap made for one grant or one object does not open under another. Subfolders open through the vault's normal folder-key chain, so a grant holds one wrap per chosen folder, not one per file. A whole-vault grant wraps the root-level files individually; no grant ever holds the root key. The Bin, Media and incoming document requests are excluded unless you opt in.
These formats live in @shieldfive/crypto/vault (1.0.0-rc.5), so the
browser and the MCP server share one reviewed implementation, checked against
vectors from an independent implementation.
Scopes and revocation
A grant is read, organize or write. It expires after at most 90 days. Revoking it deletes its key rows and fails the next request with a 401; the MCP server re-fetches the grant on every call and caches nothing that would outlive a revocation.
Nothing a grant can reach deletes anything. vault_trash moves items into a
per-connection folder in the Bin; there is no delete route in the API a grant
can call.
Moving files in, without deleting
vault_move_in is the tool behind "free up space on this machine". For each
file, up to 50 per call and 512 MB each: encrypt, upload, read the upload
back, compare by SHA-256. Only after the comparison passes is the local
original moved to .shieldfive-mcp-trash, with a manifest naming its vault
copy. The first failure stops the batch. Nothing is deleted; the disk space
comes back when you empty that folder yourself.
Release history
- 2026-09-14,
0.1.0: nine local-only tools, no vault access. - 2026-09-16,
0.2.0: first version on npm. - 2026-09-18: grant design, schema, and the crypto
vaultmodule. - 2026-09-19,
0.3.0: vault tools through grants. - 2026-09-20,
0.4.0: authorize in the browser instead of pasting a connection string. - 2026-09-21,
0.5.0to0.6.x: write scope,vault_upload,vault_move_in.
One failure is worth recording. In 0.5.0 every real upload failed at the
final step: the finalize request omitted the SHA-1 of the ciphertext and the
server returned 400. Nothing was stored or charged. The first production smoke
test caught it and 0.5.1 fixed it the same day. The tests had passed
against a test double that did not enforce the rule; it enforces it now.
Where the guarantee stops
- The model provider sees what the assistant reads. When
vault_read_filereturns a document to a hosted model, that document goes to the model's provider. Encryption protects the vault from ShieldFive, not from the assistant you chose to connect. A local model is the only way around this. This is the largest limit, and it is not one that encryption can fix. - Revoking does not un-read. Revocation stops new requests. A leaked secret together with ciphertext already downloaded stays decryptable, because revoking does not re-key the files.
- The connection string is a live key. Anyone who copies it has the
grant's access until it expires or is revoked. Kept in
SHIELDFIVE_GRANT, it is readable by anything that can read the process environment. - Grant creation trusts the page. When the grant is created, a compromised server serving the web app could mislabel which folder you picked. Later requests are checked against the owner's keys.
- Prompt injection still works inside the scope. Tool output is fenced as untrusted content, but that does not make a model ignore instructions in a file. What an injected instruction can do is bounded by the grant's scope and folders, and every change it can make is reversible.
Where to check it
- Source,
SECURITY.mdand the changelog: github.com/shieldfive/mcp, MIT licensed, published to npm with provenance as@shieldfive/mcp - The wrap formats and vectors:
github.com/shieldfive/crypto,
spec/vault-formats.md