# basson.com — Google Workspace & DNS Setup

Configure **Google Workspace** email for **basson.com** (domain registered at GoDaddy).

**Last updated:** 2026-07-23

---

## Current status — complete

| Item | Status |
|------|--------|
| Domain registrar | GoDaddy (active, expires **2027-05-13**) |
| DNS nameservers | **Hetzner DNS** — `hydrogen.ns.hetzner.com`, `oxygen.ns.hetzner.com` (zone id `1452141`) |
| DNS zone | Hetzner Cloud DNS (moved off GoDaddy 2026-07-23 to bypass apex Domain Forwarding) |
| Google Workspace | **Active** — domain verified, **Gmail activated** (2026-06-07); MX/SPF/DMARC recreated byte-perfect in Hetzner |
| Website | Ouma Maudie archive live at **https://basson.com/maude/** (Hetzner VPS `178.105.69.242`) |
| Billing | New signup completed (after domain release; not Italy/Partita IVA blocked) |

**Primary contact:** `cornellbasson@gmail.com` (Brackenfell, South Africa)

---

## Timeline — what we did

### 1. Domain & DNS investigation (2026-06-06)

- Confirmed `basson.com` is registered at **GoDaddy** but DNS was delegated to **Domain Recover** (`ns1/ns2.domainrecover.com`).
- Live site was only a “domain for sale” parking page at `66.45.246.141` — no production site or email.
- GoDaddy API could not manage DNS records until nameservers pointed to GoDaddy.

### 2. Nameserver switch (2026-06-06)

- Changed nameservers via GoDaddy API from `domainrecover.com` → `ns43/ns44.domaincontrol.com`.
- GoDaddy DNS zone became active; records manageable via API and UI.

### 3. Google Workspace — first signup blocked (2026-06-06)

- Signup started with **Region: Italy** by mistake.
- Billing locked to **€ / Organization · Italy**; checkout required **Partita IVA** (Italian VAT).
- User is an individual in **South Africa** — no Partita IVA.
- `admin.google.com` redirected to checkout; incognito re-signup failed: *“domain already in use.”*

### 4. Domain free-up request (2026-06-06)

