godaddy-tofu
Notes
Review 4
-
Review: Fix CI: split unit/integration tests
review-1479-2026-06-17Verdict: READY
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Standalone, discovered during PR #37 review
- [x] Repo -- ldraney/godaddy-tofu
- [x] User Story -- present, well-formed
- [x] Context -- clear explanation of concurrent pipeline failures (#5 and #6)
- [x] File Targets -- .woodpecker.yml identified
- [x] Feature Flag -- None (appropriate for CI config)
- [x] Acceptance Criteria -- 3 criteria, testable
- [x] Test Expectations -- 2 manual verification steps
- [x] Constraints -- golang:1.26 image, secrets, Woodpecker when conditions
- [x] Checklist -- present
- [x] Related -- present
Traceability
- [x] story:dns-iac label -- DNS as IaC
- [x] story note verified -- story-godaddy-tofu-dns-iac exists in project-godaddy-tofu user-stories section
- [x] arch:godaddy-tofu label -- provider component
- [ ] arch note MISSING -- no arch-godaddy-tofu note found in pal-e-docs. However, architecture docs exist in-repo at docs/architecture.md. Acceptable for this repo's maturity level -- the arch label correctly references the component even without a pal-e-docs note.
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/godaddy-tofu/issues/38, open
File Targets
- [x]
.woodpecker.yml-- verified: exists, currently has a single pipeline with both build and test steps running on all push+pull_request events. Structure matches what the issue describes: build (go build, go vet) and test (go test ./pkg/godaddy/) in one undifferentiated pipeline with a globalwhen: event: [push, pull_request]trigger.
Repo Placement
Correct. The CI config lives in this repo and the fix is scoped entirely to this repo.
Dependencies
No blockers. All other board items are in the done column. This is standalone work with no upstream or downstream dependencies on the board.
Acceptance Criteria
All 3 criteria are verifiable:
- "PRs trigger build + vet only" -- verifiable by opening a test PR and checking Woodpecker pipeline steps
- "Pushes to main trigger build + vet + integration tests" -- verifiable by merging and observing pipeline
- "No concurrent integration test runs possible" -- inherent from Woodpecker's pipeline serialization when only main-push triggers integration tests
Test Expectations are manual verification steps (open PR, merge to main) -- appropriate for CI config changes where automated test verification would be circular.
Blast Radius
Low. This change only affects
.woodpecker.yml. No downstream consumers are affected. Note: root-level acceptance tests (resource_dns_record_test.go,data_source_dns_records_test.go) useresource.Test()which requiresTF_ACC=1. These are not currently run by CI and the ticket does not address them -- this is a separate concern and does not block this ticket.Integration tests in
pkg/godaddy/self-skip viatestClient(t)when env vars are absent, except for two pure unit tests (TestNewClient,TestNewClientWithBaseURL) which always run. The split is clean: build-only PRs will still pass the unit tests ifgo testis run without credentials.Decomposition Assessment
No decomposition needed. Single file target, 3 acceptance criteria, single repo. Estimated agent work well under 5 minutes. This is a straightforward Woodpecker YAML restructuring.
Recommendation
No action needed.
-
Review: Integration tests for pkg/godaddy/ client
review-1425-2026-06-14Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- present (but contains wrong dependency reference)
- [x] Repo -- ldraney/godaddy-tofu
- [x] User Story -- present
- [x] Context -- present, good background
- [x] File Targets -- present, 3 files listed
- [x] Feature Flag -- present (None)
- [x] Acceptance Criteria -- present, 5 items
- [x] Test Expectations -- present, 3 items
- [x] Constraints -- present, 4 items
- [x] Checklist -- present
- [x] Related -- present
Traceability
- [x] story:dns-iac label -- present on board item
- [x] story note reference -- dns-iac entry exists in project-godaddy-tofu user-stories table (Key: dns-iac, links to story-godaddy-tofu-dns-iac)
- [ ] story note MISSING --
story-godaddy-tofu-dns-iacnote does not exist in pal-e-docs (search returned 0 results). The project page links to it but the note was never created. [SCOPE] Create story notestory-godaddy-tofu-dns-iac. - [x] arch:godaddy-tofu label -- present on board item
- [ ] arch note MISSING -- No
arch-godaddy-tofunote found in pal-e-docs. [SCOPE] Create architecture notearch-godaddy-tofu. - [x] Forgejo issue -- ldraney/godaddy-tofu#4, state: open
File Targets
- [x]
pkg/godaddy/client_test.go-- does not yet exist (to be created). Client code exists in HEAD atpkg/godaddy/client.go. Verified:NewClient(),WithBaseURL(),WithHTTPClient()options pattern is available for test setup. - [x]
pkg/godaddy/dns_test.go-- does not yet exist (to be created). DNS methods verified in HEAD:GetRecords(ctx, domain, recordType, name)-- matches ticket'sTestGetRecordsAddRecords(ctx, domain, records []DNSRecord)-- ticket saysTestAddAndDeleteRecord(singular) but method isAddRecords(plural, takes slice)DeleteRecords(ctx, domain, recordType, name)-- available for cleanupReplaceRecords(ctx, domain, recordType, name, records)-- matchesTestReplaceRecordsReplaceAllRecords(ctx, domain, records)-- NOT covered in test planReplaceRecordsByType(ctx, domain, recordType, records)-- NOT covered in test plan
- [x]
pkg/godaddy/domains_test.go-- does not yet exist (to be created). Domain methods verified in HEAD:ListDomains(ctx)-- matchesTestListDomainsGetDomain(ctx, domain)-- matchesTestGetDomain
Repo Placement
OK -- issue is filed on ldraney/godaddy-tofu, all file targets are in the same repo.
Dependencies
- [BODY] Wrong dependency reference -- Issue says "Depends on #2" but #2 is the docs foundation PR. The Go scaffold that created
pkg/godaddy/is issue #3 / PR #5. Should say "Depends on #3". - #3 (Go scaffold) is in
donecolumn on the board -- dependency is satisfied. - Issue #6 relationship -- Issue #6 (QA nits: DomainID float64->int64, io.ReadAll size limit, url variable naming) is on the board in backlog (item #1454, 1 point). These nits affect the code that #4's tests will exercise:
DomainID float64-- domain tests in #4 will deserialize this field. If #6 changes it toint64, tests written againstfloat64will break. Recommendation: land #6 first, or acknowledge in #4 that Domain struct may change.io.ReadAllunbounded -- not directly tested by #4 but could affect error handling tests.urlvsreqURLinListDomains-- confirmed: line 37 ofdomains.gousesurllocal var, shadowsnet/urlimport. Not a test-breaking issue but is a code smell the tests will exercise.
Acceptance Criteria
- [x] "go test ./pkg/godaddy/ -v passes" -- verifiable by running command
- [x] "DNS tests create, read, and delete a test record without leaving state" -- verifiable by test teardown logic
- [x] "Domain tests read existing domain data" -- verifiable
- [x] "Tests skip gracefully when credentials are not set" -- verifiable by running without env vars
- [x] "Tests handle 429 rate limiting with backoff" -- verifiable but the client has no retry/backoff logic. Tests cannot handle 429 if the client doesn't. Either the test must implement its own retry wrapper, or this AC implies adding retry logic to the client (scope creep beyond "integration tests").
Blast Radius
Low. These are new test files -- no existing code is modified. The only risk is if tests accidentally mutate production DNS records on palinks.app. The constraint to use TXT records mitigates this. No downstream consumers are affected.
Decomposition Assessment
3 file targets in 1 repo, 5 acceptance criteria, estimated agent time ~3-4 minutes. No decomposition needed.
Recommendations
[BODY]Fix dependency reference: "Depends on #2" should be "Depends on #3" (the Go scaffold issue).[BODY]Fix test nameTestAddAndDeleteRecordto reflect that the actual method isAddRecords(plural, takes a slice) -- suggestTestAddAndDeleteRecords.[BODY]Address #6 relationship: add note that #6 (QA nits) should land first to avoidDomainIDtype churn in domain tests. Or explicitly state tests should be written against the post-#6 struct.[BODY]Clarify AC for 429 handling: the client has no retry logic. Either (a) descope the 429 AC from this ticket and create a separate ticket for client retry logic, or (b) expand scope to include retry in the client. Current scope is ambiguous.[BODY]Consider adding test coverage forReplaceAllRecordsandReplaceRecordsByTypemethods (2 of 6 DNS methods are untested by this plan).[SCOPE]Create story notestory-godaddy-tofu-dns-iacin pal-e-docs (referenced from project page but does not exist).[SCOPE]Create architecture notearch-godaddy-tofuin pal-e-docs (board item has arch:godaddy-tofu label but no backing note).
-
Review: Go scaffold and pkg/godaddy/ DNS client (re-review)
review-1424-2026-06-13-r2Verdict: READY
Re-review of board item #1424. Previous review
review-1424-2026-06-13returned NEEDS_REFINEMENT with two [SCOPE] items. Both have been resolved.Template Completeness
- [x] Type -- Feature
- [x] Lineage -- depends on #1 (closed/merged)
- [x] Repo -- ldraney/godaddy-tofu
- [x] User Story -- present and well-formed
- [x] Context -- sufficient background on Go module, GoDaddy API, provider skeleton
- [x] File Targets -- 6 file entries with method signatures
- [x] Feature Flag -- None (appropriate for scaffold)
- [x] Acceptance Criteria -- 6 items, all verifiable
- [x] Test Expectations -- 2 items (go build, go vet)
- [x] Constraints -- 4 items covering Go conventions, net/http, encoding/json, stubs only
- [x] Checklist -- 6 items
- [x] Related -- project and doc references
Traceability
- [x] story:dns-iac label -- present on board item
- [x] story entry verified -- dns-iac row found in project-godaddy-tofu user-stories section (links to story-godaddy-tofu-dns-iac)
- [x] story note verified --
story-godaddy-tofu-dns-iacexists in pal-e-docs (note_type: user-story, status: active). Well-formed with Role, Want, So That, AC, Success Metric, Related Architecture sections. - [x] arch:godaddy-tofu label -- present on board item
- [x] arch notes verified -- all three architecture notes exist in pal-e-docs:
arch-domain-godaddy-tofu-- domain model with mermaid ER diagram (PROVIDER, DNS_RECORD, DOMAIN entities)arch-dataflow-godaddy-tofu-- data flow with mermaid sequence diagram (plan/apply/destroy flows)arch-deployment-godaddy-tofu-- deployment with mermaid graph (dev/CI/consumption pipeline)
- [x] Forgejo issue -- ldraney/godaddy-tofu#3, state: open
File Targets
- [x] go.mod / go.sum -- to create (greenfield repo, correct)
- [x] main.go -- to create (provider entry point)
- [x] provider.go -- to create (provider skeleton)
- [x] pkg/godaddy/client.go -- to create (Client struct, NewClient, sso-key auth). Auth format consistent with docs/auth.md at HEAD.
- [x] pkg/godaddy/dns.go -- to create (6 DNS methods). Methods consistent with docs/dns-endpoints.md at HEAD.
- [x] pkg/godaddy/domains.go -- to create (ListDomains, GetDomain). Read-only domain methods.
All file targets are new files in a greenfield repo. No conflicts with existing files. Reference docs (docs/dns-endpoints.md, docs/auth.md, docs/swagger_domains.json) exist at HEAD on main and are consistent with file target specifications.
Repo Placement
OK. Issue filed on ldraney/godaddy-tofu, all work is in ldraney/godaddy-tofu. Single repo scope.
Dependencies
Issue #1 (docs foundation) is closed and merged (commit c2119c7). Board item #1423 is in done column. Issue #4 (board item #1425, 3 pts) is a follow-up in backlog that depends on this ticket -- not a blocker. No undocumented dependencies.
Acceptance Criteria
6 criteria, all agent-verifiable:
go build .-- direct commandgo vet ./...-- direct command- NewClient instantiation -- verifiable via go build or simple test
- DNS method signatures -- verifiable via go vet and compilation
- Domain method signatures -- verifiable via go vet and compilation
- Provider skeleton with terraform-plugin-framework -- verifiable via go build and tofu init
All criteria are clear and testable. No missing criteria identified.
Blast Radius
Greenfield repo with no existing consumers. No sibling services affected. No downstream dependencies. Clean scope.
Decomposition Assessment
6 file targets in 1 repo, 6 AC. The work is a single coherent Go scaffold -- all files are tightly coupled (provider depends on client, client has dns/domains). Estimated agent time: ~5 minutes. No decomposition needed -- this is a single cohesive unit that would be harder to split than to do in one pass.
Previous Review Resolution
- [x] [SCOPE] story note
story-godaddy-tofu-dns-iac-- RESOLVED. Note created with note_type: user-story, status: active. Contains well-formed Role/Want/So-That/AC/Success-Metric. - [x] [SCOPE] arch note
arch-godaddy-tofu-- RESOLVED. Three architecture notes created (domain, dataflow, deployment), all with mermaid diagrams and key-decisions sections.
Recommendation
No action needed. All scope items from the previous review have been resolved. Ticket is ready to move to next_up.
-
Review: Go scaffold and pkg/godaddy/ DNS client
review-1424-2026-06-13Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- depends on #1 (closed)
- [x] Repo -- ldraney/godaddy-tofu
- [x] User Story -- present and well-formed
- [x] Context -- sufficient background on Go module, GoDaddy API, provider skeleton
- [x] File Targets -- 8 files listed with method signatures
- [x] Feature Flag -- None (appropriate for scaffold)
- [x] Acceptance Criteria -- 6 items, all verifiable
- [x] Test Expectations -- 2 items (go build, go vet)
- [x] Constraints -- 4 items covering Go conventions, net/http, encoding/json, stubs only
- [x] Checklist -- 6 items
- [x] Related -- project and doc references
Traceability
- [x] story:dns-iac label -- present on board item
- [x] story entry verified -- dns-iac row found in project-godaddy-tofu user-stories section
- [ ] story note MISSING -- story-godaddy-tofu-dns-iac does not exist in pal-e-docs. [SCOPE] Create story note story-godaddy-tofu-dns-iac.
- [x] arch:godaddy-tofu label -- present on board item
- [ ] arch note MISSING -- no arch-godaddy-tofu note found in pal-e-docs. [SCOPE] Create architecture note arch-godaddy-tofu.
- [x] Forgejo issue -- ldraney/godaddy-tofu#3, open
File Targets
- [x] go.mod / go.sum -- to create (greenfield repo, correct)
- [x] main.go -- to create (provider entry point)
- [x] provider.go -- to create (provider skeleton)
- [x] pkg/godaddy/client.go -- to create (Client struct, NewClient, sso-key auth). Auth format matches docs/auth.md.
- [x] pkg/godaddy/dns.go -- to create (6 DNS methods). Methods match exactly with docs/dns-endpoints.md endpoint map.
- [x] pkg/godaddy/domains.go -- to create (ListDomains, GetDomain). Read-only domain methods.
All file targets are new files in a greenfield repo. No conflicts with existing files. Reference docs (docs/dns-endpoints.md, docs/auth.md, docs/swagger_domains.json) exist and are consistent with the file target specifications.
Repo Placement
OK. Issue filed on ldraney/godaddy-tofu, all work is in ldraney/godaddy-tofu. Single repo scope.
Dependencies
Issue #1 (docs foundation) is closed and merged -- dependency satisfied. Board item #1423 is in done column. Issue #4 (integration tests) is a follow-up that depends on this ticket, not a blocker. No undocumented dependencies.
Acceptance Criteria
6 criteria, all agent-verifiable:
go build .-- direct commandgo vet ./...-- direct command- NewClient instantiation -- verifiable via go build or simple test
- DNS method signatures -- verifiable via go vet and compilation
- Domain method signatures -- verifiable via go vet and compilation
- Provider skeleton with terraform-plugin-framework -- verifiable via go build and tofu init
All criteria are clear and testable. No missing criteria identified.
Blast Radius
Greenfield repo with no existing consumers. No sibling services affected. No downstream dependencies. Clean scope.
Decomposition Assessment
8 file targets in 1 repo, 6 AC. The AC count is at the threshold (>5) but the work is a single coherent Go scaffold -- all files are tightly coupled (provider depends on client, client has dns/domains). Estimated agent time: ~5 minutes. No decomposition needed -- this is a single cohesive unit that would be harder to split than to do in one pass.
Recommendation
- [SCOPE] Create story note
story-godaddy-tofu-dns-iacin pal-e-docs. The user-stories table on project-godaddy-tofu references it but the note does not exist. - [SCOPE] Create architecture note
arch-godaddy-tofuin pal-e-docs. The board item carries the arch:godaddy-tofu label but no backing architecture note exists.
Project Page 1
-
Project: GoDaddy Tofu
project-godaddy-tofuVision
Custom OpenTofu provider for the GoDaddy API. Manages DNS records and domains as IaC, consumed by
pal-e-platform/terraform/. Replaces abandoned community providers (veksh/godaddy-dns,n3integration/godaddy— one deletes unmanaged records). Built in Go with the Terraform Plugin Framework. First dogfood target:palinks.appDNS records.User Stories
Key Story Note Role Success Metric dns-iac story-godaddy-tofu-dns-iac Platform operator palinks.app A/CNAME records managed via tofu applydomain-iac story-godaddy-tofu-domain-iac Platform operator Domain details readable via tofu planArchitecture
Go provider built on Terraform Plugin Framework. Internal Go client package (
pkg/godaddy/) wraps the GoDaddy REST API directly — no Python SDK dependency.- Resources:
godaddy_dns_record,godaddy_domain - Data sources:
godaddy_domains,godaddy_dns_records - Auth:
sso-key {key}:{secret}header from provider config
- Domain Model — provider resources, data sources, and their GoDaddy API mapping
- Data Flow — how tofu plan/apply translates to GoDaddy API calls
- Deployment — provider registry, pal-e-platform consumption
- DNS as Foundational Dependency — why DNS is the technical heart of every project that serves users
Key decisions:
- Go client package inside provider repo (not separate SDK) — single language, single test suite
- Python SDK dropped — Tofu providers must be Go, no cross-language benefit
- DNS records as individual resources (not bulk) — safer than replace-all semantics
Board
board-godaddy-tofuIntegration tests merged (PR #8, closes #4): full coverage of all 8 client methods against live GoDaddy API. Docs restructured (PR #9): README is now lightweight intro + TOC, detailed architecture/deployment docs with mermaid diagrams live in docs/. QA nits from PR #8 tracked in #10. Next: DNS resource implementation, DNS data source, provider acceptance tests.
QA nits fixed (PR #7, closes #6): DomainID changed to int64, io.ReadAll capped at 1MB, url variable naming consistent. Go scaffold and DNS client solid. Next: integration tests (#4).
- 2026-06-13 — Repo initialized, docs foundation merged (#1/#2)
- 2026-06-14 — Go scaffold + DNS client merged (#3/PR #5). Provider compiles, all client methods implemented.
- 2026-06-14 — QA nits fixed (PR #7, closes #6): DomainID int64, io.ReadAll limit, url variable naming.
- 2026-06-14 — Integration tests merged (PR #8, closes #4): all 8 client methods tested against live API.
- 2026-06-14 — Docs restructured (PR #9): README as intro+TOC, docs/architecture.md and docs/deployment.md with mermaid diagrams.
- 2026-06-13 — Repo initialized, docs foundation merged (#1/#2)
- 2026-06-14 — Go scaffold + DNS client merged (#3/PR #5). Provider compiles, all client methods implemented.
Repo Platform Role Status godaddy-tofuForgejo OpenTofu provider Docs complete, awaiting Go scaffold - Resources:
Doc 1
-
DNS as Foundational Dependency
dns-foundational-dependencyThesis
DNS is the foundational dependency of nearly every project that serves users. It's the first thing that must work and the last thing you notice — until it doesn't. Every layer above it (TLS, reverse proxy, app routing, SSO redirects) assumes DNS is already correct. When DNS is broken, nothing above it can be debugged meaningfully.
The Dependency Chain
For any custom domain to go live, three waves must complete in order:
- DNS records — A/CNAME records pointing the domain to the right IP. Without this, the domain doesn't resolve at all. This is pure IaC:
godaddy_dns_recordresources managed by OpenTofu. - Reverse proxy — Caddy/nginx config that accepts traffic for the domain, terminates TLS (via ACME/Let's Encrypt), and routes to the backend. TLS cert provisioning itself depends on DNS being correct — ACME HTTP-01 challenges need the domain to resolve to the proxy.
- Application config — Rails
config.hosts, Keycloak realm settings, CORS origins, OAuth redirect URIs. The app must know its own domain. This is the last mile, and it's meaningless without the first two.
Why DNS Is Special
- Invisible when working. DNS is infrastructure you forget exists — until propagation delays, stale records, or missing entries make everything downstream look broken for no apparent reason.
- Slow feedback loop. TTLs mean mistakes take minutes to hours to manifest and equally long to fix. You can't iterate on DNS the way you iterate on app code.
- Blast radius. A wrong A record doesn't just break one endpoint — it breaks every service, cert renewal, and auth flow on that domain.
- Hard to test locally. Unlike app code, you can't spin up DNS in a dev environment. You're always testing against the real thing (or maintaining /etc/hosts hacks that hide real issues).
- Silent failures. A missing DNS record doesn't throw an error — the domain just doesn't resolve. Curl gets "could not resolve host." Users get a browser error page. There's no stack trace pointing at the root cause.
Implications for Platform Work
- DNS goes first. When onboarding a new domain, create and verify DNS records before touching proxy or app config. Don't parallelize wave 1 with wave 2 — wave 2 literally can't succeed without wave 1.
- DNS must be IaC. Manual DNS changes via a registrar UI are invisible to the rest of the platform. The godaddy-tofu provider exists specifically to keep DNS in the same tofu state as everything else.
- Verify at the DNS layer first. When a domain isn't working, start with
dig/nslookupbefore checking proxy logs or app config. 90% of "the domain is broken" issues are DNS issues. - Treat DNS changes as high-risk. TTL propagation means rollback isn't instant. A bad DNS change is a slow-motion incident.
pal-e Platform Context
On pal-e, the chain is:
GoDaddy DNS (godaddy-tofu) → Hetzner edge VPS (178.156.129.142) → Caddy reverse proxy → Tailscale → k3s servicesThe
godaddy_dns_recordresources inpal-e-platform/terraform/dns.tfpoint domains atmodule.hetzner_edge.server_ipv4, creating an explicit Terraform dependency: DNS records can't be planned without the Hetzner module refreshing first. This is correct — the IP must exist before DNS can reference it. - DNS records — A/CNAME records pointing the domain to the right IP. Without this, the domain doesn't resolve at all. This is pure IaC:
Architecture 4
-
Architecture: GoDaddy Tofu
arch-godaddy-tofuArchitecture: GoDaddy Tofu
Diagram
graph TB subgraph "Developer Machine" TF[pal-e-platform/terraform/] PROV[godaddy-tofu binary] end subgraph "GoDaddy" API[api.godaddy.com] DNS_PA[palinks.app DNS] DNS_LA[landscaping-assistant.app DNS] end subgraph "Hetzner Edge" EDGE[178.156.129.142 Caddy + Tailscale cpx11 Ashburn] end subgraph "Home Lab" K3S[k3s cluster Arch box] end TF --> PROV PROV -->|sso-key auth| API API --> DNS_PA API --> DNS_LA DNS_PA -->|A record| EDGE DNS_LA -->|A record| EDGE EDGE -->|Tailscale mesh| K3SComponents
Component Purpose Notes godaddy-tofu binary OpenTofu provider plugin Go binary, terraform-plugin-framework, registers as registry.terraform.io/ldraney/godaddy pkg/godaddy Client HTTP client for GoDaddy REST API sso-key auth, net/http, 6 DNS + 2 domain methods godaddy_dns_record resource CRUD for individual DNS records P0 - maps to PUT/GET/DELETE /v1/domains/{d}/records/{t}/{n} godaddy_domain resource Domain settings management P1 - read-heavy, maps to PATCH /v1/domains/{d} godaddy_dns_records data source Read existing DNS records P0 - maps to GET /v1/domains/{d}/records godaddy_domains data source List/read domain details P1 - maps to GET /v1/domains Hetzner edge node Reverse proxy for custom domains 178.156.129.142, Caddy auto-TLS, Tailscale mesh to k3s pal-e-platform/terraform/ Consumer of the provider HCL config defining which DNS records exist Key Decisions
- Go client inside provider repo (not separate SDK) — single language, single test suite, no cross-language overhead
- DNS records as individual resources (not bulk replace-all) — safer than providers that delete unmanaged records
- net/http only — no external HTTP dependencies, stdlib is sufficient for REST calls
- sso-key auth — matches GoDaddy production API, credentials from provider config or env vars
- Hetzner edge as reverse proxy target — Caddy handles public TLS, Tailscale handles private mesh back to k3s
- Provider consumed by pal-e-platform — DNS config lives where all other infra config lives, not in godaddy-tofu repo
Related
project-godaddy-tofu— project pagestory-godaddy-tofu-dns-iac— user story this architecture servesproject-palinks— first consumer (palinks.app DNS)- pal-e-platform PR #420/#424 — Hetzner edge module
-
Deployment: godaddy-tofu
arch-deployment-godaddy-tofuDeployment: godaddy-tofu
Diagram
graph TB subgraph "Development" Dev[Developer workstation] --> |"go install ."|Local[~/.terraform.d/plugins/] end subgraph "CI (Woodpecker)" Repo[godaddy-tofu repo] --> |"push"|CI[Woodpecker pipeline] CI --> |"go test"|Tests[Client + acceptance tests] CI --> |"goreleaser"|Release[GitHub/Forgejo release] end subgraph "Consumption" Release --> |"provider source"|Platform[pal-e-platform/terraform/] Platform --> |"tofu init"|Init[Download provider binary] Init --> |"tofu apply"|Apply[Manage DNS records] Apply --> |"sso-key auth"|GoDaddy[api.godaddy.com] endComponents
Component Purpose Notes Developer workstation Local development and testing go installplaces binary in local plugin cacheWoodpecker pipeline CI — runs tests, builds releases Needs GODADDY_API_KEY/SECRET for acceptance tests Forgejo release Binary distribution goreleaser produces multi-arch binaries pal-e-platform Consumer — IaC repo that uses the provider References provider in required_providersblockapi.godaddy.com GoDaddy API Production endpoint for DNS/domain management Key Decisions
- Local plugin cache for development (
~/.terraform.d/plugins/) — no registry needed during development - goreleaser for releases — standard Terraform provider distribution pattern
- Woodpecker CI needs real API credentials for acceptance tests — no mocking the API
- pal-e-platform is the first and primary consumer — provider design driven by platform needs
Related
arch-domain-godaddy-tofu— entity definitionsarch-dataflow-godaddy-tofu— runtime API flowproject-godaddy-tofu— project page
- Local plugin cache for development (
-
Data Flow: godaddy-tofu
arch-dataflow-godaddy-tofuData Flow: godaddy-tofu
Diagram
sequenceDiagram participant User as tofu CLI participant Provider as godaddy provider participant Client as pkg/godaddy/client participant API as api.godaddy.com Note over User,API: tofu plan (Read) User->>Provider: Plan — read current state Provider->>Client: GetRecords(domain, type, name) Client->>API: GET /v1/domains/{domain}/records/{type}/{name} Note right of Client: Authorization: sso-key {key}:{secret} API-->>Client: []DNSRecord Client-->>Provider: []DNSRecord Provider-->>User: Plan diff Note over User,API: tofu apply (Create/Update) User->>Provider: Apply — desired state Provider->>Client: ReplaceRecords(domain, type, name, records) Client->>API: PUT /v1/domains/{domain}/records/{type}/{name} API-->>Client: 200 OK Client-->>Provider: nil error Provider-->>User: Apply complete Note over User,API: tofu destroy (Delete) User->>Provider: Destroy — remove resource Provider->>Client: DeleteRecords(domain, type, name) Client->>API: DELETE /v1/domains/{domain}/records/{type}/{name} API-->>Client: 204 No Content Client-->>Provider: nil error Provider-->>User: Destroy completeComponents
Component Purpose Notes tofu CLI User interface for plan/apply/destroy OpenTofu or Terraform CLI godaddy provider Terraform Plugin Framework provider Translates Tofu resource lifecycle to API calls pkg/godaddy/client Go HTTP client for GoDaddy API Handles auth, JSON marshaling, error handling, rate limit backoff api.godaddy.com GoDaddy REST API Returns 429 with retryAfterSec on rate limit Key Decisions
- Read uses GET by type+name (most specific) — avoids fetching all records and filtering client-side
- Create/Update uses PUT (replace by type+name) — idempotent, matches Tofu's desired-state model
- Delete uses DELETE by type+name — only removes the managed record, not siblings
- Rate limit handling in the client layer (pkg/godaddy/) — provider doesn't need to know about retries
Related
arch-domain-godaddy-tofu— entity definitionsarch-deployment-godaddy-tofu— where the provider runsproject-godaddy-tofu— project page
-
Domain Model: godaddy-tofu
arch-domain-godaddy-tofuDomain Model: godaddy-tofu
Diagram
erDiagram PROVIDER ||--o{ DNS_RECORD : "manages" PROVIDER ||--o{ DOMAIN : "manages" PROVIDER { string api_key string api_secret string base_url } DNS_RECORD { string domain PK string type PK string name PK string data int ttl int priority } DOMAIN { string domain PK string status datetime expires bool renewable bool privacy } DNS_RECORD }o--|| DOMAIN : "belongs to"Components
Component Purpose Notes PROVIDER Provider config with GoDaddy API credentials Maps to provider "godaddy" {}blockDNS_RECORD Individual DNS record (A, CNAME, TXT, MX, etc.) Resource: godaddy_dns_record. Composite key: domain+type+nameDOMAIN Registered domain with metadata Resource: godaddy_domain(read-heavy, limited write). Data source:godaddy_domainsKey Decisions
- DNS records as individual resources (not bulk replace) — avoids the dangerous "delete unmanaged records" pattern from community providers
- Composite key (domain+type+name) identifies a record — matches GoDaddy API path structure
- Domain resource is read-heavy — purchase/transfer/delete are out of scope for initial implementation
Related
arch-dataflow-godaddy-tofu— runtime API call flowarch-deployment-godaddy-tofu— provider distributionproject-godaddy-tofu— project page
User Story 1
-
DNS as IaC
story-godaddy-tofu-dns-iacstory: DNS as IaC
Role
Platform operator (Lucas)
Key
dns-iac
Want
As a platform operator, I want to manage DNS records as OpenTofu resources
So That
So that DNS changes are version-controlled, reviewable, and reproducible — no manual GoDaddy dashboard clicks
Acceptance Criteria
- [ ]
tofu planshows current DNS records as data sources - [ ]
tofu applycreates/updates A, CNAME, and TXT records - [ ]
tofu destroyremoves managed records without touching unmanaged ones - [ ] palinks.app A record managed via
tofu applyin pal-e-platform
Success Metric
palinks.app A/CNAME records fully managed via
tofu applywith no manual GoDaddy interactionRelated Architecture
arch-domain-godaddy-tofu— provider resources and data sourcesarch-dataflow-godaddy-tofu— tofu plan/apply to GoDaddy API flowarch-deployment-godaddy-tofu— provider distribution and pal-e-platform consumption
Related
project-godaddy-tofu— parent project pageboard-godaddy-tofu— project board (tickets reference this story)
- [ ]
Board 1
-
GoDaddy Tofu
board-godaddy-tofuNo content