# Google Search Console (`pa gsc`)

`pa gsc` automates Google Search Console onboarding and analytics across any
domain whose DNS lives on Cloudflare in an account the operator controls.

## Status

Implemented: `add-property`, `verify-via-cf-dns`, `submit-sitemap`,
`sitemap-status`, `pull-analytics`, `list-properties`. Auth via Google
service-account JSON loaded from the SOPS vault. Cloudflare planting via
the CF v4 REST API.

## Dependencies

Two npm packages (declared in `@stravica-ai/digital-operator-pa`):

- `googleapis` — the canonical Node SDK for Google Cloud APIs.
- `google-auth-library` — JWT auth helper used by `googleapis`.

## Operator setup (one-time per platform install)

1. **Create a Google Cloud project** dedicated to GSC automation. Pick a
   short name like `gsc-automation-prod`.

2. **Enable the APIs** in that project:
   - Search Console API:  
     https://console.cloud.google.com/apis/library/searchconsole.googleapis.com
   - Site Verification API:  
     https://console.cloud.google.com/apis/library/siteverification.googleapis.com

3. **Create a service account** under IAM & Admin → Service accounts:
   - Name: `gsc-automation`
   - No project-level roles needed (GSC permissions are granted per-property
     in the GSC dashboard, not at GCP IAM level).

4. **Generate a JSON key** for the service account (Keys tab → Add Key →
   JSON). Download the file — this is the SA key.

5. **Store the SA key in the SOPS vault**:
   ```bash
   # Legacy single-file vault:
   pnpm exec secrets set GSC_SERVICE_ACCOUNT_KEY -
   # Scoped-vault estate (workspace or canonical layout):
   pnpm exec secrets set --scope <scope> GSC_SERVICE_ACCOUNT_KEY -
   # paste the entire JSON contents, then Ctrl-D
   ```

   On scoped-vault hosts, `pa gsc` reads the key from whichever pot
   `secrets get` would use — i.e. it honours (in order) `SECRETS_DEFAULT_SCOPE`,
   the `defaultScope` in `~/.config/stravica-pa/defaults.json`, or the
   calling PA's `identity.json` — before falling back to the legacy
   single-file vault. Store the key in the pot the host is already
   configured to read; no `pa gsc`-specific configuration is needed.

6. **Store Cloudflare credentials** (also one-time). Each CF account is
   addressed by a suffixed vault-key pair. `--account <name>` is required
   on every invocation — the unsuffixed bare pair that used to be a
   single-account default was dropped in the 2026-09-01 rename
   (`w-2026-08-31-dave-010`).

   Pick the surface segment that matches how the token is actually
   scoped in Cloudflare:

   ```bash
   # Stravica account (Workers Scripts + D1) → --account stravica
   pnpm exec secrets set CLOUDFLARE_WORKERS_API_TOKEN_STRAVICA <token>
   pnpm exec secrets set CLOUDFLARE_WORKERS_ACCOUNT_ID_STRAVICA <account-id>

   # Clive account (Pages + zone purge) → --account clive
   pnpm exec secrets set CLOUDFLARE_PAGES_API_TOKEN_CLIVE <token>
   pnpm exec secrets set CLOUDFLARE_PAGES_ACCOUNT_ID_CLIVE <account-id>
   ```

   The CF API token needs the permissions its surface implies (for DNS
   planting: `Zone:DNS:Edit` and `Zone:Zone:Read` on the target zones;
   account-level "Write to all resources" tokens also work).

   For back-compat, the deprecated `CLOUDFLARE_PAGES_API_KEY_<NAME>` /
   `CLOUDFLARE_PAGES_ACCOUNT_ID_<NAME>` pair is still accepted, and a
   hit emits a stderr deprecation warning naming the current pair. Rename
   the vault entries at your convenience — the deprecated pair will be
   removed in a follow-up.

   Same scoped-vault note as the SA key: on scoped hosts these live in
   whichever pot `secrets get` reads (see step 5); use `secrets set --scope
   <scope> ...` to store them there.

## Per-domain setup (each time you onboard a new property)

7. **Grant the service-account access to the GSC property.** Until the
   property exists, you can't grant access. So the workflow is:

   1. Run `pa gsc verify-via-cf-dns <domain>` — this performs verification
      end-to-end and registers the property under the SA's identity, so
      the SA implicitly has Owner permission.

   *Or, if a property already exists under a human Google account:*

   2. Open Search Console → Settings → Users and permissions →
      Add user, paste the SA email (`<name>@<project>.iam.gserviceaccount.com`),
      grant Owner (or Full).

   The `pa gsc list-properties` command will then show the property.

