# Deploy the Edge

The edge is a single static Linux binary, `octet-edge`. It accepts the browser's connection, terminates its TLS, and forwards the collected data to Octet over mutual TLS with your license token. This guide sets it up on its own hostname, such as `octet.example.com`, with systemd.

## Before you start

- A Linux host, `amd64` or `arm64`, with a public IP address.
- A DNS name for the edge, such as `octet.example.com`, with an `A` record (and `AAAA` if you use IPv6) pointing straight at that host.
- Inbound TCP 443 open to the internet, and TCP 80 open while you get the certificate.
- Outbound TCP 443 to `geo.octetproof.com`.
- Your license token, and your license ID from the Octet portal for the edge's client certificate. See [Credentials](/docs/browser/integration/credentials/).

## Nothing may terminate TLS or TCP in front of the edge

The edge must be the machine that accepts the browser's TCP connection and terminates its TLS. It reads properties of that connection, including the user's IP address, and it ignores `X-Forwarded-For` and similar headers. Anything that terminates TCP or TLS in front of it replaces the browser's connection with its own. The edge then measures that device instead of the user, and verdicts become wrong.

These are **not supported** in front of the edge:

- A reverse proxy such as nginx, Caddy, Apache, Traefik or Envoy
- HAProxy, in HTTP or TCP mode
- An HTTP(S) or application load balancer, such as AWS ALB or a Google Cloud HTTPS load balancer
- A Kubernetes Ingress controller
- A CDN or proxy service, such as Cloudflare's proxy, CloudFront, Fastly or Akamai

These are **supported**:

- A public IP on the host itself, with an ordinary firewall or security group
- A layer 4 load balancer in passthrough mode, which forwards packets without opening its own TCP connection and keeps the client's source IP. Examples are an AWS Network Load Balancer with a TCP listener and client IP preservation, or a Google Cloud passthrough Network Load Balancer.
- DNS services that only answer queries, such as Cloudflare DNS with the proxy turned off

If you run more than one edge behind a passthrough load balancer, turn on source IP affinity. A browser's WebSocket and its POST are separate connections, and both must reach the same edge.

## 1. Download and verify the binary

