Engineering

Why we do TLS interception. And why it is safe when done right.

TLS interception has a deserved bad reputation. Enterprise middleboxes spent the 2010s implementing it badly, with weak certificate authority practices, broken cipher suite negotiation, and trust models that ended up reducing the security of the connections they intercepted. The phrase "TLS interception" in a security architecture review is a yellow flag for good reason.

We do TLS interception. We are aware of the reputation. The architecture we use is structurally different from the enterprise middlebox pattern in ways that matter, and this post walks through those differences in detail. If you are evaluating Vigil for an enterprise deployment, this is the post the security review will care about.

The question is not whether TLS interception is safe in the abstract. The question is whether a specific interception architecture preserves the security properties of the underlying connection. Some do. Most do not. Ours does, and this post explains why.

What TLS does, and what interception breaks

TLS provides three security properties to the endpoint: confidentiality (no third party can read the traffic), integrity (no third party can modify the traffic), and authenticity (the endpoint can verify it is talking to the expected counterparty, not an impostor).

A TLS interception layer sits between the client and the server, presenting itself as the server to the client and as the client to the server. It terminates one TLS connection, processes the cleartext, and originates a fresh TLS connection on the other side. This is the standard man-in-the-middle pattern, applied with the user's consent rather than against them.

The interception affects each of the three security properties differently.

Confidentiality is preserved against external observers as long as both legs of the connection are properly encrypted. It is not preserved against the interceptor, by design. The interceptor can read the cleartext. The question is whether the interceptor's access to the cleartext creates a confidentiality risk for the user, which depends on what the interceptor does with what it reads.

Integrity is preserved against external observers, again as long as both legs are properly cryptographically signed. The interceptor can modify the cleartext, in principle. Whether they do is a property of the interceptor's behavior.

Authenticity is the property most often broken by bad TLS interception. The client believes it is talking to the server, but it is actually talking to the interceptor. The interceptor signs its own certificates with a CA that the client trusts, presenting fake certificates that the client cannot distinguish from real ones. If the interceptor's CA is improperly secured, an attacker who compromises the CA can impersonate any server to the client. This is the structural risk that gives enterprise TLS interception its reputation.

A safe interception architecture has to address all three. We address them differently from the enterprise middlebox pattern.

How our architecture differs

One: the CA is on the user's machine, generated locally, never leaving

In an enterprise middlebox setup, the CA private key is on the middlebox, which is a server. If the server is compromised, the attacker has the CA key. The attacker can then impersonate any server to any client trusting that CA. The blast radius is "every client whose TLS interception trust chain leads to the compromised CA."

In Vigil's architecture, the CA is generated on the user's Mac at install time. The private key never leaves the machine. It is stored in the Mac's secure enclave where the hardware supports it, and in a file with restrictive permissions where it does not. The CA's trust scope is exactly one machine. If the user's Mac is compromised, the attacker has access to the user's Mac, which is already a worst-case scenario for confidentiality of everything on the machine. The CA does not extend the blast radius.

This is the most important architectural distinction. Enterprise middleboxes share a CA across many users, which means a single compromise affects many users. Vigil's CAs are per-user, generated locally, scoped to one device. There is no shared CA to compromise.

Two: interception is local-only, no off-machine traffic

The interceptor is a process running on the user's Mac. The cleartext that the interceptor reads stays on the Mac. The detection layer runs on the Mac. The audit chain is written to disk on the Mac. The policy engine runs on the Mac. No cleartext leaves the machine.

This is a structural property, not a configuration choice. The architecture has no path for off-machine telemetry to receive cleartext content. We do not have a "send anonymized samples to Vigil for model improvement" feature. We do not have it because the architecture would have to be redesigned to support it, and we will not redesign the architecture in that direction.

Compare this to enterprise middleboxes that send intercepted traffic to a SIEM, a cloud-hosted analytics service, or a model provider for analysis. Each of those off-machine paths is a confidentiality risk that the user has to evaluate against the security benefit. We removed the option.

Three: integrity is preserved by structure, not by promise

The interceptor has the cleartext. In principle, it could modify the request before re-encrypting it on the way out. In practice, it does not, because the architecture does not include any rewriting paths. The proxy reads the request, emits the structured form to the analysis plane, and forwards the original bytes to the upstream server. The structured form is for analysis. The bytes are forwarded unmodified.

This is verifiable in source. The proxy code path that handles request forwarding is small and isolated. It does not call into the policy engine. The policy engine operates on the response side, where the agent's actions are evaluated, not on the request side, where the user's input is in flight. The two surfaces are separate, and the request side is structurally read-only.

The user can verify this property. The source is in our repository. The path is one crate. The behavior is testable.

Four: authenticity is verified by the interceptor, then re-presented to the client

