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

# Deploy on GCP Cloud Run

> Run Keyring with Cloud Run service identity, Cloud KMS, and Cloud Armor ingress.

export const gcpDeploymentValuesStore = {
  values: {
    projectId: "my-gcp-project",
    region: "us-central1",
    kmsLocation: "global",
    environment: "production",
    serviceAccountId: "keyring-runtime",
    organizationId: "ok_example123456",
    hostname: "keyring.example.com"
  },
  listeners: []
};

export const GcpVerificationCommands = () => {
  const [values, setValues] = useState(gcpDeploymentValuesStore.values);
  useEffect(() => {
    gcpDeploymentValuesStore.listeners.push(setValues);
    return () => {
      gcpDeploymentValuesStore.listeners = gcpDeploymentValuesStore.listeners.filter(listener => listener !== setValues);
    };
  }, []);
  const commands = `curl --fail https://${values.hostname}/healthz
curl --fail https://${values.hostname}/transfer-keys`;
  return <CodeBlock language="bash" filename="Verify Keyring" wrap>
      {commands}
    </CodeBlock>;
};

export const GcpSecretManagerCommands = () => {
  const [values, setValues] = useState(gcpDeploymentValuesStore.values);
  useEffect(() => {
    gcpDeploymentValuesStore.listeners.push(setValues);
    return () => {
      gcpDeploymentValuesStore.listeners = gcpDeploymentValuesStore.listeners.filter(listener => listener !== setValues);
    };
  }, []);
  const serviceAccount = `${values.serviceAccountId}@${values.projectId}.iam.gserviceaccount.com`;
  const commands = `gcloud secrets create keyring-encryption-config \\
  --project "${values.projectId}" \\
  --replication-policy automatic \\
  --labels "composio-keyring=true,environment=${values.environment}"

gcloud secrets versions add keyring-encryption-config \\
  --project "${values.projectId}" \\
  --data-file ./config.json

gcloud secrets add-iam-policy-binding keyring-encryption-config \\
  --project "${values.projectId}" \\
  --member "serviceAccount:${serviceAccount}" \\
  --role roles/secretmanager.secretAccessor`;
  return <CodeBlock language="bash" filename="Store the encryption configuration" wrap>
      {commands}
    </CodeBlock>;
};

export const GcpProvisioningCommands = () => {
  const [values, setValues] = useState(gcpDeploymentValuesStore.values);
  useEffect(() => {
    gcpDeploymentValuesStore.listeners.push(setValues);
    return () => {
      gcpDeploymentValuesStore.listeners = gcpDeploymentValuesStore.listeners.filter(listener => listener !== setValues);
    };
  }, []);
  const commands = `gcloud services enable \\
  run.googleapis.com \\
  cloudkms.googleapis.com \\
  secretmanager.googleapis.com \\
  --project "${values.projectId}"

gcloud kms keyrings create composio-keyring \\
  --project "${values.projectId}" \\
  --location "${values.kmsLocation}"

gcloud kms keys create keyring-credential \\
  --project "${values.projectId}" \\
  --location "${values.kmsLocation}" \\
  --keyring composio-keyring \\
  --purpose encryption \\
  --labels "composio-keyring=true,environment=${values.environment},purpose=credential"

gcloud kms keys create keyring-authorization-gate \\
  --project "${values.projectId}" \\
  --location "${values.kmsLocation}" \\
  --keyring composio-keyring \\
  --purpose encryption \\
  --labels "composio-keyring=true,environment=${values.environment},purpose=authorization-gate"

gcloud kms keys create keyring-transfer \\
  --project "${values.projectId}" \\
  --location "${values.kmsLocation}" \\
  --keyring composio-keyring \\
  --purpose asymmetric-encryption \\
  --default-algorithm rsa-decrypt-oaep-3072-sha256 \\
  --labels "composio-keyring=true,environment=${values.environment},purpose=secret-transfer"

gcloud iam service-accounts create "${values.serviceAccountId}" \\
  --project "${values.projectId}" \\
  --display-name "Composio Keyring runtime"`;
  return <CodeBlock language="bash" filename="Provision keys and service identity" wrap>
      {commands}
    </CodeBlock>;
};