Download the build for your CPU and the checksum file from the [v1.3.0 release](https://github.com/octetproof/octet-browser/releases/tag/v1.3.0). On an `arm64` host, use `octet-edge-linux-arm64` in each command.

```bash
curl -fLO https://github.com/octetproof/octet-browser/releases/download/v1.3.0/octet-edge-linux-amd64
curl -fLO https://github.com/octetproof/octet-browser/releases/download/v1.3.0/SHA256SUMS
```

Check the binary against its checksum:

```bash
sha256sum --ignore-missing -c SHA256SUMS
```

The output must read `octet-edge-linux-amd64: OK`. If it reads anything else, delete the file and download it again.

To check that `SHA256SUMS` itself comes from the release workflow of [octetproof/octet-browser](https://github.com/octetproof/octet-browser), download its signature and certificate from the same release and verify them with [cosign](https://docs.sigstore.dev/cosign/system_config/installation/):

```bash
curl -fLO https://github.com/octetproof/octet-browser/releases/download/v1.3.0/SHA256SUMS.sig
curl -fLO https://github.com/octetproof/octet-browser/releases/download/v1.3.0/SHA256SUMS.pem
cosign verify-blob --signature SHA256SUMS.sig --certificate SHA256SUMS.pem \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/octetproof/octet-browser/\.github/workflows/sign-release\.yml@' \
  SHA256SUMS
```

cosign prints `Verified OK`. Then install the binary:

```bash
sudo install -m 0755 octet-edge-linux-amd64 /usr/local/bin/octet-edge
```

## 2. Create a service user and config directory

```bash
sudo useradd --system --no-create-home --shell /usr/sbin/nologin octet-edge
sudo install -d -m 0750 -o root -g octet-edge /etc/octet-edge /etc/octet-edge/tls
```

## 3. Get a TLS certificate

The edge serves HTTPS itself, from certificate files you provide. It does not obtain or renew certificates. This example uses Let's Encrypt with certbot in standalone mode, which needs port 80 free while it runs:

```bash
sudo certbot certonly --standalone -d octet.example.com
```

Install a deploy hook, so each issued or renewed certificate is copied to the edge and the edge restarts to load it:

```bash
sudo tee /etc/letsencrypt/renewal-hooks/deploy/octet-edge.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
install -m 0640 -o root -g octet-edge "$RENEWED_LINEAGE/fullchain.pem" /etc/octet-edge/tls/fullchain.pem
install -m 0640 -o root -g octet-edge "$RENEWED_LINEAGE/privkey.pem" /etc/octet-edge/tls/privkey.pem
systemctl restart octet-edge || true
EOF
sudo chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/octet-edge.sh
sudo RENEWED_LINEAGE=/etc/letsencrypt/live/octet.example.com /etc/letsencrypt/renewal-hooks/deploy/octet-edge.sh
```

The edge loads its certificate once, at startup, which is why the hook restarts it.

## 4. Install the client certificate

The edge connects to Octet only over mutual TLS, with a client certificate for your license. You issue it yourself in the Octet portal, in your Octet Browser license's **Edge certificates** tab. The certificate's common name must be exactly your license ID, which the tab shows. Only a portal user with the owner or admin role can issue it.

Create the private key and a certificate request on the edge host:

```bash
LICENSE_ID=REPLACE_WITH_YOUR_LICENSE_ID
sudo openssl ecparam -name prime256v1 -genkey -noout -out /etc/octet-edge/tls/client.key
sudo chown root:octet-edge /etc/octet-edge/tls/client.key
sudo chmod 0640 /etc/octet-edge/tls/client.key
sudo openssl req -new -key /etc/octet-edge/tls/client.key -subj "/CN=$LICENSE_ID" -out client.csr
```

This makes an ECDSA P-256 key. The portal also accepts ECDSA P-384 and RSA keys of 2048 bits or more. The private key stays on this host.

In the portal's **Edge certificates** tab, paste the contents of `client.csr` (`cat client.csr`) or choose the file, and issue the certificate. Download it, `octet-edge-<license ID>.crt`, valid for 397 days, copy it to the edge host and install it next to the key:

```bash
sudo install -m 0644 -o root -g octet-edge "octet-edge-$LICENSE_ID.crt" /etc/octet-edge/tls/client.crt
```

Each edge host needs its own key and certificate. Repeat this step on every host, with the same license ID. A license can have at most 3 live edge certificates. To renew or revoke one, see [Credentials](/docs/browser/integration/credentials/#renew-an-edge-client-certificate).

## 5. Write the environment file

Create `/etc/octet-edge/edge.env`:

```ini
PORT=443
OCTET_URL=https://geo.octetproof.com
LICENSE=octet_live_v4.public.REPLACE_WITH_YOUR_LICENSE_TOKEN
ALLOWED_ORIGIN=https://www.example.com
EDGE_TLS_CERT_FILE=/etc/octet-edge/tls/fullchain.pem
EDGE_TLS_KEY_FILE=/etc/octet-edge/tls/privkey.pem
EDGE_CLIENT_CERT_FILE=/etc/octet-edge/tls/client.crt
EDGE_CLIENT_KEY_FILE=/etc/octet-edge/tls/client.key
```

Set `ALLOWED_ORIGIN` to the origin of the pages that load the collector. Separate several origins with commas, with no spaces. Then lock the file down:

```bash
sudo chown root:octet-edge /etc/octet-edge/edge.env
sudo chmod 0640 /etc/octet-edge/edge.env
```

Every setting is described in [Edge Configuration](/docs/browser/reference/edge-config/).

## 6. Create the systemd unit

Create `/etc/systemd/system/octet-edge.service`:

```ini
[Unit]
Description=Octet edge
After=network-online.target
Wants=network-online.target

[Service]
User=octet-edge
Group=octet-edge
EnvironmentFile=/etc/octet-edge/edge.env
ExecStart=/usr/local/bin/octet-edge
Restart=on-failure
RestartSec=2
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

`CAP_NET_BIND_SERVICE` lets the service user listen on port 443. Start the edge:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now octet-edge
sudo journalctl -u octet-edge -n 20
```

The log shows `listening :443 (TLS)` when TLS is configured. If it shows `plain HTTP`, the certificate settings are missing. If `OCTET_URL` isn't `https://`, or the client certificate or key setting is missing, the edge exits and logs what to fix, for example `mTLS to octet is required, set EDGE_CLIENT_CERT_FILE, EDGE_CLIENT_KEY_FILE`.

## 7. Check it

From any machine:

```bash
curl -s https://octet.example.com/health
```

The edge answers:

```json
{"ok":true,"role":"octet-edge"}
```

A healthy `/health` shows that the edge is up and serving TLS. It does not contact Octet. To check that Octet accepts your license token, send an empty body to `/v1/signals`:

```bash
curl -s -X POST https://octet.example.com/v1/signals -H 'content-type: application/json' -d '{}'
```

Octet checks the license and the client certificate before it reads the body, so a valid license and certificate get `400` with `"reason":"unsealed_bundle"`, and a refused license gets `401`. A `401` with `"reason":"client_cert_required"` means Octet didn't receive a client certificate it issued: check `EDGE_CLIENT_CERT_FILE` and `EDGE_CLIENT_KEY_FILE`, and that the certificate hasn't expired. A `403` with `"reason":"client_cert_licence_mismatch"` means the certificate was issued for a different license than the one in `LICENSE`. A `403` with `"reason":"client_cert_revoked"` means the certificate was revoked in the portal: install a new one. See [Go-Live Checklist](/docs/browser/integration/go-live-checklist/) for the full test plan.

## Update the edge

Download and verify the new release's binary as in step 1, install it over `/usr/local/bin/octet-edge`, and restart the service:

```bash
sudo systemctl restart octet-edge
```

## Next

[Embed the Collector](/docs/browser/integration/embed-collector/) on your pages, with `apiUrl: 'https://octet.example.com'`.
