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

# Google Cloud (GCE, GKE, Cloud Run, Cloud Functions)

> Authenticate from any GCP workload to KnoxCall using the GCE metadata service

# Google Cloud Workload Identity

Every Google Cloud workload has a metadata service at `metadata.google.internal` that issues short-lived OIDC tokens signed by Google. KnoxCall accepts these tokens via RFC 8693 token exchange.

## 1. Configure the trust binding

Create the workload binding in the dashboard with **Settings → API → Workload Identity → Connect workload**, or call the admin API directly. The `/admin/*` routes are served on the admin host (`admin.knoxcall.com` / any `knoxcall.com` host) and are authenticated by your logged-in admin/owner session — a session JWT plus the `X-Tenant-ID` header — not an API key against `api.knoxcall.com`.

<Note>
  Creating a binding requires a **recent step-up verification** (passkey, TOTP or
  emailed code) within the last 5 minutes — a binding is a trust that lets an
  external workload mint tenant tokens, so it carries the same bar as creating an
  OAuth client. A `curl` carrying only a session JWT answers
  `403 {"requires_step_up": true}`: verify in the Dashboard (any action that
  prompts for your passkey or TOTP), then replay the request inside the 5-minute
  window. Each verification is single-use, so a retried request needs a fresh one.
  Listing and revoking bindings need no verification — containment must never be
  gated.
</Note>

```bash theme={"dark"}
curl -X POST https://admin.knoxcall.com/admin/oauth/workload-bindings \
  -H "Authorization: Bearer $SESSION_JWT" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "oauth_client_id": "<your_oauth_client_id>",
    "name": "gcp-prod-api",
    "issuer": "https://accounts.google.com",
    "audience": "knoxcall:api",
    "attribute_conditions": {
      "email": "knoxcall-prod@my-project.iam.gserviceaccount.com"
    },
    "allowed_scopes": ["routes:write", "secrets:read"],
    "access_token_ttl_seconds": 3600
  }'
```

Lock to the service account `email` claim, or to its numeric `sub`. A rule must pin one of the two: `https://accounts.google.com` signs tokens for every Google account, so a rule without an identifying claim would match other projects' service accounts, and KnoxCall refuses it with a `400`.

Conditions compare **top-level claims by exact string match**. Two conditions that look reasonable do not work today, so do not add them:

* `email_verified` — Google sends it as a boolean, and a condition value is a string, so the rule would never match.
* `google.compute_engine.project_id` / `instance_id` — these sit inside a nested `google` object, and a dotted key is not a path. Instance-level binding is not supported yet; use one service account per workload you want to tell apart.

## 2. Run with the right service account

### Cloud Run / Cloud Functions

```bash theme={"dark"}
gcloud run deploy my-service \
  --image gcr.io/my-project/my-service \
  --service-account knoxcall-prod@my-project.iam.gserviceaccount.com \
  --set-env-vars KNOXCALL_TENANT=acme
```

### GKE Workload Identity

```yaml theme={"dark"}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: knoxcall-caller
  annotations:
    iam.gke.io/gcp-service-account: knoxcall-prod@my-project.iam.gserviceaccount.com
```

```yaml theme={"dark"}
spec:
  template:
    spec:
      serviceAccountName: knoxcall-caller
      containers:
        - env:
            - { name: KNOXCALL_TENANT, value: acme }
```

### GCE

The VM's default or attached service account is used automatically.

## 3. Call KnoxCall

```javascript theme={"dark"}
import { KnoxCall } from "@knoxcall/sdk";
const client = new KnoxCall({ tenant: process.env.KNOXCALL_TENANT });
const routes = await client.routes.list();
```

The SDK hits `http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=knoxcall:api`, fetches the signed token, and exchanges it for a KnoxCall access token.

## Troubleshooting

* **`Could not detect KnoxCall credentials`** — the metadata service isn't reachable. This is normal locally; set `KNOXCALL_CLIENT_ID` + `KNOXCALL_CLIENT_SECRET` for local dev.
* **`invalid_grant: no binding matched`** — verify the service account email matches what's in the binding. Run `gcloud auth print-identity-token --audiences=knoxcall:api` on the host and decode it.
* **Network egress** — if your GKE pod has restricted egress, allow `metadata.google.internal:80`.
