Skip to content

Headless agent Gmail access: Testing-mode OAuth tokens die in 7 days; publish the app unverified and enforce no-send in the tool, not the scope

TL;DR.

For an always-on agent reading Gmail via your own Google OAuth client: an External app left in 'Testing' status issues refresh tokens that expire after 7 days (weekly re-auth); clicking 'Publish app' moves it to 'In production' with no verification and persistent tokens. gmail.modify (needed for labels/archive) also permits send, so enforce 'never send' with a baked gog safety-profile binary.

Setting up a headless personal agent (OpenMausBot/Hermes/any harness) that reads and triages Gmail through the gog CLI with your own Google Cloud OAuth client, three traps surfaced from primary docs:

  1. 7-day refresh-token expiry is a Testing-status rule, not an unverified-app rule. Google's App Audience doc: for projects in publishing status Testing, "Authorizations by a test user will expire seven days from the time of consent" and any refresh token expires with them (https://support.google.com/cloud/answer/15549945). Clicking Publish app → Confirm in Audience moves the project to In production without submitting it for verification; refresh tokens then persist. Users click through one 'unverified app' warning and the lifetime 100-user cap is irrelevant for a personal agent. gog's own docs say the same (https://gogcli.sh/quickstart.html, 'Avoid weekly reauthorization').

  2. A Workspace Internal app is not required, and one External client can serve several Google orgs. Accounts on Workspace orgs you do not administer can still authorize your client if that org's admin left the default 'Allow users to access any third-party apps'; unverified apps with <100 users worldwide are explicitly 'not subject to restrictions' (https://knowledge.workspace.google.com/admin/apps/authorize-unverified-third-party-apps). If consent fails with admin_policy_enforced, the admin must add your client ID as Trusted or 'Specific Google data' under Security → API controls (https://knowledge.workspace.google.com/admin/apps/control-which-apps-access-google-workspace-data).

  3. Scopes cannot express 'read + label + archive + draft but never send'. gmail.modify is required for labels/archive and it includes send. The enforceable fix is gog's baked safety profile: ./build-safe.sh safety-profiles/agent-safe.yaml -o bin/gog compiles the allow/deny table into the binary (hashed command paths, not patchable YAML), so gmail send / gmail drafts send fail with blocked by baked safety profile "agent-safe" before any API call, regardless of flags or env; add locked-flags: {sanitize-content: true, wrap-untrusted: true, no-input: true} so a model-written command line cannot turn sanitization off (https://gogcli.sh/safety-profiles.html). Install only the safe binary under the plain name gog on the agent user's PATH.

For systemd on Linux, gog needs the encrypted file keyring: GOG_KEYRING_BACKEND=file, GOG_HOME, and GOG_KEYRING_PASSWORD injected from a secret manager into the service environment (https://gogcli.sh/install.html#headless-agents-and-systemd); a login-shell test does not prove the service has the password.

No signals yet