The interceptor establishes a TLS connection to the upstream server using the standard browser certificate trust chain (the same chain the user's machine was already trusting before Vigil was installed). The interceptor verifies the upstream server's certificate against the standard CA bundle. If verification fails, the interceptor fails the connection. The client sees the failure.

Then, on the client-facing side, the interceptor presents a certificate signed by the local CA that the user has explicitly trusted. The client verifies the local CA's signature, succeeds, and proceeds with the connection. The authenticity chain on the upstream side is preserved exactly as it was before Vigil was installed. The authenticity chain on the client side terminates at a CA the user installed deliberately.

If the upstream server's certificate is invalid for any reason (expired, revoked, signed by an untrusted authority), the interceptor will not present a valid certificate to the client. The client will see the same connection failure they would have seen without Vigil. The structure of certificate verification is preserved.

What we use the cleartext for

The cleartext is read by three layers, each with documented purpose.

The detection layer extracts a feature vector for each request and response. The features are statistical: token-level statistics, structural properties, encoding indicators. The features feed the four-model ensemble. The cleartext content itself is held only as long as the request is in flight; the features survive in the audit chain, but the original cleartext is not stored unless the user explicitly opts into the conversation export.

The decomposition layer parses the response and extracts declared actions. The action structure feeds the policy engine. Again, the original cleartext is not stored beyond the duration of the request unless the user opts in.

The audit layer records the structured representation in the VOAF chain. The chain entry contains the structured form, signed by the user's local key. The original cleartext can be reconstructed from the structured form for actions that have explicit content, but the chain is not designed for raw content storage. It is designed for verifiable action records.

The user controls retention. The default is to keep the chain (which is small) and discard the cleartext (which is large). Users who want to retain conversation history opt in explicitly.

What an attacker who compromises Vigil can do

A useful exercise. If an attacker compromises the Vigil binary on a user's machine, what can they do?

They can read the cleartext of AI traffic flowing through the proxy while their compromise is active. This is a real risk. The same attacker, having achieved code execution on the user's machine, can read any other traffic the user generates from any browser or AI client, with or without Vigil. The compromise of the host is the worst-case scenario; Vigil does not extend the worst-case scenario beyond what host compromise already implies.

They can modify the audit chain entries that are written after the compromise begins. They cannot modify entries written before the compromise without detection. The chain is hash-linked. A modified entry breaks the chain, which is detectable by the verifier. An attacker can append new entries (with the user's local key, which is now compromised), but they cannot retroactively rewrite history without leaving evidence of the rewrite.

They can disable the policy engine, allowing actions that would otherwise hold or block to proceed. This is a real risk. The mitigation is that the policy engine's enforcement events are recorded in the audit chain, so a forensic review can identify the gap between what should have been blocked and what was allowed. The mitigation is forensic, not preventive.

They cannot impersonate other servers to other users. The CA is local to the compromised machine. There is no shared trust scope.

They cannot exfiltrate data through Vigil's update channel without leaving a trace, because the update channel signs releases with our build CA, which is reproducible by independent third parties (see the v2 shipped post on byte-identical builds).

The threat model is bounded. It is not zero. We are honest about it.

What this means for the security review

If you are reviewing Vigil for enterprise deployment, the questions to ask, in order:

  1. Is the CA per-user, generated locally, never leaving the machine? (Yes. Verify in the source.)
  2. Does any cleartext leave the machine? (No. Verify in the source.)
  3. Is the proxy code path read-only on the request side? (Yes. Verify in the source.)
  4. Does the upstream certificate chain verify against the standard CA bundle? (Yes. Verify in the source.)
  5. Is the audit chain hash-linked? (Yes. Verify with the open-source verifier.)
  6. Is the build reproducible? (Yes. Verify by rebuilding from the tagged commit.)

These six questions are answerable from the source tree, the spec, and the build pipeline. We do not require you to take our word for any of them.

The closing argument

TLS interception is dangerous when implemented badly. Enterprise middleboxes have demonstrated this for a decade. The architecture matters. Local-first interception, with a per-user CA, no off-machine cleartext, structurally read-only request handling, and verifiable build provenance, is a different threat model from the enterprise middlebox.

The interception is the only architecture that produces the cross-provider observability the product needs. Browser extensions alone cannot do it; they live inside the browser's security boundary and cannot see traffic from native apps. OS-level network filters cannot do it without exactly the same TLS termination problem. The interception is structurally necessary.

The interception, done with the architecture above, is structurally safe. Not safe in the abstract. Safe given the specific constraints we have built around it.

The source is open for inspection. The verifier is independent. The build is reproducible. The threat model is documented. If your review surfaces a gap, we want to hear about it.

← Back to The Vigil Journal