@viraatdas/rudder 3.9.1 → 3.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rudder-google-cloud
3
- description: Use for any Google Cloud or Google API setup — creating or naming a GCP project, enabling APIs (Gmail, Calendar, Drive, Gemini, …), "Sign in with Google" / Google OAuth clients, the consent screen (app name, logo, domains, scopes, verification, test users), redirect_uri_mismatch and other OAuth errors, gcloud auth, service accounts and keys, and storing the resulting secrets. Makes sure users see the product's own name on Google's screens, never a stray project like "My First Project".
3
+ description: Use for any Google Cloud or Google API setup — creating or naming a GCP project, enabling APIs (Gmail, Calendar, Drive, Gemini, …), "Sign in with Google" / Google OAuth clients, the consent screen (app name, logo, domains, scopes, brand verification, test users), moving sign-in to another domain, redirect_uri_mismatch and other OAuth errors, gcloud auth, service accounts and keys, and storing the resulting secrets. Makes sure users see the product's own name on Google's screens, never a stray project like "My First Project".
4
4
  ---
5
5
 
6
6
  # Google Cloud, done properly
@@ -14,6 +14,15 @@ Helper: `node <this skill dir>/scripts/gcp.mjs <command>` (Node 18+, no deps).
14
14
  It finds credentials from `gcloud`, gcloud's stored login, or ADC; run
15
15
  `whoami` first.
16
16
 
