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

# Network policy

> Choose what a Sailbox can reach: the internet, nothing, or only the destinations you list

A network policy controls the connections a Sailbox opens. It is one of:

* **Public**, the default. The Sailbox can reach the internet.
* **No network.** The Sailbox cannot reach anything, and nothing can reach it.
* **Allowlist.** The Sailbox can reach only the destinations you list.

The policy is chosen when the Sailbox is created and lasts for its whole life.
A Sailbox created from a checkpoint keeps the policy of the one it came from,
and a Sailbox that already exists keeps the policy it was created with.
Whatever its policy, a Sailbox reaches the internet over TCP and IPv4 only.

Running commands is unaffected by any policy: `exec` and the shell reach the
Sailbox over a Sail-internal path, not its network. Platform features the
Sailbox was created with, such as a mounted volume, reach their storage the
same way and keep working.

For the HTTPS requests a policy allows, [HTTP policies](/sailboxes-credentials#policies)
can change or forward them, and [credential injection](/sailboxes-credentials)
can add a credential. Connections into a Sailbox are covered in
[Networking](/sailboxes-networking).

## No network

A no-network Sailbox is cut off from other hosts and the internet:

* It cannot open outbound connections, and name resolution does not work.
* It cannot expose inbound services. Exposing a port or enabling SSH is
  rejected.

<CodeGroup>
  ```python Python theme={null}
  import sail

  app = sail.App.find(name="web-demo", mint_if_missing=True)
  box = sail.Sailbox.create(
      app=app,
      name="offline-job",
      network_policy=sail.NetworkPolicy.NO_NETWORK,
  )
  ```

  ```typescript TypeScript theme={null}
  import { App, Sailbox } from "@sailresearch/sdk";

  const app = await App.find("web-demo", { mintIfMissing: true });
  const box = await Sailbox.create({
    app,
    name: "offline-job",
    networkPolicy: "no_network",
  });
  ```

  ```rust Rust theme={null}
  use sail::{CreateSailboxRequest, NetworkPolicy};

  let app = client.find_app("web-demo", /* mint_if_missing */ true).await?;
  let sb = client
      .create_sailbox(
          &CreateSailboxRequest {
              app_id: app.id,
              name: "offline-job".into(),
              network_policy: NetworkPolicy::NoNetwork,
              ..Default::default()
          },
          /* timeout */ None,
      )
      .await?;
  ```
</CodeGroup>

## Allowlist

An allowlist lets a Sailbox reach some destinations but not the rest of the
internet:

<CodeGroup>
  ```python Python theme={null}
  import sail

  app = sail.App.find(name="web-demo", mint_if_missing=True)
  box = sail.Sailbox.create(
      app=app,
      name="limited-job",
      network_policy=sail.NetworkAllowlist(
          [
              "api.example.com",
              "*.internal.example.com",
              "203.0.113.0/24",
          ]
      ),
  )
  ```

  ```typescript TypeScript theme={null}
  import { App, Sailbox } from "@sailresearch/sdk";

  const app = await App.find("web-demo", { mintIfMissing: true });
  const box = await Sailbox.create({
    app,
    name: "limited-job",
    networkPolicy: {
      mode: "allowlist",
      allowedHosts: [
        "api.example.com",
        "*.internal.example.com",
        "203.0.113.0/24",
      ],
    },
  });
  ```

  ```rust Rust theme={null}
  use sail::{CreateSailboxRequest, NetworkPolicy};

  let app = client.find_app("web-demo", /* mint_if_missing */ true).await?;
  let sb = client
      .create_sailbox(
          &CreateSailboxRequest {
              app_id: app.id,
              name: "limited-job".into(),
              network_policy: NetworkPolicy::Allowlist(vec![
                  "api.example.com".into(),
                  "*.internal.example.com".into(),
                  "203.0.113.0/24".into(),
              ]),
              ..Default::default()
          },
          /* timeout */ None,
      )
      .await?;
  ```
</CodeGroup>

<h3 id="entries">
  Entries
</h3>

Each entry is one of:

* A hostname, such as `api.example.com`.
* A `*.` wildcard hostname. `*.example.com` matches `api.example.com`. It does
  not match `example.com` or `a.b.example.com`.
* An IPv4 address, such as `203.0.113.7`.
* An IPv4 range in CIDR form, such as `203.0.113.0/24`. The range must start
  at its first address (`1.2.3.0/24`, not `1.2.3.5/24`).

The list follows these rules:

* At least one entry, and at most 128.
* An entry names a destination, not a port. An allowed destination is
  reachable on every port, and an entry with a port is rejected.
* IPv6 entries are rejected, because a Sailbox does not reach the internet
  over IPv6.
* An address no Sailbox could ever reach, such as `127.0.0.1` or
  `169.254.0.0/16`, is rejected.
* A private range such as `10.0.0.0/8` is accepted but allows nothing,
  because a Sailbox cannot reach private addresses.
* A list that breaks these rules fails the create call with
  `InvalidArgumentError` (`InvalidArgument` in Rust) before a Sailbox is
  created.
* Entries are stored lowercased, without a trailing dot, and without
  duplicates. `get` returns that stored list.

### What an entry allows

An allowlist limits only the connections the Sailbox opens. Connections into
it are unaffected, so an allowlist Sailbox can still expose ports and enable
SSH.

| To reach                                                            | List                                                            |
| ------------------------------------------------------------------- | --------------------------------------------------------------- |
| A web site or HTTP API over HTTPS or plain HTTP/1, by name          | Its hostname, or a wildcard that matches it                     |
| Any other server the connection opens with a TLS handshake, by name | Its hostname, or a wildcard that matches it                     |
| An SSH server, a database, or any other server, by name             | Its IP address or range, plus its hostname so the name resolves |
| Any server, by IP address                                           | Its IP address or range                                         |

<Note>
  A hostname entry allows only a connection whose first bytes announce the name:
  a plain HTTP/1 request, or a TLS handshake that carries the server name. A
  connection to a listed host that starts any other way, such as SSH or a
  database connection, is closed. List the server's address or range for those.
</Note>

Name resolution follows the list. An allowlist Sailbox can resolve only names
that a hostname or wildcard entry covers; any other lookup fails immediately,
so a list of only addresses and ranges resolves nothing.

An [HTTP policy](/sailboxes-credentials#policies) that forwards a request to another
host is checked against the allowlist too. The forward is allowed when a
hostname or wildcard entry covers that host, or an address or range entry
covers the address it resolves to.

### How Sail checks a name

When a hostname entry allows a connection, Sail resolves that name itself and
connects to the result, not to an address the Sailbox chose, so an override in
the Sailbox's `/etc/hosts` does not redirect the connection. Sail checks the
name a connection announces, not what the server does with it. If an allowed
server also serves other sites, as a shared CDN does, a request can reach those
sites through it.

### Blocked connections

A connection the list does not allow is not refused when it opens. It opens,
and Sail then closes it, so a program sees the failure on its first read or
write rather than as a refused connection.

## Limitations

An address or range entry allows a connection from its destination address
alone, before Sail reads anything from it. These limits apply to a connection
that depends on a hostname or wildcard entry instead.

* A hostname entry covers only a connection whose first bytes name the
  server: a plain HTTP/1 request with a `Host` header, or a TLS handshake that
  carries the server name. A protocol that switches to TLS after it starts
  (STARTTLS), plaintext HTTP/2, and a TLS client that omits the server name or
  hides it with Encrypted Client Hello (ECH) are closed.
* Sail reads at most the first 8 KiB of a plain HTTP request to find the
  `Host` header. A request whose headers run past that is closed even when
  `Host` came first.
* Sail waits about five seconds for a connection to announce its destination.
  A connection that sends nothing in that time is closed, including a client
  that waits for the server to speak first.
