Skip to content

feat(kiloclaw): Google account access for bots#949

Open
iscekic wants to merge 93 commits intomainfrom
feat/kiloclaw-google-account-access
Open

feat(kiloclaw): Google account access for bots#949
iscekic wants to merge 93 commits intomainfrom
feat/kiloclaw-google-account-access

Conversation

@iscekic
Copy link
Contributor

@iscekic iscekic commented Mar 9, 2026

Summary

Adds end-to-end Google account access for kiloclaw bots: credential storage, a Docker-based OAuth setup flow, encrypted credential delivery to containers, and a Settings UI for managing connections.

Google Setup Docker image (kiloclaw/google-setup/)

  • Docker-based guided setup tool (setup.mjs) that walks the user through:
    1. JWT validation against the kiloclaw worker
    2. Fetching the worker's RSA public key for credential encryption
    3. gcloud auth login (browser-based)
    4. GCP project creation or selection (numbered picker)
    5. Enabling 13+ Google APIs (Gmail, Calendar, Drive, Docs, Sheets, Slides, Tasks, People, Forms, Chat, Classroom, Script, PubSub)
    6. Manual Desktop OAuth client creation in GCP Console
    7. gog auth credentials set + gog auth add --services=all to authorize
    8. Tarball ~/.config/gogcli/, encrypt with RSA-AES-256-GCM envelope, POST to worker
  • Published to GHCR (ghcr.io/kilo-org/google-setup, multi-arch amd64+arm64)

Credential storage & API (kiloclaw worker)

  • GoogleCredentials schema: encrypted gogConfigTarball (EncryptedEnvelope, max 32 KiB) + optional display email
  • Stored in DO persisted state, survives reboots/restores
  • RSA-AES-256-GCM envelope encryption (RSA-2048 OAEP wraps a 32-byte AES-GCM DEK)
  • GET /api/admin/public-key — derives and caches RSA public key from stored private key
  • POST/GET/DELETE /api/admin/google-credentials — store, check status, clear credentials
  • POST/DELETE /api/platform/google-credentials — platform-level store/clear endpoints
  • googleConnected boolean exposed in instance status response
  • Corrupted credentials don't block machine startup (wrapped in try/catch in buildEnvVars)

Controller credential writer (gog-credentials)

  • New gog-credentials.ts module extracts a pre-built gog config tarball to ~/.config/gogcli/ on container startup
  • Sets GOG_KEYRING_BACKEND=file, GOG_KEYRING_PASSWORD=kiloclaw, GOG_ACCOUNT=<email> env vars
  • Best-effort: logs warnings on failure but doesn't block controller/gateway startup
  • Cleans up stale config directory when tarball env var is absent (after disconnect)

Container image & startup

  • gog CLI (gogcli v0.11.0) already installed via Go in Dockerfile
  • uv (Python package manager) installed to /usr/local/bin
  • OpenClaw bumped to 2026.3.8
  • start-openclaw.sh additions:
    • Encrypted env var decryption (KILOCLAW_ENC_* → plaintext via AES-256-GCM)
    • Instance feature flags: npm-global-prefix, pip-global-prefix, uv-global-prefix (redirect installs to persistent volume)
    • TOOLS.md seeded on first provision
    • GOG_KEYRING_PASSWORD exported for gog CLI
  • GOOGLE_GOG_CONFIG_TARBALL classified as internal sensitive env var in secret-catalog

Next.js frontend

  • GoogleAccountSection in Settings tab: connection badge, docker setup command with copy button, disconnect button
  • Auto-fills API key and 1-hour JWT in the docker run command
  • Dev mode: adds --worker-url flag for local testing
  • tRPC routes: getGoogleSetupCommand, disconnectGoogle

Test plan

  • kiloclaw typecheck passes
  • kiloclaw tests pass (gog-credentials unit tests, env builder tests, encrypted envelope tests)
  • Docker image: gog --version works, tarball extraction verified
  • End-to-end: build google-setup image, run OAuth flow, verify credentials stored and delivered to container
  • Manual: Settings tab Google Account section renders, copy command works, disconnect flow works

iscekic added 3 commits March 9, 2026 19:52
Add GoogleCredentials schema (encrypted clientSecret + credentials envelopes),
DO storage/retrieval methods, googleConnected status field, and env builder
decryption into GOOGLE_CLIENT_SECRET_JSON / GOOGLE_CREDENTIALS_JSON.
…s writer