17
+ **Google's official skills cover the rest.** This skill is the product layer
18
+ (project naming, consent screen, OAuth client, what a user sees). For general
19
+ `gcloud` work use Google's `gcloud` skill (check leaf syntax with `gcloud help
20
+ <command>` before running it, `--limit`/`--filter`/`--format` on every list,
21
+ `--quiet`, dry-run where supported) and `google-cloud-recipe-auth` for IAM,
22
+ service accounts and ADC. Both are in the `google-cloud-developer` plugin; if
23
+ they are not loaded: `claude plugin marketplace add google/skills && claude
24
+ plugin install google-cloud-developer@google-plugins`.
25
+
17
26
  ## The rules
18
27
 
19
28
  1. **One project per product, named after the product.** Display name = the
@@ -25,7 +34,11 @@ It finds credentials from `gcloud`, gcloud's stored login, or ADC; run
25
34
  2. **The consent screen's App name is what users read**, not the project name
26
35
  and not the client name. Set it on the Branding page to the product name,
27
36
  with the product's own support email, home page, privacy and terms URLs, and
28
- the product's domain as an authorized domain.
37
+ the product's domain as an authorized domain. **It only shows once the
38
+ branding is verified.** Until then Google shows the redirect URI's domain:
39
+ the registrable domain for your own (`app.rudder.exla.ai` → "exla.ai"), the
40
+ whole host for a platform one ("rudder-cloud-control.fly.dev"). See "Brand
41
+ verification" below.
29
42
  3. **Ask for the narrowest scopes that work.** Basic (openid/email/profile) can go
30
43
  straight to production. Sensitive (Calendar, gmail.send) need verification.
31
44
  Restricted (Gmail read/modify/compose, Drive full) need verification **and**
@@ -94,8 +107,8 @@ real sign-in in a browser and say in your report what the screen showed.
94
107
  - **Testing vs production**: in Testing only listed test users can sign in and
95
108
  refresh tokens expire after 7 days — fine for a demo, wrong for real users.
96
109
  Publish as soon as the scopes allow it.
97
- - **Logo**: uploading one triggers brand verification; leave it off until the
98
- user is ready to verify. The name, links and domain still show.
110
+ - **Logo**: uploading one adds a logo review to verification; leave it off
111
+ unless the user wants it, since basic scopes verify faster without one.
99
112
  - **Internal user type** restricts sign-in to the org's Workspace accounts — only
100
113
  right for internal tools.
101
114
  - **Verification** (sensitive/restricted scopes): needs the home page on the
@@ -113,9 +126,60 @@ real sign-in in a browser and say in your report what the screen showed.
113
126
  again) and the consent App name (Branding page). The project ID can never
114
127
  change — choose it once, deliberately.
115
128
 
129
+ ## Brand verification (getting the App name on screen)
130
+
131
+ Branding page → **Verify branding**. Google checks that every **authorized
132
+ domain** is verified in Search Console by an owner of the project, that the
133
+ home page and privacy policy live on those domains and name the app, and that
134
+ the support email belongs to the owner. Basic scopes: usually a few days.
135
+
136
+ - **Redirect URIs must be on a domain the user owns.** Platform hosts
137
+ (`*.fly.dev`, `*.vercel.app`, `*.run.app`, `*.awsapprunner.com`,
138
+ `*.netlify.app`) can never be verified, and while they are authorized
139
+ domains verification fails. Serve the auth callback from the product's own
140
+ domain (e.g. `app.<domain>`), then remove the platform redirect URIs and
141
+ their authorized domains.
142
+ - **Search Console**: add a Domain property for the apex and verify it with
143
+ the DNS TXT record it gives (through the DNS host's API if you have it,
144
+ otherwise ask the user), signed in as the same account that owns the project.
145
+
146
+ ## Moving sign-in to another domain
147
+
148
+ Order matters. Switching the app first breaks sign-in for everyone.
149
+
150
+ 1. Point the new host at the app (DNS + TLS) and check it serves.
151
+ 2. Add the new redirect URI (and JS origin if used) to the Google client **and**
152
+ to every other provider on the same auth base URL (GitHub OAuth/App callback
153
+ URLs, Slack, Linear …). `check-client` must show it accepted.
154
+ 3. Make every page that STARTS a sign-in redirect to the new host before
155
+ starting it: the OAuth state cookie is set on the host that starts the flow
156
+ and must be there when the callback lands. Native shells and old CLIs keep
157
+ using the old address, so bounce their login pages; don't strand them.
158
+ 4. Flip the auth base URL (`BETTER_AUTH_URL`, `NEXTAUTH_URL`, …) and deploy.
159
+ 5. Real sign-in on the new host for each provider; then remove the old redirect
160
+ URIs and authorized domains, and verify the branding.
161
+
162
+ ## Keeping an existing client
163
+
164
+ A client already shipped in apps (an iOS bundle, a published CLI, stored in a
165
+ host's secrets) can stay in a misnamed project when moving it would change the
166
+ client ID. Its consent screen can still say the product's name: set the
167
+ Branding and verify it. Note the exception in the report; never silently.
168
+
169
+ ## In the browser
170
+
171
+ - Google may ask for a passkey, security key or password re-auth (the URL
172
+ carries `rapt=`). Stop and ask the user to complete it; never type it.
173
+ - The client page's "Last used date" lags by days. Confirm which client is live
174
+ from the app's own sign-in redirect (`curl -sI <login url>` → `client_id=`),
175
+ not from that date.
176
+ - Settings "may take 5 minutes to a few hours"; `check-client` usually sees a
177
+ new redirect URI within a minute.
178
+
116
179
  ## Report
117
180
 
118
181
  Say which project (ID and display name), which APIs, which client (ID, never
119
182
  the secret), where the secrets live, the `check-client` result per redirect
120
- URI, what the real sign-in screen showed, the publishing status, and any
121
- verification still owed (with the user cap it implies).
183
+ URI (including the name Google shows today), what the real sign-in screen
184
+ showed, the publishing and brand-verification status, and any verification
185
+ still owed (with the user cap it implies).
@@ -329,9 +329,15 @@ async function probe(clientId, redirect, scopes) {
329
329
  const url = new URL("https://accounts.google.com/o/oauth2/v2/auth");
330
330
  url.search = new URLSearchParams({ client_id: clientId, redirect_uri: redirect, response_type: "code", scope: scopes.join(" "), prompt: "select_account" }).toString();
331
331
  const response = await fetch(url, { redirect: "follow", headers: { "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36", "accept-language": "en-US" } });
332
- const text = pageText(await response.text());
332
+ const html = await response.text();
333
+ const text = pageText(html);
333
334
  const error = text.match(/Error 400: (\w+)/)?.[1] ?? text.match(/Error 401: (\w+)/)?.[1] ?? text.match(/Error 403: (\w+)/)?.[1] ?? (response.url.includes("/signin/oauth/error") ? "unknown_error" : null);
334
- const appName = text.match(/to continue to ([^.]+?)(?:\s+(?:Email|Forgot|Not your|Create account|Next)\b|$)/)?.[1]?.trim() ?? null;
335
+ // The name sits in a data-app-name attribute (seen 2026-10); the text
336
+ // match is the fallback for older page shapes. Unverified branding shows
337
+ // the redirect URI's domain here, not the App name.
338
+ const appName = pageText(html.match(/data-app-name="([^"]+)"/)?.[1] ?? "").trim()
339
+ || text.match(/to continue to ([^.]+?)(?:\s+(?:Email|Forgot|Not your|Create account|Next)\b|$)/)?.[1]?.trim()
340
+ || null;
335
341
  return { error, appName, finalUrl: response.url };
336
342
  }
337
343
 
@@ -364,7 +370,7 @@ async function checkClient(o) {
364
370
  }
365
371
  const nameOk = !expect || r.appName.toLowerCase() === expect.toLowerCase();
366
372
  if (!nameOk) ok = false;
367
- console.log(`${nameOk ? "✓" : "✗"} ${redirect}: accepted; Google shows "continue to ${r.appName}"${nameOk ? "" : `, expected "${expect}". Fix the App name on the Branding page (it is not the project name).`}`);
373
+ console.log(`${nameOk ? "✓" : "✗"} ${redirect}: accepted; Google shows "continue to ${r.appName}"${nameOk ? "" : `, expected "${expect}". ${/\./.test(r.appName) ? "Google shows a domain until the branding is verified (Branding page, Verify branding)." : "Fix the App name on the Branding page (it is not the project name)."}`}`);
368
374
  }
369
375
  if (!ok) process.exit(1);
370
376
  }
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: rudder-namecheap
3
+ description: Use WHENEVER Namecheap comes up, and for any domain registered there (nameservers dns1/dns2.registrar-servers.com). Covers the whole account through the Namecheap API and CLI — DNS records (A/AAAA/CNAME/TXT/MX, verification records, pointing a domain or subdomain at Vercel, Netlify or a server, removing the parking page), free email forwarding (hello@ to an inbox), URL redirects, nameservers, registrar lock, domain privacy (WhoisGuard), domain list, expiry and auto-renew, contacts, availability and pricing, registering and renewing domains, transfers, account balance, and API access setup. Does the change itself and never hands the user records to type into the Namecheap dashboard.
4
+ ---
5
+
6
+ # Namecheap
7
+
8
+ **The rule: do it through the API, never hand the user steps for the
9
+ dashboard.** If access is missing, the only acceptable ask is the one-time API
10
+ setup in section 6, stated once, after everything else is done.
11
+
12
+ Tool: `<this skill dir>/scripts/namecheap` (Python 3 stdlib, no installs).
13
+ `$NC --help` and `$NC <command> --help` list every option.
14
+
15
+ ```bash
16
+ NC="<this skill dir>/scripts/namecheap"
17
+ $NC check # credentials work + every domain in the account
18
+ ```
19
+
20
+ ## 1. Look before changing
21
+
22
+ | Question | Command |
23
+ |---|---|
24
+ | Which domains, when do they expire, auto-renew, privacy, lock | `$NC domains` (`--expiring`, `--expired`, `--search TEXT`) |
25
+ | One domain in full: status, expiry, privacy, nameservers, lock | `$NC info example.com` |
26
+ | Where its DNS is served from (public view) | `$NC lookup example.com NS` |
27
+ | Records Namecheap serves + email mode | `$NC list example.com` |
28
+ | Email forwards | `$NC forward example.com` |
29
+ | WHOIS contacts | `$NC contacts example.com` |
30
+ | Balance | `$NC balance` |
31
+ | Incoming transfers | `$NC transfers` |
32
+
33
+ If the nameservers are not `dns1/dns2.registrar-servers.com`, records live at
34
+ that DNS host, not at Namecheap: `*.vercel-dns.com` → `vercel dns add`;
35
+ Netlify (`dns*.p0*.nsone.net`) → the Netlify CLI/API; Cloudflare, Route 53 →
36
+ their CLI/API. Registrar settings (nameservers, lock, privacy, renewals) still
37
+ belong here.
38
+
39
+ ## 2. DNS records: list, dry-run, then apply
40
+
41
+ ```bash
42
+ $NC list example.com
43
+ $NC set example.com --add 'CNAME app site.netlify.app.' --dry-run
44
+ $NC set example.com --add 'CNAME app site.netlify.app.' # backs up, writes, re-reads, verifies
45
+ ```
46
+
47
+ Namecheap's `setHosts` **replaces every record on the domain**. Never call it
48
+ with curl or any other client: one call missing the MX or TXT records silently
49
+ breaks mail and verification. The tool merges with the live set, refuses record
50
+ types it cannot round-trip (CAA, SRV: tell the user those need the dashboard),
51
+ and backs up every write to `~/.config/namecheap/backups/`. Undo with
52
+ `$NC restore <backup.json>`.
53
+
54
+ | Goal | Command |
55
+ |---|---|
56
+ | Apex + www to Vercel (removes the parking page) | `$NC vercel example.com`, then `vercel domains add example.com <project>` and `vercel domains add www.example.com <project>` |
57
+ | Subdomain to Vercel | `$NC vercel example.com --sub app` |
58
+ | Verification TXT (Vercel, Google, …) | `$NC set example.com --add 'TXT _vercel "vc-domain-verify=..."'` |
59
+ | Subdomain to Netlify / any host | `$NC set example.com --add 'CNAME app site-name.netlify.app.'` |
60
+ | A record to a server | `$NC set example.com --add 'A api 203.0.113.7'` |
61
+ | Redirect a name (301) | `$NC set example.com --add 'URL301 old https://new.example.com'` |
62
+ | Third-party mail (MX) | `$NC set example.com --add 'MX @ mx1.provider.com. 1800 10'` |
63
+ | Replace a conflicting record | add `--replace` (without it the tool refuses and shows the clash) |
64
+ | Remove a record | `$NC set example.com --remove 'CNAME old'` (value optional) |
65
+ | Free email forwarding | `$NC forward example.com --add hello=you@example.com` (refuses to cut off live MX mail unless `--force`) |
66
+
67
+ Verify with `$NC list` (Namecheap's view, already re-read after the write) and
68
+ `$NC lookup example.com A` (public DNS, after the TTL).
69
+
70
+ ## 3. Registrar settings
71
+
72
+ | Goal | Command |
73
+ |---|---|
74
+ | Move DNS to another host (Vercel, Cloudflare, …) | `$NC nameservers example.com --set ns1.host.com ns2.host.com --yes` |
75
+ | Back to Namecheap BasicDNS | `$NC nameservers example.com --default --yes` |
76
+ | Transfer lock | `$NC lock example.com --on` / `--off` (off only to transfer it out) |
77
+ | Domain privacy | `$NC privacy example.com --on` / `--off`; `$NC privacy` lists all |
78
+
79
+ Switching nameservers moves the whole domain: copy the records to the new host
80
+ first, or mail and verification break. The tool asks for `--yes` for that reason.
81
+
82
+ ## 4. Buying and renewing (spends money)
83
+
84
+ ```bash
85
+ $NC available idea.dev other.com # free? premium?
86
+ $NC price dev # your register price; --action renew|transfer, --years N
87
+ $NC renew example.com --years 1 # prints price + balance, charges nothing
88
+ $NC register idea.dev --contacts-from example.com # same: prints, charges nothing
89
+ ```
90
+
91
+ Anything that charges the account does nothing without `--yes`. **Before
92
+ adding `--yes`, tell the user the item and total and get a clear yes in chat**,
93
+ every time; a yes for one purchase does not cover the next. Registration copies
94
+ WHOIS contacts from a domain the user already owns and turns free privacy on.
95
+ Premium names are refused (they need the dashboard). Payment comes from the
96
+ account balance; if it is short, say so and stop.
97
+
98
+ ## 5. Report
99
+
100
+ Say what changed (before → after), the backup path for record writes, what
101
+ `lookup` shows publicly, anything still propagating, and for purchases the
102
+ amount charged and the order id.
103
+
104
+ ## 6. When access is missing
105
+
106
+ The tool reads `NAMECHEAP_API_USER` / `NAMECHEAP_API_KEY` (optionally
107
+ `NAMECHEAP_USERNAME`, `NAMECHEAP_CLIENT_IP`) from the environment or
108
+ `~/.config/namecheap/credentials.env`. On a Rudder cloud machine the environment
109
+ comes from the Rudder secrets vault; `env | grep -c NAMECHEAP` shows whether it
110
+ has them.
111
+
112
+ If the tool prints `NO_NAMECHEAP_CREDENTIALS`, or an API error naming an IP that
113
+ isn't whitelisted, first do everything that doesn't need Namecheap. Then send
114
+ the user one short message with only this:
115
+
116
+ 1. Namecheap → Profile → Tools → **Namecheap API Access** → ON. Namecheap
117
+ requires a $50 balance, $50 spent in the last two years, or 20 domains.
118
+ 2. **Whitelisted IPs** → add the IP the tool printed (this machine's public
119
+ IPv4; only IPv4 works).
120
+ 3. Store the key once, from their computer, so every Rudder cloud machine has
121
+ it: `rudder cloud secrets set NAMECHEAP_API_USER <namecheap-username>` and
122
+ `rudder cloud secrets set NAMECHEAP_API_KEY` (prompts for the value). For
123
+ this machine only: `! <this skill dir>/scripts/namecheap setup <namecheap-username>`
124
+ (prompts for the key).
125
+
126
+ Never print the key, commit it, put it in a URL or pass it as a command-line
127
+ argument. Never sign in to the Namecheap dashboard yourself: the account
128
+ password is the user's. A cloud machine's outbound IP can change on restart;
129
+ when Namecheap rejects a call, the error names the new IP. Whitelist it too, or
130
+ pin a static egress IP, which costs money and needs the user's go-ahead.