Adom Google
Public Made by Adomby adom
Drive your whole Google Workspace from the terminal — Gmail, Drive, Sheets, Docs, Slides, Calendar, Tasks, Chat + any Google API.
name: adom-google
description: "Programmatic read+write access to a user's WHOLE Google Workspace — Gmail, Contacts, Drive, Sheets, Docs, Slides, Calendar, Tasks, Google Chat — plus a generic api passthrough to hit ANY Google API. CLI + OAuth only (no browser automation). Public/open-source; used daily by the Adom team. Auth is the account-specific part — Adom employees use it out of the box; other orgs either self-host their own OAuth gateway or email [email protected] for a managed OAuth container (~$5/mo). First run just authorizes with FULL Workspace access (no mode question to ask the user). Use when setting up/installing adom-google, authorizing Google, reading Gmail attachments/.ics, creating/listing/searching Contacts, editing Sheets/Docs/Slides, Drive files, Calendar events, posting to Google Chat, or any Google Workspace automation. For a brand-new ORG that needs its own Google OAuth client (non-Adom team getting started), use the companion adom-google-onboarding skill — it drives the user's real browser through Google Cloud Console and auto-configures everything. Trigger words — adom-google, install adom-google, set up adom-google, configure adom-google, google workspace cli, edit google sheet/doc/slides, drive upload, calendar event, post to google chat, post to chat as me, message colby, send a chat, dm , gmail attachment, google contacts, people api, full workspace access, google oauth gateway broker, google api passthrough, self-host oauth, managed oauth container, onboard my org to google, set up google workspace for my company/team, configure google oauth for , get my company on adom-google, create our google cloud oauth client."
adom-google
One CLI for a user's Google Workspace from inside an Adom container. REST-direct —
it calls the Google APIs straight, owns auth via the Adom OAuth Gateway, and stores a
long-lived refresh token 0600. No browser automation, no per-user Google Cloud project.
adom OAuth layer one shared Adom OAuth client + Gateway redirect → refresh token (0600)
adom-google modular wrapper: gmail · contacts · (drive/calendar = +1 scope line)
Google REST APIs called directly (gmail.googleapis.com, people.googleapis.com, …)
Why REST-direct (not a base-CLI shim)
We evaluated wrapping a base CLI — Google's googleworkspace/cli (gws, Rust,
Discovery-driven, JSON, agent skills), gog/gogcli (Go), GAM/GAMADV-XTD3, the
Gemini CLI, and gcloud. Conclusion: call the REST APIs directly.
- Auth is the part Adom must own anyway. One shared internal OAuth client + the
Gateway's static redirect + a long-lived refresh token is Adom-specific; no base CLI
speaks it. Adopting one means reverse-engineering its keyring/credential format to
inject our token — more work than one
requests.postto the token endpoint. - The data surface we need is tiny and stable. gmail.readonly attachment reads + a handful of People API calls. Each is one HTTPS request. A base CLI adds a process boundary, an install/version dependency, and output coupling for no benefit at this size.
- No per-user GCP project. REST-direct with the shared Adom client satisfies the hard
constraint.
gws/gog/GAM all push users toward their own OAuth client orgcloud. - Gemini CLI is the wrong layer (an AI coding agent, not a Workspace data client) and
gcloud can't touch Workspace user data (GCP-only).
gwsis the closest "official" Google Workspace CLI and is a reasonable future accelerator if we ever need broad Sheets/Docs/Chat coverage — but for Gmail+Contacts it's overkill.
The code is structured so a gws backend could be slotted behind the same subcommands
later; today the win is zero new runtime dependencies and a clean migration from adom-gmail.
Onboarding a brand-new org (give a team its OWN Google client)
If a non-Adom org wants to get going on their own, they need their own Google OAuth client.
Don't make them slog through Google Cloud Console by hand — use the adom-google-onboarding
skill, which drives the user's real logged-in browser (adom-desktop nbrowser_*) through the
console, auto-fills the fields (especially the redirect URI), captures the new client, and wires
it in RELAY mode (their secret stays 0600 on their box; Adom only lends a shared callback URL).
Kick it off with adom-google onboard --org <slug> (or --json for the exact values). Adom
employees skip all this — they install adom-google-adom and just run auth.
Install
adom-wiki pkg install adom/adom-google # copies the CLI to ~/.local/bin + skills to ~/.claude/skills
install.sh copies real files (no symlinks): the binaries land in ~/.local/bin and each
skill in ~/.claude/skills/<name>/. Runtime: Python 3 + requests (already in the container).
The Gateway WebSocket handshake is implemented in the stdlib (no ws/websockets package).
First-run setup — just run adom-google auth (no mode quiz)
When setting up adom-google for the first time (it's unconfigured — adom-google setup or any
command prints a Hint: block), do NOT ask the user any scope/mode question. Full Workspace
access is the default and the tool "just works." Simply run:
adom-google auth
This grants full Workspace scopes (gmail.modify, contacts, drive, spreadsheets, documents, presentations, calendar, tasks, chat.messages, chat.spaces.readonly) plus the generic
adom-google api <url> passthrough for any Google API. It prints a Google consent URL; the
user clicks Allow once.
⚠ Open the consent URL as ONE value — never unquoted in a shell. It contains
&; if you drop it into an unquoted shell/open command the shell truncates it at the first&, Google receives onlyclient_id, and shows "Error 400: invalid_request — missing response_type" (this bit real users). Safe: hand the URL to a browser tool with the URL inside JSON (adom-desktop nbrowser_open_window '{"sessionId":"gauth","url":"<URL>"}'), or single-quote the whole URL. Theadom-google authoutput prints the ready-to-paste safe command for you.
⏱ The user has ~10 minutes to click Allow. The gateway keeps the sign-in session alive with a WebSocket heartbeat, so there's no rush. If they ever see a "Session Expired" page, just re-run
adom-google auth.
(Advanced/rare: adom-google auth --safe grants narrower create-only scopes — only the
Drive/Sheets/Docs/Slides files this app makes, no "delete all". Do NOT prompt for it; use it
only if the user explicitly asks to minimize scopes.)
Auth — one consent, long-lived refresh token
adom-google auth # authorize (FULL Workspace access — the default; ~10 min to approve)
adom-google auth --safe # advanced: narrower create-only scopes (no delete-all)
adom-google auth --manual # fallback: desktop-client paste flow
adom-google auth-code '<code>' # finish --manual
adom-google status # shows access + what's authorised (never prints secrets)
How Adom handles the OAuth (Gateway broker flow)
Google requires one fixed redirect_uri, but every container has a unique hostname. The
Adom OAuth Gateway gives one static callback for all containers and acts as a
confidential-client token broker, so the client secret never reaches user containers:
adom-google authbuilds the Google consent URL withredirect_uri = <your gateway>/callback(from the resolved provider) and a randomstate, and opens a WebSocket to the Gateway registering{state, app:"adom-google", exchange:true}.- The user approves in their browser (their own Google Workspace account).
- Google redirects to the Gateway
/callback; the Gateway does the code→token exchange itself (using the secret it alone holds) and pushes{type:"tokens", …}over the WS. - The container stores only the refresh token
0600— no client secret. Later refreshes go through the gateway'sPOST /refresh, so the secret stays on the gateway forever.
The package ships NO provider — no gateway URL, no client_id, nothing Adom-specific. The
provider ({gateway_url, client_id, app}) is resolved at runtime, later wins: bundled
provider.json → legacy credentials.json → ~/.config/adom-google/provider.json → env
(OAUTH_GATEWAY_URL, ADOM_GOOGLE_CLIENT_ID/SECRET/APP). With none configured, every command
prints the NO_PROVIDER hint. Adom employees install the private adom-google-adom package,
which writes the Adom provider (gateway + Internal client_id) to ~/.config/adom-google/provider.json;
self-hosters run adom-google provider set --gateway <url> --client-id <id>. The client secret is
never in any package — in broker mode it lives only on the gateway's creds/<app>.json (0600).
If the Gateway is down, auth --manual uses a desktop-client loopback paste flow (that one
exchanges locally and does need a client secret via init).
Getting auth as a NON-Adom user (this tool is public)
The code is open source; the OAuth app + gateway are account-specific. If a user is not on Adom's Workspace, the bundled Internal client won't let them sign in — Google blocks non-adom.inc accounts at consent. Two options to surface to them:
- Managed by Adom (easiest): email [email protected] — Adom stands up a dedicated OAuth gateway container for their org (~$5/mo) wired to a Google client for their domain; they then get the full adom-google capability set, same as the Adom team.
- Self-host (DIY): fork the repo; create their own Google Cloud project + OAuth client
(Internal to their Workspace), enable the APIs they need, run the open-source OAuth gateway
(
john/service-oauth) with their client secret increds/<app>.json, then point the CLI at theirs:adom-google provider set --gateway https://their-gateway --client-id <their-id>(writes~/.config/adom-google/provider.json). Google Chat also needs a one-time Chat-app configuration (name/avatar/description) in the Cloud console's Chat API → Configuration tab, orspaces.*calls 404 with "Chat app not found".
Commands
# Generic passthrough — ANY Google API, signed with the user's token. This is the workhorse:
# Sheets/Docs/Slides/Drive/Calendar/Chat/Tasks/YouTube all go through here, zero per-API code.
adom-google api <url> [-X METHOD] [-d '<json>'|@file] [-q key=val ...] [--raw]
# e.g. create a sheet: adom-google api -X POST https://sheets.googleapis.com/v4/spreadsheets -d '{"properties":{"title":"BOM"}}'
# post to Chat: adom-google api -X POST https://chat.googleapis.com/v1/spaces/<id>/messages -d '{"text":"shipped"}'
# calendar event: adom-google api -X POST https://www.googleapis.com/calendar/v3/calendars/primary/events?sendUpdates=all -d '{...}'
# Gmail (read; behaviour-compatible with old adom-gmail)
adom-google gmail attachments <messageId>
adom-google gmail read <messageId> <attachmentId> [-o file]
adom-google gmail ics <messageId>
# Contacts (People API; read + write)
adom-google contacts create --name "First Last" [--org O] [--title T] \
[--email [email protected] ...] [--phone +1... ...] [--notes "..."] [--json]
adom-google contacts list [--limit N] [--json]
adom-google contacts search <query> [-v] [--json]
# Google Chat — posts as the SIGNED-IN USER (their own identity, NO attribution prefix).
# This is distinct from `adom-gchat`, which posts via an org webhook with "(on behalf of …)".
adom-google chat spaces [--json] # list your spaces (id · type · name)
adom-google chat dm <email> [--json] # resolve the 1:1 DM space with a person
adom-google chat send --to <space-id|email> "<text>" # post; email auto-resolves the DM
# "<text>" may be '-' (stdin) or '@file'; add --thread <key> to reply in a thread
adom-google chat read --from <space-id|email> [--limit N] [--json] # recent messages
adom-google status # access (full/safe) + what's authorized (never prints secrets)
adom-google accounts # list all configured accounts (work + personal, isolated)
Multiple accounts (v0.8.0+) — e.g. work + personal, fully isolated
adom-google --account <name> <cmd> (or ADOM_GOOGLE_ACCOUNT=<name>) runs ANY command against a
named account stored at ~/.config/adom-google/accounts/<name>/ with its OWN refresh token, mode,
and provider.json (inherits the default account's provider unless overridden — a Workspace-Internal
OAuth client can't sign in a gmail.com user, so a personal account typically needs its own External
client via --account <name> provider set). The default (unnamed) account keeps the original paths —
existing setups are untouched. First-run auth for a new account follows the SAME ask-the-user
each account authorizes independently: adom-google --account personal auth.
chat vs adom-gchat: adom-google posts as you and does the
full Chat lifecycle — post, read, edit, delete, react — as your own Google identity
(clean, no prefix). For an attributed bot post to a team space (deploys/alerts as
"Kel (on behalf of john)"), use adom-gchat, the post-only webhook bot.
Rule of thumb: posting as yourself ⇒ adom-google chat; an attributed bot announcement ⇒
adom-gchat. The chat.messages + chat.spaces.readonly scopes are in both safe and full
mode, so this works out of the box once authorized.
We use Google Contacts to store service identities (portal/account metadata for an API vendor), e.g. the JPMorgan Payments developer portal. Keep secrets (client secrets, portal passwords) in a password manager — never in a contact's notes.
Adding Drive / Calendar
Add the scope to SCOPES at the top of bin/adom-google, re-run adom-google auth, and
add a few REST calls against www.googleapis.com/drive/v3 or /calendar/v3. Token refresh
and the _request() helper are already generic.
Security
- Config
~/.config/adom-google/config.jsonis created0600; the client secret and refresh token are never echoed to stdout/stderr or logs (statusprints presence only). - The Gateway only ever sees the short-lived auth
code, never tokens or the client secret.
Migration from adom-gmail
See MIGRATION.md. Short version: adom-gmail is now a forwarding shim;
its old ~/.config/adom-gmail/config.json is auto-imported on first adom-google run
(gmail.readonly only — run adom-google auth once to add the contacts scope).
---
name: adom-google
description: "Programmatic read+write access to a user's WHOLE Google Workspace — Gmail, Contacts, Drive, Sheets, Docs, Slides, Calendar, Tasks, Google Chat — plus a generic `api` passthrough to hit ANY Google API. CLI + OAuth only (no browser automation). Public/open-source; used daily by the Adom team. Auth is the account-specific part — Adom employees use it out of the box; other orgs either self-host their own OAuth gateway or email [email protected] for a managed OAuth container (~$5/mo). First run just authorizes with FULL Workspace access (no mode question to ask the user). Use when setting up/installing adom-google, authorizing Google, reading Gmail attachments/.ics, creating/listing/searching Contacts, editing Sheets/Docs/Slides, Drive files, Calendar events, posting to Google Chat, or any Google Workspace automation. For a brand-new ORG that needs its own Google OAuth client (non-Adom team getting started), use the companion `adom-google-onboarding` skill — it drives the user's real browser through Google Cloud Console and auto-configures everything. Trigger words — adom-google, install adom-google, set up adom-google, configure adom-google, google workspace cli, edit google sheet/doc/slides, drive upload, calendar event, post to google chat, post to chat as me, message colby, send a chat, dm <person>, gmail attachment, google contacts, people api, full workspace access, google oauth gateway broker, google api passthrough, self-host oauth, managed oauth container, onboard my org to google, set up google workspace for my company/team, configure google oauth for <org>, get my company on adom-google, create our google cloud oauth client."
---
# adom-google
One CLI for a user's Google Workspace from inside an Adom container. **REST-direct** —
it calls the Google APIs straight, owns auth via the **Adom OAuth Gateway**, and stores a
long-lived refresh token `0600`. No browser automation, no per-user Google Cloud project.
```
adom OAuth layer one shared Adom OAuth client + Gateway redirect → refresh token (0600)
adom-google modular wrapper: gmail · contacts · (drive/calendar = +1 scope line)
Google REST APIs called directly (gmail.googleapis.com, people.googleapis.com, …)
```
## Why REST-direct (not a base-CLI shim)
We evaluated wrapping a base CLI — Google's `googleworkspace/cli` (`gws`, Rust,
Discovery-driven, JSON, agent skills), `gog`/`gogcli` (Go), GAM/GAMADV-XTD3, the
Gemini CLI, and `gcloud`. Conclusion: **call the REST APIs directly.**
- **Auth is the part Adom must own anyway.** One shared internal OAuth client + the
Gateway's static redirect + a long-lived refresh token is Adom-specific; no base CLI
speaks it. Adopting one means reverse-engineering its keyring/credential format to
inject our token — more work than one `requests.post` to the token endpoint.
- **The data surface we need is tiny and stable.** gmail.readonly attachment reads + a
handful of People API calls. Each is one HTTPS request. A base CLI adds a process
boundary, an install/version dependency, and output coupling for no benefit at this size.
- **No per-user GCP project.** REST-direct with the shared Adom client satisfies the hard
constraint. `gws`/`gog`/GAM all push users toward their own OAuth client or `gcloud`.
- **Gemini CLI is the wrong layer** (an AI coding agent, not a Workspace data client) and
**gcloud can't touch Workspace user data** (GCP-only). `gws` is the closest "official"
Google Workspace CLI and is a reasonable **future accelerator** if we ever need broad
Sheets/Docs/Chat coverage — but for Gmail+Contacts it's overkill.
The code is structured so a `gws` backend *could* be slotted behind the same subcommands
later; today the win is zero new runtime dependencies and a clean migration from adom-gmail.
## Onboarding a brand-new org (give a team its OWN Google client)
If a **non-Adom** org wants to get going on their own, they need their own Google OAuth client.
Don't make them slog through Google Cloud Console by hand — use the **`adom-google-onboarding`**
skill, which drives the user's real logged-in browser (adom-desktop `nbrowser_*`) through the
console, auto-fills the fields (especially the redirect URI), captures the new client, and wires
it in RELAY mode (their secret stays `0600` on their box; Adom only lends a shared callback URL).
Kick it off with `adom-google onboard --org <slug>` (or `--json` for the exact values). Adom
employees skip all this — they install `adom-google-adom` and just run `auth`.
## Install
```bash
adom-wiki pkg install adom/adom-google # copies the CLI to ~/.local/bin + skills to ~/.claude/skills
```
`install.sh` **copies** real files (no symlinks): the binaries land in `~/.local/bin` and each
skill in `~/.claude/skills/<name>/`. Runtime: Python 3 + `requests` (already in the container).
The Gateway WebSocket handshake is implemented in the stdlib (no `ws`/`websockets` package).
## First-run setup — just run `adom-google auth` (no mode quiz)
When setting up adom-google for the first time (it's unconfigured — `adom-google setup` or any
command prints a `Hint:` block), **do NOT ask the user any scope/mode question.** Full Workspace
access is the default and the tool "just works." Simply run:
```bash
adom-google auth
```
This grants full Workspace scopes (`gmail.modify, contacts, drive, spreadsheets, documents,
presentations, calendar, tasks, chat.messages, chat.spaces.readonly`) plus the generic
`adom-google api <url>` passthrough for **any** Google API. It prints a Google consent URL; the
user clicks **Allow** once.
> ⚠ **Open the consent URL as ONE value — never unquoted in a shell.** It contains `&`; if you
> drop it into an unquoted shell/open command the shell truncates it at the first `&`, Google
> receives only `client_id`, and shows *"Error 400: invalid_request — missing response_type"*
> (this bit real users). Safe: hand the URL to a browser tool with the URL **inside JSON**
> (`adom-desktop nbrowser_open_window '{"sessionId":"gauth","url":"<URL>"}'`), or single-quote the
> whole URL. The `adom-google auth` output prints the ready-to-paste safe command for you.
> ⏱ **The user has ~10 minutes to click Allow.** The gateway keeps the sign-in session alive
> with a WebSocket heartbeat, so there's no rush. If they ever see a "Session Expired" page, just
> re-run `adom-google auth`.
*(Advanced/rare: `adom-google auth --safe` grants narrower create-only scopes — only the
Drive/Sheets/Docs/Slides files this app makes, no "delete all". Do NOT prompt for it; use it
only if the user explicitly asks to minimize scopes.)*
## Auth — one consent, long-lived refresh token
```bash
adom-google auth # authorize (FULL Workspace access — the default; ~10 min to approve)
adom-google auth --safe # advanced: narrower create-only scopes (no delete-all)
adom-google auth --manual # fallback: desktop-client paste flow
adom-google auth-code '<code>' # finish --manual
adom-google status # shows access + what's authorised (never prints secrets)
```
### How Adom handles the OAuth (Gateway broker flow)
Google requires one fixed `redirect_uri`, but every container has a unique hostname. The
Adom OAuth Gateway gives one static callback for all containers **and** acts as a
confidential-client **token broker**, so the client secret never reaches user containers:
1. `adom-google auth` builds the Google consent URL with
`redirect_uri = <your gateway>/callback` (from the resolved provider) and a random `state`,
and opens a WebSocket to the Gateway registering `{state, app:"adom-google", exchange:true}`.
2. The user approves in their browser (their own Google Workspace account).
3. Google redirects to the Gateway `/callback`; the Gateway **does the code→token exchange
itself** (using the secret it alone holds) and pushes `{type:"tokens", …}` over the WS.
4. The container stores only the **refresh token** `0600` — no client secret. Later refreshes
go through the gateway's `POST /refresh`, so the secret stays on the gateway forever.
**The package ships NO provider** — no gateway URL, no client_id, nothing Adom-specific. The
provider (`{gateway_url, client_id, app}`) is resolved at runtime, later wins: bundled
`provider.json` → legacy `credentials.json` → `~/.config/adom-google/provider.json` → env
(`OAUTH_GATEWAY_URL`, `ADOM_GOOGLE_CLIENT_ID/SECRET/APP`). With none configured, every command
prints the NO_PROVIDER hint. Adom employees install the private **`adom-google-adom`** package,
which writes the Adom provider (gateway + Internal client_id) to `~/.config/adom-google/provider.json`;
self-hosters run `adom-google provider set --gateway <url> --client-id <id>`. The client secret is
never in any package — in broker mode it lives only on the gateway's `creds/<app>.json` (0600).
If the Gateway is down, `auth --manual` uses a desktop-client loopback paste flow (that one
exchanges locally and does need a client secret via `init`).
### Getting auth as a NON-Adom user (this tool is public)
The code is open source; the OAuth app + gateway are account-specific. If a user is **not**
on Adom's Workspace, the bundled Internal client won't let them sign in — Google blocks
non-adom.inc accounts at consent. Two options to surface to them:
1. **Managed by Adom (easiest):** email **[email protected]** — Adom stands up a dedicated
OAuth gateway container for their org (~$5/mo) wired to a Google client for their domain;
they then get the full adom-google capability set, same as the Adom team.
2. **Self-host (DIY):** fork the repo; create their own Google Cloud project + OAuth client
(Internal to their Workspace), enable the APIs they need, run the open-source OAuth gateway
(`john/service-oauth`) with their client secret in `creds/<app>.json`, then point the CLI at
theirs: `adom-google provider set --gateway https://their-gateway --client-id <their-id>`
(writes `~/.config/adom-google/provider.json`). Google **Chat** also needs a one-time
Chat-app configuration (name/avatar/description) in the Cloud console's Chat API →
Configuration tab, or `spaces.*` calls 404 with "Chat app not found".
## Commands
```bash
# Generic passthrough — ANY Google API, signed with the user's token. This is the workhorse:
# Sheets/Docs/Slides/Drive/Calendar/Chat/Tasks/YouTube all go through here, zero per-API code.
adom-google api <url> [-X METHOD] [-d '<json>'|@file] [-q key=val ...] [--raw]
# e.g. create a sheet: adom-google api -X POST https://sheets.googleapis.com/v4/spreadsheets -d '{"properties":{"title":"BOM"}}'
# post to Chat: adom-google api -X POST https://chat.googleapis.com/v1/spaces/<id>/messages -d '{"text":"shipped"}'
# calendar event: adom-google api -X POST https://www.googleapis.com/calendar/v3/calendars/primary/events?sendUpdates=all -d '{...}'
# Gmail (read; behaviour-compatible with old adom-gmail)
adom-google gmail attachments <messageId>
adom-google gmail read <messageId> <attachmentId> [-o file]
adom-google gmail ics <messageId>
# Contacts (People API; read + write)
adom-google contacts create --name "First Last" [--org O] [--title T] \
[--email [email protected] ...] [--phone +1... ...] [--notes "..."] [--json]
adom-google contacts list [--limit N] [--json]
adom-google contacts search <query> [-v] [--json]
# Google Chat — posts as the SIGNED-IN USER (their own identity, NO attribution prefix).
# This is distinct from `adom-gchat`, which posts via an org webhook with "(on behalf of …)".
adom-google chat spaces [--json] # list your spaces (id · type · name)
adom-google chat dm <email> [--json] # resolve the 1:1 DM space with a person
adom-google chat send --to <space-id|email> "<text>" # post; email auto-resolves the DM
# "<text>" may be '-' (stdin) or '@file'; add --thread <key> to reply in a thread
adom-google chat read --from <space-id|email> [--limit N] [--json] # recent messages
adom-google status # access (full/safe) + what's authorized (never prints secrets)
adom-google accounts # list all configured accounts (work + personal, isolated)
```
## Multiple accounts (v0.8.0+) — e.g. work + personal, fully isolated
`adom-google --account <name> <cmd>` (or `ADOM_GOOGLE_ACCOUNT=<name>`) runs ANY command against a
named account stored at `~/.config/adom-google/accounts/<name>/` with its OWN refresh token, mode,
and provider.json (inherits the default account's provider unless overridden — a Workspace-Internal
OAuth client can't sign in a gmail.com user, so a personal account typically needs its own External
client via `--account <name> provider set`). The default (unnamed) account keeps the original paths —
existing setups are untouched. First-run auth for a new account follows the SAME ask-the-user
each account authorizes independently: `adom-google --account personal auth`.
**`chat` vs [adom-gchat](/adom/adom-gchat):** adom-google posts as **you** and does the
**full Chat lifecycle** — post, read, edit, delete, react — as your own Google identity
(clean, no prefix). For an **attributed bot post** to a team space (deploys/alerts as
"Kel (on behalf of john)"), use **[adom-gchat](/adom/adom-gchat)**, the post-only webhook bot.
Rule of thumb: posting *as yourself* ⇒ `adom-google chat`; an *attributed bot announcement* ⇒
`adom-gchat`. The `chat.messages` + `chat.spaces.readonly` scopes are in both safe and full
mode, so this works out of the box once authorized.
We use Google Contacts to store **service identities** (portal/account metadata for an API
vendor), e.g. the JPMorgan Payments developer portal. Keep secrets (client secrets, portal
passwords) in a password manager — **never** in a contact's notes.
## Adding Drive / Calendar
Add the scope to `SCOPES` at the top of `bin/adom-google`, re-run `adom-google auth`, and
add a few REST calls against `www.googleapis.com/drive/v3` or `/calendar/v3`. Token refresh
and the `_request()` helper are already generic.
## Security
- Config `~/.config/adom-google/config.json` is created `0600`; the client secret and
refresh token are never echoed to stdout/stderr or logs (`status` prints presence only).
- The Gateway only ever sees the short-lived auth `code`, never tokens or the client secret.
## Migration from adom-gmail
See [MIGRATION.md](MIGRATION.md). Short version: `adom-gmail` is now a forwarding shim;
its old `~/.config/adom-gmail/config.json` is auto-imported on first `adom-google` run
(gmail.readonly only — run `adom-google auth` once to add the `contacts` scope).