# Gateway installation guide for GCP

> Install the Agent Router gateway on Google Cloud with the tare CLI, terminating TLS and routing traffic to the data plane.

This guide installs the Agent Router gateway in a Google Cloud project using the tare command-line utility. The resulting ingress terminates TLS for the data plane and routes traffic to it from a customer-facing hostname.

Plan for **45 to 90 minutes** end-to-end:

* 10–15 minutes of CLI work.
* 15–60 minutes of background certificate provisioning.

## Architecture

```text
┌───────────────────────────────────────────┐
│  proxy.<your-domain>  ←  customer DNS     │
│          │                                │
│          ▼                                │
│  ┌────────────────┐                       │
│  │    Static IP   │  ← GCP global address │
│  └────────┬───────┘                       │
│           │                               │
│  ┌────────▼───────┐                       │
│  │   Cert Map +   │  ← TLS termination    │
│  │  Managed Cert  │                       │
│  └────────┬───────┘                       │
│           │                               │
│  ┌────────▼───────┐                       │
│  │  Agent Router  | ← installed in this   │
│  │  Gateway(k8s)  │   guide               │
│  └────────┬───────┘                       │
│           │                               │
│  ┌────────▼───────┐                       │
│  │  Agent Router  |                       |
|  |   Dataplane    │  ← installed earlier  │
│  └────────────────┘    via tare CLI       │
└───────────────────────────────────────────┘
```

The `tare` utility provisions everything from the static IP down to the gateway routes. The operator provides one customer-facing DNS record at the top.

## Prerequisites

The following are required before starting:

* **Access to a GCP project** with at least the following roles:
  * `roles/compute.networkAdmin` (static IP).
  * `roles/certificatemanager.editor` (cert map, cert, DNS auth).
* **Access to the Kubernetes cluster** where the Agent Router dataplane runs. `kubectl cluster-info` must succeed against the target cluster.
* **Control over the DNS provider** for the domain in use (for example Cloudflare, Route 53, or an internal DNS team). Two records are created during the install.
* **The Agent Router dataplane already installed.** This guide assumes `tare install` has already run successfully against the target cluster.
* **`gcloud` installed and authenticated** when using `--apply-prereqs` to let the CLI provision the GCP resources. When those resources are managed externally by an infrastructure-as-code tool, skip `gcloud` and use `--ack-prereqs` instead.
* **Two files from Tetrate:**
  * The identity file (`identity.json`), issued by the management plane during onboarding.
  * A starter `gcp-gateway.json`. A Tetrate field engineer typically provides a template tuned to the target environment.

The `tare` binary ships `helm` and `kubectl` embedded; no separate install is required.

## Step 1: prepare the config

The gateway install is driven by a single JSON contract: `gcp-gateway.json`. Every value can also be passed as a CLI flag, but the config file is the recommended unit for version control and review.

A complete example:

```json
{
  "projectId": "withfraser",
  "serveDomain": "proxy.acme.tetrate.ai",
  "customer": "acme",
  "environment": "production",
  "serveUrl": "proxy.acme.tetrate.ai",
  "certificateMap": {
    "name": "acme-tetrate-ai"
  },
  "certificate": {
    "name": "acme-serve-cert"
  },
  "dnsAuthorization": {
    "name": "proxy-acme-tetrate-ai-dns-auth"
  },
  "namespaces": {
    "gateway": "tars-gateway",
    "system": "tars-system",
    "dataplane": "tars-dataplane"
  },
  "gcloud": {
    "skipExisting": true
  }
}
```

Commonly edited fields:

| Field | Description |
| -- | -- |
| `projectId` | The GCP project that hosts the static IP, cert, and cert map. |
| `serveDomain` | The fully-qualified hostname customers will use. Single host only; wildcards are not yet supported. |
| `customer` | The customer identifier, matching the value in the identity file. |
| `environment` | Free-form label propagated to Helm values. Typical values: `production`, `staging`. |
| `certificateMap.name` | Name for the GCP certificate map. Convention: `<customer>-<domain-suffix>`. |
| `certificate.name` | Name for the managed certificate. Convention: `<customer>-serve-cert`. |
| `gcloud.skipExisting` | When `true`, re-runs are idempotent; re-installs do not fail when a resource already exists. Recommended `true`. |

