Giving Your AI Agents Google Workspace Access from Kubernetes

My AI orchestrator can now read my calendar, search my email, and browse my Drive, all from inside a Kubernetes pod with no browser.

Giving Your AI Agents Google Workspace Access from Kubernetes

My AI orchestrator (Henry, running on OpenClaw inside a Kubernetes pod) can now read my Google Calendar, search my Gmail, browse my Drive, and pull content from Google Docs. No browser. No desktop app. Just API calls from inside a container on my homelab cluster.

This turned out to be more interesting than I expected. Not because the Google APIs are hard (they're well-documented), but because OAuth and containers don't naturally get along.

Why This Matters

An AI agent that can only chat is a toy. An agent that can check your calendar before suggesting a meeting time, scan your recent emails for context on a project, or search Drive for that architecture doc you wrote last quarter: that's actually useful.

I wanted Henry to have read access to my Google Workspace as a baseline capability. Not "let me open a browser tab for you": actual programmatic access that works in automated workflows, from a pod with no display server and no user session.

The Tool: gog CLI

gog by steipete is a Go binary that wraps Google Workspace APIs into a clean CLI. It covers Gmail, Calendar, Drive, Docs, Sheets, and Contacts. Simple commands like:

gog calendar list --days 7
gog gmail search "from:boss subject:quarterly"
gog drive search "architecture doc"
gog docs read <document-id>

OpenClaw registers it as a skill, so Henry can call these commands directly. But getting gog to work inside a Kubernetes container required solving three problems.

Challenge 1: OAuth Without a Browser

gog uses Google's OAuth2 flow, which normally opens a browser for consent. Containers don't have browsers. The fix is gog's --manual flag:

gog auth login --manual

This prints a URL instead of opening a browser. You authorize in your own browser, Google redirects to localhost with an auth code in the URL, and you paste that redirect URL back into the terminal. Since there's no browser in the container, you do this via kubectl exec:

kubectl exec -it -n openclaw deploy/openclaw -- gog auth login --manual

Copy the URL, open it on your laptop, authorize, copy the redirect URL from your browser's address bar, paste it back. One-time setup: the token refreshes automatically after that.

Challenge 2: Token Storage Without a Keyring

gog defaults to the OS keyring for storing OAuth tokens: macOS Keychain on Mac, Secret Service on Linux. Containers have neither. Running gog auth login inside a pod fails with:

failed to open keyring: dbus: couldn't determine address of session bus

The solution is file-based keyring storage:

gog auth keyring file

This tells gog to store credentials in an encrypted file instead of the OS keyring. The encryption key comes from the GOG_KEYRING_PASSWORD environment variable, which I inject via a SOPS-encrypted Kubernetes secret:

apiVersion: v1
kind: Secret
metadata:
  name: openclaw-gog-secrets
  namespace: openclaw
type: Opaque
stringData:
  GOG_KEYRING_PASSWORD: "<random-password>"

Encrypted with sops --encrypt --in-place before committing, obviously. The deployment references it via envFrom.

Challenge 3: Config Persistence Across Pod Restarts

gog stores its config and tokens in ~/.config/gogcli/. In the OpenClaw container, that's /home/node/.config/gogcli/. Problem: /home/node/.config is on the ephemeral container filesystem. Pod restarts wipe it. You'd have to re-authenticate every time the pod gets rescheduled.

OpenClaw already has a PVC mounted at /home/node/.openclaw for persistent data. The question was how to get gog to write there instead of ~/.config.

My first attempt: add an init container that creates a symlink from /home/node/.config/gogcli to /home/node/.openclaw/gogcli. Seemed clean. Didn't work.

Init containers and the main container share volumes, but writes to the container's filesystem layer don't carry over. The init container creates the symlink on its own ephemeral layer. When the main container starts, /home/node/.config/gogcli doesn't exist. The symlink is gone. You'd need to mount a shared emptyDir at /home/node/.config to make this work, and that creates its own problems (other tools also write to .config).

The Clean Fix: XDG_CONFIG_HOME

The XDG Base Directory Specification defines XDG_CONFIG_HOME as the override for ~/.config. gog respects it. Set one environment variable:

env:
  - name: XDG_CONFIG_HOME
    value: /home/node/.openclaw

Now gog looks for config at /home/node/.openclaw/gogcli/, which is on the PVC. Tokens survive pod restarts. No symlinks, no init containers, no shared volumes. One env var.

Google Cloud Setup

The Google side is straightforward but has steps:

  • Create a Google Cloud project (or use an existing one)
  • Enable APIs: Calendar, Gmail, Drive, Docs, Sheets
  • Configure the OAuth consent screen (External, add your email as a test user)
  • Create OAuth credentials: Desktop application type
  • Download the client_secret.json

The Desktop application type is important. Web application credentials expect a redirect to a real URL. Desktop credentials allow the localhost redirect that --manual mode uses.

Get the credentials file into the container:

kubectl cp client_secret.json openclaw/openclaw-xxx:/home/node/.openclaw/gogcli/client_secret.json

Then run the auth flow:

kubectl exec -it -n openclaw deploy/openclaw -- gog auth login --manual

Incremental Scope Rollout

I started conservative: Calendar read-only. Once I confirmed Henry was using it responsibly (checking my schedule before suggesting times, not trying to create events), I expanded:

  • Calendar (read-only): calendar.readonly, calendar.events.readonly
  • Gmail (read/write): gmail.readonly, gmail.send, gmail.modify
  • Drive (full): drive.readonly, drive.file
  • Docs + Sheets (read): documents.readonly, spreadsheets.readonly

One thing to know: Google's consent screen doesn't support partial scope grants per API. When you re-authorize with new scopes, you grant all of them at once. There's no "grant Calendar but deny Gmail" in the consent flow. You control what scopes to *request*, but the user either approves all requested scopes or none.

For sensitive scopes like Gmail, Google shows an "unverified app" warning. For personal use this is fine: click through "Advanced" → "Go to [app name]." For production use, you'd need to go through Google's verification process.

Security Considerations

Let's be direct about what this means: your Google OAuth tokens now live on a Longhorn PVC in your Kubernetes cluster. The file-based keyring encrypts them with GOG_KEYRING_PASSWORD, so they're not plaintext on disk. But the encryption key is in a Kubernetes secret (SOPS-encrypted at rest in Git, decrypted in-cluster by Flux).

The threat model:

  • Pod compromise = access to your Google Workspace with whatever scopes you granted
  • PVC access = encrypted token file (need GOG_KEYRING_PASSWORD to decrypt)
  • SOPS key compromise = everything, but that was already true for all your other secrets

Mitigations I'm using:

  • Network policies restrict OpenClaw's egress to only what it needs
  • The namespace has its own RBAC: no cross-namespace access
  • runAsNonRoot with a non-root user in the security context
  • Token scopes limited to what's actually needed (no admin scopes)
  • Audit logs in Google Workspace show all API access

This is a homelab. I'm the only user, and I'm the one granting consent. For a shared or production environment, you'd want stricter controls: short-lived tokens, a proper OAuth proxy, maybe Workload Identity Federation instead of long-lived credentials.

The End Result

After setup, gog shows as a ready skill in OpenClaw. Henry can:

> Henry, what's on my calendar today?

You have 3 events today:
- 9:00 AM: Team standup (30 min)
- 11:00 AM: Architecture review — migration planning (1 hr)
- 2:00 PM: 1:1 with Sarah (30 min)

Your architecture review has a Google Doc linked:
"Q2 Migration Plan" — last edited yesterday. Want me to pull the current content?
> Search my email for anything from IT Security this week

Found 4 emails from IT Security (last 7 days):
1. "CyberArk pilot — Phase 2 timeline" (Tuesday)
2. "Conditional Access policy update — MFA enforcement" (Monday)
3. "Sentinel alert rule changes — review needed" (Monday)
4. "Weekly security digest" (Friday, last week)

Want me to read any of these?
> Find the network architecture doc in Drive

Found: "Network Architecture — HomeLab v3" in My Drive/Infrastructure
Last modified: 2026-03-15
Type: Google Doc

Want me to pull the contents?

Calendar awareness alone makes the agent dramatically more useful for scheduling and context. Email search means I can ask "what was the last update on the CyberArk pilot?" without digging through my inbox. Drive search means documents I've written are accessible as context for conversations.

Summary

Getting Google Workspace access into a Kubernetes-hosted AI agent requires solving three problems:

  • OAuth flow: use --manual mode via kubectl exec
  • Token storage: file-based keyring with GOG_KEYRING_PASSWORD from a SOPS secret
  • Config persistence: XDG_CONFIG_HOME pointed at the PVC mount

The actual API integration is the easy part. The container lifecycle plumbing is where the work is. But once it's set up, it's stable: tokens auto-refresh, config survives pod restarts, and your agent has real context about your work life.

*Tags: Kubernetes, AI Agents, OpenClaw, Google Workspace, OAuth, Self-Hosting*