Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
grr
Google tools from the terminal, at maximum performance. grr-cli is one published Rust package with the grr command-line binary and the grr_cli library behind it. Gmail, Calendar, Drive, Contacts, Chat, and Forms share one OAuth login, while stdout stays clean and machine-readable.
Project site: grr-cli.pages.dev · Privacy
grr is an independent project and is not affiliated with or endorsed by Google.
Install
Prebuilt binaries — Windows x64, Linux x64, macOS ARM: GitHub Releases
From source (the default CLI build requires Rust nightly — see Development):
or from a local clone at the repository root:
Package managers:
The crates.io CLI build needs nightly Rust and RUSTFLAGS="--cfg reqwest_unstable" for HTTP/3; the prebuilt releases avoid that source-build step. Library consumers pick services with cargo features; every build, library or CLI, requires Rust nightly (see Library use).
60-second quickstart
-
One-time Google Cloud setup (~5 min): create a Desktop OAuth client and copy its client ID. Follow docs/gcp-setup.md, or run scripts/setup-gcp.ps1 (Windows) / scripts/setup-gcp.sh (macOS/Linux) — they automate the gcloud parts and print the console links for the browser steps.
-
Configure —
~/.grr/config.toml(Windows:%USERPROFILE%\.grr\config.toml):[] = "123456789-abc.apps.googleusercontent.com" # Optional — Google shows it next to the client ID. Sent at the token # endpoint only when present; PKCE is always on. # client_secret = "GOCSPX-..." -
Authenticate and verify:
The project/fork is Google Rust Rewrite; the Google consent-screen application is named Rust Rewrite. The consent screen is where that shorter name appears.
0.4 re-consent: if you used a pre-0.4 token, run
grr auth loginagain. The new service permissions includechat.delete,chat.memberships,chat.messages.reactions, andcontacts.other.readonly; an existing grant does not pick them up automatically.Headless machine?
grr auth login --deviceprints a URL + code instead of opening a browser.
Usage highlights
Every data command takes -f/--format json|jsonl|table|pretty (default json). For gmail message get, --message-format selects the stored Gmail MIME format (full, metadata, minimal, or raw) while -f/--format still selects the printed output format. Logs go to stderr, so stdout is always parseable:
|
Command reference
| Group | Commands |
|---|---|
grr auth |
login [--device], status |
grr gmail |
message, label, draft, send, thread, history, send-as, profile, watch, import, msg |
grr gmail message |
search <query>, get <id> [--message-format full|metadata|minimal|raw] [--body] [--max-length N], thread <id>, batch-read --ids a,b,c [--body] [--max-length N], attachment <msg-id> <att-id> [-o FILE] |
grr gmail label |
list, get <id>, create <name>, update <id> (the CLI spelling for label updates; the library also exposes GmailClient::patch_label), delete <id> |
grr gmail draft |
create <to> <subject> <body>, list, get <id>, update <id> <to> <subject> <body>, delete <id>, send <id> |
grr gmail send |
send <to> <subject> <body> [--cc] [--bcc], send-attach <to> <subject> <body> --attachments f1,f2 [--thread-id] [--cc] [--bcc] |
grr gmail thread |
list, label <id> --add/--remove ..., trash <id>, untrash <id>, delete <id> |
grr gmail history |
<start-history-id> [--label-id] [--max N]; the client follows Gmail history pages up to the limit |
grr gmail send-as |
list, get <email>, create <email>, update <email>, delete <email>, verify <email> |
grr gmail profile |
mailbox profile |
grr gmail watch |
start <topic>, stop (push notifications) |
grr gmail import |
<rfc822-file> [--deleted] |
grr gmail msg |
label, trash, untrash, delete, batch-label, batch-delete, filter-list, filter, filter-create, filter-delete, forwarding-list, forwarding-create, forwarding-delete, autoforwarding, autoforwarding-set, pop, pop-set, imap, imap-set |
grr calendar |
list, events, get, new, create, update, delete, free-busy, set, instances, patch, move, watch, stop, colors, settings, share, shares, unshare |
grr drive |
list [--trashed], search, get, mkdir, trash, restore, copy, empty-trash, download, upload, rename, delete, export, share, shares, unshare, comments, comment, comment-add, comment-delete, revisions, revision, quota |
grr contacts |
list, search, get, create, update, delete, groups, group, group-new, group-rename, group-delete, group-add, group-remove, get-batch, others, adopt, photos, photo-set, photo-remove |
grr chat |
spaces, space, space-new, space-rename, space-delete, members, member, member-add, member-remove, messages, send, react, reactions, unreact |
grr forms |
get, responses, new, update, watch, watches, watch-delete, watch-renew |
grr transport |
negotiated HTTP version + runtime features |
grr schema |
the full command tree as JSON |
Details worth knowing:
--bodyreturns{message, body}wherebodyis the decoded text (text/plainpreferred,text/htmlfallback), truncated to--max-length(default 800) with a...[truncated]marker — sized for LLM context windows.- Batch ops accept
--ids a,b,cor--search "query"(resolves IDs by running the query, capped by--max, default 10000), plus--dry-run.batch-label/batch-deletecalls are chunked at 1000 IDs (Gmail's batchModify/batchDelete limit);batch-readfans its fetches out in parallel, preserving input order. attachmentprints base64 (URL-safe) to stdout when no-o FILEis given, so piping is always binary-safe.- Gmail settings and routing live under
grr gmail msg: filters (filter-list,filter,filter-create,filter-delete), forwarding addresses, auto-forwarding, POP, and IMAP.grr gmail thread listlists threads,grr gmail send-as verify <email>verifies an alias, andgrr gmail historyfollows Gmail's paginated history response. - Label PATCH is available to library users through
GmailClient::patch_label; the CLI exposes label updates asgrr gmail label update <id>.
Design philosophy
- Zero-config.
~/.grr/config.tomlholds exactly one thing: an OAuth client ID (and optionally a secret). Scopes, redirect URI, pool sizes, timeouts, and retry policy are compile-time constants tuned for Google's frontends (src/core/http.rs).GRR_CONFIG_PATHoverrides the file location,RUST_LOGthe log level (GRR_OAUTH__*env vars exist for headless overrides) — nothing else is configurable, on purpose. - Namespaced services. Mail is
grr gmail ...; Calendar, Drive, Contacts, Chat, and Forms live alongside it (grr calendar ...,grr drive ..., ...), and account-level concerns stay top-level (grr auth,grr transport,grr schema). One login covers every service; each service client builds lazily so running one never probes another's endpoints. (Keep has no consumer-facing API.) - stdout purity. Logs go to stderr, results go to stdout, so
| jqalways works.-f jsonlstreams arrays one object per line. - Keyring-first token storage. Tokens live in the OS keyring (Windows Credential Manager, macOS Keychain, Linux Secret Service via D-Bus), with automatic fallback to
<cache dir>/grr/token.jsonon headless systems. A token found in the fallback file auto-imports into the keyring on first sight. - Agent-first.
grr schemadumps the complete command tree as JSON with zero configuration — the machine-readable contract for AI agents, discoverable without touching a config file or scraping--help. One fast CLI replaces per-service MCP servers: no MCP setup, justgrr schema.
Performance
- HTTP/3 (QUIC) by default — prior-knowledge h3 with one authenticated probe at startup and silent HTTP/2 fallback;
grr transportshows what was actually negotiated. - Tokio multi-threaded runtime, auto-sized to cores — no thread pool to tune.
- In-flight request valve — a semaphore (not a thread pool) caps concurrent HTTP requests at 64, staying under Gmail's per-user rate limits so bursts don't self-DOS into 429s. 429s are retried honoring
Retry-After(waits capped at 30s); whole-request timeout is 30s. - Compression always on — gzip, deflate, zstd, and brotli response decompression.
- Streaming uploads — RFC 822 media upload streams 192 KiB chunks with incremental base64, so large attachments never sit fully in memory.
- io_uring file I/O is a Linux-only target-specific dependency, auto-detected at runtime; it is not a Cargo feature.
Architecture
grr-cli is one published crate at the repository root. src/lib.rs builds the library target grr_cli; src/main.rs is a thin wrapper over src/cli.rs, and the binary is named grr. The grr binary declares required-features = ["cli"].
.
├── src/
│ ├── core/ # auth/device/oauth/server/store, http.rs,
│ │ # config.rs, config_loader.rs, error.rs,
│ │ # fs_io.rs, runtime.rs
│ ├── gmail/ # client/, models.rs, mod.rs
│ ├── calendar/ # client.rs, models.rs, mod.rs
│ ├── drive/ # client.rs, models.rs, mod.rs
│ ├── people/ # client.rs, models.rs, mod.rs
│ ├── chat/ # client.rs, models.rs, mod.rs
│ ├── forms/ # client.rs, models.rs, mod.rs
│ ├── commands/ # one module per command group
│ ├── schema.rs
│ ├── output.rs
│ ├── cli.rs
│ ├── lib.rs
│ └── main.rs
└── tests/ # 18 flattened integration test files
The service modules sit behind one shared core rather than separate published crates. The repository also contains the assets/, site/ (the Astro GitHub Pages site with base /grr-cli), packaging/, and scripts/ material used for the project site and distribution.
Library use
The package exposes the same clients through the grr_cli library. The cli feature enables all six service features and is required by the grr binary; individual services can be selected independently with gmail, calendar, drive, people, chat, and forms. The Cargo feature declarations are:
= ["cli"]
= ["gmail", "calendar", "drive", "people", "chat", "forms"]
= []
= []
= []
= []
= []
= []
There is no http3 feature: HTTP/3 (rustls + quinn, via reqwest's unstable http3 support) is always compiled in, and HTTP/2 exists only as a runtime fallback. A library dependency can select only the services it needs:
[]
= { = "0.4.0", = false, = ["gmail"] }
Every build — CLI or library, any feature subset — requires Rust nightly and the reqwest_unstable cfg (.cargo/config.toml supplies it for in-repo builds; downstream users need RUSTFLAGS="--cfg reqwest_unstable"). There is deliberately no stable-Rust path. The old per-crate io_uring feature is gone: io_uring is a Linux-only target-specific dependency that is detected at runtime.
Development
grr requires Rust nightly — the build script fails with a clear message on any other toolchain. rust-toolchain.toml pins it and supplies the components needed by the build, while .cargo/config.toml sets the reqwest_unstable cfg for HTTP/3.
- Tests never touch real credentials — token paths are injected, and wiremock/mockito serve the API endpoints.
RUST_LOG=debugtraces requests; quinn's harmless IPv6 warnings are muted by default.
Packaging & status
| Channel | Install | Status |
|---|---|---|
| GitHub Releases | 3-platform binaries (Windows x64, Linux x64, macOS ARM) built on v* tags |
live for existing releases; 0.4.0 release in preparation — releases |
| crates.io | cargo install grr-cli (binary installs as grr; needs nightly + RUSTFLAGS="--cfg reqwest_unstable" for the default CLI HTTP/3 build) |
0.3.0 live; 0.4.0 release in preparation — one crate now; the 0.3.0 library crates are legacy/unpublished going forward. Trusted publishing uses OIDC (no stored API tokens) |
| winget | winget install debanjanbasu.grr |
live at 0.2.0; update PR to 0.3.0 pending Microsoft review |
| Homebrew | brew install debanjanbasu/tap/grr (tap: debanjanbasu/homebrew-grr) |
live (0.3.0, arm64 macOS + x86_64 Linux) |
The project publishes one package, grr-cli (library grr_cli plus binary grr). v* tags trigger the release workflow, and crates.io publishing is handled through trusted publishing; the 0.3.0 service/core crates do not receive new releases.
License
MIT — see LICENSE.