Handoff: NamecheapProxyCli + DNS-skill migration, and the (held) "WUI at kotlin.build apex" question
Written: 2026-07-09 ~01:00 UTC (effort began 2026-07-08).
⚠️ RE-VERIFY BEFORE ACTING. Every state claim here was true only when written. Re-verify PRs with
gh pr view <n> --repo CodexCoder21Organization/<repo> --json state,mergedAt,statusCheckRollup,
published artifacts with curl -s https://kotlin.directory/api/list-artifacts | grep <coord>,
ContainerNursery routes with the CN CLI routes --json, and DNS with the new NamecheapProxyCli
(... list-records kotlin.build). A fresh query always wins over this document.
Mission
The user wants the KompileRemoteBuild optimizer WUI served at the kotlin.build apex ("the domain
should be just kotlin.build, we should take the main website for the wui"), and — as enabling
infrastructure — the DNS management skill migrated to the
NamecheapProxy
(url://namecheap/) so DNS can be edited from any machine without IP whitelisting, plus any
NamecheapProxy-CLI bugs fixed.
This is a follow-on to the (separately handed-off) remote-build-optimizer effort — see
handoffs/2026-07-07-kompile-remote-build-optimizer-impl-and-deploy.md.
What was done (verified) — DNS tooling: COMPLETE
-
Built NamecheapProxyCli (new repo) — a
CLI over url://namecheap/ driving the typed
NamecheapDnsProxy contract:
health, list-domains, list-records, upsert-record, delete-record, with --json and
--url, and full-text surfacing of the apex/NS ForbiddenDnsOperationException and
NamecheapProxyException. Patterned on
CockroachDbCli /
GithubProxyCli. Merged (PR #1,
main head 60cc183a), kotlin.build (remote) CI green, published
namecheapproxy.cli:namecheap-proxy-cli:0.0.2. 4 test suites (arg-parsing, record lifecycle, JSON
output, error surfacing) against in-memory NamecheapDnsProxy fakes — all green.
- Verified end-to-end against LIVE Namecheap from a non-whitelisted box (the whole point of the
proxy):
health → "OK (connected to Namecheap)"; listed all kotlin.build records; and a
self-cleaning _ncpcli-verify TXT round-trip (upsert → confirm present → delete → confirm 0
remaining), leaving the zone otherwise untouched.
-
Rewrote the namecheap-domains skill (~/.claude/skills/namecheap-domains/SKILL.md, a local
file — not a repo, so no PR) to use NamecheapProxyCli over url://namecheap/ instead of the old
direct community.contrib.namecheap.cli:namecheap-cli (which required a hardcoded API key + a
whitelisted home IP 73.241.194.125 on the command line). Command mapping: domains→list-domains,
dns→list-records, add-record→upsert-record. Bumped to reference the published 0.0.2.
-
"Bug" the user asked to watch for, resolved as a convention, not a defect: coursier launch of
the published skinny jar failed (masked util.stacktrace error) because a kompile buildMaven
artifact has no Main-Class (only buildFatJar sets one). GithubProxyCli's build is identical,
so the established convention is to pass -M namecheapproxycli.MainKt. The skill's commands use that
form; verified working. (A future nicety: give buildMaven a Main-Class manifest so -M isn't
needed — but that would diverge from the sibling CLIs.)
The held item — "WUI at kotlin.build apex" — NOT started, needs the user
Nothing was changed on DNS or routing for this (the only DNS write was the self-cleaning test
record above, already deleted). Held because it displaces the org-CI webhook receiver.
Verified routing/DNS facts (via the new CLI + CN CLI + live curls):
-
Every kotlin.build record — including the apex @ — A-records to the ContainerNursery host
198.199.106.165 (ttl=300), which routes by Host: header. So serving a different jar at the
apex is a CN route swap, not a DNS edit.
-
The apex kotlin.build HTTPS route → kotlin-build-ci.jar — this is a GitHub App (its own
repo) that receives check_suite
webhooks and sets the kotlin.build (remote) check on every org PR. Verified live:
POST https://kotlin.build/webhook → 401 (HMAC-verified webhook endpoint rejecting an unsigned
POST); GET https://kotlin.build/ → 200 landing page. One jar serves both /webhook and /.
-
There are three distinct "kotlin build" web things (the source of the user's — and my earlier —
confusion):
| Domain | Jar / host | Role |
|---|---|---|
| kotlin.build | kotlin-build-ci.jar (ContainerNursery) | GitHub App: receives webhooks (/webhook) + landing page |
| www.kotlin.build | kotlin-build-wui.jar (Google Cloud Run) | existing user-facing build dashboard |
| kompile-build-ci.kotlin.build | kompile-build-ci-wui.jar (ContainerNursery) | the optimizer/analytics WUI deployed in the prior effort (/queue verified live; /optimizer,/accuracy gated on BTE #266) |
-
Hosted doc also names a github-watchman service "receives webhook events to trigger builds",
and there is a live route github-webhooks.wasmserver.com → github-watchman-integration-server.jar.
NOT yet traced whether GitHub posts straight to kotlin.build/webhook or via github-watchman
first. This must be mapped before any cutover.
Two decisions the user has not answered (asked twice via AskUserQuestion):
- Which WUI goes at the apex — the optimizer WUI built this session, or the existing
www.kotlin.build dashboard?
- Where the webhook goes. Recommended, staged so CI never drops: add
ci.kotlin.build
(upsert-record kotlin.build ci A 198.199.106.165 300 via the new CLI + a CN route for it →
kotlin-build-ci.jar), repoint the GitHub App's webhook URL to it, verify a signed test
delivery, then swap the apex CN route to the chosen WUI jar. Repointing a GitHub App's webhook
URL needs App-admin access in GitHub settings — likely a user-only step.
Relevant PRs / refs
| Item | State |
|---|---|
| NamecheapProxyCli PR #1 | MERGED, main 60cc183a, CI green, namecheap-proxy-cli:0.0.2 published |
| ~/.claude/skills/namecheap-domains/SKILL.md | updated locally (not a repo) |
| Apex-move | not started — held on the two user decisions above |
| BuildTestEmbedded #266 (prior effort) | still OPEN/FAILURE — real duplicateGhostDropletReaped flake + harness corruption; see the optimizer handoff |
Nothing unsaved: the CLI is merged; the skill edit is on local disk (~/.claude).
Next steps
- Re-verify (banner). Note the CLI is already at
0.0.2, not the 0.0.1 I first published.
- Get the two decisions (which WUI at apex; webhook relocation). Nothing destructive should proceed
without them and without confirming who can change the GitHub App webhook URL.
- Optional read-only prep: trace the true webhook path (GitHub App → github-watchman? → build
dispatch) so the cutover plan is airtight. Safe to do anytime.
- When approved, execute staged:
ci.kotlin.build A-record (new CLI) → CN route for it → repoint
App webhook → verify a signed delivery reaches it and a build dispatches → only then swap the apex CN
route to the chosen WUI jar → verify GET https://kotlin.build/ serves the WUI and a fresh PR still
gets its kotlin.build (remote) check. Never leave a window where webhooks 404.
- Separately, the optimizer effort's BTE #266 remains blocked — see its own handoff.
Reusable / operational knowledge
- NamecheapProxyCli:
coursier launch namecheapproxy.cli:namecheap-proxy-cli:0.0.2 -r https://kotlin.directory -M namecheapproxycli.MainKt -- <cmd> (the -M is required — no Main-Class in the skinny jar). Fat-jar fallback: scripts/build.bash namecheapproxycli.buildFatJar /tmp/x.jar && java -jar /tmp/x.jar <cmd>. It reaches url://namecheap/ from any box — no IP whitelisting, no credential.
- NamecheapProxy guardrail:
upsert-record/delete-record refuse the apex (@) and NS — apex/delegation edits must go through the Namecheap web console, not the proxy.
- DNS vs routing: all
*.kotlin.build A-record to 198.199.106.165; ContainerNursery routes by Host: header. Adding a subdomain = one A record (proxy) + one CN route. Serving a different jar at an existing host = a CN route change only.
- CN url-facade routes can't be created by the CN CLI (no
url flag) — use POST/PUT /routes on https://api.nursery.wasmserver.com/routes with transport: RPC.
- kompile test files: exactly one top-level function per
tests/*.kts; no top-level classes and top-level functions may not declare a return type — put fakes and helpers as locals inside the test function.