# Gmail plugin

The Gmail plugin lets a Vaubaan Gateway mission use Gmail without handing the agent a credential. Experimental, classifier-only : identity-zoned risks for reading, drafting and labelling in a Gmail mailbox. No tool is executable yet.

- Version: `0.3.0` (package `@vaubaan/plugin-gmail`)
- Status: experimental
- Health: not enough reports yet — [what this means](/plugins/health)
- Source: [plugins/gmail](https://github.com/vaubaan/gateway/tree/main/plugins/gmail), catalog [risk-catalog.yaml](https://github.com/vaubaan/gateway/blob/main/plugins/gmail/src/risk-catalog.yaml) version 4 of 2026-10-14
- How to connect an account: [Connect Gmail](/docs/providers/gmail)
- How it is modelled: [handbook](https://github.com/vaubaan/gateway/blob/main/docs/handbook/providers/gmail.md)

Experimental: the gateway registers this plugin and classifies its calls, but keeps it out of the connect and mission flows and lists none of its tools to agents.

Experimental. One account is one Gmail mailbox, which is also its single resource. The plugin models the permissions of reading, drafting and labelling by who the sender or the recipient is, but it executes nothing yet: no tool is offered to agents.

## Permissions

Each permission is a named risk of the plugin's catalog: a mission holds an explicit list of them, never "all". The severity is computed from the permission's six axes (undo, blast radius, data, cost, visible to, lasts); nothing is decided from it.

| Permission | What it allows | Severity | Why it is a risk |
| --- | --- | --- | --- |
| `gmail.read-mailbox` | Read mailbox structure (labels, threads, message headers) | low | Inventory of labels, thread ids, subjects and sender names, and the containment slice of a thread fetch (whose content risk is carried by the from-zone marker emitted alongside). Limited attack surface, but the baseline every read needs. Also covers the synthetic \`gmail\_\_describe\_zones\` tool, which returns the zoning configuration. |
| `gmail.read-from-zone-automated` | Read messages from automated / notification senders | low | Notifications (GitHub, Stripe, Vercel…). Little direct PII, but the bodies often carry injectable URLs and markdown (OWASP LLM01). Kept distinct so an "ops" agent can read alerts without unlocking personal correspondence. |
| `gmail.read-from-zone-internal` | Read messages from internal-domain senders | low | Correspondence between colleagues : may contain secrets, strategic decisions and privileged context. Distinct from the automated zone by least privilege. |
| `gmail.read-from-zone-known-external` | Read messages from known external contacts (allowlist) | low | Messages from pre-approved partners and customers (allowlist). Routine business traffic, comparatively low risk. |
| `gmail.read-from-zone-unknown` | Read messages from unknown senders | low | Messages whose sender is neither internal nor allowlisted. The cardinal OWASP LLM01 vector — prompt injection through the message body. Strong posture recommended, tight quota and rate limit. |
| `gmail.read-drafts` | Read existing drafts | low | Drafts reveal unsent intentions and earlier versions of external communications. |
| `gmail.read-sensitive-label` | Read messages tagged with a sensitive label (financial, legal, HR, …) | low | Zone override : whoever the sender is, a thread carrying a label the owner declared sensitive is escalated. Guard rail for structurally private correspondence (payroll, legal, HR, invoices). The labels of a thread are unknown before the fetch, so on a mailbox that declares sensitive labels every thread fetch needs this permission, and so does every search that does not exclude each of them with \`-label:\<name\>\`. |
| `gmail.bulk-read` | Bulk read across the mailbox (non-discriminating query) | medium | \`search\_threads(q="is:unread")\`, or any query that is not a plain conjunction bounded by a \`from:\<address\>\` clause (a union, a grouping, a negated or missing sender), may return hundreds of threads at once and bypass per-message zoning. A distinct marker forces an explicit acceptance. |
| `gmail.draft-write` | Create / modify a draft (any recipient) | low | Baseline write of a draft, whatever its recipients ; the exfiltration axis is carried by the \`draft-to-zone-\*\` permission needed alongside. Drafts are reversible (delete) and never sent by this plugin. |
| `gmail.draft-to-zone-self` | Create / modify a draft addressed to self only | low | A memo to oneself. No external leak as long as the draft is not sent ; the official Google MCP server cannot send, so a draft is a terminal action here. |
| `gmail.draft-to-zone-internal` | Create / modify a draft addressed to internal recipients only | medium | A draft for colleagues. Stays internal while unsent ; no external exfiltration path. |
| `gmail.draft-to-zone-allowlist` | Create / modify a draft to pre-approved external contacts (allowlist) | medium | A draft to pre-approved external contacts (CRM-like). Limited risk while unsent ; if a \`send\_draft\` tool is ever wrapped, this permission covers it naturally. |
| `gmail.draft-to-zone-external-unbounded` | Create / modify a draft to an external recipient not in the allowlist | high (deprecated) | A draft to an arbitrary external address : the cardinal exfiltration vector once sent, and even unsent its existence can leak intentions. Never acceptable : the classifier refuses such a draft before any permission is evaluated, so holding this one grants nothing. The id is kept (ids are never deleted) and marked deprecated so no new mission is offered it. |
| `gmail.label-write` | Apply / remove labels on a thread or message (single) | low | Mailbox organisation on one identified thread or message. UI-level and reversible (re-label). \`TRASH\` and \`SPAM\` are refused by the classifier (a deletion is not a label write) ; a label that is, or may be, one the owner declared sensitive also needs the fallback write permission. |
| `gmail.bulk-label-write` | Bulk apply / remove labels across many threads | medium | One call labelling many threads at once. Distinct from the single label write. No modelled tool can touch it today : \`label\_thread\` takes exactly one thread id and a call without one is refused. Kept for a future batch tool ; in no preset until then. |
| `gmail.uncategorized-write` | Uncategorized Gmail write | medium (the catch-all: touched only by a call no other permission covers) | Catch-all for writes no entry above covers. Today : applying or removing a label the owner declared sensitive, or a user label the classifier cannot tell apart from one (\`sensitive-label\[..\].W\`). If the plugin ever surfaces \`send\_message\`, \`delete\_message\` or \`create\_filter\`, they land here until a dedicated permission is added — fail-loud by design. Never offered in a preset. |

## Presets

A preset pre-fills the permissions of a mission for one account, with a limit per period, a rate and whether each permission runs freely or asks the mission creator first. The person creating the mission adjusts it; the mission stores the explicit list.

### Read-only triage

`read-only` — List labels and drafts, search and read threads from automated, internal, allowlisted and unknown senders; no write, no bulk read, no sensitive label.

| Permission | What it allows | Limit per period | Rate | Runs |
| --- | --- | --- | --- | --- |
| `gmail.read-mailbox` | Read mailbox structure (labels, threads, message headers) | 2000 | 120 per minute | runs freely |
| `gmail.read-from-zone-automated` | Read messages from automated / notification senders | 1000 | 60 per minute | runs freely |
| `gmail.read-from-zone-internal` | Read messages from internal-domain senders | 500 | 30 per minute | runs freely |
| `gmail.read-from-zone-known-external` | Read messages from known external contacts (allowlist) | 500 | 30 per minute | runs freely |
| `gmail.read-from-zone-unknown` | Read messages from unknown senders | 200 | 20 per minute | runs freely |
| `gmail.read-drafts` | Read existing drafts | 200 | 30 per minute | runs freely |

### Developer

`developer` — Experimental — read-only triage plus drafts to self, colleagues and allowlisted contacts and single-thread labelling; bulk reads ask first.

| Permission | What it allows | Limit per period | Rate | Runs |
| --- | --- | --- | --- | --- |
| `gmail.read-mailbox` | Read mailbox structure (labels, threads, message headers) | 5000 | 120 per minute | runs freely |
| `gmail.read-from-zone-automated` | Read messages from automated / notification senders | 2000 | 60 per minute | runs freely |
| `gmail.read-from-zone-internal` | Read messages from internal-domain senders | 1000 | 30 per minute | runs freely |
| `gmail.read-from-zone-known-external` | Read messages from known external contacts (allowlist) | 1000 | 30 per minute | runs freely |
| `gmail.read-from-zone-unknown` | Read messages from unknown senders | 300 | 20 per minute | runs freely |
| `gmail.read-drafts` | Read existing drafts | 500 | 30 per minute | runs freely |
| `gmail.draft-write` | Create / modify a draft (any recipient) | 300 | 20 per minute | runs freely |
| `gmail.draft-to-zone-self` | Create / modify a draft addressed to self only | 200 | 20 per minute | runs freely |
| `gmail.draft-to-zone-internal` | Create / modify a draft addressed to internal recipients only | 200 | 20 per minute | runs freely |
| `gmail.draft-to-zone-allowlist` | Create / modify a draft to pre-approved external contacts (allowlist) | 100 | 10 per minute | runs freely |
| `gmail.label-write` | Apply / remove labels on a thread or message (single) | 500 | 60 per minute | runs freely |
| `gmail.bulk-read` | Bulk read across the mailbox (non-discriminating query) | 20 | 5 per minute | ask me first |

## Tools

This plugin exposes no tool yet.

## Health

Health: not enough reports yet. A plugin version is deemed validated by the community with at least 200 reports and 90 % of them successful, over the last 90 days, as reported by the instances whose operators turned community telemetry on. It is a community signal, not a security guarantee.
