---
title: "Google Cloud KMS"
description: "Use Google Cloud KMS for MainSDK encryption keys - key rings per region, IAM roles, rotation, main.yaml, local testing and swapping."
section: "integrations"
canonical: "https://mainsdk.dev/docs/integrations/google-cloud-kms"
updated: "2026-10-28"
---

# Google Cloud KMS

> Use Google Cloud KMS for MainSDK encryption keys - key rings per region, IAM roles, rotation, main.yaml, local testing and swapping.

Canonical: https://mainsdk.dev/docs/integrations/google-cloud-kms · Updated: 2026-10-28

Google Cloud KMS is one of the key services behind [`keys`](https://mainsdk.dev/docs/interfaces/keys). MainSDK encrypts each record or file with its own data key and Cloud KMS wraps it with a key that never leaves Cloud KMS, one key per MainSDK region. Pick it when your app runs on Google Cloud.

## Interfaces it serves

### Encryption keys (`keys`)

- Status: Building for 0.3 · ships as a beta API
- Driver: `gcp-kms` (Google Cloud KMS)
- Docs: https://mainsdk.dev/docs/interfaces/keys.md

```yaml
keys:
  driver: gcp-kms
```

Swap to another provider: `aws-kms`, `azure-keyvault`, `vault-transit`, `local`.

## Before you start

1. **Enable the Cloud KMS API** in your Google Cloud project.
2. **Create a key ring and a symmetric encryption key in a location for each MainSDK region** (US, EU and India). Key rings belong to one location, so each region gets its own.
3. **Give your app an identity.** Prefer the service account your compute runs as, or Workload Identity Federation outside Google Cloud. KMS credentials must come from workload identity or the `secrets` driver, never from code.
4. **Grant only encrypt and decrypt** on those keys, such as the Cloud KMS CryptoKey encrypter/decrypter role. Keep key administration with a separate identity.

## Configure

```yaml title="main.yaml"
keys:
  driver: gcp-kms
  key:                       # one wrapping key per region; [TBD] confirm the identifier form the driver takes
    us: projects/pinecart/locations/us-east1/keyRings/app/cryptoKeys/pinecart-us
    eu: projects/pinecart/locations/europe-west1/keyRings/app/cryptoKeys/pinecart-eu
    in: projects/pinecart/locations/asia-south1/keyRings/app/cryptoKeys/pinecart-in
  per-user: true
  rotate: 365d
```

`mainsdk check` refuses the local driver in production with `MAIN-KEYS-0001`, so keep it in a development overlay.

## Use it

```ts title="app/lib/notes.ts"
import { keys } from "mainsdk";

export async function sealNote(note: string, user: { id: string }) {
  return keys.encrypt(note, { owner: user, region: "in", label: "sensitive" });
  // wrapped with the key under key.in
}
```

`db` fields with `encrypt: true` and `files` use the same keys without any call from you.

## Local development and testing

- **No Google Cloud project:** use the `local` keys driver in development.
- **Tests:** `createTestApp()` from `@mainsdk/testing` runs `keys` on its in-memory fake, and `chaos.on("keys", { outage: true })` simulates Cloud KMS being unreachable. MainSDK's conformance suite runs the `gcp-kms` driver against an emulator or mock.

```ts title="app/lib/notes.test.ts"
import { it } from "node:test";
import assert from "node:assert/strict";
import { createTestApp } from "@mainsdk/testing";
import { keys } from "mainsdk";

it("round-trips a sealed note", async () => {
  createTestApp({ main: "./main.yaml" });
  const sealed = await keys.encrypt("hello", { region: "eu", label: "personal" });
  const plain = await keys.decrypt(sealed, { purpose: "test" });
  assert.equal(new TextDecoder().decode(plain), "hello");
});
```

## What MainSDK adds on top

- A data key per record, wrapped by a Cloud KMS key that never leaves it.
- One wrapping key per region, chosen from the record owner's region.
- Every key use audited with key ID, purpose and caller, never the key material.
- Automatic rotation with old data still readable, and per-user keys for crypto-erasure.

See [Encryption keys](https://mainsdk.dev/docs/interfaces/keys) for the full contract.

## Limits and notes

- The driver comes with the keys spec (phase 1), after the AWS KMS and Vault Transit drivers.
- Cloud KMS can rotate keys on its own schedule; how that interacts with MainSDK's `rotate` period is not specified yet [TBD].
- Google Cloud HSM and customer-held keys through Google External Key Manager come with the hsm-keys spec in phase 3.
- `keys.raw()` returns the KMS client; `mainsdk check` lists every use.

## Swap to or from it

```yaml title="main.yaml"
keys:
  driver: gcp-kms   # was aws-kms; point key.us, key.eu and key.in at Cloud KMS keys
```

> **Warning:** Data keys wrapped by the old service still need it to unwrap. Plan a rewrap of existing data keys and keep the old keys enabled until it is done. A guided migration between KMS drivers is not specified yet.

## FAQ

**Can I use a multi-region location such as `europe`?** MainSDK only asks for one key per MainSDK region; any Cloud KMS location that keeps the data where it must stay will do.

**Does erasure reach backups?** Yes, with `per-user: true`: the backup still holds ciphertext, but the key to open it is gone.

## Next

- [Encryption keys](https://mainsdk.dev/docs/interfaces/keys) and [Secrets](https://mainsdk.dev/docs/interfaces/secrets)
- [Google Secret Manager](https://mainsdk.dev/docs/integrations/google-secret-manager), [AWS KMS](https://mainsdk.dev/docs/integrations/aws-kms) and [Azure Key Vault](https://mainsdk.dev/docs/integrations/azure-key-vault)
- [Swap a driver](https://mainsdk.dev/docs/guides/swap-a-driver)
