{
  "schema": "aaes/capability-pack/1",
  "provider": "gcp",
  "version": "1.1.0",
  "description": "Google Cloud pack: read projects, project access policies and billing accounts, and administer Vertex AI endpoints, read Model Garden models and manage Vertex AI quotas. The access-policy POST retrieves data without changing it.",
  "notes": "Every endpoint here is on shared Google API hosts — cloudresourcemanager.googleapis.com and cloudbilling.googleapis.com — which stay real: they are the documented authorities for these APIs, not tenant placeholders. PROJECT_ID and PARENT_RESOURCE are placeholders the deployment binds before enabling: PARENT_RESOURCE takes the documented form folders/FOLDER_ID or organizations/ORG_ID (projects.list lists direct children of the named parent, and the caller needs resourcemanager.projects.list on that parent), PROJECT_ID a concrete project id or number.\n\nThe token-minting host oauth2.googleapis.com is likewise a real shared host, but minting is the external process's call, not a capability: the executor injects the minted value and never calls the token endpoint itself, so no capability allowlists it.\n\nApproval classification: all four capabilities sit at R0, the read floor. projects.getIamPolicy is a POST by HTTP shape (a gRPC-transcoding custom verb with a body carrying only GetPolicyOptions) but a read by effect — it returns the IAM policy bound to the project and changes nothing; it is registered as read with the POST shape stated plainly in the description.\n\nThe returned policy reveals which principals hold which roles on the project — security configuration that includes human principals' email addresses, so it registers at the pii class — and the exact input a later setIamPolicy (not registered) would rewrite.\n\nBilling account list is financial at the read floor — a lower bound: an R0 financial read can still be a serious confidentiality risk.\n\nDocumented exclusions: this pack does not register projects create/delete/undelete/patch/move, setIamPolicy on any resource, testIamPermissions, organizations or folders listing (projects.list with an organization parent covers the projects directly parented by the organization — projects beneath folders are not returned — and the organizations.search and folders.list methods are not registered), billing account updates (projects.updateBillingInfo, which links or unlinks billing and is a money-relevant write), or the Cloud Billing Budgets and Cloud Asset Inventory APIs.\n\nThe two list reads describe collection reads only; projects.get is a record read and is registered as one.\n\nVertex AI administration (added in 1.1.0): nine capabilities cover Vertex AI endpoints (list, get, create, deploy a model, undeploy a model, delete), one Model Garden catalog read, and Vertex AI quotas through the Cloud Quotas API (list quotas, request a new value). The endpoint capabilities use the regional host us-central1-aiplatform.googleapis.com with locations/us-central1 in the path. For another region, substitute both the host and the location, and the egress allowlist with them. PROJECT_ID, ENDPOINT_ID, PUBLISHER and MODEL are placeholders. The Model Garden read uses the global host aiplatform.googleapis.com and the quota capabilities cloudquotas.googleapis.com with locations/global.\n\nThese nine capabilities name a separate reference, secret-ref-gcp-vertex-admin-token. Google's discovery documents list only the aiplatform or cloud-platform scopes for the Vertex AI methods, and only cloud-platform for Cloud Quotas, so the read-only token above cannot call them. Mint the admin token for a second service account by the same JWT process, with scope https://www.googleapis.com/auth/cloud-platform, and limit that account with IAM to the aiplatform.endpoints.* and cloudquotas.quotas.* permissions the enabled capabilities need.\n\nTiers sit at the floor of the declared actions. Deploying a model declares pay because deployed models bill by node hour until undeployed. Undeploying and deleting are R3. A quota request is R2 by the action floor. Model Garden enablement is not registered: Google documents enabling a partner model as a Console step that needs the Consumer Procurement Entitlement Manager role and the Cloud Commerce Consumer Procurement API, and the retrieved references publish no REST operation for it that this review could check. Every other method of the Vertex AI and Cloud Quotas APIs is excluded, among them the prediction and generateContent calls, endpoints.patch, endpoints.update and mutateDeployedModel, model upload, the Model Garden deploy method, tuning and batch prediction jobs, quotaInfos.get, quotaPreferences get, list and patch, and the quota adjuster settings.",
  "credential": {
    "secret_ref_id": "secret-ref-gcp-service-account-token",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "Store an OAuth 2.0 access token for a dedicated, minimally privileged Google Cloud service account in the client's secret manager. The stored value is the complete Authorization header: 'Bearer <token>'. customer_vault is the supported production custody model; identity_federation and customer_broker are refused at startup, and inline is lab-only.\n\nToken issuance runs outside the executor. An external process holds the service account's RSA private key and signs an RS256 JWT with these fields: iss is the service-account email; scope is https://www.googleapis.com/auth/cloud-platform.read-only plus https://www.googleapis.com/auth/cloud-billing.readonly; aud is https://oauth2.googleapis.com/token; iat and exp bound the assertion to at most one hour. billingAccounts.list accepts cloud-platform, cloud-billing, and cloud-billing.readonly scopes; cloud-platform.read-only alone does not authorize that operation.\n\nThe process sends the assertion to https://oauth2.googleapis.com/token using grant_type urn:ietf:params:oauth:grant-type:jwt-bearer and stores the returned token as the complete header value. The documented response has expires_in 3600. Renew before expiry by repeating the grant; the executor injects the stored value unchanged and performs no token lifecycle.\n\nDisabling or deleting the service-account key or account blocks future token minting. Google documents invalid_grant on JWT exchanges when the signing key is deleted, disabled, or expired. This does not revoke an issued access token: it remains valid until expiry, with no documented per-token revocation. Treat its 3600-second lifetime as the exposure window.\n\nGrant roles/browser, or a custom role containing exactly resourcemanager.projects.get, resourcemanager.projects.list, and resourcemanager.projects.getIamPolicy, at the reviewed parent. Add billing.viewer only on billing accounts in scope; grant nothing broader.\n\nKeep the value only in executor memory for a call, never in logs, traces, diagnostics, or crash dumps. Do not enable the pack if the deployment cannot meet those requirements."
  },
  "capabilities": [
    {
      "capability_id": "gcp.projects.list",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "cloudresourcemanager.googleapis.com"
      ],
      "endpoint": "https://cloudresourcemanager.googleapis.com/v3/projects?parent=PARENT_RESOURCE",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-service-account-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-projects",
      "description": "List the projects that are direct children of the named parent (folders/FOLDER_ID or organizations/ORG_ID): project ids, numbers, display names, states and labels — the estate inventory. Google documents this as a strongly consistent view requiring resourcemanager.projects.list on the parent. PARENT_RESOURCE is a placeholder the deployment binds before enabling; pagination is the documented pageToken loop. The registered path covers the collection read only.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.projects.get",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "cloudresourcemanager.googleapis.com"
      ],
      "endpoint": "https://cloudresourcemanager.googleapis.com/v3/projects/PROJECT_ID",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-service-account-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-projects",
      "description": "Retrieve one project by id or number (the documented GET /v3/{name=projects/*}): its display name, state, labels, parent and create time. This is the registered record read of the collection listed by gcp.projects.list. PROJECT_ID is a placeholder the deployment binds to a concrete project before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.projects.iam-policy.get",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "cloudresourcemanager.googleapis.com"
      ],
      "endpoint": "https://cloudresourcemanager.googleapis.com/v3/projects/PROJECT_ID:getIamPolicy",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-service-account-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-projects",
      "description": "Return the IAM access control policy for one project (the documented POST /v3/{resource=projects/*}:getIamPolicy). This is a POST-shaped read and is described as one: the method is a gRPC-transcoding custom verb whose body carries only an optional GetPolicyOptions object, and the response is the Policy (bindings of members to roles, with any conditions) — nothing is created or changed at the provider, so the declared action is read at the minimum R0 tier.\n\nThe policy reveals which principals hold which roles on the project — security configuration that includes human principals' email addresses, hence the pii class — and the exact input a setIamPolicy write (not registered by this pack) would replace. PROJECT_ID is a placeholder the deployment binds to a concrete project before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.billing.accounts.list",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "financial",
      "egress_allowlist": [
        "cloudbilling.googleapis.com"
      ],
      "endpoint": "https://cloudbilling.googleapis.com/v1/billingAccounts",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-service-account-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-billing-accounts",
      "description": "List the Cloud Billing accounts the credential has permission to view: account ids, display names, open/closed state and master accounts — billing-structure data. This is financial data at the read floor — a lower bound: an R0 financial read can still be a serious confidentiality risk. The registered path covers the collection read only — single-account reads and per-project billing info (projects/{id}/billingInfo) are not registered by this pack.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.vertex.endpoints.list",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "us-central1-aiplatform.googleapis.com"
      ],
      "endpoint": "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/endpoints",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-vertex-endpoints",
      "description": "List the Vertex AI endpoints in one project and location (projects.locations.endpoints.list), with their deployed models, traffic split and machine settings.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.vertex.endpoint.get",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "us-central1-aiplatform.googleapis.com"
      ],
      "endpoint": "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/endpoints/ENDPOINT_ID",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-vertex-endpoints",
      "description": "Read one Vertex AI endpoint (projects.locations.endpoints.get). ENDPOINT_ID is a placeholder.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.vertex.endpoint.create",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "us-central1-aiplatform.googleapis.com"
      ],
      "endpoint": "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/endpoints",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create"
      ],
      "resource_id": "gcp-vertex-endpoints",
      "description": "Create an empty Vertex AI endpoint (projects.locations.endpoints.create). The call returns a long-running operation. Deploying a model to the endpoint is the separate deploy-model capability.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.vertex.endpoint.deploy-model",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "financial",
      "egress_allowlist": [
        "us-central1-aiplatform.googleapis.com"
      ],
      "endpoint": "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/endpoints/ENDPOINT_ID:deployModel",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create",
        "pay"
      ],
      "resource_id": "gcp-vertex-endpoints",
      "description": "Deploy a model to an endpoint (projects.locations.endpoints.deployModel), creating a DeployedModel on the machines the request body names. Google bills each deployed model by node hour even when it serves no predictions, until it is undeployed, so the capability declares pay at R3. The machine type and replica counts come from the request body. ENDPOINT_ID is a placeholder.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.vertex.endpoint.undeploy-model",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "us-central1-aiplatform.googleapis.com"
      ],
      "endpoint": "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/endpoints/ENDPOINT_ID:undeployModel",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "gcp-vertex-endpoints",
      "description": "Undeploy a model from an endpoint (projects.locations.endpoints.undeployModel), removing the DeployedModel and freeing its resources. Predictions sent to it fail afterwards. ENDPOINT_ID is a placeholder.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.vertex.endpoint.delete",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "us-central1-aiplatform.googleapis.com"
      ],
      "endpoint": "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/endpoints/ENDPOINT_ID",
      "method": "DELETE",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "gcp-vertex-endpoints",
      "description": "Delete a Vertex AI endpoint (projects.locations.endpoints.delete). Google allows this only after every model has been undeployed from it. ENDPOINT_ID is a placeholder.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.vertex.publisher-model.get",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "aiplatform.googleapis.com"
      ],
      "endpoint": "https://aiplatform.googleapis.com/v1/publishers/PUBLISHER/models/MODEL",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-vertex-model-garden",
      "description": "Read one Model Garden publisher model (publishers.models.get), for example PUBLISHER google or anthropic, from the global Vertex AI host. This reads the catalog entry; it does not enable the model. PUBLISHER and MODEL are placeholders.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.quotas.aiplatform.quota-infos.list",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "cloudquotas.googleapis.com"
      ],
      "endpoint": "https://cloudquotas.googleapis.com/v1/projects/PROJECT_ID/locations/global/services/aiplatform.googleapis.com/quotaInfos",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "gcp-aiplatform-quotas",
      "description": "List the project's Vertex AI quotas through the Cloud Quotas API (quotaInfos.list for service aiplatform.googleapis.com): each quota's dimensions, current limits and whether it can be adjusted.",
      "requires_review": true
    },
    {
      "capability_id": "gcp.quotas.quota-preference.create",
      "version": "1.1.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "cloudquotas.googleapis.com"
      ],
      "endpoint": "https://cloudquotas.googleapis.com/v1/projects/PROJECT_ID/locations/global/quotaPreferences",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-gcp-vertex-admin-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create"
      ],
      "resource_id": "gcp-aiplatform-quotas",
      "description": "Ask for a new quota value (quotaPreferences.create): the body names the service, quota id, dimensions and preferred value. Google may grant an increase without further review, which raises how much the project can spend, so policy should raise this tier where quotas cap cost.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "gcp-projects",
      "kind": "api",
      "locator": "https://cloudresourcemanager.googleapis.com/v3/projects",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    },
    {
      "resource_id": "gcp-billing-accounts",
      "kind": "api",
      "locator": "https://cloudbilling.googleapis.com/v1/billingAccounts",
      "data_class": "financial",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    },
    {
      "resource_id": "gcp-vertex-endpoints",
      "kind": "api",
      "locator": "https://us-central1-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/endpoints",
      "data_class": "financial",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    },
    {
      "resource_id": "gcp-vertex-model-garden",
      "kind": "api",
      "locator": "https://aiplatform.googleapis.com/v1/publishers",
      "data_class": "internal",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    },
    {
      "resource_id": "gcp-aiplatform-quotas",
      "kind": "api",
      "locator": "https://cloudquotas.googleapis.com/v1/projects/PROJECT_ID/locations/global/services/aiplatform.googleapis.com",
      "data_class": "internal",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-10-22",
    "sources": [
      "https://docs.cloud.google.com/resource-manager/reference/rest/v3/projects/list (retrieved 2026-09-21)",
      "https://docs.cloud.google.com/resource-manager/reference/rest/v3/projects/get (retrieved 2026-09-21)",
      "https://docs.cloud.google.com/resource-manager/reference/rest/v3/projects/getIamPolicy (retrieved 2026-09-21)",
      "https://cloud.google.com/billing/docs/reference/rest/v1/billingAccounts/list (retrieved 2026-09-21)",
      "https://developers.google.com/identity/protocols/oauth2/service-account (retrieved 2026-09-21)",
      "https://docs.cloud.google.com/vertex-ai/docs/reference/rest (retrieved 2026-10-22)",
      "https://aiplatform.googleapis.com/$discovery/rest?version=v1 (retrieved 2026-10-22)",
      "https://cloud.google.com/vertex-ai/pricing (retrieved 2026-10-22)",
      "https://docs.cloud.google.com/vertex-ai/docs/predictions/undeploy-model (retrieved 2026-10-22)",
      "https://docs.cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models (retrieved 2026-10-22)",
      "https://docs.cloud.google.com/docs/quotas/reference/rest/v1/projects.locations.services.quotaInfos/list (retrieved 2026-10-22)",
      "https://docs.cloud.google.com/docs/quotas/reference/rest/v1/projects.locations.quotaPreferences/create (retrieved 2026-10-22)",
      "https://cloudquotas.googleapis.com/$discovery/rest?version=v1 (retrieved 2026-10-22)"
    ],
    "reviewer_id": "owner-delegated"
  }
}