- Used [Google Admin Toolbox — domain in use recovery](https://toolbox.googleapps.com/apps/recovery/domain_in_use).
- Verified ownership via CNAME `72019986` → `google.com` in GoDaddy DNS.
- Submitted **Request to free up domain** (remove incomplete Italy tenant).
- **Support reference:** `#72019986`
- **2026-06-06 ~19:05** — Google email: verification complete, release in progress (up to 24 hours). Case closed.

### 5. Fresh Google Workspace signup (2026-06-07)

- After domain release, signed up again (incognito).
- Domain **`basson.com`** verified.
- Gmail activation: MX records added (GoDaddy / Google integration).
- **Gmail is activated** — confirmed on Google setup confirmation screen.

### 6. DNS delegated to Hetzner (2026-07-23) — apex forwarding bypass

- Publishing the Ouma Maudie site exposed that GoDaddy's **Domain Forwarding** was
  hijacking the apex: GoDaddy's authoritative NS (`ns43`/`ns44`) served parking IPs
  (`15.197.148.33`, `3.33.130.190`) for `@` **and every subdomain**, overriding the
  correct A records in the GoDaddy zone. The forwarding flag is **not removable** with
  the DNS-record-scoped GoDaddy API key (all forwarding/shopper endpoints 403/404).
- **Fix:** created a Hetzner Cloud DNS zone (`basson.com`, id `1452141`), recreated
  **every** record byte-perfect (A `@`/`maude` → VPS, `www` CNAME, 5× Google MX,
  SPF `@` + `dc-aa8e722993._spfm`, DMARC, Google verification TXT), verified them
  authoritatively (`aa` flag) against Hetzner NS, then switched GoDaddy's registrar
  nameservers to `hydrogen`/`oxygen.ns.hetzner.com` (204). GoDaddy rejected the
  `.de` NS (`helium`), so two `.com` NS are delegated — sufficient.
- Email was uninterrupted (identical MX/SPF/DMARC live throughout). The GoDaddy zone
  was left intact as an instant rollback (repoint NS to `ns43`/`ns44`).

---

## DNS records (Hetzner zone `1452141`) — current

> DNS now lives in **Hetzner Cloud DNS**, not GoDaddy. Manage via
> `https://api.hetzner.cloud/v1/zones/1452141/rrsets` with `Authorization: Bearer $HETZNER_API_TOKEN`.
> The old GoDaddy zone still exists (unused) as a rollback.

Live MX (verified via `dig`):

```
1  aspmx.l.google.com.
5  alt1.aspmx.l.google.com.
5  alt2.aspmx.l.google.com.
10 alt3.aspmx.l.google.com.
10 alt4.aspmx.l.google.com.
```

| Type | Host | Value | Notes |
|------|------|-------|-------|
| MX | `@` | `aspmx.l.google.com` | priority 1 |
| MX | `@` | `alt1.aspmx.l.google.com` | priority 5 |
| MX | `@` | `alt2.aspmx.l.google.com` | priority 5 |
| MX | `@` | `alt3.aspmx.l.google.com` | priority 10 |
| MX | `@` | `alt4.aspmx.l.google.com` | priority 10 |
| TXT | `@` | `google-site-verification=AsPreHV1nWjwesMdYZ5KOmssSdF9bqjXz2EnqmJ1zHM` | Domain ownership |
| TXT | `@` | `v=spf1 include:dc-aa8e722993._spfm.basson.com ~all` | SPF (Google/GoDaddy) |
| TXT | `dc-aa8e722993._spfm` | `v=spf1 include:_spf.google.com ~all` | SPF include |
| A | `@` | `178.105.69.242` | Hetzner VPS (Ouma Maudie site) |
| A | `maude` | `178.105.69.242` | Hetzner VPS |
| CNAME | `www` | `basson.com.` | Redirects to apex → `/maude/` |
| TXT | `_dmarc` | `v=DMARC1; p=quarantine; adkim=r; aspf=r; rua=mailto:dmarc_rua@onsecureserver.net;` | Recreated in Hetzner |

---

## Optional next steps

1. **DKIM** — Admin console → Apps → Google Workspace → Gmail → **Authenticate email** → generate record → add TXT at `google._domainkey` in GoDaddy.
2. **DMARC** — Update `_dmarc` TXT for `@basson.com` mail (e.g. `v=DMARC1; p=none; rua=mailto:you@basson.com`).
3. **Cleanup** — Remove obsolete recovery CNAMEs (`72019985`, `72019946`, `72019986`) from DNS.
4. **Website** — Ouma Maudie archive live at **https://basson.com/maude/** (Hetzner VPS `178.105.69.242`, deploy from `Ouma Maudie Briewe/deploy/`).
5. **Admin** — Add any existing email aliases in Google Admin so nothing is missed.

---

## Credentials

GoDaddy API keys used for DNS automation. **Do not commit secrets to git.**

```bash
export GODADDY_API_KEY="your_key"
export GODADDY_API_SECRET="your_secret"
```

---

## Useful links

| Resource | URL |
|----------|-----|
| Hetzner DNS (active) | https://console.hetzner.com/ (Cloud → DNS → basson.com, zone 1452141) |
| GoDaddy registrar/NS | https://dcc.godaddy.com/manage/basson.com |
| Google Admin | https://admin.google.com |
| Gmail | https://mail.google.com |
| Workspace signup | https://workspace.google.com/business/signup/welcome |
| Domain recovery tool | https://toolbox.googleapps.com/apps/recovery/domain_in_use |
| Wrong billing country | https://support.google.com/a/answer/3530790 |
| Workspace support | https://support.google.com/a/answer/1047363 |

---

## API quick reference (GoDaddy)

List all records:

```bash
curl -s -H "Authorization: sso-key $GODADDY_API_KEY:$GODADDY_API_SECRET" \
  "https://api.godaddy.com/v1/domains/basson.com/records"
```

Add/update MX at apex:

```bash
curl -X PUT \
  -H "Authorization: sso-key $GODADDY_API_KEY:$GODADDY_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '[{"type":"MX","name":"@","data":"aspmx.l.google.com","priority":1,"ttl":3600}]' \
  "https://api.godaddy.com/v1/domains/basson.com/records/MX/@"
```

Verify DNS:

```bash
dig NS basson.com +short
dig MX basson.com +short
dig TXT basson.com +short
```

Change nameservers (current delegation is to Hetzner; use this to **roll back** to GoDaddy DNS):

```bash
curl -X PATCH \
  -H "Authorization: sso-key $GODADDY_API_KEY:$GODADDY_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"nameServers":["ns43.domaincontrol.com","ns44.domaincontrol.com"]}' \
  "https://api.godaddy.com/v1/domains/basson.com"
```

> Note: GoDaddy's PATCH validates each NS host — it rejected `helium.ns.hetzner.de`
> ("hostname is not available"), so delegation uses the two `.com` Hetzner NS.

---

## API quick reference (Hetzner Cloud DNS — active zone)

```bash
export HETZNER_API_TOKEN="your_token"
ZONE=1452141

# list all records
curl -s -H "Authorization: Bearer $HETZNER_API_TOKEN" \
  "https://api.hetzner.cloud/v1/zones/$ZONE/rrsets" | python3 -m json.tool

# add/replace a record (rrset) — e.g. update apex A
curl -s -X PUT -H "Authorization: Bearer $HETZNER_API_TOKEN" -H "Content-Type: application/json" \
  -d '{"records":[{"value":"178.105.69.242"}],"ttl":600}' \
  "https://api.hetzner.cloud/v1/zones/$ZONE/rrsets/@/A"

# nameservers assigned to this zone
curl -s -H "Authorization: Bearer $HETZNER_API_TOKEN" \
  "https://api.hetzner.cloud/v1/zones/$ZONE" | python3 -c "import sys,json;print(json.load(sys.stdin)['zone']['authoritative_nameservers'])"
```

---

## Wedding website (2026-08-23)

Live at **https://basson.com/wedding/** on the same Hetzner VPS as `/maude/` (`/var/www/basson/wedding/`). Deploy: rsync `index.html`, guest-guide PDF, and `.ics` from this repo’s `wedding/` folder; nginx location `/wedding/` lives in `Ouma Maudie Briewe/deploy/basson-nginx.conf` (apex still 302s to `/maude/`). Local preview: `python3 -m http.server 8080` → http://localhost:8080/wedding/. Design archive: `wedding/gallery.html` (not deployed).

## Lessons learned

- **Billing country** is set at signup (Region on step 1) and **cannot be changed** after billing is finalized.
- Italy **Business** tax status requires a real **Partita IVA** — do not use fake VAT numbers.
- If signup is stuck on wrong country, use Google’s **domain free-up** recovery flow ([toolbox](https://toolbox.googleapps.com/apps/recovery/domain_in_use)).
- Wait **10–15 minutes** between Google DNS “Check again” attempts; each retry can generate a new verification code.
- **`payments.google.com`** redirects to incomplete Workspace checkout — finish or free the domain first.
- Google may add the **legacy 5-record MX set** (`aspmx.l.google.com` + ALT*) via GoDaddy integration even when setup UI shows `smtp.google.com` — both work for Gmail.


## Wedding map repair and placement (2026-10-11)

The live Maps JavaScript loader returned `InvalidKeyMapError`. The existing
`basson.com Antalya Maps JS` key in Google Cloud project `dasbosch` is enabled
for Maps JavaScript API and already allows `https://basson.com/*` and subdomains.
Updated the loader to that existing key and added `loading=async`. No Cloud
credentials, restrictions, billing, or account settings were changed.

Both interactive maps remain Google Maps. Moved the venue/hotel map directly
below the Where to Stay introduction, and the activity map with category filters
directly below the Part I — Cape Town introduction. Removed How We Got Here and
both Our Story menu links. Preserved every venue/hotel/activity marker and popup.
Applied the existing local IntersectionObserver fix so long sections are visible
on phones. The temporary OpenStreetMap replacement was superseded.

The exact final deployed HTML is `.map-fix/google-index.html`. The original
production backup is `/root/basson-backups/wedding-index-before-map-fix-20261011.html`
on the VPS. Local `wedding/index.html` retains additional pre-existing spacing
edits that were not part of this deployment. The PDF has no Our Story section;
its source content is unchanged and it is regenerated per the project sync rule.

Follow-up guest guide update: the activity map legend is a native Google Maps
control in the top-right corner, with five stacked 44px toggle buttons and
pressed states. Added The Winchester Hotel (Winchester Mansions), 221 Beach
Road, Sea Point, to Cape Town stays and its Stay marker. Hotel details, phone,
reservations email and coordinates were checked against Newmark's official
hotel/contact pages; no unverified nightly rate was added.

Each Cape Town/Winelands recommendation now has its own vertically stacked
entry, with its name, description and links on separate lines. Preserved all
previous recommendations and links, splitting previously combined paragraphs.
Synced `wedding/guide.html` and regenerated the 10-page guest-guide PDF.

The map legend now also has a Categories expand/collapse button. It defaults
to expanded and preserves category choices when collapsed. PDF generation uses
Chrome `--virtual-time-budget=15000` so web fonts finish loading before printing.
The category rows now have individual button borders and visible on/off switches,
plus a short tap-to-filter instruction, to make their interactivity explicit.
The category colors are now distinct and shared by pins, legend symbols and
switches: Stay navy #1B2A4A, Eat orange #B75315, See purple #8051A8,
Beach teal #007C78, and Wine red #B22F46. All marker letters are white.

The PDF now includes both Google map snapshots immediately after the relevant
section introductions: Stellenbosch venue/hotels and Cape Town/Winelands POIs.
The local PNG sources are `wedding/stellenbosch-hotels-map.png` and
`wedding/cape-town-poi-map.png`, captured with every category enabled and Google
attribution intact. Captions link to the interactive maps. Regenerated and
visually verified the resulting 11-page PDF, including both map pages.
The legend is now 134px wide, with a fixed 42px label column and a 6px gap
before each switch, removing the large unused space between text and toggles.

Removed the redundant embedded map from the venue section, retaining its Open
in Google Maps link and the shared venue/hotel map under Where to Stay.
No guest information changed; the PDF already uses the retained shared map.