Add POST/DELETE /api/platform/google-credentials routes for storing and
clearing encrypted Google credentials. Add gws-credentials module that
writes client_secret.json and credentials.json to ~/.config/gws/ on
controller startup when the env vars are present.
Docker container that orchestrates the Google OAuth flow:
gws auth setup -> gws auth login -> encrypt credentials -> POST to platform.
Published as kilocode/google-setup for users to run locally.
@iscekic iscekic self-assigned this Mar 9, 2026
@kilo-code-bot
Copy link
Contributor

kilo-code-bot bot commented Mar 9, 2026

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
kiloclaw/controller/src/index.ts 120 Startup logs writeGogCredentials() failures and keeps booting, so the UI can still report googleConnected=true even though this machine never got a usable gog config for the current boot.
kiloclaw/src/gateway/env.ts 180 Decryption failures fall back to "start without Google access", but status endpoints still derive googleConnected from stored state alone and can show the account as connected while runtime access is gone.
Other Observations (not in diff)

N/A

Files Reviewed (32 files)
  • kiloclaw/Dockerfile - 0 issues
  • kiloclaw/controller/src/gog-credentials.test.ts - 0 issues
  • kiloclaw/controller/src/gog-credentials.ts - 0 new issues
  • kiloclaw/controller/src/index.ts - 1 issue
  • kiloclaw/e2e/docker-image-testing.md - 0 issues
  • kiloclaw/e2e/google-credentials-integration.mjs - 0 issues
  • kiloclaw/e2e/google-setup-e2e.mjs - 0 issues
  • kiloclaw/google-setup/.dockerignore - 0 issues
  • kiloclaw/google-setup/Dockerfile - 0 new issues
  • kiloclaw/google-setup/README.md - 0 issues
  • kiloclaw/google-setup/package.json - 0 issues
  • kiloclaw/google-setup/setup.mjs - 0 new issues
  • kiloclaw/packages/secret-catalog/src/__tests__/catalog.test.ts - 0 issues
  • kiloclaw/packages/secret-catalog/src/catalog.ts - 0 issues
  • kiloclaw/packages/secret-catalog/src/index.ts - 0 issues
  • kiloclaw/src/durable-objects/kiloclaw-instance/config.ts - 0 issues
  • kiloclaw/src/durable-objects/kiloclaw-instance/index.ts - 0 new issues
  • kiloclaw/src/durable-objects/kiloclaw-instance/state.ts - 0 issues
  • kiloclaw/src/durable-objects/kiloclaw-instance/types.ts - 0 issues
  • kiloclaw/src/gateway/env.test.ts - 0 issues
  • kiloclaw/src/gateway/env.ts - 1 issue
  • kiloclaw/src/routes/api.ts - 0 issues
  • kiloclaw/src/routes/platform.ts - 0 issues
  • kiloclaw/src/schemas/instance-config.ts - 0 new issues
  • src/app/(app)/claw/components/SettingsTab.tsx - 0 new issues
  • src/app/(app)/claw/components/withStatusQueryBoundary.test.ts - 0 issues
  • src/hooks/useKiloClaw.ts - 0 issues
  • src/lib/kiloclaw/kiloclaw-internal-client.ts - 0 issues
  • src/lib/kiloclaw/types.ts - 0 new issues
  • src/lib/tokens.ts - 0 issues
  • src/routers/kiloclaw-router.ts - 0 new issues
  • kiloclaw/start-openclaw.sh - 0 issues

Reviewed by gpt-5.4-20260305 · 624,195 tokens

iscekic added 3 commits March 9, 2026 21:08
GET /api/platform/public-key derives the RSA public key from the
worker's private key so the google-setup container can encrypt
credentials before POSTing them.
- Move public-key endpoint to /public-key (public, no auth needed)
- Add POST/DELETE /api/admin/google-credentials (JWT auth, auto-resolves userId)
- Update setup.mjs to use Bearer auth against the worker directly
  instead of x-internal-api-key against the platform API
Add .dockerignore for google-setup. Mark gws CLI npm package as
pending publication with a TODO and graceful build fallback.
Add types, internal client methods, and tRPC mutations for managing
Google credentials from the dashboard (connectGoogle/disconnectGoogle).
iscekic added 2 commits March 9, 2026 22:59
- Use non-destructive auth probe in setup.mjs (GET /api/admin/storage
  instead of DELETE /api/admin/google-credentials)
