Skip to content

feat(backends): namecheap CA + registrar - #1

Merged
albedosehen merged 1 commit into
mainfrom
feat/namecheap-backend
May 7, 2026
Merged

feat(backends): namecheap CA + registrar#1
albedosehen merged 1 commit into
mainfrom
feat/namecheap-backend

Conversation

@albedosehen

@albedosehen albedosehen commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary

First concrete backends behind the rota-core trait surface plus the config-to-trait-object dispatch the scheduler will drive next.

  • NamecheapCanamecheap.ssl.reissue + namecheap.ssl.getInfo polling. Surfaces TXT-style DCV (newer Sectigo flow) with CNAME-style fallback.
  • NamecheapRegistrarnamecheap.domains.dns.{getHosts,setHosts} with the get-merge-set pattern setHosts requires (it's a full-replacement API; skipping the read step would wipe every other record on the domain).
  • NamecheapClient — shared XML-over-HTTPS client with query-string auth. One instance per RotaConfig, reused across both surfaces since they share creds.
  • xml — tolerant walker for the <ApiResponse Status="…"> envelope plus first_text / first_attribute helpers. A typed-deserializer-per-command would be too brittle against Namecheap's inconsistent response shapes.

Config schema

Account creds are now top-level rather than duplicated per cert:

namecheap:
  api_key_file: /etc/rota/secrets/namecheap-api.key
  username: your-namecheap-username
  client_ip: 1.2.3.4

certs:
  - id: example-public
    domains: [example.com, www.example.com]
    ca:
      kind: namecheap
      ssl_id: 12345678
    registrar:
      kind: namecheap
    install:
      kind: dsm
      description: My Public Site

backends::build_from_config hands the daemon a Vec<CertBackends> where each entry holds Arc<dyn CABackend> + Arc<dyn RegistrarBackend>. Install backends stub to None until the next PR.

Toolchain

Pinned to 1.88.0[email protected] and the ICU stack (hyper/reqwest transitive) require it. Dropped two nightly-only rustfmt options that were emitting warnings on every fmt run.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo test --workspace — 8 passing (config round-trip, name splitting, OK/ERROR envelopes, host-record element extraction)
  • End-to-end against the example.com SSL ID once DsmInstall lands and the scheduler loop is wired (next two PRs)

What's not in this PR

  • DsmInstall + FilesystemInstall (next PR)
  • Daemon scheduler loop, UNIX socket, SQLite audit DB
  • Dashboard
  • ACME / Let's Encrypt CA backend

First concrete backends behind the rota-core trait surface, plus the
config-to-trait-object dispatch the scheduler will drive.

- NamecheapClient: shared XML-over-HTTPS client with query-string auth
  (ApiUser / ApiKey / UserName / ClientIp); single instance reused
  across the CA + registrar since both surfaces share the same creds.
- NamecheapCa: namecheap.ssl.reissue + namecheap.ssl.getInfo polling.
  Surfaces TXT-style DCV (newer Sectigo flow) with CNAME-style as
  a fallback.
- NamecheapRegistrar: namecheap.domains.dns.{getHosts,setHosts} with
  the get-merge-set pattern setHosts requires (full-replacement API).
- xml: tolerant walker for the OK/ERROR envelope plus first_text /
  first_attribute helpers — typed-deserializer-per-command would be
  too brittle against Namecheap's inconsistent response shapes.
- Config: dedupe Namecheap creds into a top-level `namecheap` block.
  CaSpec::Namecheap now carries only ssl_id; RegistrarSpec::Namecheap
  is a marker. Account block is optional so non-Namecheap configs
  parse cleanly.
- backends::build_from_config: hands the daemon a Vec<CertBackends>
  with Arc<dyn CABackend> + Arc<dyn RegistrarBackend> per cert.
  Install backends stub to None until the next PR (DSM + filesystem).
- Toolchain: bump pin to 1.88.0 ([email protected] + icu deps require it);
  drop nightly-only rustfmt options that were emitting warnings on
  every fmt run.

Tests: rota.example.yaml round-trips, name splitting handles apex /
nested subdomains, OK/ERROR envelopes parse, host-record element
extraction.
@albedosehen
albedosehen merged commit 663743c into main May 7, 2026
1 check passed
@albedosehen
albedosehen deleted the feat/namecheap-backend branch May 7, 2026 19:09
albedosehen added a commit that referenced this pull request May 7, 2026
First concrete backends behind the rota-core trait surface, plus the
config-to-trait-object dispatch the scheduler will drive.

- NamecheapClient: shared XML-over-HTTPS client with query-string auth
  (ApiUser / ApiKey / UserName / ClientIp); single instance reused
  across the CA + registrar since both surfaces share the same creds.
- NamecheapCa: namecheap.ssl.reissue + namecheap.ssl.getInfo polling.
  Surfaces TXT-style DCV (newer Sectigo flow) with CNAME-style as
  a fallback.
- NamecheapRegistrar: namecheap.domains.dns.{getHosts,setHosts} with
  the get-merge-set pattern setHosts requires (full-replacement API).
- xml: tolerant walker for the OK/ERROR envelope plus first_text /
  first_attribute helpers — typed-deserializer-per-command would be
  too brittle against Namecheap's inconsistent response shapes.
- Config: dedupe Namecheap creds into a top-level `namecheap` block.
  CaSpec::Namecheap now carries only ssl_id; RegistrarSpec::Namecheap
  is a marker. Account block is optional so non-Namecheap configs
  parse cleanly.
- backends::build_from_config: hands the daemon a Vec<CertBackends>
  with Arc<dyn CABackend> + Arc<dyn RegistrarBackend> per cert.
  Install backends stub to None until the next PR (DSM + filesystem).
- Toolchain: bump pin to 1.88.0 ([email protected] + icu deps require it);
  drop nightly-only rustfmt options that were emitting warnings on
  every fmt run.

Tests: rota.example.yaml round-trips, name splitting handles apex /
nested subdomains, OK/ERROR envelopes parse, host-record element
extraction.
@albedosehen albedosehen mentioned this pull request May 8, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant