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

# Install Formal Endpoint on Grok Bot

> Install Formal Endpoint on Grok Bot computers and block Gmail with a network rule and policy

export const G = ({term, anchor, children}) => {
  const href = anchor ? `/docs/glossary/index#${anchor}` : `/docs/glossary/index`;
  return <a href={href} className="glossary-link" style={{
    textDecoration: "underline",
    textDecorationLine: "underline",
    textDecorationColor: "#6b7280",
    textDecorationThickness: "1px",
    textUnderlineOffset: "2px",
    color: "inherit",
    transition: "text-decoration-color 0.2s ease",
    borderBottom: "none"
  }} onMouseEnter={e => e.target.style.textDecorationColor = "#fff"} onMouseLeave={e => e.target.style.textDecorationColor = "#6b7280"}>
  {children || term}
</a>;
};

## Overview

[Grok Bot](https://cursor.com/docs/grok-bot) computers are Linux VMs
with a browser and a terminal. Install the Formal Endpoint on every team
computer with Enterprise [Team Setup](https://cursor.com/docs/grok-bot/private-networks).
Transparent Mode then evaluates the VM traffic against Formal policy.

This guide blocks `https://mail.google.com` and `https://gmail.com`. Gmail
serves the inbox on both hostnames. A rule that matches only one is bypassed.

## Prerequisites

* A Cursor Enterprise team with Grok Bot Team Setup
* Outbound HTTPS to `api.joinformal.com`

## Create an API key

Use one machine-user API key for the Grok Bot fleet.

<Steps>
  <Step title="Create the key">
    Go to [API Keys](https://app.formal.ai/api-keys) and click **Create API
    Key**. Assign it to a machine user.
  </Step>

  <Step title="Store it as a Team Secret">
    Open **Grok Bot** in the Cursor dashboard. Add a Team Secret named
    `FORMAL_API_KEY`. Paste the Formal key. Team Setup injects Team Secrets
    as environment variables. Do not put the key in the setup script.
  </Step>
</Steps>

## Install the Endpoint with Team Setup

<Steps>
  <Step title="Create a manifest">
    Go to **Grok Bot** → **Team Setup**. Create a manifest, for example
    `formal-endpoint`. Add one entry with ID `install-formal-endpoint`.
  </Step>

  <Step title="Copy the package link">
    In the [Control Plane](https://app.formal.ai), click **Download Formal
    Endpoint** at the bottom of the sidebar. Open **Linux**, right-click
    **Ubuntu/Debian (x86, headless)**, and copy the link.
  </Step>

  <Step title="Paste the setup script">
    ```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    #!/usr/bin/env bash
    set -euo pipefail

    : "${FORMAL_API_KEY:?Set the FORMAL_API_KEY Team Secret}"

    curl --fail --silent --show-error --location \
      "<FORMAL_ENDPOINT_PACKAGE_URL>" \
      --output /tmp/formal-desktop-headless.deb
    # Skip apt-get update — it can hang here; the local .deb is enough.
    sudo apt-get install --yes /tmp/formal-desktop-headless.deb

    mkdir -p ~/.formal/logs
    printf 'FORMAL_API_KEY=%s\n' "$FORMAL_API_KEY" > ~/.formal/formal.env
    chmod 0600 ~/.formal/formal.env
    cat > ~/.formal/config.toml <<'EOF'
    [transparent_proxy]
    enable = true
    EOF

    sudo mkdir -p /var/lib/formal/ca
    sudo chgrp "$(id -gn)" /var/lib/formal/ca
    sudo chmod 0775 /var/lib/formal/ca
    sudo usermod -aG sudo "$(id -un)" || true
    sudo setcap 'cap_net_admin,cap_net_bind_service+ep' /usr/bin/formal || true

    set -a
    # shellcheck disable=SC1091
    source "$HOME/.formal/formal.env"
    set +a

    # Do not use pkill -f '/usr/bin/formal agent' — under bash -c the setup
    # cmdline embeds that string and the script will kill itself.
    pkill -f '^/usr/bin/formal agent --headless$' 2>/dev/null || true
    # Wait for the old agent to drop its instance lock before starting another.
    for _ in $(seq 1 20); do
      pgrep -f '^/usr/bin/formal agent --headless$' >/dev/null 2>&1 || break
      sleep 0.25
    done

    # New session so the agent is not killed with the setup process group.
    setsid nohup /usr/bin/formal agent --headless \
      >> ~/.formal/logs/agent.log 2>&1 < /dev/null &

    ca_pem=/var/lib/formal/ca/localca-cert.pem
    for _ in $(seq 1 60); do
      if [[ -f "$ca_pem" ]] && formal auth status >/dev/null 2>&1; then
        break
      fi
      sleep 1
    done
    [[ -f "$ca_pem" ]] || { echo "Formal CA not ready; see ~/.formal/logs/agent.log" >&2; exit 1; }
    formal auth whoami >/dev/null || { echo "Formal auth failed; see ~/.formal/logs/agent.log" >&2; exit 1; }

    sudo formal ca trust

    ca_crt=/usr/local/share/ca-certificates/formal-localca.crt
    sudo mkdir -p /etc/opt/chrome/policies/managed
    # Cert is root-only after `formal ca trust`; openssl must run as root.
    der_b64="$(sudo openssl x509 -in "$ca_crt" -outform DER | base64 -w0)"
    printf '{"CACertificates":["%s"]}\n' "$der_b64" \
      | sudo tee /etc/opt/chrome/policies/managed/formal-localca.json >/dev/null
    sudo chmod 644 /etc/opt/chrome/policies/managed/formal-localca.json

    formal auth status
    formal auth whoami
    formal transparent-proxy status || true
    ```

    The Chrome policy trusts the Formal CA. Without it, Chrome shows a
    certificate warning instead of the Formal block page.

    Replace `<FORMAL_ENDPOINT_PACKAGE_URL>` with the copied link. To upgrade
    the Endpoint, copy the link again and update the script.
  </Step>

  <Step title="Add the check script">
    ```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    #!/usr/bin/env bash
    set -euo pipefail
    formal auth whoami >/dev/null

    ca_pem=/var/lib/formal/ca/localca-cert.pem
    [[ -f "$ca_pem" ]]
    sudo mkdir -p /etc/opt/chrome/policies/managed
    der_b64="$(openssl x509 -in "$ca_pem" -outform DER | base64 -w0)"
    printf '{"CACertificates":["%s"]}\n' "$der_b64" \
      | sudo tee /etc/opt/chrome/policies/managed/formal-localca.json >/dev/null
    sudo chmod 644 /etc/opt/chrome/policies/managed/formal-localca.json
    ```

    A zero exit skips setup. After setup runs, Team Setup runs this check
    again. The check also rewrites Chrome's Formal CA policy. Headless
    Linux issues a new CA when the agent starts. `formal auth whoami`
    succeeding means that new CA is already on disk. If the agent is not
    running after a reboot, the check fails and setup starts it again.
  </Step>

  <Step title="Save and apply">
    Save the manifest. New computers apply it on start. To apply it now, open
    Grok Bot → **Settings** → **Updates** → **Reset**, or recreate the
    computer from the dashboard.
  </Step>
</Steps>

**Verify:**

```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
formal auth whoami
formal transparent-proxy status
```

`formal auth whoami` should show the machine user that owns the API key.
Transparent Mode should be enabled.

## Create the network rule

The Endpoint forwards a connection unless a network rule matches. This rule
terminates TLS for Gmail so policy can run.

<Tabs>
  <Tab title="Control Plane">
    1. Navigate to [Network Rules](https://app.formal.ai/network-rules)
    2. Create a rule named `gmail-grok-bot`
    3. Paste the CEL below
    4. Leave **Forward to Connector** unset
    5. Save the rule and set it to **Active**

    ```cel theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    {
      "condition": {
        "pre_tls": hostname in ["gmail.com", "www.gmail.com", "mail.google.com"],
        "post_tls": true
      }
    }
    ```
  </Tab>

  <Tab title="Terraform">
    ```hcl theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    resource "formal_network_rule" "gmail_grok_bot" {
      name        = "gmail-grok-bot"
      description = "Intercept Gmail traffic from Grok Bot computers"
      status      = "active"

      cel_expression = <<-EOT
        {
          "condition": {
            "pre_tls": hostname in ["gmail.com", "www.gmail.com", "mail.google.com"],
            "post_tls": true
          },
          "outputs": {}
        }
      EOT
    }
    ```
  </Tab>
</Tabs>

To limit the rule to the Grok Bot machine user, add
`&& user.id == "<MACHINE_USER_ID>"` to `pre_tls`.

## Create the Gmail policy

Block every HTTP request to those hostnames. Create the policy in and set it to **Active**.

<Tabs>
  <Tab title="Control Plane">
    1. Navigate to [Policies](https://app.formal.ai/policies)
    2. Click **Create Policy**
    3. Name it `block-grok-bot-gmail`
    4. Paste the Rego below
    5. Save and set the policy to **Active**

    ```rego theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    package formal.v2

    import future.keywords.if
    import future.keywords.in

    request := {
      "action": "block",
      "type": "block_with_formal_message",
      "reason": "Grok Bot cannot open Gmail",
    } if {
      input.http.hostname in {"gmail.com", "www.gmail.com", "mail.google.com"}
    }
    ```
  </Tab>

  <Tab title="Terraform">
    ```hcl theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    resource "formal_policy" "block_grok_bot_gmail" {
      name        = "block-grok-bot-gmail"
      description = "Block Grok Bot from opening Gmail"
      status      = "active"

      module = <<-EOT
        package formal.v2

        import future.keywords.if
        import future.keywords.in

        request := {
          "action": "block",
          "type": "block_with_formal_message",
          "reason": "Grok Bot cannot open Gmail",
        } if {
          input.http.hostname in {"gmail.com", "www.gmail.com", "mail.google.com"}
        }
      EOT
    }
    ```
  </Tab>
</Tabs>

## Verify Gmail is blocked

Ask a Bot to open `https://mail.google.com` in the computer browser. Chrome
should show Formal's block page with a policy ID, session ID, and request ID.

Also try `https://gmail.com`. Both hosts should block.

Open [Logs](https://app.formal.ai/logs). Filter to Endpoint. Confirm the
blocked request hostname is `mail.google.com` or `gmail.com`.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Team Setup did not install Formal">
    Confirm the team is on Enterprise and the manifest is saved. Reset the
    computer from Grok Bot → **Settings** → **Updates** → **Reset**. Confirm
    the Team Secret is named `FORMAL_API_KEY`. A computer that belongs to
    more than one team does not receive Team Secrets.
  </Accordion>

  <Accordion title="formal auth whoami fails">
    Confirm the agent is running and read its log:

    ```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
    pgrep -x formal || true
    tail -n 50 ~/.formal/logs/agent.log
    ```

    Confirm the API key is valid. Reset the computer so Team Setup starts
    the agent again.
  </Accordion>

  <Accordion title="Chrome shows a certificate warning">
    Confirm
    `/etc/opt/chrome/policies/managed/formal-localca.json` exists. The
    check script rewrites it from the live Formal CA after the agent
    starts. Reset the computer if Team Setup has not run since boot.
    Restart Chrome after that. Do not disable TLS verification.
  </Accordion>

  <Accordion title="The policy does not evaluate">
    Confirm the policy and the Gmail network rule are **Active**. Confirm
    Transparent Mode is enabled. Without a matching rule, the Endpoint
    forwards Gmail without inspecting it. Confirm Grok Bot's own network
    policy allows `mail.google.com`.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Linux Endpoint" icon="linux" href="/docs/guides/client-apps/linux">
    Review the headless package and systemd service
  </Card>

  <Card title="Network Rules" icon="filter" href="/docs/guides/network-rules">
    Select which traffic Transparent Mode intercepts
  </Card>

  <Card title="Policy Evaluation" icon="gavel" href="/docs/guides/policies/evaluation">
    Explore HTTP policy inputs
  </Card>

  <Card title="Endpoint Logs" icon="chart-line" href="/docs/guides/observability/logs">
    Review requests and policy decisions
  </Card>
</CardGroup>


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