Repository navigation
feat(dns): emit external-dns DNSEndpoints for authorized tunnel hostnames - #94
Merged
Merged
Conversation
Register the DNSEndpoint API through one scheme helper that also adds metav1 options for the group, and load the DNSEndpoint CRD in envtest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mechanical go fix rewrite after the toolchain bump (promoted-field composite literals, new(true), slices.Contains); no behavior change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The field was never populated or read; the DNS handoff that reserved it now derives its target from the edge-target map instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ostnames 409-absorbed hostnames stay authorized in status but are excluded from the confirmed set; revoked invites are reported, not mutated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #38. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…de warning Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…D doc Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
go fix removed the last direct import of k8s.io/utils; go mod tidy moves it to the indirect block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HostnamesSynced no longer reads True "converged" for a revoked invite, where convergeHostnames mutates nothing; it is now False/InviteRevoked. When the hub answers 404 for the invite (deleted hub-side) nothing can be confirmed, so DNS fails closed and withdraws published records before the reconcile fails. The region-status 404 message no longer blames only an old hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…en state Pins that a Forbidden pre-delete DNS sweep keeps the finalizer, that invalid spec.dns.labels report InvalidConfig and a missing spec.dns.namespace reports APIError "not found", and that the retained-without-token state is an adopted invite (status.inviteId set), not an unissued one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 409 hostname_conflict comments and Warning event now say the hostname is reserved by another invite and kept in status.authorizedHostnames but not DNS-confirmed. dns.md gains the upgrade remediation step, the stale-CRD warning, the edge-target replace semantics, the orphan-hash lookup, a valid namespace example, and an accurate APIError row; the hub-deleted-invite withdrawal is documented. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
convergeHostnames wraps GetInvite, AddHostnames and RemoveHostname errors alike, so the reconciler's 404 check also fired when RemoveHostname got "hostname not found on invite" (another writer already removed it) and withdrew the tunnel's whole DNSEndpoint. Tag only the GetInvite 404 with errInviteNotFound and withdraw on that sentinel. Adds a fake-hub knob (SetHostnameDeleteNotFound) to reproduce the race, and fixes a stale comment on the region-status 404. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jacaudi
force-pushed
the
feat/dns-endpoint-handoff
branch
from
October 30, 2026 23:56
e7d55e8 to
14dc22f
Compare
Two self-contained SVG overview slides in the style of the GSDP diagrams: 01-how-it-works (sources → operator → hub, agents, edge, request path) and 02-dns-handoff (hub-confirmed hostnames → DNSEndpoint → public and LAN external-dns instances, withdrawal cases, rollout order). Logos embedded as data URIs; role="img" with aria-labels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e house docs style README follows the owner's other repos: a pitch that says what the operator is not, Features as bold-lead bullets (DNS handoff, region endpoints status), How it works and DNS handoff sections with the new diagrams and captions, a Doc | Default | Covers table, and an external-dns credit. docs/ gains status lines for optional features, the diagrams in architecture.md and dns.md, a File | Covers index, and Back-to-README footers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Optional, vendor-neutral DNS for tunnel hostnames. With
spec.dns.mode: DNSEndpoint, the operator emits an external-dnsDNSEndpointthat CNAMEs every hub-confirmed HTTPS hostname to the tunnel's regional Towonel edge. Any external-dns instance with--source=crdthen publishes it. Closes #38.Safety properties, each pinned by a test:
spec.region.DNSEndpointCRDMissing) and never loops. Picked up without a restart once installed.Evidence
task ciexits 0 ate7d55e8(fmt, vet, build,-racetests including envtest, helm lint/template, generated-artifact drift).go test ./test/envtest/ -run TestDNS -count=5: all 14 DNS envtests pass 5/5 (flake check).TestDNSEndpointPublishedForConfirmedHostnamesTestDNSEndpointShrinksThenRemovedTestDNSRevokedInviteWithdrawsRecordsTestDNSInviteDeletedHubSideWithdrawsRecordsTestDNSDeleteSweepsWithoutAPIKeyTestDNSWithoutDNSEndpointCRDTestReconcileDNSDisabledNoMatchDoesNotRetryTestReconcileDeleteSweepForbiddenKeepsFinalizer--dns-edge-target=badexits 1 before the manager is created.No before: the DNS handoff and
status.regionEndpointsdidn't exist.spec.dnswas inert.Merge Danger
Door: two-way. It's additive and defaults to
Disabled. The only removal is the deadstatus.httpsEdgeTarget, which was never populated.Blast Radius: cluster DNS
Before upgrading:
spec.dns.mode: DNSEndpoint(accepted but inert until now) starts emitting on upgrade, which skips the mandatory consumer rollout order. Find such tunnels first:kubectl get twt -A -o jsonpath='{range .items[?(@.spec.dns.mode=="DNSEndpoint")]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'Disabled, or finish the rollout indocs/dns.md, before upgrading.DNSEndpointSynced=False/APIError, emits a Warning, and requeues every 2 minutes. A stale CRD prunesspec.dns.labels, so an emitted DNSEndpoint would lack the label your public and LAN external-dns instances filter on. The chart handles both.What changes on tunnels that never use DNS:
status.dnsRecordsis populated with intent;RegionEndpointsAvailablecondition andstatus.regionEndpointsappear;Before enabling
mode: DNSEndpoint: followdocs/dns.md. Leaking public CNAMEs into a LAN split-horizon instance is the main hazard, so do it in this order:!=publiclabel filter to the LAN instance.Fail-closed scope (fixed in
14dc22f): DNS is withdrawn only when the hub'sGetInvitereturns 404, meaning the invite was deleted hub-side. A sentinelerrInviteNotFoundis wrapped on that path alone. ARemoveHostname404 ("hostname not found on invite", e.g. two clusters racing to drop the same hostname) no longer withdraws records. Pinned by:TestDNSHostnameRemove404KeepsRecords: RED on the old predicate, then GREEN.TestConvergeHostnamesInviteNotFoundClassification.TestDNSInviteDeletedHubSideWithdrawsRecords: now also assertsReady=False/APIErrorandDNSEndpointSynced=InviteRevoked.Related follow-up: #95 (a
Retaintunnel's token Secret is garbage-collected, so recreating the tunnel can't re-adopt it).Unverified:
DEFAULT_REGION="EU", and the/statusshape. These were read from hub source and are covered only by the fake hub.CA→edge-na.towonel.devmapping, inferred from working records and DNS.🤖 Generated with Claude Code