- Clean up stale gws credential files on disconnect (controller)
- Add googleConnected to getDebugState() return type
- Exclude googleCredentials from ProvisionRequestSchema
Install @googleworkspace/cli (https://github.com/googleworkspace/cli)
instead of the non-existent @anthropic/gws placeholder. Remove the
|| echo fallback so the build fails fast if install fails.
iscekic added 4 commits March 10, 2026 13:30
Node.js script that tests public-key endpoint, platform API store/clear,
JWT auth routes, input validation, and idempotency against a local worker.
Creates a temporary DB user for JWT auth and cleans up after.
- Add gcloud CLI to Dockerfile (required by `gws auth setup`)
- Replace gws auth login with our own Node.js OAuth flow to avoid
  gws's encrypted credential store (keyring issues in Docker)
- Add google-setup-e2e.mjs: end-to-end test that provisions a user,
  builds the Docker image, runs the interactive OAuth flow, and
  verifies googleConnected=true
Show docker setup command with copy button when not connected,
and a disconnect button when connected. Adds connectGoogle and
disconnectGoogle mutations to useKiloClawMutations hook.
Add getGoogleSetupCommand tRPC query that generates the docker
command with the user's API key pre-filled, so they can just
copy and paste without visiting the Profile page.
iscekic added 2 commits March 10, 2026 16:28
Update Docker image reference to ghcr.io/kilo-org/google-setup and add
README with multi-arch build and publishing instructions.
iscekic added 5 commits March 10, 2026 17:02
Install @googleworkspace/cli (gws) so bots can use Google Workspace
APIs with the credentials written by the controller at startup.
…nnected

Runs `npx skills add` in the background after writing Google credentials,
so OpenClaw agents automatically get access to 90+ Google Workspace skills.
OpenClaw changes HOME to the workspace dir at runtime, so gws can't
find credentials at ~/.config/gws/. Hardcode /root/.config/gws and
set the env var so gws finds them regardless of HOME.
iscekic added 2 commits March 11, 2026 22:07
… code

- Read secrets from .dev.vars instead of hardcoded defaults
- Fix public-key endpoint path: /public-key → /api/admin/public-key
- Public-key test now uses JWT auth (endpoint is behind auth)
…low provision

- Read secrets from .dev.vars instead of hardcoded defaults
- Use fire-and-forget provision with DO polling (handles Fly timeouts)
- Use safer env-var-based sql() helper
iscekic added 18 commits March 11, 2026 22:26
The gog config tarball includes OAuth tokens, client secrets, and keyring
data for 13+ services. 32 KiB of encrypted base64 could get tight as gog
adds more data over time. 64 KiB gives more breathing room.
The same password string is hardcoded in 3 locations across different
languages. Add comments cross-referencing all three so future changes
don't miss one.
Each refetch generates a new 1-hour JWT. The 50-minute refetchInterval
is sufficient; window focus refetches are unnecessary churn.
On reconnect (disconnect → new connect → redeploy), files from the old
bundle that don't exist in the new one would linger. rmSync the config
dir before unpacking to ensure a clean slate.
Defense-in-depth against path traversal in user-supplied tarballs.
The tarball is encrypted with the worker's RSA key so exploitation
requires compromising that key, but the flag is cheap insurance.
Previously only 401/403 were rejected, so a 500/503 would fall through
as "verified" and send the user through the entire OAuth flow before
failing at the final POST step.
make startController wait for credentials write, exposing gog env vars to the
child process on first spawn; preserve best-effort error handling by logging
and continuing startup
Run `gog auth list --json` after OAuth flow to confirm the account was
actually stored before creating and uploading the config tarball.
This flag was causing tar extraction to fail silently on the container,
resulting in empty gogcli config directories and no Google credentials.
…account-access

# Conflicts:
#	src/app/(app)/claw/components/SettingsTab.tsx
Reconnecting requires re-running the full Docker OAuth setup flow,
so accidental disconnects are costly. Use ConfirmActionDialog to
match the pattern used by other destructive actions in SettingsTab.
@iscekic iscekic enabled auto-merge March 12, 2026 12:14
@iscekic iscekic requested review from St0rmz1 and pandemicsyn March 12, 2026 12:15
Copy link
Contributor

@St0rmz1 St0rmz1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is GOG_KEYRING_PASSWORD intended to remain hardcoded as a plaintext string in three production files (controller/src/gog-credentials.ts, start-openclaw.sh, google-setup/setup.mjs)? I understand it's needed to avoid TTY prompts from the 99designs/keyring file backend, but is there a reason this couldn't be pulled from an env var or a shared constant so it's not scattered across TypeScript, shell, and JS?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants