DNS Handling is a lightweight Go HTTP service designed for platforms that need to support custom domains. It automates the two most friction-heavy parts of that workflow: proving that a customer actually controls the domain they want to attach, and then obtaining a trusted TLS certificate for it — all without requiring any HTTP-based challenge infrastructure. Instead, the service relies entirely on DNS TXT records, making it compatible with any domain behind a CDN, load balancer, or proxy.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/plutoploy/dns-handling/llms.txt
Use this file to discover all available pages before exploring further.
What the service does
DNS Handling exposes a small REST API that orchestrates a well-defined pipeline for each domain:- Domain registration — a caller submits a domain name, and the service generates a unique verification token and instructs the caller to publish it as a DNS TXT record at
_acme-challenge.<domain>. - Ownership verification — the service performs a live DNS TXT lookup and confirms the expected token is present before advancing the domain.
- ACME certificate issuance — once ownership is confirmed, the service opens an ACME order with Let’s Encrypt using a DNS-01 challenge. It polls the
_acme-challengerecord in the background and completes the order automatically when the correct value is detected, then stores the issued certificate and private key. - Certificate retrieval — the caller can fetch certificate metadata (issued/expiry timestamps) for any domain that has reached the
activestate.
Domain lifecycle states
Every domain record moves through a strict state machine defined ininternal/domain/domain.go. The five states are:
| State | Meaning |
|---|---|
pending | Domain registered; verification token generated, DNS record not yet confirmed. |
verified | Ownership verified via TXT record; ready for certificate issuance. |
certificate_pending | ACME order started; service is polling DNS for the challenge TXT record. |
active | Certificate successfully issued and stored. |
failed | A verification or ACME step failed, or the background poll timed out (default: 5 minutes). |
issue-certificate on a domain that is not yet verified returns a 400 Bad Request.
Key technical choices
| Concern | Choice |
|---|---|
| Language & runtime | Go 1.26 |
| HTTP routing | chi v5 with Logger, Recoverer, RequestID, and RealIP middleware |
| Database | LibSQL / Turso (file-based SQLite by default via file:./tls.db) |
| ACME client | acmez v3 (DNS-01 only) |
| Structured logging | Zap (production JSON mode; development mode when LOG_LEVEL=debug) |
| Container build | Containerfile with a two-stage Alpine build; ships as tls-server |
The default
ACME_DIRECTORY is the Let’s Encrypt staging endpoint
(https://acme-staging-v02.api.letsencrypt.org/directory). Certificates issued
against staging are not trusted by browsers. Switch to
https://acme-v02.api.letsencrypt.org/directory only when you are ready for
production traffic.Next steps
Quickstart
Clone, configure, and run the service in under ten minutes.
Deployment
Build and run the service as a container with Docker or Podman.