Prove it yourself
Do not trust us. Audit it.
SealedBrief is built for people who are personally answerable for the confidentiality of their files — lawyers, clinicians, journalists, researchers. You should not take a privacy claim from any vendor on faith, ours included. So here is how to check the ones that matter, on your own machine, in a few minutes. Screenshots are fakeable; these are procedures you run.
Everything below runs against the same free 14-day trial you can download today — the full product, not a demo. The same walkthroughs ship inside the app. And the honest boundaries are on this page too, stated plainly: an audit you can only pass by hiding the caveats is not worth running. Jump to what this does not protect first if you like.
The product ships today for Linux (AppImage) and macOS (Apple Silicon); Windows is on the waitlist. The deep-check commands below are given for both Linux and macOS. The five-minute behavioural check at the top needs no terminal and behaves the same wherever the app runs.
The five-minute check anyone can run
You do not need to be technical for this one. The clearest evidence that an app genuinely runs on-device is simple: cut the network, and see whether it still works. If it makes you more comfortable, run this first with a folder of ordinary, non-sensitive documents.
# 1. Take this machine off the network completely. # Linux: nmcli networking off (or just switch Wi-Fi off) # macOS: turn Wi-Fi off from the menu bar and unplug Ethernet # Either: flip on airplane mode / pull the cable. # 2. WITH THE NETWORK ALREADY DOWN, open SealedBrief and: # - index a folder of documents # - ask a few questions and read the cited answers # - search your vault # Everything still works. The model, the retrieval, the OCR and the # search all run on this machine. Notice the order: the network is cut # BEFORE you index anything, so even your very first test never has the # chance to upload a file. If a cloud service were secretly involved, # these steps would fail with the network gone. They don't.
That is the whole claim, and you just tested it. If SealedBrief quietly depended on a cloud model or a remote service, indexing and answering would break the moment the network went away. They don't — because the engine that reads your documents runs on your hardware.
You have now done the check that matters most — and you did it yourself
Everything past this point is for whoever looks after your computers. If that is not you, that is completely fine: copy this page's link, forward the deep-check section, and they can confirm the same claims at the packet and file level in about fifteen minutes. You do not need to run any of it yourself to trust the result — the behaviour you just watched is the same thing the deep checks measure from the inside.
Deep check 1 — the document engine opens no network connection
In plain words: the steps below let you (or your IT person) watch the part of the app that reads your documents and confirm it never opens a single connection to the outside world.
SealedBrief splits into two separate programs — one that reads your documents and one that talks to the network — and they can never trade jobs. (We call this split-brain.) They are ordinary operating-system processes:
- Presentation / UI plane — the interface. This is the process with network access: licence checks, an opt-in update check, and help links you click.
- Compute plane — the LLM, embeddings, hybrid retrieval, OCR, key management, and the encrypted SQLCipher + LanceDB stores. It is the one process that ever sees your files, your queries, and your decrypted text — and it has no network egress by construction.
The Compute plane's isolation rests on three independent layers:
- No first-party networking code. The Compute plane's own module
(
compute_service.py) imports no networking module — nosocket,requests,urllib,http,httpxoraiohttp. The bundled ML libraries technically can open connections, which is exactly why the next two layers exist. - Offline pins. Before any model loads, it sets
HF_HUB_OFFLINE=1andTRANSFORMERS_OFFLINE=1, so the embedder and reranker never reach out to huggingface.co even on a captive network. There is a single, loud, first-run-only override (SEALEDBRIEF_ALLOW_MODEL_DOWNLOAD=1) used to fetch models on a dev build; the shipped build bundles every model, so a normal install never exercises it and the guard stays active. - A process-wide runtime guard. It monkey-patches
socket.socket.connect,socket.socket.connect_exandsocket.getaddrinfo: any non-loopback connection — or even an external DNS lookup, which is itself observable egress — raises a hard error. Only loopback and AF_UNIX (the in-process IPC and the OS keyring) stay allowed.
Be clear about what you can check versus what you are taking on design faith. From the shipped AppImage, layers 1 and 2 are assertions about our source, which is not open for inspection — so do not accept them as proof. What is directly falsifiable from the binary is layer 3's behaviour: the strace / ss / tcpdump procedures below, which measure what the running process actually does. Those procedures, not our description of the code, are the proof.
The boundary claim, stated exactly
It is not that the app never touches the network. And it is not that document text never enters the network-facing process — the UI plane renders your answers and citations, so that text is necessarily in its memory to be drawn on screen. The precise claim is narrower, and checkable: the process that reads your documents never touches the network, and the process that does touch the network never transmits your document content — what it sends is bounded to the fixed, tiny licence and update request shapes listed in the next section. Rendered text sitting in UI memory is not egress; a byte on the wire is, and that is what the capture measures.
Linux (the currently shipped platform)
Step 1 — identify the two processes
# List SealedBrief's processes, largest first. # (For the shipped AppImage build, swap -C python for -C sealedbrief.) ps -o pid,ppid,rss,comm --sort=-rss -C python # The UI / Presentation plane is the parent. The Compute plane is its # largest-RSS child — it holds the ~15 GB models (and, on Linux, the # GPU). Confirm the parent/child link: pgrep -P <UI_PID> # There is no dedicated OS process *name* for the Compute plane, so do # NOT take the app's word for which PID it is. The signals here are # circumstantial (child of the UI PID; the huge-RSS process; the GPU # owner in nvidia-smi) or self-reported (each line it writes to # <vault>/logs/sealedbrief.log.json is tagged processName="Compute"). # Note the candidate as COMPUTE_PID, then BIND it to your documents in # the next step — that binding, not the log tag, is what proves identity.
Step 2 — bind the Compute PID to your documents (identity proof)
# Open a document in the app, then confirm the SAME PID you are about to # trace actually holds your document + vault file descriptors. A decoy # "no-egress" process would fail this test — this is what ties the # no-egress proof to the process that really reads your files. sudo lsof -p <COMPUTE_PID> | grep -iE 'core\.db|lancedb|/your-docs-folder/' # Expected: the Compute PID has core.db, the lancedb store, and the file # you just opened held open. Combined with the socket check below (zero # network sockets on this same PID), you have proven the file-reading # process and the no-network process are one and the same.
Step 3 — show the Compute plane owns no public socket (TCP + UDP)
# Prove the Compute plane owns no non-loopback socket — TCP *and* UDP, # so QUIC / HTTP-3 (which rides on UDP port 443) cannot hide. Run this # WHILE you actively ingest a large corpus and fire several chat queries: sudo ss -tuanp | grep "pid=<COMPUTE_PID>" # Expected: nothing, or only loopback / AF_UNIX IPC. You should never # see an ESTABLISHED TCP connection — or an unconnected UDP socket — to # a public IP owned by this PID.
Step 4 — prove it at the syscall level (strongest)
# The strongest single proof — trace every connect/send attempt the # Compute plane makes at the syscall level while you ingest and chat. # sendmmsg is included because a UDP/QUIC sender batches through it: sudo strace -f -e trace=connect,sendto,sendmsg,sendmmsg -p <COMPUTE_PID> # Expected: every connect() targets 127.0.0.1 or an AF_UNIX path. Zero # connects to a routable AF_INET / AF_INET6 address. (A regression would # surface as a hard NetworkEgressError, not a silent connection.) # # NOTE: strace attaches AFTER the process is running, so it covers only # the attached window. Startup and shutdown are covered by the from-launch # tcpdump capture in the next step — which is why you start that BEFORE # opening the app.
Step 5 — enumerate the whole app's real egress
# Enumerate the WHOLE app's real egress — TCP and UDP/QUIC alike. Start
# the capture BEFORE you launch SealedBrief so startup traffic is covered.
# There is no 'tcp' filter here, so QUIC / HTTP-3 on UDP/443 is included:
sudo tcpdump -n -i any -w vault.pcap 'not net 127.0.0.0/8 and not net ::1/128'
# Now launch the app and drive a full session: activate a licence, ingest
# a large corpus, run many chats, then quit. Stop tcpdump and list the
# endpoints across BOTH transports:
tshark -r vault.pcap -q -z endpoints,ip
# tcpdump cannot filter by PID on Linux, so attribute each flow with:
sudo ss -tuanp # snapshot TCP + UDP connections during the session
sudo nethogs -v 3 # per-process byte counts (add an interface, e.g. eth0,
# if none is auto-detected)
# Expected remote endpoints: the IPs behind licenses.sealedbrief.com,
# plus — ONLY if you opted in to update checks —
# sealedbrief-updates.s3.amazonaws.com. Every byte is attributed to the
# UI PID, never to COMPUTE_PID. Step 6 — confirm the flows carry no documents
# The licence/update flows are HTTPS, and the licence host is TLS-pinned, # so a capture cannot READ the payload and you cannot MITM it. Bound them # the two honest ways instead: # # (a) Shape: the licence bodies are a few hundred bytes of fixed JSON # (the exact fields are listed just below); the update check is a # bodyless GET. # (b) Volume-invariance: ingest a multi-GB corpus and run many chats, # then compare byte counts. Document volume drives ZERO extra egress, # which rules out BULK exfiltration. It cannot, by itself, exclude a # slow low-bandwidth covert channel — but what IS reproducible is # that request count and timing track the fixed licence-heartbeat # interval, not your document activity. tshark -r vault.pcap -q -z conv,tcp # compare sizes before vs after # Also confirm the Compute plane triggers no DNS lookups at all — # external name resolution is itself observable egress, and it is guarded, # so it simply never happens: sudo tcpdump -n -i any 'udp port 53'
macOS (Apple Silicon)
macOS ships today as a signed, notarized build for Apple Silicon. The checks translate directly from Linux; this is what a Mac-using reviewer or their IT contact runs.
Identify the processes
# Identify the two processes — the Compute plane is the high-RSS child. ps -ax -o pid,ppid,rss,comm | grep -i sealedbrief
Bind the Compute PID to your documents
# Bind the candidate PID to your documents before trusting any egress # result on it — open a file in the app, then confirm this PID holds your # document + vault descriptors: sudo lsof -p <COMPUTE_PID> | grep -iE 'core.db|lancedb'
Per-process sockets — no remote connections (TCP + UDP)
# Per-process socket list for the Compute plane. lsof -i covers TCP and # UDP, so QUIC/HTTP-3 is included. Expect no non-loopback network files, # and zero remote connections during ingest + chat: sudo lsof -i -a -p <COMPUTE_PID> nettop -m tcp -p <COMPUTE_PID> # then repeat with -m udp for QUIC/HTTP-3
Whole-machine capture
# Whole-machine capture, started BEFORE you launch the app (swap en0 for # your active interface). No transport filter, so UDP/QUIC is captured # too; read vault.pcap in Wireshark — the same two hosts, UI process only: sudo tcpdump -n -i en0 -w vault.pcap 'not net 127.0.0.0/8 and not host localhost'
Little Snitch, in alert / interactive mode, is a clear check on a Mac: the Compute helper
process never raises a connection prompt because it makes no attempts, while the UI process
prompts only for licenses.sealedbrief.com and — if you opted in —
sealedbrief-updates.s3.amazonaws.com. That per-process attribution is the
split-brain design made visible.
A capture tool cannot read inside HTTPS, so we do not claim to have "read" that no document bytes are sent. The two checks above bound it correctly instead: the request shape is fixed and tiny, and the egress volume does not grow when your document volume grows.
What the interface does contact
A skeptic inspecting the capture from Step 5 deserves to know, in advance, exactly what they should see. This is the complete list. None of it carries your documents or your queries.
-
licenses.sealedbrief.com— licence activation and heartbeat (paths/v1/activate,/v1/heartbeat,/v1/devices,/v1/deregister,/v1/crl), TLS-pinned. The activate request body is exactly{ user_email, machine_fingerprint, requested_tier, stripe_event, license_id }; the heartbeat body is exactly{ license_id, machine_fingerprint, beacon_at }. No document text, no query text, no vault content. -
sealedbrief-updates.s3.amazonaws.com/v1/manifest.json— a signed update-manifest check. It is opt-in and off by default; it sends no body and downloads a small Ed25519-signed JSON. A default install shows only the licence host. -
docs.sealedbrief.com— help and troubleshooting links, opened in your browser only if you click them. - Telemetry — opt-in and off by default. When enabled it is
a PII-scrubbed crash report (paths and filenames stripped), never document content; the
licence beacon it can carry is
{ license_id, machine_fingerprint, beacon_at }and nothing more.
If your capture shows anything leaving the machine that is not on this list, that is a finding — email support@sealedbrief.com and we will treat it as a bug.
Deep check 2 — your documents are encrypted at rest
In plain words: your vault on disk is scrambled ciphertext. Anyone who copies the files without your login and keychain sees only noise — and you can watch that be true.
SealedBrief keeps two stores on disk. core.db is SQLCipher — whole-database
AES-256 page encryption (the file registry and the full-text index). In lancedb/
the text and metadata are AES-256-GCM field-encrypted, with HKDF-SHA256 subkeys and a fresh
random nonce per record, and the source path is stored as a keyed HMAC-SHA256 digest rather
than in plaintext. You can confirm the ciphertext yourself.
# The shipped build keeps your vault in a per-user data directory: # Linux: ~/.local/share/SealedBrief # macOS: ~/Library/Application Support/SealedBrief # (a dev/repo build with SEALEDBRIEF_DATA_DIR set uses that path instead) # cd into it so the paths below resolve, then locate the two stores: cd ~/.local/share/SealedBrief # macOS: cd ~/Library/Application\ Support/SealedBrief ls -la core.db lancedb/ # core.db is the SQLCipher store (the file registry + full-text index). # lancedb/vectors.lance/ is the vector store.
# 1. file(1) cannot recognise it — the plaintext SQLite header has been
# encrypted away. Expect the generic type "data":
file core.db
# => core.db: data (NOT "SQLite 3.x database")
# 2. The first 16 bytes are random — NOT the ASCII magic that every
# plaintext SQLite file begins with ("SQLite format 3\0"):
head -c 16 core.db | xxd
# 3. strings finds no readable text, no document content, no file paths
# — only high-entropy noise:
strings -n 8 core.db | head
# 4. Shannon entropy of the whole file, pure stdlib (no dependencies).
# AES ciphertext reads at ~8.0 bits/byte, indistinguishable from
# random. SealedBrief's CI gate pins a floor of 7.5 bits/byte:
python3 -c 'import collections,math; d=open("core.db","rb").read(); c=collections.Counter(d); n=len(d); print(round(-sum((v/n)*math.log2(v/n) for v in c.values()),4),"bits/byte")'
Three of those checks — the entropy floor, the absence of readable substrings, and the
non-plaintext header — are exactly what SealedBrief's CI gate asserts (a counter-control in
that test proves an ordinary plaintext SQLite file fails all three). The
file check is a fourth, human-facing confirmation of the same thing.
Extend the same scrutiny to the SQLCipher sidecars and the local log, so nothing slips out the side door:
# The SQLCipher sidecars (-wal / -journal / -shm) are page-encrypted the
# same way as the main DB — confirm none of them leaks plaintext:
for f in core.db-wal core.db-journal core.db-shm; do [ -e "$f" ] && { file "$f"; strings -n 8 "$f" | head -3; }; done
# Audit the local operational log yourself too. It is PII-scrubbed (file
# paths and names are redacted before write) and carries no document text
# — but do not take our word for it, read it:
strings logs/sealedbrief.log.json | head
grep -c '"processName": "Compute"' logs/sealedbrief.log.json # the plane tag The one thing that is not encrypted — say it first
The embedding vectors in lancedb/ are stored
unencrypted, by construction. Approximate-nearest-neighbour search is
plaintext float math; you cannot run cosine similarity over ciphertext. Only the associated
text and metadata are AEAD-encrypted (and the source path is an HMAC digest). Vectors are
lossy and hard to invert back into readable prose, but they are not sealed — so do not treat
them as if the underlying text were. You can see this directly: a LanceDB data file reads at
a lower entropy than core.db, precisely because plaintext vectors are
interleaved with encrypted blobs.
# The honest carve-out, made visible. A LanceDB data file reads at a
# LOWER entropy than core.db (~7.3 vs ~8.0 bits/byte) precisely because it
# interleaves PLAINTEXT embedding vectors with the AES-256-GCM-encrypted
# text and metadata (the source path is stored as a keyed HMAC-SHA256
# digest, never in plaintext):
python3 -c 'import collections,math,glob; f=glob.glob("lancedb/vectors.lance/data/*.lance")[0]; d=open(f,"rb").read(); c=collections.Counter(d); n=len(d); print(round(-sum((v/n)*math.log2(v/n) for v in c.values()),4),"bits/byte")' High entropy proves the file is not plaintext. It does not, on its own, prove the file is decryptable only with a key you hold. Prove that stronger, protective property directly — the master key lives in the OS keychain (macOS Keychain, or the Secret Service via libsecret on Linux), never inside the vault:
# Entropy proves "not plaintext" — NOT "decryptable only with YOUR key". # Prove the stronger property directly. The master key lives in the OS # keychain, never inside the vault, so a copy on a machine (or a fresh OS # user) without that keychain entry must fail to open: cp -r ~/.local/share/SealedBrief /tmp/stolen-vault SEALEDBRIEF_DATA_DIR=/tmp/stolen-vault sealedbrief # or point the AppImage at it # Expected: it fails CLOSED — the SQLCipher key handshake rejects the # absent/wrong key and the app refuses to open the vault (DatabaseError), # with no graceful degradation. The ciphertext is bound to a key you never # stored on disk, not merely high-entropy.
A stolen data directory or backup snapshot — including the -wal /
-journal / -shm sidecars — is unreadable without the matching
keychain entry, which the OS encrypts under your login password. Wrong key means the vault does
not open at all: there is no graceful degradation, on purpose.
Every claim here protects a powered-down or locked machine. Once the app is unlocked and running, the key and your decrypted documents are necessarily in memory — see the boundaries below.
Deep check 3 — the document engine runs offline
The AI models — the LLM plus the embedding and reranking models — are bundled inside the download. That is why the file is large, and it is what lets the document work run with the network gone. Nothing is fetched at runtime: the offline pins from Deep check 1 mean the model loaders never reach out, even on a captive Wi-Fi portal.
To be exact: the document work — indexing, retrieval, OCR and Q&A — runs offline once your licence is activated. The app is not silent on the network overall; licence activation and the periodic heartbeat are the one network dependency, and they are disclosed in full under what the interface contacts. What runs offline is the engine that touches your files.
You already proved this in the five-minute check — ingestion and Q&A both worked with the network cut. The packet capture from Deep check 1 is the same result seen from the other side: no model weights, no inference, and no document content ever cross the wire.
What this does not protect — the honest boundaries
Honesty is the credibility. Here is what the design above does not defend against — state these to yourself before you rely on the tool:
- An unlocked, running machine. While the vault is open, the master key and your decrypted documents are in RAM by necessity. Encryption at rest protects a powered-down or locked device, not a live session someone is sitting in front of.
- Root / administrator on your host. An account with root can attach to the running app and read the key from memory. No local-first app can prevent this.
- A memory dump while the app is unlocked. Same reason — the working key and plaintext are legitimately in memory at that moment.
- Device seizure or coercion directed at you. If someone can compel your login password or your cooperation, the OS keychain hands over the key. This is a legal and physical-security problem, not one software can solve.
- The plaintext embedding vectors in
lancedb/, as described above — a disk-at-rest nuance, though note the vectors never leave the machine regardless. - Nation-state / APT adversaries and side channels are out of scope for the v1 release. No local-first tool on ordinary hardware defends against a determined state actor, and this is the same boundary every such tool has. What the design still removes is the cloud-vendor, subpoena, and network-exfiltration exposure that most confidentiality worries actually come from.
And plainly: this page is not a compliance certification. There is no formal STRIDE/DREAD scoring and no third-party penetration test on the v1 release — an external application-security review is planned, not yet done. What SealedBrief gives you is a mechanism you can inspect, not a stamp.
A few things are load-bearing on your side: enable full-disk encryption (FileVault / dm-crypt), don't run as root, lock your screen, and back up the master key out-of-band. There is intentionally no key escrow — if your keychain entry is wiped and you have no backup, the vault is unrecoverable. That is the cost of nobody but you holding the key.
Threat model at a glance
| Protects against | Does not defend against |
|---|---|
| Another non-privileged local user reading your vault | Root / administrator on your host (they read the key from RAM) |
| Physical theft of a powered-down or locked laptop | A memory dump taken while the vault is unlocked and running |
| Capture of a backup or Time-Machine snapshot | Device seizure or coercion directed at you (they compel the key) |
| Network exfiltration — the document engine has no egress | Nation-state / APT adversaries and hardware side channels |
| Cloud-vendor access, subpoena, or breach of your documents (they never leave the box) | The plaintext embedding vectors (lossy, unencrypted, but on-device only) |
| Hostile ingested documents (parser runs in the no-egress Compute plane) | Being read as a compliance certification (no third-party pen test on the v1 release) |
Verify the download itself
The checks above audit a running install. The complementary question — is the binary you downloaded the one we published? — is answered by the Ed25519-signed release manifest. The full walkthrough (SHA-256 match plus signature verification, Linux and macOS) lives on the download page: verifying integrity.
Run all of this on the free trial
The artifact under audit is the free 14-day trial itself — the full product, not a limited demo. To evaluate a confidentiality tool, run every check on this page against your own files, on your own hardware, watching your own network. Download it, work through the checks, and decide once you have seen the evidence.