export const GcpIamCommands = () => {
  const [values, setValues] = useState(gcpDeploymentValuesStore.values);
  useEffect(() => {
    gcpDeploymentValuesStore.listeners.push(setValues);
    return () => {
      gcpDeploymentValuesStore.listeners = gcpDeploymentValuesStore.listeners.filter(listener => listener !== setValues);
    };
  }, []);
  const serviceAccount = `${values.serviceAccountId}@${values.projectId}.iam.gserviceaccount.com`;
  const commands = `for KEY_NAME in keyring-credential keyring-authorization-gate; do
  gcloud kms keys add-iam-policy-binding "$KEY_NAME" \\
    --project "${values.projectId}" \\
    --location "${values.kmsLocation}" \\
    --keyring composio-keyring \\
    --member "serviceAccount:${serviceAccount}" \\
    --role roles/cloudkms.cryptoKeyEncrypterDecrypter
done

gcloud kms keys add-iam-policy-binding keyring-transfer \\
  --project "${values.projectId}" \\
  --location "${values.kmsLocation}" \\
  --keyring composio-keyring \\
  --member "serviceAccount:${serviceAccount}" \\
  --role roles/cloudkms.cryptoKeyDecrypter

gcloud kms keys add-iam-policy-binding keyring-transfer \\
  --project "${values.projectId}" \\
  --location "${values.kmsLocation}" \\
  --keyring composio-keyring \\
  --member "serviceAccount:${serviceAccount}" \\
  --role roles/cloudkms.publicKeyViewer`;
  return <CodeBlock language="bash" filename="Grant access to exact keys" wrap>
      {commands}
    </CodeBlock>;
};

export const GcpDeploymentInputs = () => {
  const [values, setValues] = useState(gcpDeploymentValuesStore.values);
  const inputClassName = "mt-1 w-full rounded-lg border border-zinc-950/15 bg-white px-3 py-2 font-mono text-sm text-zinc-950 outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 dark:border-white/15 dark:bg-zinc-950 dark:text-white";
  const updateValue = (name, value) => {
    setValues(current => {
      const next = {
        ...current,
        [name]: value
      };
      gcpDeploymentValuesStore.values = next;
      for (const listener of gcpDeploymentValuesStore.listeners) {
        listener(next);
      }
      return next;
    });
  };
  const inputs = [{
    label: "GCP project ID",
    name: "projectId"
  }, {
    label: "Cloud Run region",
    name: "region"
  }, {
    label: "KMS location",
    name: "kmsLocation"
  }, {
    label: "Runtime service account ID",
    name: "serviceAccountId"
  }, {
    label: "Composio organization ID",
    name: "organizationId"
  }, {
    label: "Keyring hostname",
    name: "hostname"
  }];
  return <div className="not-prose my-6 rounded-xl border border-zinc-950/10 bg-zinc-950/[0.025] p-4 dark:border-white/10 dark:bg-white/[0.035]">
      <div className="mb-4">
        <p className="m-0 text-sm font-semibold text-zinc-950 dark:text-white">
          Fill in your deployment values
        </p>
        <p className="mb-0 mt-1 text-sm text-zinc-600 dark:text-zinc-400">
          Every command on this page updates as you type.
        </p>
      </div>

      <div className="grid gap-4 sm:grid-cols-2">
        {inputs.map(input => <label className="block text-sm font-medium text-zinc-800 dark:text-zinc-200" key={input.name}>
            {input.label}
            <input type="text" value={values[input.name]} onChange={event => updateValue(input.name, event.target.value)} className={inputClassName} autoComplete="off" spellCheck={false} />
          </label>)}

        <label className="block text-sm font-medium text-zinc-800 dark:text-zinc-200">
          Environment
          <select value={values.environment} onChange={event => updateValue("environment", event.target.value)} className={inputClassName}>
            <option value="production">production</option>
            <option value="staging">staging</option>
          </select>
        </label>
      </div>
    </div>;
};

