fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(tunnel): add --overwrite-dns to obol tunnel login/setup - #471

Merged
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns
May 12, 2026
Merged

fix(tunnel): add --overwrite-dns to obol tunnel login/setup#471
bussyjd merged 1 commit into
mainfrom
fix/tunnel-overwrite-dns

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

Re-running obol tunnel login (or obol tunnel setup --management local) against a hostname that already has a DNS record fails with Cloudflare API error 1003:

cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.

cloudflared has supported --overwrite-dns on tunnel route dns for years (also TUNNEL_FORCE_PROVISIONING_DNS) — the obol wrapper just never passed it.

This adds the --overwrite-dns flag to both obol tunnel login and obol tunnel setup, plumbs it through LoginOptions/SetupOptions, and includes a hint on the existing error path so operators learn about the flag at the moment they need it.

Default is off — replacing an existing record is destructive (it could flatten a CNAME placed by someone else), so opt-in is the right shape. Remote-managed mode is unaffected because the Cloudflare API path already upserts.

Operators hit this when

  • They retry the wizard after fixing an earlier issue (e.g. wrong CF account on first try).
  • They move an existing hostname onto a fresh in-cluster tunnel (cluster recreated, new petname, same DNS target).
  • Surfaced today on spark2 while re-pointing inference.v1337.org from a stale (sacred-magpie) tunnel to a fresh dev-mode (merry-troll) tunnel.

Test plan

  • go test ./internal/tunnel -run TestRouteDNSArgs -count=1 — new helper passes both default and overwrite cases.
  • go build ./... clean.
  • Manual on spark2: obol tunnel login --hostname inference.v1337.org --overwrite-dns replaces the existing CNAME and finishes the wizard cleanly.

Full report (PR template) added as the first comment.

Re-running `obol tunnel login` or `obol tunnel setup --management local`
against a hostname that already has an A/AAAA/CNAME record fails with:
cloudflared tunnel route dns failed: exit status 1
Failed to add route: code: 1003, reason: Failed to create record
inference.example.com with err An A, AAAA, or CNAME record with that
host already exists.
cloudflared has supported `--overwrite-dns` on `tunnel route dns` for
years for exactly this case (also exposed as `TUNNEL_FORCE_PROVISIONING_DNS`)
but the obol wrapper never passed it.
Operators hit this whenever they:
- Retry a wizard run after fixing an earlier failure.
- Move an existing hostname over to a new in-cluster tunnel (cluster
re-created, new petname, same DNS target intended).
Changes:
- internal/tunnel/login.go: add `OverwriteDNS bool` to LoginOptions and
pass `--overwrite-dns` through a new `routeDNSArgs` helper. When the
caller did not opt in and cloudflared returns the "record already
exists" error, append a hint pointing at --overwrite-dns.
- internal/tunnel/domain_setup.go: add the same field to SetupOptions
and forward it into the Login call in local-managed mode. Remote
mode is unaffected because the Cloudflare API path already upserts.
- cmd/obol/tunnel_domain.go: expose `--overwrite-dns` on
`obol tunnel login` and `obol tunnel setup`.
- Default stays false — the user has to opt in. The flag is genuinely
destructive (you may flatten a CNAME that someone else placed), so
surfacing it as a hint when the conflict happens is the right
ergonomic shape.
Test plan:
- `go test ./internal/tunnel -run TestRouteDNSArgs` passes both
default and overwrite cases.
- Manual: confirmed end-to-end on spark2 — the prior CNAME for
`inference.v1337.org` was replaced when the wizard was re-run after
the cluster was recreated.
@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed: internal/tunnel/login.go now appends --overwrite-dns to the cloudflared tunnel route dns invocation when the new OverwriteDNS option is set; LoginOptions and SetupOptions carry it; obol tunnel login and obol tunnel setup expose --overwrite-dns. When cloudflared returns "record already exists" without the flag, the error now also prints a one-line hint pointing at it.

Why it matters: obol tunnel login / obol tunnel setup --management local are guaranteed to fail on the second run for the same hostname because Cloudflare DNS rejects record collisions (API code 1003). Operators have to drop to raw cloudflared --origincert … tunnel route dns --overwrite-dns <UUID> <host> to recover, which requires reading internal CLI source to find the tunnel UUID. We hit this twice today on spark2.

Risk level: low

Commit under test: tip of fix/tunnel-overwrite-dns (1b25806)

Base branch: main

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
GitHub Actions CIpending(set by PR open)

Unit tests:

$ go test ./internal/tunnel -run TestRouteDNSArgs -count=1
=== RUN TestRouteDNSArgs
=== RUN TestRouteDNSArgs/default_(no_overwrite)
=== RUN TestRouteDNSArgs/overwrite-dns_inserted_before_tunnel/hostname
--- PASS: TestRouteDNSArgs (0.00s)
PASS
ok github.com/ObolNetwork/obol-stack/internal/tunnel	1.107s

Integration tests:

n/a — wizard is not yet covered by an integration test on the obol side.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
(none — flag is opt-in, default behaviour unchanged)

Release smoke:

n/a — no release artefact change.

Live Chain Evidence

n/a — this PR is CLI plumbing; no on-chain side effects.

Runtime Evidence

QA environment:

ItemValue
OS / archlinux/arm64 (Ubuntu 24.04.4 LTS on NVIDIA GB10)
Backendk3d, dev mode (OBOL_DEVELOPMENT=true)
Tool versionsobol@dev (go run wrapper, source at fix/tunnel-overwrite-dns), cloudflared 2026.3.0, k3d 5.8.3
QA agent/modeln/a

Images: n/a

Kubernetes / stack:

ItemValue
Stack IDsmerry-troll
Namespacestraefik (cloudflared chart)
Pod readinesscloudflared 1/1 Running after re-run with --overwrite-dns
Cleanup resulttunnel UUID a7729815-… retained; CNAME for the test hostname re-pointed at it

Model and routing: n/a

Artifacts and logs:

ArtifactLocation / linkNotes
Before outputreproduced in PR bodyAPI code 1003
After outputreproduced in PR bodywizard reaches Tunnel ready

Demo readiness:

ItemStatusNotes
Seller visible / registeredn/aPR is plumbing
Buyer discovery worksn/a
Paid route worksn/a
Settlement visible on-chainn/a

Review Notes

Known gaps:

  • No automated end-to-end test of the wizard — TestRouteDNSArgs only covers argument construction. A live cloudflared test would require Cloudflare credentials in CI.

Follow-ups:

  • Apply the same -y/--yes aliasing pattern to other destructive commands (obol stack purge, obol agent delete, etc.) — filed separately (fix(sell): accept --yes / -y as aliases for --force on sell delete #472 starts the scope with sell delete).
  • Consider auto-detecting "the existing record already points at our tunnel" and retrying with overwrite silently for that idempotent case. Out of scope here because it needs an API call against the zone to read the existing record.

Reviewer focus:

  • Is "opt-in --overwrite-dns" the right shape, or should we always pass it? Argument against always: stomping a CNAME that someone else placed.
  • The error-path hint string in login.go. Acceptable phrasing?

@bussyjd
bussyjd merged commit 163bb91 into mainMay 12, 2026
6 checks passed
@bussyjd
bussyjd deleted the fix/tunnel-overwrite-dns branch May 12, 2026 09:06
Sign up for freeto 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

@bussyjd