> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tinfoil.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Security and threat model

> What a Bunker protects, what it does not, and where each guarantee comes from.

A Bunker is designed around two adversaries:

* **Infrastructure** - Tinfoil and the cloud host it runs on should not be able to read your work, log in, or impersonate your machine.
* **Malicious agents / compromised bunkers** - A coding agent with your credentials can be manipulated by API responses or malicious web pages. It's also possible for a malicious dependency to be unknowingly installed, then later used to attempt exfiltrating all available data as much as possible. Giving meaningful access control for reducing the blast radius is very important in such cases.

Secure enclaves and remote attestation handle the first. The [Perimeter](https://docs.tinfoil.sh/bunkers/perimeters.md) handles the second. This page explains how the pieces fit and what you are trusting.

## Root of trust

A Bunker runs as a confidential VM on AMD SEV-SNP or Intel TDX, ultimately all trust comes back to the hardware. Before the first instruction begins executing, the exact image of what was loaded is **measured** and is captured immutably for the runtime of the enclave. The processor signs an **attestation report** using its key, describing exactly what was launched. Your client checks that report before it sends a key or opens a session. See the [secure enclave primer](https://docs.tinfoil.sh/verification/secure-enclave-primer.md) and [how Tinfoil verifies enclaves](https://docs.tinfoil.sh/verification/verification-in-tinfoil.md) for the general mechanism.

The attestation covers the [CVM image](https://docs.tinfoil.sh/verification/cvm-image.md) and a hash of the Bunker's `tinfoil-config.yml`. That config pins everything else:

* the CVM version,
* the Bunker image, by SHA-256 digest,
* the toolchain pack, by its dm-verity root hash,
* the launch settings, including your owner public key and the Bunker's instance ID.

The attestation also commits to keys generated inside the enclave at each boot: an SSH host key and an owner session key. A client that has verified the attestation knows that whoever holds those keys is the measured Bunker.

### Bunker image

The Bunker image is built from the public [confidential-agent-sandbox](https://github.com/tinfoilsh/confidential-agent-sandbox) repository by GitHub Actions. Its base is Ubuntu 24.04, pinned by digest, and downloaded tools are checked against pinned SHA-256 hashes. Each release attests the image digest and the config with Sigstore build provenance, and the config pins the image by digest. The CLI and the web terminal check the Bunker's attestation against that published release.

### Software in the Nix closure

The agents and developer tools are not in the image. They ship as a separate **toolchain pack**: a Nix closure built from [nixpkgs](https://github.com/NixOS/nixpkgs) by the public [nix-closure](https://github.com/tinfoilsh/nix-closure) repository and packed into a read-only filesystem image protected by dm-verity.

The config pins the pack's dm-verity root hash, so the hash is part of the attestation. At boot, the enclave builds the verity table only from that measured hash and never trusts metadata inside the pack itself. Every block read from the pack is then checked against the hash tree, so a single altered byte in any binary produces a read error instead of running. The pack is mounted read-only and becomes the base layer of the Bunker's Nix store.

Anything you add later, with `nix`, `apt`, Docker, or an installer script, lives on your workspace volume. It is not covered by the measurement and is only as trustworthy as where it came from.

### The owner public key is the identity

A Bunker has no account-based login. Its only identity is the Ed25519 **owner public key** in its measured config, chosen when you create it:

* At every boot, the Bunker builds its SSH `authorized_keys` from the measured key alone, even on an existing disk. Password and keyboard-interactive login are disabled, and root login is not allowed.
* Unlocking the workspace and opening a shell both require proof of the owner key: an SSH login, or a web terminal session signed by the owner key and encrypted to the attested owner session key.
* Your Tinfoil account controls the Bunker's lifecycle (start, stop, update, delete) but cannot grant access. Neither Tinfoil nor your organization's admins can add a key. In an organization, only a Bunker's creator can open it.

The workspace disk key is generated by your client, in your browser or the CLI, and is released to the Bunker only after its attestation has been verified. The disk is encrypted and integrity-protected like other [encrypted volumes](https://docs.tinfoil.sh/containers/encrypted-volumes.md). With [synced keys](https://docs.tinfoil.sh/bunkers/quickstart.md#web-dashboard-vs-tinfoil-perimeter-open), Tinfoil stores the disk key only inside a bundle your passkey or recovery key must open. With CLI-only keys, it never leaves your computer. If you lose every copy, nobody can recover the workspace.

## How networking works

```mermaid theme={"dark"}
flowchart LR
    subgraph bunker["Bunker enclave"]
        agent["Agent"] --> tun["TUN device<br/>(all TCP and DNS)"]
    end
    subgraph perimeter["Perimeter enclave"]
        policy["Policy and<br/>credential substitution"]
    end
    tun -->|"SSH, Perimeter host key pinned"| policy
    policy -->|"TLS"| services["Connected services"]
    you["Your CLI or browser"] -->|"verifies both attestations,<br/>then SSH or encrypted session<br/>relayed by the Perimeter"| bunker
```

### Bunker to Perimeter

Tinfoil's host only lets a Bunker's VM open connections to its Perimeter's SSH port. That rule is enforced outside the enclave, so it is containment, not a confidentiality guarantee. Confidentiality comes from keys set up after attestation.

When you first connect, your CLI or browser verifies both enclaves, then hands the Bunker two things over its owner channel: a **Perimeter identity key**, which the Perimeter registers as that Bunker's agent identity, and the **Perimeter's attested SSH host key**, which the Bunker pins. The Bunker connects to the Perimeter over SSH, checking that pin. If anything on the host redirected the connection, the handshake would fail.

Inside the Bunker, a TUN device sends all TCP traffic through that SSH connection. DNS queries get placeholder addresses, so the hostname reaches the Perimeter intact and real DNS resolution happens there. UDP, ICMP, and IPv6 are dropped. If the tunnel drops, traffic stops: DNS stays pointed at the tunnel, and the host allows no other destination.

### Perimeter to services

The Perimeter terminates the Bunker's TLS with its own certificate authority, which the Bunker trusts. It identifies each request, applies your policy, substitutes credentials, and opens a new TLS connection to the service. Its outbound connections refuse private and internal address ranges, checked after DNS resolution, so a Bunker cannot use the Perimeter to probe the host's network. For [Tinfoil inference](https://docs.tinfoil.sh/models/overview.md), the Perimeter also verifies the inference enclave's attestation before sending anything.

### You to the Bunker

The Bunker keeps a reverse SSH connection to its Perimeter, authenticated with its attested host key, that exposes its SSH server and owner channel to you. When you connect:

1. Your client verifies the Perimeter's attestation and connects to it.
2. It asks the Perimeter to fetch the Bunker's attestation with a fresh random nonce, and verifies that report itself. The Perimeter only relays it and is not trusted to vouch for the Bunker.
3. It opens SSH, or the web terminal's encrypted session, to the Bunker through the Perimeter, pinned to the key in the verified attestation.

The Perimeter forwards these bytes but cannot read or alter them.

## Threat model

| Adversary | Can | Cannot |
| - | - | - |
| Tinfoil or the cloud host | Stop, delete, or refuse to run your Bunkers and Perimeters. See traffic metadata, such as which services your Perimeter connects to and when. Roll the workspace disk, or individual sectors, back to an earlier state. | Read the Bunker's memory, workspace, or traffic. Log in, unlock the workspace, or obtain your keys. Impersonate your Bunker or Perimeter to your client. |
| An agent or malicious code inside the Bunker | Act as root inside the Bunker: read, change, or delete anything on the workspace. Use the connections you granted, within your policy. | See your real credentials. Reach services you did not connect. Skip an approval you required. Change its own policy, other Bunkers' access, or MCP tool pins. |
| Someone with your Tinfoil account login | Stop or delete your Bunkers. | Open a Bunker or its Perimeter without your passkey, recovery key, or CLI-only private key. |
| A network attacker | Observe encrypted traffic. | Read or modify it, or impersonate either enclave. |

### What you are trusting

* **The hardware vendors**, AMD or Intel, to implement memory encryption and attestation correctly.
* **The measured software**: the CVM image, the Bunker image, the Perimeter, and the toolchain pack.
* **The client that holds your keys.** With CLI-only keys, that is the Tinfoil CLI on your computer. With synced keys, it is also the `terminal.tinfoil.sh` page each time you load it; see [the trust model](https://docs.tinfoil.sh/bunkers/quickstart.md#web-dashboard-vs-tinfoil-perimeter-open).
* **The services you connect.** The Perimeter controls what reaches them, not what they do with it. A vendor-hosted coding agent sends your code to its vendor; use [Tinfoil inference](https://docs.tinfoil.sh/bunkers/features.md#running-your-coding-agent) to keep prompts inside enclaves.
* **Your policy.** Allowing a connection lets the agent use it. Use **Ask** for actions you want to see first.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.