export const GcpDeployCommand = () => {
  const [values, setValues] = useState(gcpDeploymentValuesStore.values);
  useEffect(() => {
    gcpDeploymentValuesStore.listeners.push(setValues);
    return () => {
      gcpDeploymentValuesStore.listeners = gcpDeploymentValuesStore.listeners.filter(listener => listener !== setValues);
    };
  }, []);
  const serviceAccount = `${values.serviceAccountId}@${values.projectId}.iam.gserviceaccount.com`;
  const command = `gcloud run deploy composio-keyring \\
  --image "composiohq/keyring:alpha" \\
  --project "${values.projectId}" \\
  --region "${values.region}" \\
  --service-account "${serviceAccount}" \\
  --set-env-vars "APP_ENV=${values.environment},RUNTIME=node,HOST=0.0.0.0,AUTH_JWKS_URL=https://backend.composio.dev/.well-known/jwks.json,AUTH_ISSUER=https://backend.composio.dev,AUTH_AUDIENCE=${values.organizationId},AUTH_JWT_ALGORITHMS=RS256,AUDIT_DURABILITY=required,OTEL_COLLECTOR_URL=https://otel.example.com,LOG_LEVEL=info" \\
  --set-secrets "ENCRYPTION_CONFIG=keyring-encryption-config:latest" \\
  --ingress internal-and-cloud-load-balancing \\
  --no-default-url \\
  --allow-unauthenticated`;
  return <CodeBlock language="bash" filename="Deploy Keyring" wrap>
      {command}
    </CodeBlock>;
};

