Audit Google Ads with exact account identity, reproducible evidence, and recommendations that cannot silently become account changes.
Google Ads Guard CLI is an independent open-source tool for read-only Google Ads reporting, configuration audits, health checks, and guarded optimization proposals. It is designed for operators and AI agents that need traceable evidence without giving a reporting tool permission to modify an account.
Status:
0.1.0is intentionally read-only. It has no create, update, remove, mutate, upload, budget-change, or bid-change command.
| Google Ads Guard CLI | |
|---|---|
| Best for | Agencies, operators, analysts, and AI agents that need safe account evidence |
| Account access | Read-only SearchStream requests |
| Identity gate | Customer ID, account name, currency, and timezone must all match |
| Credentials | Environment variables or fixed gcloud calls; never stored in the profile |
| Output | JSON, NDJSON, or CSV with request IDs and query digests |
| Recommendations | Evidence-threshold proposals with missing facts and requiresApproval: true |
| Account changes | Unsupported by design in 0.1.x |
Google Ads reporting scripts often solve the API call but leave the dangerous decisions to convention:
- a customer ID is accepted without verifying the human-readable account identity;
- developer tokens or OAuth material are stored in JSON or shell history;
- dates, segments, and limits are inserted into GAQL without one strict validation boundary;
- a successful response is treated as proof that tracking and business outcomes are healthy;
- a suggestion is emitted without the evidence threshold or missing facts that produced it.
Google Ads Guard turns those controls into product behavior:
- exact customer allowlists with name, currency, and timezone readback;
- a fixed
googleAds:searchStreamtransport and boundedSELECT-only GAQL; - no credential values in profile files or CLI arguments;
- identity-aware
gcloudor environment-token authentication; - timeouts, limited retries, request IDs, query digests, and redacted errors;
- explicit evidence thresholds and missing-fact lists for every recommendation;
- new-file-only output: existing evidence is never overwritten;
- a bundled Codex skill for evidence-first Google Ads analysis.
The tool is not affiliated with Google, OpenAI, or google-ads-open-cli.
Use Google Ads Guard to:
- capture campaign performance for an explicit date range;
- audit conversion-action configuration;
- inventory campaigns, ad groups, ads, keywords, and campaign negatives;
- screen search terms, budgets, and bids against declared evidence thresholds;
- hand a provenance-rich report to another operator or AI agent;
- explain a saved JSON report without reconnecting to Google.
It does not:
- create or edit campaigns, ads, keywords, budgets, bids, conversions, or feeds;
- decide that a recommendation is safe merely because a numeric threshold was crossed;
- store Google Ads credential values;
- replace attribution, profit-margin, lead-quality, or experiment analysis.
The profile contains identifiers and policy, but no token values. A live request is allowed only after all of these checks pass:
- the profile is structurally valid and names an exact allowed customer;
- the credential principal matches the configured principal when
gcloudis used; - an identity query returns exactly one account;
- customer ID, descriptive name, currency, and timezone all match the profile;
- the requested query is one bounded
SELECTstatement; - the request goes only to the SearchStream endpoint.
Recommendations are proposals. Every recommendation contains requiresApproval: true; this package has no mutation transport.
Requires Node.js 20 or newer.
git clone https://github.com/Teide131/google-ads-guard-cli.git
cd google-ads-guard-cli
npm ci --ignore-scripts
npm run verify
npm linknpm link exposes the google-ads-guard command on your current machine. If global linking is unavailable, use node dist/cli.js in place of google-ads-guard.
The project is not published to npm yet. Do not install an unrelated package with a similar name.
Copy examples/profile.example.json to a private local path, replace the synthetic account identity, and keep it outside a public repository if the identifiers are sensitive.
The profile is a fail-closed allowlist:
| Field | Purpose |
|---|---|
apiVersion |
One explicitly supported Google Ads API version |
expectedPrincipal |
Exact active gcloud account expected by the operator |
managerCustomerId |
Manager account used in the login-customer header, when applicable |
allowedCustomers[].id |
Exact 10-digit client customer ID |
name |
Exact Google Ads descriptive name expected from API readback |
currencyCode |
Exact three-letter account currency |
timeZone |
Exact Google Ads account timezone |
policy |
Row, timeout, retry, and recommendation evidence thresholds |
Unknown profile keys are rejected. A live read stops if any configured account identity field differs from API readback.
Set only the developer token in the environment when using the recommended gcloud provider:
$env:GOOGLE_ADS_DEVELOPER_TOKEN = "set-in-your-current-secure-session"
gcloud auth login --scopes=https://www.googleapis.com/auth/adwordsOn macOS or Linux:
export GOOGLE_ADS_DEVELOPER_TOKEN="set-in-your-current-secure-session"
gcloud auth login --scopes=https://www.googleapis.com/auth/adwordsThe CLI invokes gcloud with fixed arguments, verifies the active account against expectedPrincipal, and requests a short-lived access token. Google also documents service-account authentication; a service account can be activated in gcloud and pinned as the expected principal. See the official OAuth overview and service-account guide.
The environment provider is available for CI. It reads the access token and developer token from named environment variables; it does not persist them.
All profile, query, report, and output paths must be absolute.
# 1. Local-only validation: no token resolution and no Google Ads request
google-ads-guard --profile D:\private\google-ads-guard.json doctor
# 2. Capture an exact date-bounded campaign snapshot
google-ads-guard --profile D:\private\google-ads-guard.json snapshot 1234567890 `
--start 2026-07-01 --end 2026-07-31 --segment device
# 3. Audit configuration
google-ads-guard --profile D:\private\google-ads-guard.json audit tracking 1234567890
google-ads-guard --profile D:\private\google-ads-guard.json audit structure 1234567890
google-ads-guard --profile D:\private\google-ads-guard.json health 1234567890
# 4. Generate review candidates, never account mutations
google-ads-guard --profile D:\private\google-ads-guard.json recommend negatives 1234567890 `
--start 2026-07-01 --end 2026-07-31doctor and profile validate never resolve credentials and never call Google Ads.
For a custom query, put GAQL in a file rather than a shell argument:
google-ads-guard --profile D:\private\google-ads-guard.json query 1234567890 `
--file D:\private\campaign-query.gaql --allow-raw-query `
--out D:\evidence\campaigns-2026-08-16.jsonThe query validator rejects comments, separators, mutation-like keywords, multiple SELECT/FROM clauses, unbounded row counts, and unsupported row limits. Google documents the Search and SearchStream behavior in the reporting guide.
Every online JSON report is an evidence envelope:
{
"schemaVersion": "1.0",
"generatedAt": "2026-08-16T00:00:00.000Z",
"command": "audit tracking",
"readOnly": true,
"source": {
"apiVersion": "v25",
"principalVerification": "verified",
"customer": {
"id": "0987654321",
"name": "Example Customer",
"currencyCode": "USD",
"timeZone": "America/Los_Angeles"
},
"requestIds": ["..."],
"queryDigests": ["..."]
},
"data": [],
"findings": [],
"recommendations": [],
"warnings": []
}Request IDs help investigate API failures. Query digests identify the exact query text without repeating it throughout an audit trail. Neither one proves that tracking, attribution, or business outcomes are correct; those remain separate findings.
| Command | Network | Purpose |
|---|---|---|
doctor |
No | Validate profile and show the declared boundary |
profile validate |
No | Strict schema and unknown-key validation |
snapshot |
Yes | Campaign metrics for an explicit date range |
audit tracking |
Yes | Conversion-action configuration evidence |
audit structure |
Yes | Campaign, group, ad, keyword, and negative inventory |
health |
Yes | Tracking and structural health score; not a performance score |
recommend negatives |
Yes | Threshold-qualified search-term review candidates |
recommend budget |
Yes | Target-ROAS evidence screening only |
recommend bid |
Yes | Target-CPA evidence screening only |
query |
Yes | One explicitly acknowledged bounded GAQL SELECT |
explain |
No | Summarize a saved JSON evidence envelope |
Output formats are JSON, NDJSON, and CSV. CSV is available only when data is an array. Every online result includes the verified account identity, API version, principal status, request IDs, query digests, generation time, findings, recommendations, and warnings.
A recommendation is emitted only when configured minimum evidence gates are crossed. It still includes:
- the observed values and thresholds;
- a confidence level;
- the rationale;
- facts the report cannot establish;
requiresApproval: true.
For example, a high-cost search term with zero returned conversions is a negative-keyword review candidate, not an automatically safe negative. Semantic relevance, offline conversions, assisted value, and match scope remain human verification steps.
The design review used Bin-Huang/google-ads-open-cli at commit cadc567a9b5a0942609a42d94fe5395da32b9960 as a public reference, then implemented an independent codebase.
| Area | Reference limitation at reviewed commit | Guard design |
|---|---|---|
| Credentials | Long-lived credential JSON and secret-bearing CLI options are supported | Profile contains variable names only; secrets come from environment or fixed gcloud calls |
| OAuth | Local callback flow lacks a documented state/PKCE gate | Delegates interactive OAuth to Google Cloud CLI; no custom callback server |
| Account boundary | Customer IDs can be supplied directly | Exact allowlist plus ID/name/currency/timezone API readback |
| GAQL | Several user values are interpolated into query strings | Validated identifiers, dates, segments, enums, limits, and a guarded raw-query file path |
| API lifecycle | Version is hard-coded in source | Strict profile version allowlist, with v25 in the example |
| Reliability | Limited timeout/retry/request-ID evidence | Abort timeout, bounded retry, backoff, request IDs, and query SHA-256 digests |
| Recommendations | Query cookbook; limited decision gates | Thresholds, consequences, missing facts, confidence, and mandatory approval flag |
| Testing/release | No test suite or release workflow found at the reviewed commit | Cross-platform CI, fake-transport tests, package scan, and deterministic verification |
The review is documented in docs/reference-review.md.
npm install --ignore-scripts
npm run verify
npm pack --dry-runTests use synthetic fixtures and a fake HTTP transport. They do not require a Google Ads account, OAuth token, developer token, or network request.
The public-package scan checks candidate Git files and the package manifest for secret patterns, machine-specific paths, credential files, mutation endpoints, and unintended build residue.
Contributions are welcome. Read CONTRIBUTING.md, and report vulnerabilities according to SECURITY.md. Never put a developer token, OAuth token, customer export, or private account identifier in a public issue.
- No live-account verification is included in the automated test suite.
- A valid Google Ads developer token and an authorized OAuth principal are still required for live reads.
- SearchStream quota and response behavior remain governed by Google Ads API limits; request failures preserve request IDs when the server supplies them. See Google's quota guidance and API error guide.
- Recommendation rules are deliberately conservative and are not a replacement for conversion-quality, attribution, business-margin, or experiment analysis.
0.1.0does not plan, stage, or execute account mutations.
- Validate more reporting recipes against separately authorized test accounts.
- Add versioned JSON Schema files for profiles and evidence envelopes.
- Add offline comparison of two saved evidence periods.
- Expand conservative audit coverage without adding mutation support.
- Publish signed releases after cross-platform CI is proven on the public repository.
Feature requests that preserve the read-only boundary are welcome in GitHub Issues.
MIT. See LICENSE.