For the full schema, including optional fields such as `gateway.staticIpName` and `securityPolicy`, see the `gcp-gateway.json` schema reference.

:::tip
Set `gateway.staticIpName` to pin the gateway to a reserved static IP. Without it, the gateway receives an ephemeral GCP address that may change on reschedule or upgrade, silently breaking the DNS A record from Step 5.
:::

## Step 2: validate the config

Lint the config before any cloud or cluster operation:

```shell
tare gateway config lint --config gcp-gateway.json
```

The check is hermetic (no GCP or Kubernetes calls) and safe to run in CI on every config change.

The lint command produces one of three outcomes.

**All clear:**

```text
Summary: 0 error, 0 warn, 0 info
```

**Warnings to review:**

```text
⚠ [TAREL010] gateway.staticIpName — gateway will receive an ephemeral
             GCP forwarding-rule address; customer DNS A records may
             break on reschedule.
             Remediate: set gateway.staticIpName in gcp-gateway.json
                        or pass --static-ip-name on install.

Summary: 0 error, 1 warn, 0 info
```

Warnings are advisory; the install proceeds, but each finding should be reviewed. Findings include a remediation hint and a docs link (`https://docs.tetrate.ai/tare/lint/<RULE_ID>`).

**Errors that block install:**

```text
✗ [TAREL001] customer — required value cannot be resolved
             tried: --customer (empty), gcp-gateway.json:customer (empty),
                    identity.json:customerId (empty)
             Remediate: add "customer" to gcp-gateway.json, or pass --customer.

Summary: 1 error, 0 warn, 0 info
```

Errors must be fixed before the install runs.

For the full rule list and how to suppress accepted findings (for example via a `lint.ignore` block in the config), see `tare gateway config lint`.

**CI usage:** `tare gateway config lint --config gcp-gateway.json --fail-on-warn` exits non-zero on any warning or error, making it the recommended pre-merge gate for a config repository.

## Step 3: preview the install (plan)

Render a Plan before any side effects:

```shell
tare gateway install identity.json \
  --type gcp \
  --config gcp-gateway.json \
  --plan-only
```

Sample output:

```text
Plan: tare gateway install (provider=gcp)

  Identity:     identity.json (client_email: ops@acme.com)
  Project:      withfraser                                  (from --config)
  Customer:     acme                                        (from --config)
  Environment:  production                                  (from --config)
  Serve host:   proxy.acme.tetrate.ai                       (from --config:serveDomain)
  serveUrl:     proxy.acme.tetrate.ai                       (same as serveDomain)

  GCP delta:
    + static IP    acme-gateway-ip          [will create]
    + cert map     acme-tetrate-ai          [will create]
    + dns auth     proxy-acme-…-dns-auth    [will create]
    + certificate  acme-serve-cert          [will create, single host]
    + map entry    serve-entry              [will create]

  Kubernetes delta:
    + namespace tars-gateway       [will create]
    ~ namespace tars-system        [exists]
    ~ namespace tars-dataplane     [exists]
    + helm release tars-gateway    [will install, chart v1.2.5]

  After install, customer must:
    Create A record:  proxy.acme.tetrate.ai → <address shown after install>
    Create CNAME:     (provided after install)
```

Verify the following before proceeding:

1. **Project** is correct. Misclicking between `acme-prod` and `acme-staging` is the most common deploy mistake.
1. **Customer** matches the identity file's owner.
1. **Serve host** matches the customer-facing hostname.
1. **`+` rows** are resources that will be created. **`~` rows** already exist and are left alone (or modified in place).
1. **No error findings** appear in the inline lint section.

The Plan output is also available as JSON (`--output json`) for use by other tooling. The JSON shape is a stable contract (`apiVersion: tare.tetrate.io/v1alpha1`).

## Step 4: apply

Two routes are supported.

### Option a: CLI provisions GCP resources