export const AnimatedRequestFlow = ({flow}) => {
  const iconStroke = {
    fill: "none",
    stroke: "currentColor",
    strokeWidth: 1.7,
    strokeLinecap: "round",
    strokeLinejoin: "round"
  };
  function diagramIcon(name, x, y) {
    const transform = `translate(${x - 10} ${y - 10})`;
    if (name === "composio") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <path d="M4 6.5 15.5 4v4L7 9.3v1.4l8.5 1.3v4L4 13.5z" {...iconStroke} />
          <path d="M8 2.7v14.6" {...iconStroke} />
        </g>;
    }
    if (name === "key") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <circle cx="7" cy="9" r="3.2" {...iconStroke} />
          <path d="m9.8 7.5 6-3m-2 1 1.5 2" {...iconStroke} />
        </g>;
    }
    if (name === "lock") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <rect x="4" y="8" width="12" height="9" rx="2.5" {...iconStroke} />
          <path d="M7 8V6a3 3 0 0 1 6 0v2m-3 3.5v2" {...iconStroke} />
        </g>;
    }
    if (name === "shield") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <path d="M10 2.5 16 5v4.5c0 4-2.4 6.5-6 8-3.6-1.5-6-4-6-8V5z" {...iconStroke} />
          <path d="m7.3 10 1.8 1.8 3.8-4" {...iconStroke} />
        </g>;
    }
    if (name === "sparkles") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <path d="m10 2 1.2 4.2L15 8l-3.8 1.8L10 14l-1.2-4.2L5 8l3.8-1.8zM4 14l.5 1.5L6 16l-1.5.5L4 18l-.5-1.5L2 16l1.5-.5z" {...iconStroke} />
        </g>;
    }
    if (name === "check") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <circle cx="10" cy="10" r="7" {...iconStroke} />
          <path d="m6.5 10 2.2 2.2 4.8-5" {...iconStroke} />
        </g>;
    }
    if (name === "settings") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <circle cx="10" cy="10" r="2.6" {...iconStroke} />
          <path d="M10 2.5v2m0 11v2M2.5 10h2m11 0h2M4.7 4.7l1.4 1.4m7.8 7.8 1.4 1.4m0-10.6-1.4 1.4m-7.8 7.8-1.4 1.4" {...iconStroke} />
        </g>;
    }
    if (name === "network") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <circle cx="4" cy="10" r="2" {...iconStroke} />
          <circle cx="16" cy="5" r="2" {...iconStroke} />
          <circle cx="16" cy="15" r="2" {...iconStroke} />
          <path d="m6 9 8-3m-8 5 8 3" {...iconStroke} />
        </g>;
    }
    if (name === "route") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <circle cx="4" cy="5" r="2" {...iconStroke} />
          <circle cx="16" cy="15" r="2" {...iconStroke} />
          <path d="M6 5h3a3 3 0 0 1 3 3v4a3 3 0 0 0 3 3" {...iconStroke} />
        </g>;
    }
    if (name === "identity") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <circle cx="10" cy="7" r="3.5" {...iconStroke} />
          <path d="M3.5 17c.8-3.2 3-5 6.5-5s5.7 1.8 6.5 5" {...iconStroke} />
        </g>;
    }
    if (name === "server") {
      return <g className="keyring-diagram__icon" transform={transform}>
          <rect x="3" y="3" width="14" height="6" rx="2" {...iconStroke} />
          <rect x="3" y="11" width="14" height="6" rx="2" {...iconStroke} />
          <path d="M6 6h.1M6 14h.1M9 6h5M9 14h5" {...iconStroke} />
        </g>;
    }
    return <g className="keyring-diagram__icon" transform={transform}>
        <rect x="3" y="3" width="14" height="14" rx="4" {...iconStroke} />
        <path d="M7 7h6v6H7z" {...iconStroke} />
      </g>;
  }
  function serviceIcon(step, x, y) {
    if (step.icon === "composio") {
      return <image className="minimal-flow__composio-mark" href="/assets/brand/composio_mark_black.svg" x={x - 13} y={y - 13} width="26" height="26" />;
    }
    return diagramIcon(step.icon, x, y);
  }
  function flowTiming(count) {
    const pause = 0.72;
    const travel = 0.28;
    const total = count * pause + (count - 1) * travel;
    const keyPoints = [0, 0];
    const keyTimes = [0, pause / total];
    let elapsed = pause;
    for (let index = 1; index < count; index += 1) {
      elapsed += travel;
      const position = index / (count - 1);
      keyPoints.push(position);
      keyTimes.push(elapsed / total);
      elapsed += pause;
      keyPoints.push(position);
      keyTimes.push(elapsed / total);
    }
    return {
      duration: `${Math.max(7, count * 1.35)}s`,
      keyPoints: keyPoints.join(";"),
      keyTimes: keyTimes.join(";")
    };
  }
  function flowCanvas(spec, flowName, mobile) {
    const width = mobile ? 420 : 1000;
    const height = mobile ? 68 + spec.steps.length * 96 : 250;
    const positions = spec.steps.map((step, index) => {
      if (mobile) return {
        x: 78,
        y: 46 + index * 96,
        step
      };
      const gap = 820 / (spec.steps.length - 1);
      return {
        x: 90 + index * gap,
        y: 112,
        step
      };
    });
    const path = mobile ? `M 78 ${positions[0].y} L 78 ${positions[positions.length - 1].y}` : `M ${positions[0].x} 112 L ${positions[positions.length - 1].x} 112`;
    const timing = flowTiming(spec.steps.length);
    const titleId = `minimal-${flowName}-${mobile ? "mobile" : "desktop"}-title`;
    const descriptionId = `minimal-${flowName}-${mobile ? "mobile" : "desktop"}-description`;
    return <svg className={mobile ? "minimal-flow__mobile" : "minimal-flow__desktop"} viewBox={`0 0 ${width} ${height}`} role="img" aria-labelledby={`${titleId} ${descriptionId}`}>
        <title id={titleId}>{spec.steps.map(step => step.title).join(" to ")}</title>
        <desc id={descriptionId}>{spec.description}</desc>
        <path className="minimal-flow__track" d={path} />
        {!mobile && spec.packet ? <g className="minimal-flow__request" aria-hidden="true">
            <rect x="-88" y="-88" width="176" height="54" rx="11" />
            <text className="minimal-flow__request-title" x="0" y="-67" textAnchor="middle">
              {spec.packet.title}
            </text>
            <text className="minimal-flow__request-detail" x="0" y="-47" textAnchor="middle">
              {spec.packet.detail}
            </text>
            <animateMotion path={path} dur={timing.duration} keyPoints={timing.keyPoints} keyTimes={timing.keyTimes} calcMode="linear" repeatCount="indefinite" />
          </g> : null}
        <g className="minimal-flow__streak" aria-hidden="true">
          <line x1="-18" x2="18" />
          <line className="minimal-flow__streak-glow" x1="-12" x2="12" />
          <animateMotion path={path} dur={timing.duration} keyPoints={timing.keyPoints} keyTimes={timing.keyTimes} calcMode="linear" rotate="auto" repeatCount="indefinite" />
        </g>
        {positions.map(({x, y, step}, index) => <g className={`minimal-flow__node minimal-flow__node--${step.kind}`} key={`${step.title}-${index}`}>
            <circle className="minimal-flow__disc" cx={x} cy={y} r="25" />
            {serviceIcon(step, x, y)}
            <text className="minimal-flow__title" x={mobile ? x + 46 : x} y={mobile ? y - 2 : y + 45} textAnchor={mobile ? "start" : "middle"}>
              {step.title}
            </text>
            <text className="minimal-flow__detail" x={mobile ? x + 46 : x} y={mobile ? y + 18 : y + 64} textAnchor={mobile ? "start" : "middle"}>
              {step.detail}
            </text>
          </g>)}
      </svg>;
  }
  const flowSpecs = {
    connection: {
      description: "A signed request from Composio reaches the customer Keyring endpoint, is verified for the customer organization, and continues to an approved provider.",
      steps: [{
        title: "Composio",
        detail: "Signed request",
        kind: "composio",
        icon: "composio"
      }, {
        title: "HTTPS edge",
        detail: "Composio-only ingress",
        kind: "customer",
        icon: "network"
      }, {
        title: "Keyring",
        detail: "Organization verified",
        kind: "customer",
        icon: "shield"
      }, {
        title: "Provider",
        detail: "Connected route",
        kind: "provider",
        icon: "check"
      }]
    },
    policy: {
      description: "Keyring authenticates Composio and checks the requested toolkit, destination, credential placement, and redirects before allowing credential use.",
      steps: [{
        title: "Composio",
        detail: "Signed tool call",
        kind: "composio",
        icon: "composio"
      }, {
        title: "Identity",
        detail: "JWT + source IP",
        kind: "customer",
        icon: "identity"
      }, {
        title: "Toolkit",
        detail: "Operation allowed",
        kind: "control",
        icon: "toolkit"
      }, {
        title: "Destination",
        detail: "Host + redirects",
        kind: "control",
        icon: "route"
      }, {
        title: "Provider",
        detail: "Approved request",
        kind: "provider",
        icon: "check"
      }],
      packet: {
        title: "PROVIDER_REQUEST",
        detail: "Policy: pending"
      }
    },
    deployment: {
      description: "Composio reaches a restricted customer HTTPS edge, Keyring uses workload identity for customer KMS access, and approved traffic continues to providers.",
      steps: [{
        title: "Composio",
        detail: "Allowed source",
        kind: "composio",
        icon: "composio"
      }, {
        title: "HTTPS edge",
        detail: "TLS + allowlist",
        kind: "customer",
        icon: "network"
      }, {
        title: "Keyring",
        detail: "Customer runtime",
        kind: "customer",
        icon: "shield"
      }, {
        title: "Cloud identity",
        detail: "Short-lived access",
        kind: "customer",
        icon: "identity"
      }, {
        title: "Your KMS",
        detail: "Scoped keys",
        kind: "customer",
        icon: "key"
      }, {
        title: "Provider",
        detail: "Approved traffic",
        kind: "provider",
        icon: "sparkles"
      }]
    },
    aws: {
      description: "Composio reaches an allowlisted AWS load balancer, private ECS tasks run Keyring, and the task role authorizes access to tagged KMS keys before provider traffic leaves.",
      steps: [{
        title: "Composio",
        detail: "Allowed source",
        kind: "composio",
        icon: "composio"
      }, {
        title: "AWS ALB",
        detail: "TLS + allowlist",
        kind: "customer",
        icon: "network"
      }, {
        title: "ECS Keyring",
        detail: "Private task",
        kind: "customer",
        icon: "server"
      }, {
        title: "Task role",
        detail: "Short-lived identity",
        kind: "customer",
        icon: "identity"
      }, {
        title: "AWS KMS",
        detail: "Tagged keys",
        kind: "customer",
        icon: "key"
      }, {
        title: "Provider",
        detail: "Approved traffic",
        kind: "provider",
        icon: "sparkles"
      }]
    },
    gcp: {
      description: "Composio reaches Cloud Armor and the HTTPS load balancer, Cloud Run hosts Keyring, and its service identity authorizes exact-key Cloud KMS access.",
      steps: [{
        title: "Composio",
        detail: "Allowed source",
        kind: "composio",
        icon: "composio"
      }, {
        title: "Cloud Armor",
        detail: "TLS + allowlist",
        kind: "customer",
        icon: "network"
      }, {
        title: "Cloud Run",
        detail: "Keyring service",
        kind: "customer",
        icon: "server"
      }, {
        title: "Service identity",
        detail: "Metadata token",
        kind: "customer",
        icon: "identity"
      }, {
        title: "Cloud KMS",
        detail: "Exact-key IAM",
        kind: "customer",
        icon: "key"
      }, {
        title: "Provider",
        detail: "Approved traffic",
        kind: "provider",
        icon: "sparkles"
      }]
    }
  };
  const spec = flowSpecs[flow];
  if (!spec) return null;
  const figureClassName = `keyring-diagram minimal-flow minimal-flow--${flow}`;
  return <figure className={figureClassName}>
      {flowCanvas(spec, flow, false)}
      {flowCanvas(spec, flow, true)}
    </figure>;
};

