Skip to content

feat: DEP-0003 verified record withdrawal before address reuse - #17

Merged
reoring merged 20 commits into
mainfrom
work/20261018/4b-integration
Oct 18, 2026
Merged

reoring merged 20 commits into
mainfrom
work/20261018/4b-integration

Conversation

@reoring

@reoring reoring commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Implements DEP-0003 (deps/dep-0003-record-withdrawal.md, Draft → to be marked Accepted on merge): verified DNS withdrawal before a retired address is reused. Stacked on #16 (this branch contains it; merge #16 first or merge this and close #16).

What

  • Core API: RecordWithdrawal{zoneRef,name,address} (immutable, address label, no ownerReferences), RecordWithdrawalWaiver (create-RBAC is the authority; never sets Withdrawn=True), ZoneUnit.spec/status.withdrawals[] (keyed claimNamespace/claimName, ≤256), Provider.versions[].recordWithdrawal capability (external supported: true; route53/cloudflare false), ZoneUnit.spec.zone.zoneClassUID/zoneClassObservedGeneration fence so a Class policy edit cannot be consumed as old success.
  • Core: composition with the same Zone grants as A/AAAA RecordSets (wildcards need explicit grants), projection only for exact UID/generation + current Unit/Class binding, waiver binding, Zone deletion blocked while withdrawals are outstanding; admission for both kinds and the ERS retire-only state (CEL, no ERS webhook).
  • Endpoint app: the Gateway/Service source writers never drop a published pair: removed pairs move to EndpointRecordSet.spec.retiredTargets[]/retiredHostnames[] (snapshot associations, bounded); finalizer set at ERS create; Gateway deletion, rejected admission and cleanupForRoute retire instead of delete; the endpoint controller creates RecordWithdrawal claims for retired pairs (deterministic names, create-then-prune, crash-safe) and captures live+retired pairs on direct delete; retire-only ERS → Ready=False/Retiring.
  • External provider: probe.EvaluateWithdrawal (address absent from terminal A∪AAAA in every required view, both families; NXDOMAIN/NODATA count as absence), absentSince/notBefore from negative and retained positive cache horizons, a confirming post-horizon probe sets verifiedAt; reappearance/probe failure/policy change invalidate; waiver → OverrideAccepted; existing scheduler bounds; dns_external_withdrawals_pending{reason}.
  • Chart/Kustomize: CRDs, RBAC, admission parity.

Verification

  • task test — all packages ok (incl. envtest for composition/projection/waiver/zone-deletion gate; fake-resolver withdrawal tests); task controller-chart:check ok; go vet ./... clean; git diff --check clean.
  • Not yet proven live: the platform-side acceptance (two sshd hosts, one VIP) runs after the platform PR and this release land.

Make ZoneClass identityRef optional with Provider-version admission, preserve projected condition messages, gate cloud registrations and privileges, and support credentialless ZoneClass UI creation. Regenerate deepcopy and both CRD distributions; add deterministic admission, projection, chart and UI regressions.

Contract: dns-api PR #15 / 99d0753 docs/design/external-provider.md (ZoneClass Parameters, Conditions and Public Data, Packaging), core-api.md, provider-extension.md, controllers-webhooks.md (Provider Enablement), ui.md (Zone Classes), and test-policy.md. Platform PR #5331 / cd995cf0ed design/platform/onprem/README.md sections 4.3, 4.4 step 4, 4.7, and Phase 4.

Codegen: devbox run -- task generate manifests controller-chart:sync-crds completed. Tests, builds, linters and UI typecheck intentionally deferred to the primary integration agent.
Provider external.dns.appthrust.io/v1alpha1 (A/AAAA/CNAME, no identity), ZoneClass resolver policy and expectedNameservers, exact-set A/AAAA verification with bounded CNAME chains and wildcard witnesses, Cluster/Recursive views plus RD=0 authority checks, bounded scheduling (30-300s, jitter, backoff, 32 names/pass, 40 QPS/80 burst), UID/generation/policy-bound receipts, bookkeeping-only deletion, endpoint conversion handler, metrics. Design: docs/design/external-provider.md.
…8/external-integration

# Conflicts:
#	docs/design/controllers-webhooks.md
#	docs/design/core-api.md
#	docs/design/ui.md
…ernal-integration

# Conflicts:
#	docs/design/provider-extension.md
#	docs/design/test-policy.md
…ore admission accepts the manifest

The core webhook requires a description on every property and array item; the external Provider was rejected on install. The built-in-manifest validation test now covers external_extensions.yaml.
…gration

# Conflicts:
#	app/operator/config/external/external_extensions.yaml
#	deploy/charts/dns-api/templates/provider-external.yaml
#	deps/README.md
#	deps/dep-0003-record-withdrawal.md
#	docs/design/controllers-webhooks.md
#	docs/design/external-provider.md
#	internal/go/providers/external/controllers/zoneunit/controller.go
#	internal/go/providers/external/controllers/zoneunit/model.go
#	internal/go/providers/external/controllers/zoneunit/status.go
#	internal/go/providers/external/controllers/zoneunit/watches.go
#	internal/go/providers/external/manifest/manifest_test.go
#	internal/go/providers/external/manifest/provider.yaml
#	internal/go/providers/external/probe/metrics.go
#	internal/go/providers/external/probe/types.go
#	internal/go/providers/external/probe/wire.go
@reoring
reoring merged commit 8e28439 into main Oct 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant