Production Setup Runbook¶
Step-by-step actions the user must perform before running gsm against real
Workspace + Cloudflare resources. This document is consumed by the human
operator; the CLI cannot perform these steps because they require browser
authentication and physical credential issuance.
Step 1 — Rotate Cloudflare API token¶
Why: the legacy token shipped in legacy/gsuite_cloudflare_bot.py was
committed to source control and is shown as Invalid API Token by
gsm doctor. Any token that ever lived in a repo or chat must be considered
compromised.
Required scopes:
| Permission group | Scope | Resource |
|---|---|---|
| Account | Cloudflare Tunnel: Read (optional) | All accounts (or specific) |
| Zone | Zone: Edit | All zones (or specific) |
| Zone | DNS: Edit | All zones (or specific) |
Procedure:
- Open https://dash.cloudflare.com/profile/api-tokens in a browser.
- Click Create Token.
- Use the Edit zone DNS template OR custom token with scopes above.
- Under Account Resources, scope to your account (account ID
0061a056f8cbc860fb9ec99bd41a0cccper current config). - Under Zone Resources, choose
Include - All zones from an accountfor the same account. - Click Continue to summary → Create Token.
- Copy the token immediately (Cloudflare only shows it once).
- Edit
.envin the project root:
- Verify:
The cloudflare row should now show PASS.
- Revoke the old token at the same URL: find the token whose first chars
match
Qh1dr2J1..., click Roll, confirm.
Step 2 — (Optional) Rotate Google OAuth Desktop App client secret¶
Why: the client_secret_*.json for the OAuth Desktop App was committed and
should be considered compromised. Lower priority than CF because Desktop App
secrets are not cryptographic secrets in the strict sense (Google docs even
note they may be shipped with installed apps), but rotation is good hygiene.
Procedure:
- Open https://console.cloud.google.com/apis/credentials in the project that issued the current OAuth client.
- Find the Desktop App OAuth 2.0 Client ID matching the local
client_secret_*.json. - Click the trash icon to delete the old client (or click the entry and then Reset Secret if your Cloud project supports it).
- Click Create Credentials → OAuth client ID → Desktop app.
- Download the JSON file. Save it as
credentials.jsonin the project root (or whereverGSM_GOOGLE_OAUTH_CLIENT_PATHpoints). - Delete
token.json(forces re-auth on next run):
- Run any
gsmcommand that needs auth (e.g.gsm domains add example.com). A browser window will open for re-authentication. - Verify scopes were granted: token.json now exists and
gsm doctorpasses theoauth_clientrow.
Step 3 — Run a real fresh-domain smoke test¶
Why: unit tests cover all branches with mocks, but only a real run validates that the integrated pipeline (Workspace → CF → DNS injection → propagation poll → Google verify) actually completes against production APIs.
Pre-conditions:
- Step 1 (CF token) complete.
- Step 2 OR existing valid
token.jsonworking. - A test domain you own and have not yet onboarded (or are willing to re-onboard).
- The domain's registrar nameserver edit page open (for the next step).
Procedure:
# 1. Run the onboarding (idempotent - safe to retry)
gsm domains add my-test-domain.example
# Watch the output. Expected sequence:
# step_add_gsuite - registers domain in Workspace
# step_fetch_token - gets DNS_TXT verification token from Google
# step_ensure_zone - creates CF zone, prints nameservers
# step_inject_dns - upserts 5 MX records + 1 TXT
# step_wait_dns - polls 8.8.8.8 + 1.1.1.1 until TXT visible
# step_verify - tells Google to verify via DNS_TXT
# If `step_wait_dns` reports `propagated=False`:
# This is the historical 271-failure scenario. The workflow now correctly
# marks the domain as DNS_PENDING instead of failing verify_domain.
# Action: copy the CF nameservers from earlier output, paste them at your
# registrar, wait 5-10 minutes, then:
gsm domains verify --only-pending
# 2. Inspect ledger state
gsm domains list
# 3. Re-running the same command should be a no-op (idempotency check)
gsm domains add my-test-domain.example
# Expect: "skipped (already verified)"
Pass criteria:
- Final status
VERIFIEDingsm domains list - Ledger file
gsm_state.jsoncontains the domain with full history - Re-running
gsm domains addreturnsskipped - No exceptions, no
[-]lines in the log output
Step 4 — Bulk user creation smoke test¶
Pre-conditions:
- At least one domain in
VERIFIEDstate from Step 3. - An
akun.txtfile with at least 2 test users using that domain.
Procedure:
# Format of akun.txt:
# firstuser@verified-domain.example | TempPassword123! | optional-code
# second.user@verified-domain.example | OtherPassword456!
gsm users add --file akun.txt
gsm users list
Pass criteria:
- All users show status
CREATED - Re-running with the same file returns
skippedfor each (idempotency)