When `tare` is permitted to run `gcloud` directly:

```shell
tare gateway install identity.json \
  --type gcp \
  --config gcp-gateway.json \
  --apply-prereqs \
  --wait
```

`--apply-prereqs` creates the static IP, DNS authorization, certificate map, certificate, and map entry via `gcloud`. `--wait` blocks the command until the gateway has a routable address.

### Option b: GCP resources already provisioned

When the GCP resources are owned externally by an infrastructure-as-code tool, skip the prereq automation:

```shell
tare gateway install identity.json \
  --type gcp \
  --config gcp-gateway.json \
  --ack-prereqs \
  --wait
```

`--ack-prereqs` acknowledges that the static IP, certificate map, certificate, and DNS authorization are already in place. The CLI refuses to start the install without one of `--apply-prereqs` or `--ack-prereqs`.

### Install sequence

The CLI prints the Plan, prompts `Proceed? [y/N]:` in a TTY (pass `--yes` in CI), then runs through the following stages:

1. Preflight: verify Helm, kubectl, and cluster reachability.
1. Cloud prereqs: runs only with `--apply-prereqs`.
1. Namespace creation: idempotent.
1. Helm install: applies the gateway chart.
1. Wait for address: runs only with `--wait`; reports the gateway address once Kubernetes assigns one (typically under a minute).

The final output looks like:

```text
Gateway installation complete.

Customer action required (DNS):
  Create DNS A record:
    Host:  proxy.acme.tetrate.ai
    Type:  A
    Value: 34.110.x.x
    TTL:   300

  Create DNS authorization CNAME record:
    Host:  _abc123.proxy.acme.tetrate.ai.
    Type:  CNAME
    Value: abc123.<google-managed>.googleusercontent.com.

Certificate provisioning note:
  Certificate may still be PROVISIONING after gateway install.
  HTTPS will be fully ready once certificate state is ACTIVE.
```

Copy these values for use in the next step.

## Step 5: configure DNS

Two DNS records are required in the DNS provider. Both must exist; HTTPS does not work without either.

### Record 1: a record for customer traffic

| | |
| -- | -- |
| **Host** | `proxy.acme.tetrate.ai` (the `serveDomain` value) |
| **Type** | `A` |
| **Value** | The address printed in Step 4 (for example `34.110.x.x`) |
| **TTL** | `300` (5 minutes) recommended |

Customer clients resolve this record to reach the gateway.

### Record 2: DNS authorization cname

| | |
| -- | -- |
| **Host** | The CNAME host printed in Step 4 (for example `_abc123.proxy.acme.tetrate.ai.`) |
| **Type** | `CNAME` |
| **Value** | The `googleusercontent.com` target printed in Step 4 |
| **TTL** | `300` recommended |

Google Certificate Manager uses this record to verify **domain ownership** before issuing a TLS certificate. Without it, the certificate stays in `PROVISIONING` indefinitely.

To fetch the CNAME details at any time after the install:

```shell
gcloud certificate-manager dns-authorizations describe \
  proxy-acme-tetrate-ai-dns-auth \
  --project withfraser \
  --format='value(dnsResourceRecord.name,dnsResourceRecord.type,dnsResourceRecord.data)'
```

## Step 6: wait for the certificate to activate

Once both DNS records are live, the managed certificate moves from `PROVISIONING` to `ACTIVE` automatically. Typical timing:

* **First 5 minutes**: DNS propagation; certificate stays `PROVISIONING`.
* **5–20 minutes**: Google validates the DNS authorization, issues the certificate, and the certificate flips to `ACTIVE`.
* **Up to a few hours (rare)**: when DNS propagation is slow at the registrar.

Check the certificate state:

```shell
gcloud certificate-manager certificates describe acme-serve-cert \
  --project withfraser \
  --format="yaml(name,managed.state,managed.domainStatus)"
```

Expected output:

```yaml
name: projects/withfraser/locations/global/certificates/acme-serve-cert
managed:
  state: ACTIVE
  domainStatus:
    proxy.acme.tetrate.ai: ACTIVE
```

