Skip to content

Terraform Infrastructure Encoding — 2 March 2026

What Was Built

Created ucca-infra repo (uccaonline/ucca-infra, private) containing Terraform declarations for all UCCA Cloudflare infrastructure. Audited every resource via the Cloudflare API, declared it in .tf files, and imported existing state.

116 resources imported, zero drift. Full technical documentation at docs.ucca.online/surfaces/infrastructure/terraform.

Key Decisions

  • Terraform manages infra only — zones, DNS, storage, zone settings, Pages projects. Worker deployment stays in ucca-surfaces via wrangler. No overlap between tools.
  • Cloudflare provider v5 (5.18.0) — significant schema differences from v4. Import IDs use simplified zone_id/record_id format, zones use account = { id = ... } instead of account_id, individual cloudflare_zone_setting resources instead of the v4 cloudflare_zone_settings_override monolith.
  • Terraform 1.5.7 — last open-source release before the BSL license change. Native import blocks (no terraform import CLI commands needed).
  • Local stateterraform.tfstate is gitignored. Migrate to remote backend when team access is needed.
  • Repo location: infra/ucca-infra/ in the workspace, matching the engine/, surfaces/, docs/ pattern.

What Was Learned

  • Cloudflare's API returns different record data than external DNS lookups. Proxied records show Cloudflare edge IPs via dig, but the actual records are AAAA 100:: (Workers), CNAMEs to .pages.dev (Pages), and CNAMEs to public.r2.dev (R2). Always use the API for record data.
  • Provider v5 documentation is sparse. Used terraform providers schema -json to introspect the actual schema when docs were wrong or missing.
  • DKIM TXT records are truncated in API listings. The full keys only appear during terraform plan when the provider reads the actual record content. Declared values must match exactly or Terraform will try to overwrite them.
  • CAA records use a data block (flags/tag/value), not the content string format used by other record types.
  • API token permissions are granular — Pages requires a separate "Cloudflare Pages: Read" permission that isn't included in zone or DNS scopes.

Resources Under Management

Category Count Notes
Zones 10 7 active, 3 pending (dlongglobal)
DNS records 83 Across 6 zones (rignold.com is empty)
Zone settings 16 8 settings x 2 zones (ucca.online, rtopacks.com.au)
Pages projects 2 ucca-docs, ucca-knowledge (GitHub-connected)
Pages domains 2 docs.ucca.online, knowledge.ucca.online
D1 database 1 rtopacks-db
R2 bucket 1 rtopacks-media (APAC)
KV namespace 1 LEADS

Infrastructure Issues Noted During Audit

  • api.ucca.online — CNAME pointing to ucca.online, returns 522 (no Worker behind it). Orphaned DNS record.
  • ucca.com.au — has two SPF TXT records (Google + Mailchimp). Only one SPF record per domain is valid per RFC 7208. Potential mail delivery issue.
  • dlongglobal.com/net/org — 3 zones in "pending" status. Nameservers not yet pointed to Cloudflare.
  • Security headers — ucca.online and rtopacks.com.au have full hardened headers. ops/docs/knowledge surfaces are missing some (CSP, Permissions-Policy).

Documentation Standards Established

Same session established documentation standards in CLAUDE.md:

  • All docs on both surfaces require YAML frontmatter header (title, author, created, updated, status, description)
  • All docs require a version history footer table
  • Two documentation surfaces: docs.ucca.online (technical/operational) and knowledge.ucca.online (product knowledge/company context)
  • Document at milestones, not every commit

Version History

Version Date Change Author
1.0 2026-03-02 Initial creation Claude Code