Repository · workstreams
Workstream: NamecheapProxy
Status: Planned · Component: Maximize developer productivity
A third-party service proxy in the GithubProxy / CockroachDb mold — it wraps the Namecheap registrar API so creating, updating, and deleting subdomains (DNS records) is an ordinary
url://namecheap/call, and it is the credential-containment boundary for the Namecheap API key. But it carries one property those siblings do not: because it is hosted on the ContainerNursery host's stable, already-whitelisted IP, it doubles as a whitelisted egress gateway — the reason it exists at all, and the thing no library-in-every-consumer can provide. Like the rest of the plan, this describes the target state; the Current state section keeps it honest about what exists today.
Goal
Make managing a domain's DNS a proxy call, reachable from anywhere:
- Create / update / delete subdomains and DNS records over
url://namecheap/—listDomains,listRecords(domain),upsertRecord(domain, host, type, address, ttl),deleteRecord(domain, host, type)— with a typed API, not raw REST againstapi.namecheap.com. (A "subdomain" is not a separate object in Namecheap: creatingapi.example.comis adding anA/AAAA/CNAMErecord whose host label isapi. The API deliberately speaks in records and treats subdomain creation as record upsert, removing that ambiguity up front.) - The Namecheap API key lives only in the service — never baked into every consumer, script, or agent that needs to touch DNS. The proxy is the one place the credential resides, the same credential-containment role GithubProxy plays for a GitHub token and CockroachDb plays for cluster credentials.
- Any machine can drive it, whitelisted or not. Namecheap authenticates each API call against a small allow-list of client IPs. The proxy runs on a stable, pre-whitelisted IP; every consumer reaches Namecheap through it and inherits that whitelist — so a home dev box, an ephemeral droplet, a CI runner, or an agent container with an unknowable, rotating IP can create a subdomain without ever being whitelisted itself. This is the workstream's defining property (below).
The IP-whitelist problem — why a stable-IP egress is the crux
Namecheap's API does not authenticate solely by key; it also requires the caller's source IP to appear in a whitelist the account holder maintains (this is why the namecheap skill must pass a --client-ip and why "IP address is not whitelisted" is its most common error). That model is workable for one fixed office IP and hostile to everything the plan actually runs on:
- Ephemeral cloud hosts — a freshly-spawned droplet, a remote build worker, or an AiCliHostSupervisor agent container gets whatever IP it gets, and it is gone minutes later. It can never be whitelisted ahead of time.
- Dynamic residential IPs — a developer's connection changes without notice, silently breaking every DNS script the next morning.
- A swarm of agents — hundreds of short-lived containers cannot each register their IP with Namecheap and de-register on reap; the whitelist is not built for churn.
Today's NamecheapCli papers over this with a single hard-coded client IP baked into its default credentials, so it only actually works from one network. The proxy inverts the constraint: whitelist the proxy's one stable IP with Namecheap exactly once, and every consumer — regardless of its own IP — reaches Namecheap through that pinhole. It is the DNS-management analog of a bastion / allow-listed NAT-egress gateway: the caller's identity and authority are checked at the proxy, and the outbound leg to the third party always originates from the one address the third party trusts.
Why the ContainerNursery host is the right home
The ContainerNursery host already has a stable public IP (it is the fleet's deployment target and long-lived front door), so it is the natural place to whitelist with Namecheap and the natural place to run the egress. Co-locating DNS provisioning with the hosting host also closes a loop: a service deployed to ContainerNursery can, in the same session, be given a real public subdomain whose A/CNAME record points back at that very host — turning "deployed" into "publicly addressable by name" without leaving the fabric. See Relationship to other workstreams.
Current state — built vs. aspirational
Already built (the Embedded + CLI layers exist):
- NamecheapApi — a Kotlin client for the Namecheap registrar API.
- NamecheapCli — a CLI (published as
community.contrib.namecheap.cli:namecheap-cli) exposingdomains,dns <domain>,add-record, anddelete-record, with a--sandboxmode against Namecheap's sandbox environment.
Aspirational / not yet built (the substance of this workstream):
- No
url://namecheap/ServiceServer. The proxy layer — the whole point — does not exist yet. Today every consumer links NamecheapApi directly, holds the raw key, and must run from a whitelisted IP. - No stable-IP egress. Because there is no hosted service, there is no shared whitelisted address; the CLI's single hard-coded client IP is the closest thing, and it works from exactly one network.
- No credential containment. The key is an ambient secret copied into every caller instead of residing in one service.
- No multi-tenant / capability-gated access, no subdomain-scoped guardrails, and no adoption by the consumers that would benefit most (acme4j DNS-01 cert issuance, ContainerNursery auto-subdomain-on-deploy, VpnUrlRegistrarServer).
The API client and CLI exist. What is missing is the service in front of them — hosted on a whitelisted IP, holding the key, and reachable over
url://— not a new Namecheap client.
Shape
The service follows the standard layered architecture as a Namecheap-branded family — NamecheapApi (exists), NamecheapEmbedded (the domain logic over the API client), NamecheapProxyServerService (at url://namecheap/, deployed on the ContainerNursery host's whitelisted IP), NamecheapCli (exists, re-pointed at the proxy instead of calling Namecheap directly).
Decisions that define the shape:
- The
url://service on the ContainerNursery stable IP is the whitelisted egress. Consumers authenticate to the proxy; the proxy is the only thing Namecheap ever sees, from the one IP it trusts. This is the load-bearing decision — every other property follows from putting one stable IP in the path. - Credential-containment boundary. The Namecheap API key lives only inside NamecheapProxyServerService, per the proxy doctrine; consumers hold a capability to use it, never the key itself — exactly the containment GithubProxy gives a GitHub token.
- Per-record semantics over a whole-zone-replace backend. Namecheap's DNS write API replaces a domain's entire record set at once (
setHosts), not individual records — so two independent callers each doing read-modify-write on the full zone will clobber each other's edits. Centralizing behind one service is therefore not only credential containment; it is the natural place to serialize zone edits and expose safe, idempotent per-recordupsert/delete. A single serialized authority over each zone is a correctness requirement, not just a convenience. - Bring-your-own-key and a shared account. Per the proxy doctrine, the service both resells a shared operator Namecheap account (for domains the operator owns) and accepts consumer-supplied Namecheap credentials for domains they own — both reachable through the same whitelisted egress.
- Subdomain-scoped attenuation and guardrails (with W3Wallet, aspirational). A
usegrant is attenuable by domain and by host-prefix / subdomain namespace — the DNS analog of GithubProxy's per-repo attenuation — so a grant can be scoped to "may manage*.myservice.example.comonly." And, deny-by-default, ausegrant cannot delete or overwrite the zone apex or critical records (the@A/AAAA,NS,MX): a consumer scoped to one subdomain can never clobber the zone root or a sibling service, mirroring GithubProxy's forbidden-by-default classes. Until W3Wallet capability gating lands, tenancy is enforced at the service tier as with CockroachDb.
Plan / roadmap
These are proposed and need sharpening before work starts.
- [ ] Stand up NamecheapProxyServerService. A thin
url://namecheap/service over the existing NamecheapApi, deployed on the ContainerNursery host, with that host's IP whitelisted in Namecheap. Prove a non-whitelisted machine can create a record through it. - [ ] Serialize whole-zone edits. Make read-modify-write on
setHostssafe under concurrency, and expose idempotentupsertRecord/deleteRecordso callers manipulate one record without racing the rest of the zone. - [ ] Credential containment. Move the Namecheap key into the service only; stop shipping it (and the hard-coded client IP) inside consumers and the CLI's defaults.
- [ ] Re-point NamecheapCli at the proxy. The CLI talks to
url://namecheap/instead of calling Namecheap directly, so it works from any host and holds no key. - [ ] Subdomain convenience + guardrails. First-class "create a subdomain pointing at
<ip/host>" ergonomics, with apex/NS/MXclobber denied by default. - [ ] Tenancy: bring-your-own-key + shared account, enforced at the service tier.
- [ ] W3Wallet capability gating. Attenuate
usegrants by domain and host-prefix; deny-by-default the apex/critical-record classes; instant revoke. - [ ] Adoption. Wire up the consumers the egress unblocks: acme4j DNS-01 cert issuance (write/remove the
_acme-challengeTXT record from any host), ContainerNursery auto-subdomain-on-deploy, and VpnUrlRegistrarServer. - [ ] End-to-end tests per the testing standards: a real NamecheapProxyServerService in-process driving Namecheap's sandbox environment (the CLI already supports
--sandbox), asserting create/upsert/delete round-trips, concurrent-edit serialization does not lose records, and a subdomain-scoped grant is refused when it tries to touch the apex — never against a live production zone.
Relationship to other workstreams
- ContainerNursery — the home. Its host supplies the stable, whitelisted IP the egress depends on, it is the deployment target, and it is where provisioned subdomains point. Auto-provisioning a public subdomain when a service is deployed is the loop this closes: deploy → real
https://name, alongside the service'surl://name. - UrlResolver — the public-DNS complement to
url://naming. UrlResolver gives a service an internal, verifiedurl://identity; NamecheapProxy gives it an internet-facing DNS name for browsers and third parties. The two are the private and public halves of "a deployed service has a name," and UrlResolver's move toward DNS-like verified ownership is the conceptual sibling of managing real DNS here. - GithubProxy & CockroachDb — fellow third-party service proxies: same credential-containment role, same bring-your-own-key-and-shared doctrine, same trajectory toward W3Wallet-gated attenuation. NamecheapProxy adds the dimension they do not have — a stable-IP egress that launders an IP-whitelisted third party for consumers whose IPs are unstable.
- W3Wallet — the substrate for capability-gated, per-subdomain, revocable DNS access (aspirational), exactly as for GithubProxy: hold the Namecheap key as a capability secret, hand out attenuated
usegrants, never disclose the key. - acme4j DNS-01 cert issuance — the marquee downstream. Automated Let's Encrypt certificates via the DNS-01 challenge require writing (then deleting) a
_acme-challengeTXT record; routing that through the proxy means any host can complete the challenge without being whitelisted, so every provisioned subdomain can get TLS automatically. Combined with ContainerNursery hosting, this is "deploy → named → TLS-secured" with no manual DNS step. - VpnUrlRegistrarServer — an existing subdomain-registration service (filesystem-backed) that solves an adjacent slice of the same problem; a candidate to fold onto, or front with, the proxy so subdomain registration and real DNS provisioning share one authority.
Graduation
This workstream's plan graduates when url://namecheap/ is deployed on the ContainerNursery host's whitelisted IP, a non-whitelisted machine can create, update, and delete a subdomain through it, the Namecheap key no longer lives in any consumer, concurrent zone edits are serialized safely, and a first-class project page exists in the Documentation Repository (added to ALL_PROJECTS.md) — at which point this document has done its job. Capability-gated multi-tenancy tracks with the W3Wallet workstream and need not block graduation of the egress itself.