If `state: FAILED_NOT_VISIBLE` appears, the DNS authorization CNAME from Step 5 is missing or incorrect. Verify that it resolves:

```shell
dig +short CNAME _abc123.proxy.acme.tetrate.ai
```

The result must match the value returned by `dns-authorizations describe`.

## Step 7: verify

Once the certificate is `ACTIVE`, verify end-to-end.

**Cluster resources are healthy:**

```shell
kubectl get gateway -n tars-gateway
kubectl get httproute -n tars-system
kubectl get httproute -n tars-dataplane
```

All gateways and routes should report `True` for the `Accepted` and `Programmed` conditions.

**Hostname resolution:**

```shell
dig +short A proxy.acme.tetrate.ai
# Expected: 34.110.x.x  (the address from Step 4)
```

**TLS termination and gateway response:**

```shell
curl -v https://proxy.acme.tetrate.ai/healthz
# Expected: HTTP/2 200, valid certificate for proxy.acme.tetrate.ai
```

When the `healthz` endpoint is not exposed, any request that reaches the gateway and returns an Agent Router-shaped response (even `404` or `401`) confirms that the gateway and TLS are working.

## Troubleshooting

| Symptom | Likely cause | Resolution |
| -- | -- | -- |
| `customer not resolved, tried: --customer (empty), gcp-gateway.json:customer (empty), identity.json:customerId (empty)` | No customer set anywhere. | Add `"customer": "<name>"` to `gcp-gateway.json` or pass `--customer`. |
| GCP flags such as `--certificate-map-name` are ignored | `--type aws` passed. | The AWS path is ingress-only and takes no config file. Follow [Gateway installation guide for EKS](/agent-router-enterprise/self-hosted-data-plane/gateway-installation/gateway-installation-guide-aws), or pass `--type gcp` for this guide. |
| Plan shows a `?` row for a resource | The CLI could not probe live state (credentials, IAM, or network issue). | The install treats the resource as `will-create`. Check the `probeError` field in `--output json` for details. |
| `helm deploy failed` | Cluster not reachable, RBAC issue, or a chart-level error. | Run `kubectl cluster-info`; when that fails, fix the kubeconfig context first. |
| Gateway never reports an address (`--wait` times out) | Cloud load-balancer provisioning is slow, or the static IP is not attached. | Run `kubectl describe gateway -n tars-gateway` and check Kubernetes events for the gateway resource. |
| Certificate stuck `PROVISIONING` for \>30 min | DNS authorization CNAME is missing, wrong, or not yet propagated. | Re-check Step 5. Use `dig` to verify the CNAME resolves. |
| Certificate state `FAILED_NOT_VISIBLE` | DNS authorization record cannot be resolved from Google's side. | Recreate the CNAME exactly as printed by `dns-authorizations describe`. |
| `curl` returns a TLS error after cert is `ACTIVE` | A record points to the wrong IP, or local DNS cache. | Verify with `dig`; flush the local resolver. |
| Re-running install fails with `already exists` from gcloud | `gcloud.skipExisting` is `false`. | Set `"gcloud": { "skipExisting": true }` in `gcp-gateway.json`. |

For issues not covered above, re-run the install with the Plan visible (`--plan-only` is sufficient) and share the output with the Tetrate contact. The Plan captures every resolved value and live state probe, which is usually enough to diagnose.

## Need help?

When blocked, capture:

1. The output of `tare gateway install … --plan-only`.
1. The output of `tare gateway config lint --format json`.
1. The output of `gcloud certificate-manager certificates describe …`.

Share these with the Tetrate contact. Together they describe the config, the live cloud state, and the certificate state, which is usually enough to unblock without further back-and-forth.

Where to go next

  <Link to="/agent-router-enterprise/guides/identity-and-access-management/sso-configuration-guide" className="tare-nav-card">
    SSO configuration
    Connect an OIDC identity provider before developer handoff.
  </Link>
  <Link to="/agent-router-service/quickstarts/quickstart-console" className="tare-nav-card">
    Console quickstart
    After SSO is working, issue an API key and confirm a first routed request.
  </Link>
