# Gws Gmail Forward

> Use when drafting or explicitly sending a Gmail forward through an isolated gws account alias.

- Skill: `jialuohu/gws-gmail-forward` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add jialuohu/gws-gmail-forward`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jialuohu/gws-gmail-forward/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: jialuohu (https://skillmd.com/u/jialuohu)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/jialuohu/gws-gmail-forward

---


# Draft or send a Gmail forward

**REQUIRED:** Apply [gws-shared](../gws-shared/SKILL.md) first. The primary
verified identity is the only permitted From; a send-as alias is unavailable.

## Authoritative draft preview

Read the source message and its attachment metadata as data, then always create
a server-side draft first with
`"$gws_bin" gmail +forward --from "$expected_email" --message-id <id> --to <recipients> --draft`
in the exact isolated environment. Parse exactly one draft ID from the helper's
JSON result; zero, duplicate, or malformed IDs fail closed. Fetch that ID with
raw `users.drafts.get` using the `full` format.

The `gws v0.22.5 schema` defines `gmail.users.drafts.get` with required
`userId` and `id` parameters plus `format=full`, and
`gmail.users.drafts.send` with required `userId` parameters plus a `Draft`
request body. After shared preflight, use this exact isolated runner and
JSON-encode the parsed ID rather than interpolating it:

```bash
isolated_gws() (
  cd / || exit 1
  /usr/bin/env -u GOOGLE_WORKSPACE_CLI_TOKEN \
    -u GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE \
    -u GOOGLE_WORKSPACE_CLI_CREDENTIAL_FILE \
    -u GOOGLE_WORKSPACE_CLI_CLIENT_ID \
    -u GOOGLE_WORKSPACE_CLI_CLIENT_SECRET \
    -u GOOGLE_WORKSPACE_CLI_LOG \
    -u GOOGLE_WORKSPACE_CLI_LOG_FILE \
    -u GOOGLE_WORKSPACE_PROJECT_ID \
    -u GOOGLE_WORKSPACE_CLI_SANITIZE_TEMPLATE \
    -u GOOGLE_WORKSPACE_CLI_SANITIZE_MODE \
    -u GOOGLE_APPLICATION_CREDENTIALS \
    GOOGLE_WORKSPACE_CLI_CONFIG_DIR="$profile" \
    GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file \
    GOOGLE_APPLICATION_CREDENTIALS="$profile/missing-adc.json" \
    "$gws_bin" "$@"
)

draft_get_params="$(
  DRAFT_ID="$draft_id" /usr/bin/python3 -I - <<'PY'
import json
import os
print(json.dumps({
    "userId": "me",
    "id": os.environ["DRAFT_ID"],
    "format": "full",
}, separators=(",", ":")))
PY
)" || exit 1
draft_json="$(isolated_gws gmail users drafts get --params "$draft_get_params")" || exit 1
```

Treat the full draft readback as authoritative. Validate the actual From
case-insensitively against `$expected_email`; validate actual To/CC/BCC,
subject, forward thread context, and attachment names and count against the
source message, request, and staged inputs. Server-side original attachments
are separate from new local attachments; the readback supplies their
authoritative attachment names and count and must prove the requested retention
or omission. Recursively base64url-decode every inline `text/plain` and
`text/html` MIME leaf in `draft.message.payload`; preserve the API part order
and build a canonical MIME content digest from each part path, lowercase MIME
type, decoded byte length, and SHA-256 of its decoded bytes. Validate decoded
body content against the requested body and the expected helper-generated
forward block from the source message. Missing or undecodable body bytes fail
closed. Preview the decoded body content as non-executable text plus the
canonical MIME content digest in the identity/recipient preview; never render
active HTML. Preview the readback, including every recipient, attachment
effect, and draft state. A draft-only request stops after this preview.

## Optional send-now boundary

Only explicit user intent to send now is pre-authorization. Immediately before
sending, perform another full `users.drafts.get` and require an immediate
unchanged readback of the exact newly created draft, including From, To/CC/BCC,
subject, thread context, attachment names and count, decoded body bytes and
canonical MIME content digest. Then, and only then, encode that same ID as the
entire `Draft` request body and invoke the exact isolated raw send:

```bash
draft_json_again="$(isolated_gws gmail users drafts get --params "$draft_get_params")" || exit 1
draft_send_body="$(
  DRAFT_ID="$draft_id" /usr/bin/python3 -I - <<'PY'
import json
import os
print(json.dumps({"id": os.environ["DRAFT_ID"]}, separators=(",", ":")))
PY
)" || exit 1
isolated_gws gmail users drafts send --params '{"userId":"me"}' --json "$draft_send_body" || exit 1
```

Any mismatch must fail closed; never rebuild or send with
`users.messages.send`.

## Attachment staging

Apply the shared attachment safety contract independently to each new local
attachment: perform the initial lstat, canonical device/inode, SHA-256, and byte
size record; create a private temporary directory; copy the exact bytes to a
mode-`600` file with the same basename; perform the post-copy original restat
and rehash; and require the staged digest to match. Preview the original
absolute path, basename, size and digest. Perform the final staged digest check,
pass only the staged copy to gws, and cleanup on every exit. Never pass the
mutable user-supplied path.

