Skip to main content

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.

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.

What the service does

DNS Handling exposes a small REST API that orchestrates a well-defined pipeline for each domain:
  1. 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>.
  2. Ownership verification — the service performs a live DNS TXT lookup and confirms the expected token is present before advancing the domain.
  3. 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-challenge record in the background and completes the order automatically when the correct value is detected, then stores the issued certificate and private key.
  4. Certificate retrieval — the caller can fetch certificate metadata (issued/expiry timestamps) for any domain that has reached the active state.

Domain lifecycle states

Every domain record moves through a strict state machine defined in internal/domain/domain.go. The five states are:
StateMeaning
pendingDomain registered; verification token generated, DNS record not yet confirmed.
verifiedOwnership verified via TXT record; ready for certificate issuance.
certificate_pendingACME order started; service is polling DNS for the challenge TXT record.
activeCertificate successfully issued and stored.
failedA verification or ACME step failed, or the background poll timed out (default: 5 minutes).
State transitions are enforced server-side — for example, calling issue-certificate on a domain that is not yet verified returns a 400 Bad Request.

Key technical choices

ConcernChoice
Language & runtimeGo 1.26
HTTP routingchi v5 with Logger, Recoverer, RequestID, and RealIP middleware
DatabaseLibSQL / Turso (file-based SQLite by default via file:./tls.db)
ACME clientacmez v3 (DNS-01 only)
Structured loggingZap (production JSON mode; development mode when LOG_LEVEL=debug)
Container buildContainerfile 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.

Build docs developers (and LLMs) love