GCP Cloud Run is the preferred GCP deployment. Keyring obtains a short-lived OAuth access token from the metadata server as the service’s assigned identity; no service-account key file is required.

<AnimatedRequestFlow flow="gcp" />

## 1. Provision keys and service identity

Create two symmetric Cloud KMS CryptoKeys and one asymmetric decrypt CryptoKeyVersion using an RSA OAEP SHA-256 algorithm.

Enter your values once. Every command on this page fills them in automatically.

<GcpDeploymentInputs />

<GcpProvisioningCommands />

Assign a dedicated Cloud Run service account. At the narrowest practical key scope, grant:

| Key                         | Permissions                                                                           |
| --------------------------- | ------------------------------------------------------------------------------------- |
| Credential + gate roots     | `cloudkms.cryptoKeyVersions.useToEncrypt`, `cloudkms.cryptoKeyVersions.useToDecrypt`  |
| Secret-transfer key version | `cloudkms.cryptoKeyVersions.viewPublicKey`, `cloudkms.cryptoKeyVersions.useToDecrypt` |

The predefined `roles/cloudkms.cryptoKeyEncrypterDecrypter` covers symmetric use. Grant public-key access only for the transfer key, or create a custom role with the exact permissions above. See [Cloud KMS IAM guidance](https://cloud.google.com/kms/docs/iam).

Bind the runtime identity on each exact key instead of granting a project-wide KMS role:

<GcpIamCommands />

Labels make the three keys easy to inventory and audit. The exact per-key IAM bindings—not the
labels—enforce access. Do not grant the Cloud Run identity KMS Admin, project Editor, or permission
to change IAM policies.

Cloud Run makes the assigned [service identity available through metadata](https://cloud.google.com/run/docs/securing/service-identity); Keyring requests and refreshes its short-lived token.

## 2. Store the encryption configuration

Use the complete [GCP KMS configuration](/configuration/kms_adapters#gcp-cloud-kms), with each adapter configured for metadata auth:

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
type: gcp_cloud_kms
auth:
  method: metadata
```

Store the complete configuration as compact JSON in a Secret Manager secret named `keyring-encryption-config`. Do not set `origin_policy` for the standard bundled Composio policy.

<GcpSecretManagerCommands />

## 3. Deploy the service

Choose a [telemetry delivery path](/deployment/observability) before deploying. A collector sidecar is
recommended; use a direct OTLP endpoint only when a sidecar is not feasible.

Use the official Docker Hub image. The container reads Cloud Run’s injected `PORT`; set only
`HOST=0.0.0.0`:

<GcpDeployCommand />

Grant the Cloud Run service identity Secret Manager access to that secret. Google documents [secret environment-variable injection](https://cloud.google.com/run/docs/configuring/services/secrets).

<Warning>
  `--allow-unauthenticated` disables the Google identity-token gate because Composio authenticates
  with a Keyring JWT, not a Google ID token. It does not make the intended path open to every
  source: the next step forces traffic through the load balancer and Cloud Armor allowlist.
</Warning>

## 4. Add the restricted HTTPS edge

Create a global external Application Load Balancer with a serverless NEG pointing to the Cloud Run service. Attach a Cloud Armor policy that:

1. allows the four [Composio backend `/32` addresses](/deployment/overview#network-access);
2. denies all other source addresses by default; and
3. optionally rate-limits the allowed sources at a value agreed with Composio.

Keep Cloud Run ingress at `internal-and-cloud-load-balancing` and the default `run.app` URL disabled. This prevents direct internet traffic from bypassing the load balancer and Cloud Armor. Follow Google’s [serverless NEG load-balancer guide](https://cloud.google.com/load-balancing/docs/https/setting-up-https-serverless) and [Cloud Run ingress reference](https://cloud.google.com/run/docs/securing/ingress).

## 5. Verify and connect

After DNS and a managed certificate are ready:

<GcpVerificationCommands />

Then [connect the deployment to Composio](/deployment/overview#connect-keyring-to-composio). In logs, verify
that Cloud KMS initialized with metadata identity. Never log metadata tokens, environment secrets, or
encrypted credential data.