## Usage

```bash
# Get the verification TXT token for a domain (no DNS change yet)
pnpm exec pa gsc add-property stravica.ai

# Plant the TXT in Cloudflare, wait, verify, register
pnpm exec pa gsc verify-via-cf-dns stravica.ai
# Optional --wait SECONDS to override the default 60s propagation pause:
pnpm exec pa gsc verify-via-cf-dns stravica.ai --wait 120
# Multi-account: select which CF account's vault keys to use:
pnpm exec pa gsc verify-via-cf-dns stravica.ai --account stravica
pnpm exec pa gsc verify-via-cf-dns besafetraining.co.uk --account clive

# Submit a sitemap
pnpm exec pa gsc submit-sitemap stravica.ai https://stravica.ai/sitemap.xml

# Read sitemap state (list every sitemap the property has)
pnpm exec pa gsc sitemap-status stravica.ai
# Read state for one named sitemap
pnpm exec pa gsc sitemap-status stravica.ai https://stravica.ai/sitemap.xml

# Pull search analytics (default 28d, top queries, 100 rows)
pnpm exec pa gsc pull-analytics stravica.ai
pnpm exec pa gsc pull-analytics stravica.ai --days 90 --dimensions query,page --rows 500

# List every property the SA can see (sanity-check setup)
pnpm exec pa gsc list-properties
```

## Multi-account selection

`--account <name>` is required on every invocation. The tool upper-cases
the name and tries the candidate vault-key pairs below in order — the
first fully-present pair wins. Storing multiple pairs for the same
account is a supported migration state; the tool always picks the
highest-priority pair that resolves.

```
1.  CLOUDFLARE_WORKERS_API_TOKEN_<NAME>
    CLOUDFLARE_WORKERS_ACCOUNT_ID_<NAME>
    Workers surface — Workers Scripts, D1, KV. Used by --account stravica.

2.  CLOUDFLARE_PAGES_API_TOKEN_<NAME>
    CLOUDFLARE_PAGES_ACCOUNT_ID_<NAME>
    Pages surface — Pages + zone purge. Used by --account clive.

3.  CLOUDFLARE_PAGES_API_KEY_<NAME>
    CLOUDFLARE_PAGES_ACCOUNT_ID_<NAME>
    Deprecated pre-2026-09-01 naming. Accepted with a stderr warning;
    will be removed in a follow-up. Rename the vault entries at your
    convenience.
```

If `--account` is passed but none of the candidate pairs are fully set,
the command fails fast with a message naming the primary (WORKERS) pair
you're expected to store. It never silently falls back to any pair not
addressed by the suffix — that would defeat the safety the flag exists
to provide.

The unsuffixed bare pair (`CLOUDFLARE_PAGES_API_KEY` /
`CLOUDFLARE_PAGES_ACCOUNT_ID`) and the very-legacy
`CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID` pair are no longer
read — `--account <name>` is mandatory.

The account name accepts letters and digits only — anything else is a
usage error.

## Notes

- Properties are always registered as **domain-properties** (`sc-domain:<host>`),
  which cover https + http + every subdomain under a single property. The
  legacy URL-prefix shape is not supported by this tool.
- The verification TXT record is planted at the **apex** of the domain
  (no `_google-site-verification` subdomain prefix — that is required for
  URL-prefix properties only).
- The Cloudflare DNS write is **idempotent**: a re-run with the same
  token leaves DNS untouched; a re-run after a token rotation updates
  the existing record in place.
- Verification can fail if DNS hasn't propagated yet. Re-run with
  `--wait 180` if the default 60s is too short.

## Exit codes

```
0  OK
1  Usage error / vault uninitialised / domain ambiguous
2  Operational failure (Google API, Cloudflare API, verification failed)
3  Transport / network / missing SDK
```

## Live-test gap

The mock test suite covers the auth-construction path, domain
normalisation, CF API request shape, and the GSC verb plumbing. Live
testing requires:

1. The operator setup above completed (GCP project + APIs + SA key in vault).
2. CF credentials in vault.
3. One real domain to verify.

After GCP setup, run `pa gsc list-properties` first — it requires no
DNS or verification and proves the SA key + scopes work end-to-end.
