diff --git a/.dockerignore b/.dockerignore
index ae055285..e27fb10a 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -5,3 +5,4 @@ lego.exe
.vscode/
dist/
builds/
+docs/
diff --git a/.travis.yml b/.travis.yml
index 69e3fbd6..5cc08977 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,14 @@
language: go
go:
- - 1.10.x
+ - 1.12.x
- 1.x
services:
- memcached
+go_import_path: github.com/xenolf/lego
+
addons:
hosts:
# for e2e tests
@@ -33,6 +35,10 @@ before_install:
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
- golangci-lint --version
+ # Hugo - documentation
+ - wget -O /tmp/hugo.deb https://github.com/gohugoio/hugo/releases/download/v0.54.0/hugo_0.54.0_Linux-64bit.deb
+ - sudo dpkg -i /tmp/hugo.deb
+
install:
- echo "TRAVIS_GO_VERSION=$TRAVIS_GO_VERSION"
- dep status -v
@@ -40,6 +46,13 @@ install:
after_success:
- make clean
+before_deploy:
+ - >
+ if ! [ "$BEFORE_DEPLOY_RUN" ]; then
+ export BEFORE_DEPLOY_RUN=1;
+ make docs-build
+ fi
+
deploy:
- provider: script
skip_cleanup: true
@@ -57,3 +70,11 @@ deploy:
on:
tags: true
condition: $TRAVIS_GO_VERSION =~ ^1\.x$
+
+ - provider: pages
+ local_dir: docs/public
+ skip_cleanup: true
+ github_token: ${GITHUB_TOKEN}
+ on:
+ tags: true
+ condition: $TRAVIS_GO_VERSION =~ ^1\.x$
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8a4afbd5..0f2ef95f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -73,62 +73,3 @@ git push -u origin my-feature
## create a pull request on Github ##
```
-
-
-## DNS Providers: API references
-
-| DNS provider | Code | Documentation | Go client |
-|---------------------------|----------------|--------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
-| Acme DNS | `acmedns` | [documentation](https://github.com/joohoi/acme-dns#api) | [Go client](https://github.com/cpu/goacmedns) |
-| Alibaba Cloud | `alidns` | [documentation](https://www.alibabacloud.com/help/doc-detail/42875.htm) | [Go client](https://github.com/aliyun/alibaba-cloud-sdk-go) |
-| Aurora DNS | `auroradns` | [documentation](https://libcloud.readthedocs.io/en/latest/dns/drivers/auroradns.html#api-docs) | [Go client](https://github.com/nrdcg/auroradns) |
-| Azure | `azure` | [documentation](https://docs.microsoft.com/en-us/go/azure/) | [Go client](https://github.com/Azure/azure-sdk-for-go) |
-| Bluecat | `bluecat` | ? | - |
-| Cloudflare | `cloudflare` | [documentation](https://api.cloudflare.com/) | [Go client](https://github.com/cloudflare/cloudflare-go) |
-| ClouDNS | `cloudns` | [documentation](https://www.cloudns.net/wiki/article/42/) | - |
-| CloudXNS | `cloudxns` | [documentation](https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip) | - |
-| ConoHa | `conoha` | [documentation](https://www.conoha.jp/docs/) | - |
-| Openstack Designate | `designate` | [documentation](https://docs.openstack.org/designate/latest/) | [Go client](https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2) |
-| Digital Ocean | `digitalocean` | [documentation](https://developers.digitalocean.com/documentation/v2/#domain-records) | - |
-| DNSimple | `dnsimple` | [documentation](https://developer.dnsimple.com/v2/) | [Go client](https://github.com/dnsimple/dnsimple-go) |
-| DNS Made Easy | `dnsmadeeasy` | [documentation](https://api-docs.dnsmadeeasy.com/) | - |
-| DNSPod | `dnspod` | [documentation](https://www.dnspod.cn/docs/index.html) | [Go client](https://github.com/decker502/dnspod-go) |
-| DreamHost | `dreamhost` | [documentation](https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview) | - |
-| Duck DNS | `duckdns` | [documentation](https://www.duckdns.org/spec.jsp) | - |
-| Dyn | `dyn` | [documentation](https://help.dyn.com/rest/) | - |
-| exec | `exec` | - | - |
-| Exoscale | `exoscale` | [documentation](https://community.exoscale.com/documentation/dns/api/) | [Go client](https://github.com/exoscale/egoscale) |
-| FastDNS | `fastdns` | [documentation](https://developer.akamai.com/api/web_performance/fast_dns_record_management/v1.html) | [Go client](https://github.com/akamai/AkamaiOPEN-edgegrid-golang) |
-| Gandi | `gandi` | [documentation](http://doc.rpc.gandi.net/index.html) | - |
-| Gandi v5 | `gandiv5` | [documentation](http://doc.livedns.gandi.net) | - |
-| Google Cloud | `gcloud` | ? | [Go client](https://github.com/googleapis/google-api-go-client) |
-| Glesys | `glesys` | [documentation](https://github.com/GleSYS/API/wiki/API-Documentation) | - |
-| Go Daddy | `godaddy` | [documentation](https://developer.godaddy.com/doc/endpoint/domains) | - |
-| hosting.de | `hostingde` | [documentation](https://www.hosting.de/api/#dns) | - |
-| Internet Initiative Japan | `iij` | [documentation](http://manual.iij.jp/p2/pubapi/) | [Go client](https://github.com/iij/doapi) |
-| INWX | `inwx` | [documentation](https://www.inwx.de/en/help/apidoc) | [Go client](https://github.com/nrdcg/goinwx) |
-| Lightsail | `lightsail` | ? | [Go client](https://github.com/aws/aws-sdk-go/aws) |
-| Linode (deprecated) | `linode` | [documentation](https://www.linode.com/api/dns) | [Go client](https://github.com/timewasted/linode) |
-| Linodev4 | `linodev4` | [documentation](https://developers.linode.com/api/v4) | [Go client](https://github.com/linode/linodego) |
-| Namecheap | `namecheap` | [documentation](https://www.namecheap.com/support/api/methods.aspx) | - |
-| Name.com | `namedotcom` | [documentation](https://www.name.com/api-docs/DNS) | [Go client](https://github.com/namedotcom/go) |
-| manual | `manual` | - | - |
-| MyDNS.jp | `mydnsjp` | [documentation](https://www.mydns.jp/?MENU=030) | - |
-| Netcup | `netcup` | [documentation](https://www.netcup-wiki.de/wiki/DNS_API) | - |
-| NIFCloud | `nifcloud` | [documentation](https://mbaas.nifcloud.com/doc/current/rest/common/format.html) | - |
-| NS1 | `ns1` | [documentation](https://ns1.com/api) | [Go client](https://github.com/ns1/ns1-go) |
-| Open Telekom Cloud | `otc` | [documentation](https://docs.otc.t-systems.com/en-us/dns/index.html) | - |
-| Oracle Cloud | `oraclecloud` | [documentation](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm) | [Go Client](https://github.com/oracle/oci-go-sdk) |
-| OVH | `ovh` | [documentation](https://eu.api.ovh.com/) | [Go client](https://github.com/ovh/go-ovh) |
-| PowerDNS | `pdns` | [documentation](https://doc.powerdns.com/md/httpapi/README/) | - |
-| Rackspace | `rackspace` | [documentation](https://developer.rackspace.com/docs/cloud-dns/v1/) | - |
-| RFC2136 | `rfc2136` | [documentation](https://tools.ietf.org/html/rfc2136) | - |
-| Route 53 | `route53` | [documentation](https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html) | [Go client](https://github.com/aws/aws-sdk-go/aws) |
-| Sakura Cloud | `sakuracloud` | [documentation](https://developer.sakura.ad.jp/cloud/api/1.1/) | [Go client](https://github.com/sacloud/libsacloud) |
-| Selectel | `selectel` | [documentation](https://kb.selectel.com/23136054.html) | - |
-| Stackpath | `stackpath` | [documentation](https://developer.stackpath.com/en/api/dns/#tag/Zone) | - |
-| TransIP | `transip` | [documentation](https://api.transip.nl/docs/transip.nl/package-Transip.html) | [Go client](https://github.com/transip/gotransip) |
-| VegaDNS | `vegadns` | [documentation](https://github.com/shupp/VegaDNS-API) | [Go client](https://github.com/OpenDNS/vegadns2client) |
-| Vultr | `vultr` | [documentation](https://www.vultr.com/api/#dns) | [Go client](https://github.com/JamesClonk/vultr) |
-| Vscale | `vscale` | [documentation](https://developers.vscale.io/documentation/api/v1/#api-Domains_Records) | - |
-| Zone.ee | `zone` | [documentation](https://api.zone.eu/v2) | - |
diff --git a/Dockerfile b/Dockerfile
index cb942944..ed36091f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:alpine3.8 as builder
+FROM golang:alpine3.9 as builder
RUN apk --update upgrade \
&& apk --no-cache --no-progress add make git \
@@ -8,7 +8,7 @@ WORKDIR /go/src/github.com/xenolf/lego
COPY . .
RUN make build
-FROM alpine:3.8
+FROM alpine:3.9
RUN apk update && apk add --no-cache --virtual ca-certificates
COPY --from=builder /go/src/github.com/xenolf/lego/dist/lego /usr/bin/lego
ENTRYPOINT [ "/usr/bin/lego" ]
diff --git a/Gopkg.lock b/Gopkg.lock
index ba372228..3e3a84d7 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -38,6 +38,14 @@
revision = "39013ecb48eaf6ced3f4e3e1d95515140ce6b3cf"
version = "v10.15.2"
+[[projects]]
+ digest = "1:5d72bbcc9c8667b11c3dc3cbe681c5a6f71e5096744c0bf7726ab5c6425d5dc4"
+ name = "github.com/BurntSushi/toml"
+ packages = ["."]
+ pruneopts = "NUT"
+ revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005"
+ version = "v0.3.1"
+
[[projects]]
digest = "1:ed3fc9992df610d07c85c24e0b792268cc1ce226dd9bf8cb2e6ad9a377b35415"
name = "github.com/JamesClonk/vultr"
@@ -671,6 +679,7 @@
"github.com/Azure/go-autorest/autorest/azure",
"github.com/Azure/go-autorest/autorest/azure/auth",
"github.com/Azure/go-autorest/autorest/to",
+ "github.com/BurntSushi/toml",
"github.com/JamesClonk/vultr/lib",
"github.com/OpenDNS/vegadns2client",
"github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1",
diff --git a/Makefile b/Makefile
index b6ba5c36..88e25a1b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-.PHONY: clean checks test build image dependencies
+.PHONY: clean checks test build image dependencies e2e fmt
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
@@ -10,7 +10,7 @@ TAG_NAME := $(shell git tag -l --contains HEAD)
SHA := $(shell git rev-parse HEAD)
VERSION := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
-default: clean checks test build
+default: clean generate-dns checks test build
clean:
rm -rf dist/ builds/ cover.out
@@ -39,6 +39,7 @@ fmt:
gofmt -s -l -w $(SRCS)
# Release helper
+.PHONY: patch minor major detach
patch:
go run internal/release.go release -m patch
@@ -51,3 +52,21 @@ major:
detach:
go run internal/release.go detach
+
+# Docs
+.PHONY: docs-build docs-serve docs-themes
+
+docs-build: generate-dns
+ @make -C ./docs hugo-build
+
+docs-serve: generate-dns
+ @make -C ./docs hugo
+
+docs-themes:
+ @make -C ./docs hugo-themes
+
+# Generate DNS
+.PHONY: generate-dns
+
+generate-dns:
+ go generate ./...
diff --git a/README.md b/README.md
index fe7a7646..9a2551d7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# lego
-Let's Encrypt client and ACME library written in Go
+Let's Encrypt client and ACME library written in Go.
[![GoDoc](https://godoc.org/github.com/xenolf/lego?status.svg)](https://godoc.org/github.com/xenolf/lego/acme)
[![Build Status](https://travis-ci.org/xenolf/lego.svg?branch=master)](https://travis-ci.org/xenolf/lego)
@@ -8,37 +8,6 @@ Let's Encrypt client and ACME library written in Go
[![Dev Chat](https://img.shields.io/badge/dev%20chat-gitter-blue.svg?label=dev+chat)](https://gitter.im/xenolf/lego)
[![Beerpay](https://beerpay.io/xenolf/lego/badge.svg)](https://beerpay.io/xenolf/lego)
-## Installation
-
-### Binaries
-
-To get the binary just download the latest release for your OS/Arch from [the release page](https://github.com/xenolf/lego/releases) and put the binary somewhere convenient.
-lego does not assume anything about the location you run it from.
-
-### From Docker
-
-```bash
-docker run xenolf/lego -h
-```
-
-### From package managers
-
-- [ArchLinux (AUR)](https://aur.archlinux.org/packages/lego):
-
-```bash
-yay -S lego
-```
-
-**Note**: only the package manager for Arch Linux is officially supported by the lego team.
-
-### From sources
-
-To install from sources, just run:
-
-```bash
-go get -u github.com/xenolf/lego/cmd/lego
-```
-
## Features
- Register with CA
@@ -55,266 +24,38 @@ go get -u github.com/xenolf/lego/cmd/lego
- Certificate bundling
- OCSP helper function
-Please keep in mind that CLI switches and APIs are still subject to change.
+lego introduced support for ACME v2 in [v1.0.0](https://github.com/xenolf/lego/releases/tag/v1.0.0). If you still need to utilize ACME v1, you can do so by using the [v0.5.0](https://github.com/xenolf/lego/releases/tag/v0.5.0) version.
-When using the standard `--path` option, all certificates and account configurations are saved to a folder `.lego` in the current working directory.
+## Installation
+
+How to [install](https://xenolf.github.io/lego/installation/).
## Usage
-```text
-NAME:
- lego - Let's Encrypt client written in Go
+- as a [CLI](https://xenolf.github.io/lego/usage/cli)
+- as a [library](https://xenolf.github.io/lego/usage/lib)
-USAGE:
- lego [global options] command [command options] [arguments...]
+## Documentation
-COMMANDS:
- run Register an account, then create and install a certificate
- revoke Revoke a certificate
- renew Renew a certificate
- dnshelp Shows additional help for the --dns global option
- list Display certificates and accounts information.
- help, h Shows a list of commands or help for one command
+Documentation is hosted live at https://xenolf.github.io/lego/.
-GLOBAL OPTIONS:
- --domains value, -d value Add a domain to the process. Can be specified multiple times.
- --server value, -s value CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. (default: "https://acme-v02.api.letsencrypt.org/directory")
- --accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
- --email value, -m value Email used for registration and recovery contact.
- --csr value, -c value Certificate signing request filename, if an external CSR is to be used.
- --eab Use External Account Binding for account registration. Requires --kid and --hmac.
- --kid value Key identifier from External CA. Used for External Account Binding.
- --hmac value MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.
- --key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384. (default: "rsa2048")
- --filename value (deprecated) Filename of the generated certificate.
- --path value Directory to use for storing the data. (default: "./.lego")
- --http Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.
- --http.port value Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port. (default: ":80")
- --http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge.
- --http.memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.
- --tls Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.
- --tls.port value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port. (default: ":443")
- --dns value Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
- --dns.disable-cp By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.
- --dns.resolvers value Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
- --http-timeout value Set the HTTP timeout value to a specific value in seconds. (default: 0)
- --dns-timeout value Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. (default: 10)
- --pem Generate a .pem file by concatenating the .key and .crt files together.
- --cert.timeout value Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
- --help, -h show help
- --version, -v print the version
-```
+## DNS providers
-### Sudo
+Detailed documentation is available [here](https://xenolf.github.io/lego/dns).
-The CLI does not require root permissions but needs to bind to port 80 and 443 for certain challenges.
-To run the CLI without sudo, you have four options:
-
-- Use setcap 'cap_net_bind_service=+ep' /path/to/program
-- Pass the `--http.port` or/and the `--tls.port` option and specify a custom port to bind to. In this case you have to forward port 80/443 to these custom ports (see [Port Usage](#port-usage)).
-- Pass the `--http.webroot` option and specify the path to your webroot folder. In this case the challenge will be written in a file in `.well-known/acme-challenge/` inside your webroot.
-- Pass the `--dns` option and specify a DNS provider.
-
-### Port Usage
-
-By default lego assumes it is able to bind to ports 80 and 443 to solve challenges.
-If this is not possible in your environment, you can use the `--http.port` and `--tls.port` options to instruct
-lego to listen on that interface:port for any incoming challenges.
-
-If you are using this option, make sure you proxy all of the following traffic to these ports.
-
-HTTP Port:
-
-- All plaintext HTTP requests to port 80 which begin with a request path of `/.well-known/acme-challenge/` for the HTTP challenge.
-
-TLS Port:
-
-- All TLS handshakes on port 443 for the TLS-ALPN challenge.
-
-This traffic redirection is only needed as long as lego solves challenges. As soon as you have received your certificates you can deactivate the forwarding.
-
-### CLI Example
-
-Assumes the `lego` binary has permission to bind to ports 80 and 443.
-You can get a pre-built binary from the [releases](https://github.com/xenolf/lego/releases) page.
-If your environment does not allow you to bind to these ports, please read [Port Usage](#port-usage).
-
-Obtain a certificate:
-
-```bash
-lego --email="foo@bar.com" --domains="example.com" --http run
-```
-
-(Find your certificate in the `.lego` folder of current working directory.)
-
-To renew the certificate:
-
-```bash
-lego --email="foo@bar.com" --domains="example.com" --http renew
-```
-
-To renew the certificate only if it expires within 30 days
-
-```bash
-lego --email="foo@bar.com" --domains="example.com" --http renew --days 30
-```
-
-Obtain a certificate using the DNS challenge and AWS Route 53:
-
-```bash
-AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=my_id AWS_SECRET_ACCESS_KEY=my_key lego --email="foo@bar.com" --domains="example.com" --dns="route53" run
-```
-
-Obtain a certificate given a certificate signing request (CSR) generated by something else:
-
-```bash
-lego --email="foo@bar.com" --http --csr=/path/to/csr.pem run
-```
-
-(lego will infer the domains to be validated based on the contents of the CSR, so make sure the CSR's Common Name and optional SubjectAltNames are set correctly.)
-
-lego defaults to communicating with the production Let's Encrypt ACME server.
-If you'd like to test something without issuing real certificates, consider using the staging endpoint instead:
-
-```bash
-lego --server=https://acme-staging-v02.api.letsencrypt.org/directory …
-```
-
-## ACME Library Usage
-
-A valid, but bare-bones example use of the acme package:
-
-```go
-package main
-
-import (
- "crypto"
- "crypto/ecdsa"
- "crypto/elliptic"
- "crypto/rand"
- "fmt"
- "log"
-
- "github.com/xenolf/lego/certcrypto"
- "github.com/xenolf/lego/certificate"
- "github.com/xenolf/lego/challenge/http01"
- "github.com/xenolf/lego/challenge/tlsalpn01"
- "github.com/xenolf/lego/lego"
- "github.com/xenolf/lego/registration"
-)
-
-// You'll need a user or account type that implements acme.User
-type MyUser struct {
- Email string
- Registration *registration.Resource
- key crypto.PrivateKey
-}
-
-func (u *MyUser) GetEmail() string {
- return u.Email
-}
-func (u MyUser) GetRegistration() *registration.Resource {
- return u.Registration
-}
-func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
- return u.key
-}
-
-func main() {
-
- // Create a user. New accounts need an email and private key to start.
- privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
- if err != nil {
- log.Fatal(err)
- }
-
- myUser := MyUser{
- Email: "you@yours.com",
- key: privateKey,
- }
-
- config := lego.NewConfig(&myUser)
-
- // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
- config.CADirURL = "http://192.168.99.100:4000/directory"
- config.Certificate.KeyType = certcrypto.RSA2048
-
- // A client facilitates communication with the CA server.
- client, err := lego.NewClient(config)
- if err != nil {
- log.Fatal(err)
- }
-
- // We specify an http port of 5002 and an tls port of 5001 on all interfaces
- // because we aren't running as root and can't bind a listener to port 80 and 443
- // (used later when we attempt to pass challenges). Keep in mind that you still
- // need to proxy challenge traffic to port 5002 and 5001.
- err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
- if err != nil {
- log.Fatal(err)
- }
- err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
- if err != nil {
- log.Fatal(err)
- }
-
- // New users will need to register
- reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
- if err != nil {
- log.Fatal(err)
- }
- myUser.Registration = reg
-
- request := certificate.ObtainRequest{
- Domains: []string{"mydomain.com"},
- Bundle: true,
- }
- certificates, err := client.Certificate.Obtain(request)
- if err != nil {
- log.Fatal(err)
- }
-
- // Each certificate comes back with the cert bytes, the bytes of the client's
- // private key, and a certificate URL. SAVE THESE TO DISK.
- fmt.Printf("%#v\n", certificates)
-
- // ... all done.
-}
-```
-
-## DNS Challenge API Details
-
-### AWS Route 53
-
-The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge.
-
-```json
-{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Sid": "",
- "Effect": "Allow",
- "Action": [
- "route53:GetChange",
- "route53:ChangeResourceRecordSets",
- "route53:ListResourceRecordSets"
- ],
- "Resource": [
- "arn:aws:route53:::hostedzone/*",
- "arn:aws:route53:::change/*"
- ]
- },
- {
- "Sid": "",
- "Effect": "Allow",
- "Action": "route53:ListHostedZonesByName",
- "Resource": "*"
- }
- ]
-}
-```
-
-## ACME v1
-
-lego introduced support for ACME v2 in [v1.0.0](https://github.com/xenolf/lego/releases/tag/v1.0.0), if you still need to utilize ACME v1, you can do so by using the [v0.5.0](https://github.com/xenolf/lego/releases/tag/v0.5.0) version.
+| | | | |
+|----------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------------------|------------------------------------------------------------------|
+| [Alibaba Cloud DNS](https://xenolf.github.io/lego/dns/alidns/) | [Amazon Lightsail](https://xenolf.github.io/lego/dns/lightsail/) | [Amazon Route 53](https://xenolf.github.io/lego/dns/route53/) | [Aurora DNS](https://xenolf.github.io/lego/dns/auroradns/) |
+| [Azure](https://xenolf.github.io/lego/dns/azure/) | [Bluecat](https://xenolf.github.io/lego/dns/bluecat/) | [ClouDNS](https://xenolf.github.io/lego/dns/cloudns/) | [CloudXNS](https://xenolf.github.io/lego/dns/cloudxns/) |
+| [Cloudflare](https://xenolf.github.io/lego/dns/cloudflare/) | [ConoHa](https://xenolf.github.io/lego/dns/conoha/) | [DNS Made Easy](https://xenolf.github.io/lego/dns/dnsmadeeasy/) | [DNSPod](https://xenolf.github.io/lego/dns/dnspod/) |
+| [DNSimple](https://xenolf.github.io/lego/dns/dnsimple/) | [Designate DNSaaS for Openstack](https://xenolf.github.io/lego/dns/designate/) | [Digital Ocean](https://xenolf.github.io/lego/dns/digitalocean/) | [DreamHost](https://xenolf.github.io/lego/dns/dreamhost/) |
+| [Duck DNS](https://xenolf.github.io/lego/dns/duckdns/) | [Dyn](https://xenolf.github.io/lego/dns/dyn/) | [Exoscale](https://xenolf.github.io/lego/dns/exoscale/) | [External program](https://xenolf.github.io/lego/dns/exec/) |
+| [FastDNS](https://xenolf.github.io/lego/dns/fastdns/) | [Gandi](https://xenolf.github.io/lego/dns/gandi/) | [Gandi Live DNS (v5)](https://xenolf.github.io/lego/dns/gandiv5/) | [Glesys](https://xenolf.github.io/lego/dns/glesys/) |
+| [Go Daddy](https://xenolf.github.io/lego/dns/godaddy/) | [Google Cloud](https://xenolf.github.io/lego/dns/gcloud/) | [HTTP request](https://xenolf.github.io/lego/dns/httpreq/) | [Hosting.de](https://xenolf.github.io/lego/dns/hostingde/) |
+| [INWX](https://xenolf.github.io/lego/dns/inwx/) | [Internet Initiative Japan](https://xenolf.github.io/lego/dns/iij/) | [Joohoi's ACME-DNS](https://xenolf.github.io/lego/dns/acme-dns) | [Linode (deprecated)](https://xenolf.github.io/lego/dns/linode/) |
+| [Linode (v4)](https://xenolf.github.io/lego/dns/linodev4/) | [Manual](https://xenolf.github.io/lego/dns/manual/) | [MyDNS.jp](https://xenolf.github.io/lego/dns/mydnsjp/) | [NIFCloud](https://xenolf.github.io/lego/dns/nifcloud/) |
+| [NS1](https://xenolf.github.io/lego/dns/ns1/) | [Name.com](https://xenolf.github.io/lego/dns/namedotcom/) | [Namecheap](https://xenolf.github.io/lego/dns/namecheap/) | [Netcup](https://xenolf.github.io/lego/dns/netcup/) |
+| [OVH](https://xenolf.github.io/lego/dns/ovh/) | [Open Telekom Cloud](https://xenolf.github.io/lego/dns/otc/) | [Oracle Cloud](https://xenolf.github.io/lego/dns/oraclecloud/) | [PowerDNS](https://xenolf.github.io/lego/dns/pdns/) |
+| [RFC2136](https://xenolf.github.io/lego/dns/rfc2136/) | [Rackspace](https://xenolf.github.io/lego/dns/rackspace/) | [Sakura Cloud](https://xenolf.github.io/lego/dns/sakuracloud/) | [Selectel](https://xenolf.github.io/lego/dns/selectel/) |
+| [Stackpath](https://xenolf.github.io/lego/dns/stackpath/) | [TransIP](https://xenolf.github.io/lego/dns/transip/) | [VegaDNS](https://xenolf.github.io/lego/dns/vegadns/) | [Vscale](https://xenolf.github.io/lego/dns/vscale/) |
+| [Vultr](https://xenolf.github.io/lego/dns/vultr/) | [Zone.ee](https://xenolf.github.io/lego/dns/zoneee/) | | |
diff --git a/cmd/cmd_dnshelp.go b/cmd/cmd_dnshelp.go
index 99873435..8852833f 100644
--- a/cmd/cmd_dnshelp.go
+++ b/cmd/cmd_dnshelp.go
@@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"os"
+ "strings"
"text/tabwriter"
"github.com/urfave/cli"
@@ -11,141 +12,37 @@ import (
func createDNSHelp() cli.Command {
return cli.Command{
Name: "dnshelp",
- Usage: "Shows additional help for the --dns global option",
+ Usage: "Shows additional help for the '--dns' global option",
Action: dnsHelp,
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "code, c",
+ Usage: fmt.Sprintf("DNS code: %s", allDNSCodes()),
+ },
+ },
}
}
-func dnsHelp(_ *cli.Context) error {
- fmt.Printf(
- `Credentials for DNS providers must be passed through environment variables.
+func dnsHelp(ctx *cli.Context) error {
+ code := ctx.String("code")
+ if code == "" {
+ w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
-Here is an example bash command using the CloudFlare DNS provider:
+ fmt.Fprintln(w, `Credentials for DNS providers must be passed through environment variables.`)
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `To display the documentation for a DNS providers:`)
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, "\t$ lego dnshelp -c code")
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, "All DNS codes:")
+ fmt.Fprintf(w, "\t%s\n", allDNSCodes())
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, "More information: https://xenolf.github.io/lego/dns")
- $ CLOUDFLARE_EMAIL=foo@bar.com \
- CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
- lego --dns cloudflare --domains www.example.com --email me@bar.com run
+ return w.Flush()
+ }
-`)
-
- w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
- fmt.Fprintln(w, "Valid providers and their associated credential environment variables:")
- fmt.Fprintln(w)
- fmt.Fprintln(w, "\tacme-dns:\tACME_DNS_API_BASE, ACME_DNS_STORAGE_PATH")
- fmt.Fprintln(w, "\talidns:\tALICLOUD_ACCESS_KEY, ALICLOUD_SECRET_KEY")
- fmt.Fprintln(w, "\tauroradns:\tAURORA_USER_ID, AURORA_KEY, AURORA_ENDPOINT")
- fmt.Fprintln(w, "\tazure:\tAZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESOURCE_GROUP")
- fmt.Fprintln(w, "\tbluecat:\tBLUECAT_SERVER_URL, BLUECAT_USER_NAME, BLUECAT_PASSWORD, BLUECAT_CONFIG_NAME, BLUECAT_DNS_VIEW")
- fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY")
- fmt.Fprintln(w, "\tcloudns:\tCLOUDNS_AUTH_ID, CLOUDNS_AUTH_PASSWORD")
- fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_API_KEY, CLOUDXNS_SECRET_KEY")
- fmt.Fprintln(w, "\tconoha:\tCONOHA_TENANT_ID, CONOHA_API_USERNAME, CONOHA_API_PASSWORD")
- fmt.Fprintln(w, "\tdesignate:\tOS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_REGION_NAME")
- fmt.Fprintln(w, "\tdigitalocean:\tDO_AUTH_TOKEN")
- fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_EMAIL, DNSIMPLE_OAUTH_TOKEN")
- fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET")
- fmt.Fprintln(w, "\tdnspod:\tDNSPOD_API_KEY")
- fmt.Fprintln(w, "\tdreamhost:\tDREAMHOST_API_KEY")
- fmt.Fprintln(w, "\tduckdns:\tDUCKDNS_TOKEN")
- fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD")
- fmt.Fprintln(w, "\texec:\tEXEC_PATH, EXEC_MODE")
- fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT")
- fmt.Fprintln(w, "\tfastdns:\tAKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET, AKAMAI_ACCESS_TOKEN")
- fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY")
- fmt.Fprintln(w, "\tgandiv5:\tGANDIV5_API_KEY")
- fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT, 'Application Default Credentials', [GCE_SERVICE_ACCOUNT_FILE], [GCE_SERVICE_ACCOUNT]")
- fmt.Fprintln(w, "\tglesys:\tGLESYS_API_USER, GLESYS_API_KEY")
- fmt.Fprintln(w, "\tgodaddy:\tGODADDY_API_KEY, GODADDY_API_SECRET")
- fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_API_KEY, HOSTINGDE_ZONE_NAME")
- fmt.Fprintln(w, "\thttpreq:\tHTTPREQ_ENDPOINT, HTTPREQ_MODE, HTTPREQ_USERNAME, HTTPREQ_PASSWORD")
- fmt.Fprintln(w, "\tiij:\tIIJ_API_ACCESS_KEY, IIJ_API_SECRET_KEY, IIJ_DO_SERVICE_CODE")
- fmt.Fprintln(w, "\tinwx:\tINWX_USERNAME, INWX_PASSWORD")
- fmt.Fprintln(w, "\tlightsail:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DNS_ZONE")
- fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY")
- fmt.Fprintln(w, "\tlinodev4:\tLINODE_TOKEN")
- fmt.Fprintln(w, "\tmanual:\tnone")
- fmt.Fprintln(w, "\tmydnsjp:\tMYDNSJP_MASTER_ID, MYDNSJP_PASSWORD")
- fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_API_USER, NAMECHEAP_API_KEY")
- fmt.Fprintln(w, "\tnamedotcom:\tNAMECOM_USERNAME, NAMECOM_API_TOKEN")
- fmt.Fprintln(w, "\tnetcup:\tNETCUP_CUSTOMER_NUMBER, NETCUP_API_KEY, NETCUP_API_PASSWORD")
- fmt.Fprintln(w, "\tnifcloud:\tNIFCLOUD_ACCESS_KEY_ID, NIFCLOUD_SECRET_ACCESS_KEY")
- fmt.Fprintln(w, "\tns1:\tNS1_API_KEY")
- fmt.Fprintln(w, "\toraclecloud:\tOCI_PRIVKEY_FILE, OCI_PRIVKEY_PASS, OCI_TENANCY_OCID, OCI_USER_OCID, OCI_PUBKEY_FINGERPRINT, OCI_REGION, OCI_COMPARTMENT_OCID")
- fmt.Fprintln(w, "\totc:\tOTC_USER_NAME, OTC_PASSWORD, OTC_PROJECT_NAME, OTC_DOMAIN_NAME, OTC_IDENTITY_ENDPOINT")
- fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY")
- fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL")
- fmt.Fprintln(w, "\trackspace:\tRACKSPACE_USER, RACKSPACE_API_KEY")
- fmt.Fprintln(w, "\trfc2136:\tRFC2136_TSIG_KEY, RFC2136_TSIG_SECRET,\n\t\tRFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER")
- fmt.Fprintln(w, "\troute53:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_HOSTED_ZONE_ID")
- fmt.Fprintln(w, "\tsakuracloud:\tSAKURACLOUD_ACCESS_TOKEN, SAKURACLOUD_ACCESS_TOKEN_SECRET")
- fmt.Fprintln(w, "\tselectel:\tSELECTEL_API_TOKEN")
- fmt.Fprintln(w, "\tstackpath:\tSTACKPATH_CLIENT_ID, STACKPATH_CLIENT_SECRET, STACKPATH_STACK_ID")
- fmt.Fprintln(w, "\ttransip:\tTRANSIP_ACCOUNT_NAME, TRANSIP_PRIVATE_KEY_PATH")
- fmt.Fprintln(w, "\tvegadns:\tSECRET_VEGADNS_KEY, SECRET_VEGADNS_SECRET, VEGADNS_URL")
- fmt.Fprintln(w, "\tvscale:\tVSCALE_API_TOKEN")
- fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY")
- fmt.Fprintln(w, "\tzoneee:\tZONEEE_ENDPOINT, ZONEEE_API_USER, ZONEEE_API_KEY")
- fmt.Fprintln(w)
- fmt.Fprintln(w, "Additional configuration environment variables:")
- fmt.Fprintln(w)
- fmt.Fprintln(w, "\talidns:\tALICLOUD_POLLING_INTERVAL, ALICLOUD_PROPAGATION_TIMEOUT, ALICLOUD_TTL, ALICLOUD_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tauroradns:\tAURORA_POLLING_INTERVAL, AURORA_PROPAGATION_TIMEOUT, AURORA_TTL")
- fmt.Fprintln(w, "\tazure:\tAZURE_POLLING_INTERVAL, AZURE_PROPAGATION_TIMEOUT, AZURE_TTL, AZURE_METADATA_ENDPOINT")
- fmt.Fprintln(w, "\tbluecat:\tBLUECAT_POLLING_INTERVAL, BLUECAT_PROPAGATION_TIMEOUT, BLUECAT_TTL, BLUECAT_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_POLLING_INTERVAL, CLOUDFLARE_PROPAGATION_TIMEOUT, CLOUDFLARE_TTL, CLOUDFLARE_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tcloudns:\tCLOUDNS_POLLING_INTERVAL, CLOUDNS_PROPAGATION_TIMEOUT, CLOUDNS_TTL, CLOUDNS_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_POLLING_INTERVAL, CLOUDXNS_PROPAGATION_TIMEOUT, CLOUDXNS_TTL, CLOUDXNS_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tconoha:\tCONOHA_POLLING_INTERVAL, CONOHA_PROPAGATION_TIMEOUT, CONOHA_TTL, CONOHA_HTTP_TIMEOUT, CONOHA_REGION")
- fmt.Fprintln(w, "\tdesignate:\tDESIGNATE_POLLING_INTERVAL, DESIGNATE_PROPAGATION_TIMEOUT, DESIGNATE_TTL")
- fmt.Fprintln(w, "\tdigitalocean:\tDO_POLLING_INTERVAL, DO_PROPAGATION_TIMEOUT, DO_TTL, DO_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_TTL, DNSIMPLE_POLLING_INTERVAL, DNSIMPLE_PROPAGATION_TIMEOUT")
- fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_POLLING_INTERVAL, DNSMADEEASY_PROPAGATION_TIMEOUT, DNSMADEEASY_TTL, DNSMADEEASY_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tdnspod:\tDNSPOD_POLLING_INTERVAL, DNSPOD_PROPAGATION_TIMEOUT, DNSPOD_TTL, DNSPOD_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tdreamhost:\tDREAMHOST_POLLING_INTERVAL, DREAMHOST_PROPAGATION_TIMEOUT, DREAMHOST_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tduckdns:\tDUCKDNS_POLLING_INTERVAL, DUCKDNS_PROPAGATION_TIMEOUT, DUCKDNS_HTTP_TIMEOUT, DUCKDNS_SEQUENCE_INTERVAL")
- fmt.Fprintln(w, "\tdyn:\tDYN_POLLING_INTERVAL, DYN_PROPAGATION_TIMEOUT, DYN_TTL, DYN_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\texec:\tEXEC_POLLING_INTERVAL, EXEC_PROPAGATION_TIMEOUT")
- fmt.Fprintln(w, "\texoscale:\tEXOSCALE_POLLING_INTERVAL, EXOSCALE_PROPAGATION_TIMEOUT, EXOSCALE_TTL, EXOSCALE_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tfastdns:\tAKAMAI_POLLING_INTERVAL, AKAMAI_PROPAGATION_TIMEOUT, AKAMAI_TTL")
- fmt.Fprintln(w, "\tgandi:\tGANDI_POLLING_INTERVAL, GANDI_PROPAGATION_TIMEOUT, GANDI_TTL, GANDI_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tgandiv5:\tGANDIV5_POLLING_INTERVAL, GANDIV5_PROPAGATION_TIMEOUT, GANDIV5_TTL, GANDIV5_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tgcloud:\tGCE_POLLING_INTERVAL, GCE_PROPAGATION_TIMEOUT, GCE_TTL")
- fmt.Fprintln(w, "\tglesys:\tGLESYS_POLLING_INTERVAL, GLESYS_PROPAGATION_TIMEOUT, GLESYS_TTL, GLESYS_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tgodaddy:\tGODADDY_POLLING_INTERVAL, GODADDY_PROPAGATION_TIMEOUT, GODADDY_TTL, GODADDY_HTTP_TIMEOUT, GODADDY_SEQUENCE_INTERVAL")
- fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_POLLING_INTERVAL, HOSTINGDE_PROPAGATION_TIMEOUT, HOSTINGDE_TTL, HOSTINGDE_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\thttpreq:\tHTTPREQ_POLLING_INTERVAL, HTTPREQ_PROPAGATION_TIMEOUT, HTTPREQ_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tiij:\tIIJ_POLLING_INTERVAL, IIJ_PROPAGATION_TIMEOUT, IIJ_TTL")
- fmt.Fprintln(w, "\tinwx:\tINWX_POLLING_INTERVAL, INWX_PROPAGATION_TIMEOUT, INWX_TTL, INWX_SANDBOX")
- fmt.Fprintln(w, "\tlightsail:\tLIGHTSAIL_POLLING_INTERVAL, LIGHTSAIL_PROPAGATION_TIMEOUT")
- fmt.Fprintln(w, "\tlinode:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tlinodev4:\tLINODE_POLLING_INTERVAL, LINODE_TTL, LINODE_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tmydnsjp:\tMYDNSJP_PROPAGATION_TIMEOUT, MYDNSJP_POLLING_INTERVAL, MYDNSJP_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_POLLING_INTERVAL, NAMECHEAP_PROPAGATION_TIMEOUT, NAMECHEAP_TTL, NAMECHEAP_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tnamedotcom:\tNAMECOM_POLLING_INTERVAL, NAMECOM_PROPAGATION_TIMEOUT, NAMECOM_TTL, NAMECOM_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tnetcup:\tNETCUP_POLLING_INTERVAL, NETCUP_PROPAGATION_TIMEOUT, NETCUP_TTL, NETCUP_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tnifcloud:\tNIFCLOUD_POLLING_INTERVAL, NIFCLOUD_PROPAGATION_TIMEOUT, NIFCLOUD_TTL, NIFCLOUD_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tns1:\tNS1_POLLING_INTERVAL, NS1_PROPAGATION_TIMEOUT, NS1_TTL, NS1_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\toraclecloud:\tOCI_TTL, OCI_PROPAGATION_TIMEOUT, OCI_POLLING_INTERVAL")
- fmt.Fprintln(w, "\totc:\tOTC_POLLING_INTERVAL, OTC_PROPAGATION_TIMEOUT, OTC_TTL, OTC_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tovh:\tOVH_POLLING_INTERVAL, OVH_PROPAGATION_TIMEOUT, OVH_TTL, OVH_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tpdns:\tPDNS_POLLING_INTERVAL, PDNS_PROPAGATION_TIMEOUT, PDNS_TTL, PDNS_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\trackspace:\tRACKSPACE_POLLING_INTERVAL, RACKSPACE_PROPAGATION_TIMEOUT, RACKSPACE_TTL, RACKSPACE_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\trfc2136:\tRFC2136_POLLING_INTERVAL, RFC2136_PROPAGATION_TIMEOUT, RFC2136_TTL, RFC2136_SEQUENCE_INTERVAL, RFC2136_DNS_TIMEOUT")
- fmt.Fprintln(w, "\troute53:\tAWS_POLLING_INTERVAL, AWS_PROPAGATION_TIMEOUT, AWS_TTL")
- fmt.Fprintln(w, "\tsakuracloud:\tSAKURACLOUD_POLLING_INTERVAL, SAKURACLOUD_PROPAGATION_TIMEOUT, SAKURACLOUD_TTL")
- fmt.Fprintln(w, "\tselectel:\tSELECTEL_BASE_URL, SELECTEL_TTL, SELECTEL_PROPAGATION_TIMEOUT, SELECTEL_POLLING_INTERVAL, SELECTEL_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\ttransip:\tTRANSIP_POLLING_INTERVAL, TRANSIP_PROPAGATION_TIMEOUT, TRANSIP_TTL")
- fmt.Fprintln(w, "\tstackpath:\tSTACKPATH_POLLING_INTERVAL, STACKPATH_PROPAGATION_TIMEOUT, STACKPATH_TTL")
- fmt.Fprintln(w, "\tvegadns:\tVEGADNS_POLLING_INTERVAL, VEGADNS_PROPAGATION_TIMEOUT, VEGADNS_TTL")
- fmt.Fprintln(w, "\tvscale:\tVSCALE_BASE_URL, VSCALE_TTL, VSCALE_PROPAGATION_TIMEOUT, VSCALE_POLLING_INTERVAL, VSCALE_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tvultr:\tVULTR_POLLING_INTERVAL, VULTR_PROPAGATION_TIMEOUT, VULTR_TTL, VULTR_HTTP_TIMEOUT")
- fmt.Fprintln(w, "\tzoneee:\tZONEEE_POLLING_INTERVAL, ZONEEE_PROPAGATION_TIMEOUT, ZONEEE_HTTP_TIMEOUT")
-
- w.Flush()
-
- fmt.Println(`
-For a more detailed explanation of a DNS provider's credential variables,
-please consult their online documentation.`)
+ displayDNSHelp(strings.ToLower(code))
return nil
}
diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go
new file mode 100644
index 00000000..1ba4172b
--- /dev/null
+++ b/cmd/zz_gen_cmd_dnshelp.go
@@ -0,0 +1,1144 @@
+package cmd
+
+// CODE GENERATED AUTOMATICALLY
+// THIS FILE MUST NOT BE EDITED BY HAND
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/xenolf/lego/log"
+)
+
+func allDNSCodes() string {
+ return strings.Join([]string{
+ "acme-dns",
+ "alidns",
+ "auroradns",
+ "azure",
+ "bluecat",
+ "cloudflare",
+ "cloudns",
+ "cloudxns",
+ "conoha",
+ "designate",
+ "digitalocean",
+ "dnsimple",
+ "dnsmadeeasy",
+ "dnspod",
+ "dreamhost",
+ "duckdns",
+ "dyn",
+ "exec",
+ "exoscale",
+ "fastdns",
+ "gandi",
+ "gandiv5",
+ "gcloud",
+ "glesys",
+ "godaddy",
+ "hostingde",
+ "httpreq",
+ "iij",
+ "inwx",
+ "lightsail",
+ "linode",
+ "linodev4",
+ "mydnsjp",
+ "namecheap",
+ "namedotcom",
+ "netcup",
+ "nifcloud",
+ "ns1",
+ "oraclecloud",
+ "otc",
+ "ovh",
+ "pdns",
+ "rackspace",
+ "rfc2136",
+ "route53",
+ "sakuracloud",
+ "selectel",
+ "stackpath",
+ "transip",
+ "vegadns",
+ "vscale",
+ "vultr",
+ "zoneee",
+ }, ", ")
+}
+
+func displayDNSHelp(name string) {
+ w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
+ switch name {
+
+ case "acme-dns":
+ // generated from: providers/dns/acmedns/acmedns.toml
+ fmt.Fprintln(w, `Configuration for Joohoi's ACME-DNS.`)
+ fmt.Fprintln(w, `Code: 'acme-dns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "ACME_DNS_API_BASE": The ACME-DNS API address`)
+ fmt.Fprintln(w, ` - "ACME_DNS_STORAGE_PATH": The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates.`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/acme-dns`)
+
+ case "alidns":
+ // generated from: providers/dns/alidns/alidns.toml
+ fmt.Fprintln(w, `Configuration for Alibaba Cloud DNS.`)
+ fmt.Fprintln(w, `Code: 'alidns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "ALICLOUD_ACCESS_KEY": Access key ID`)
+ fmt.Fprintln(w, ` - "ALICLOUD_SECRET_KEY": Access Key secret`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "ALICLOUD_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "ALICLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "ALICLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "ALICLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/alidns`)
+
+ case "auroradns":
+ // generated from: providers/dns/auroradns/auroradns.toml
+ fmt.Fprintln(w, `Configuration for Aurora DNS.`)
+ fmt.Fprintln(w, `Code: 'auroradns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "AURORA_ENDPOINT": API endpoint URL`)
+ fmt.Fprintln(w, ` - "AURORA_KEY": User API key`)
+ fmt.Fprintln(w, ` - "AURORA_USER_ID": User ID`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "AURORA_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "AURORA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "AURORA_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/auroradns`)
+
+ case "azure":
+ // generated from: providers/dns/azure/azure.toml
+ fmt.Fprintln(w, `Configuration for Azure.`)
+ fmt.Fprintln(w, `Code: 'azure'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "AZURE_CLIENT_ID": Client ID`)
+ fmt.Fprintln(w, ` - "AZURE_CLIENT_SECRET": Client secret`)
+ fmt.Fprintln(w, ` - "AZURE_RESOURCE_GROUP": Resource group`)
+ fmt.Fprintln(w, ` - "AZURE_SUBSCRIPTION_ID": Subscription ID`)
+ fmt.Fprintln(w, ` - "AZURE_TENANT_ID": Tenant ID`)
+ fmt.Fprintln(w, ` - "instance metadata service": If the credentials are **not** set via the environment, then it will attempt to get a bearer token via the [instance metadata service](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service).`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "AZURE_METADATA_ENDPOINT": Metadata Service endpoint URL`)
+ fmt.Fprintln(w, ` - "AZURE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "AZURE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "AZURE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/azure`)
+
+ case "bluecat":
+ // generated from: providers/dns/bluecat/bluecat.toml
+ fmt.Fprintln(w, `Configuration for Bluecat.`)
+ fmt.Fprintln(w, `Code: 'bluecat'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "BLUECAT_CONFIG_NAME": Configuration name`)
+ fmt.Fprintln(w, ` - "BLUECAT_DNS_VIEW": External DNS View Name`)
+ fmt.Fprintln(w, ` - "BLUECAT_PASSWORD": API password`)
+ fmt.Fprintln(w, ` - "BLUECAT_SERVER_URL": The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve`)
+ fmt.Fprintln(w, ` - "BLUECAT_USER_NAME": API username`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "BLUECAT_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "BLUECAT_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "BLUECAT_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "BLUECAT_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/bluecat`)
+
+ case "cloudflare":
+ // generated from: providers/dns/cloudflare/cloudflare.toml
+ fmt.Fprintln(w, `Configuration for Cloudflare.`)
+ fmt.Fprintln(w, `Code: 'cloudflare'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "CLOUDFLARE_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "CLOUDFLARE_EMAIL": Account email`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "CLOUDFLARE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "CLOUDFLARE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "CLOUDFLARE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "CLOUDFLARE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/cloudflare`)
+
+ case "cloudns":
+ // generated from: providers/dns/cloudns/cloudns.toml
+ fmt.Fprintln(w, `Configuration for ClouDNS.`)
+ fmt.Fprintln(w, `Code: 'cloudns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "CLOUDNS_AUTH_ID": The API user ID`)
+ fmt.Fprintln(w, ` - "CLOUDNS_AUTH_PASSWORD": The password for API user ID`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "CLOUDNS_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "CLOUDNS_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "CLOUDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "CLOUDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/cloudns`)
+
+ case "cloudxns":
+ // generated from: providers/dns/cloudxns/cloudxns.toml
+ fmt.Fprintln(w, `Configuration for CloudXNS.`)
+ fmt.Fprintln(w, `Code: 'cloudxns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "CLOUDXNS_API_KEY": The API key`)
+ fmt.Fprintln(w, ` - "CLOUDXNS_SECRET_KEY": THe API secret key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "CLOUDXNS_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "CLOUDXNS_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "CLOUDXNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "CLOUDXNS_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/cloudxns`)
+
+ case "conoha":
+ // generated from: providers/dns/conoha/conoha.toml
+ fmt.Fprintln(w, `Configuration for ConoHa.`)
+ fmt.Fprintln(w, `Code: 'conoha'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "CONOHA_API_PASSWORD": The API password`)
+ fmt.Fprintln(w, ` - "CONOHA_API_USERNAME": The API username`)
+ fmt.Fprintln(w, ` - "CONOHA_TENANT_ID": Tenant ID`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "CONOHA_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "CONOHA_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "CONOHA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "CONOHA_REGION": The region`)
+ fmt.Fprintln(w, ` - "CONOHA_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/conoha`)
+
+ case "designate":
+ // generated from: providers/dns/designate/designate.toml
+ fmt.Fprintln(w, `Configuration for Designate DNSaaS for Openstack.`)
+ fmt.Fprintln(w, `Code: 'designate'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "OS_AUTH_URL": Identity endpoint URL`)
+ fmt.Fprintln(w, ` - "OS_PASSWORD": Password`)
+ fmt.Fprintln(w, ` - "OS_REGION_NAME": Region name`)
+ fmt.Fprintln(w, ` - "OS_TENANT_NAME": Tenant name`)
+ fmt.Fprintln(w, ` - "OS_USERNAME": Username`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DESIGNATE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DESIGNATE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DESIGNATE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/designate`)
+
+ case "digitalocean":
+ // generated from: providers/dns/digitalocean/digitalocean.toml
+ fmt.Fprintln(w, `Configuration for Digital Ocean.`)
+ fmt.Fprintln(w, `Code: 'digitalocean'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "DO_AUTH_TOKEN": Authentication token`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DO_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "DO_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DO_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/digitalocean`)
+
+ case "dnsimple":
+ // generated from: providers/dns/dnsimple/dnsimple.toml
+ fmt.Fprintln(w, `Configuration for DNSimple.`)
+ fmt.Fprintln(w, `Code: 'dnsimple'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "DNSIMPLE_BASE_URL": API endpoint URL`)
+ fmt.Fprintln(w, ` - "DNSIMPLE_OAUTH_TOKEN": OAuth token`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DNSIMPLE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DNSIMPLE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DNSIMPLE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dnsimple`)
+
+ case "dnsmadeeasy":
+ // generated from: providers/dns/dnsmadeeasy/dnsmadeeasy.toml
+ fmt.Fprintln(w, `Configuration for DNS Made Easy.`)
+ fmt.Fprintln(w, `Code: 'dnsmadeeasy'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "DNSMADEEASY_API_KEY": The API key`)
+ fmt.Fprintln(w, ` - "DNSMADEEASY_API_SECRET": The API Secret key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DNSMADEEASY_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "DNSMADEEASY_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DNSMADEEASY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DNSMADEEASY_SANDBOX": Activate the sandbox (boolean)`)
+ fmt.Fprintln(w, ` - "DNSMADEEASY_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dnsmadeeasy`)
+
+ case "dnspod":
+ // generated from: providers/dns/dnspod/dnspod.toml
+ fmt.Fprintln(w, `Configuration for DNSPod.`)
+ fmt.Fprintln(w, `Code: 'dnspod'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "DNSPOD_API_KEY": The user token`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DNSPOD_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "DNSPOD_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DNSPOD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DNSPOD_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dnspod`)
+
+ case "dreamhost":
+ // generated from: providers/dns/dreamhost/dreamhost.toml
+ fmt.Fprintln(w, `Configuration for DreamHost.`)
+ fmt.Fprintln(w, `Code: 'dreamhost'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "DREAMHOST_API_KEY": The API key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DREAMHOST_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "DREAMHOST_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DREAMHOST_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DREAMHOST_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dreamhost`)
+
+ case "duckdns":
+ // generated from: providers/dns/duckdns/duckdns.toml
+ fmt.Fprintln(w, `Configuration for Duck DNS.`)
+ fmt.Fprintln(w, `Code: 'duckdns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "DUCKDNS_TOKEN": Account token`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DUCKDNS_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "DUCKDNS_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DUCKDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DUCKDNS_SEQUENCE_INTERVAL": Interval between iteration`)
+ fmt.Fprintln(w, ` - "DUCKDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/duckdns`)
+
+ case "dyn":
+ // generated from: providers/dns/dyn/dyn.toml
+ fmt.Fprintln(w, `Configuration for Dyn.`)
+ fmt.Fprintln(w, `Code: 'dyn'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "DYN_CUSTOMER_NAME": Customer name`)
+ fmt.Fprintln(w, ` - "DYN_PASSWORD": Paswword`)
+ fmt.Fprintln(w, ` - "DYN_USER_NAME": User name`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "DYN_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "DYN_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "DYN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "DYN_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/dyn`)
+
+ case "exec":
+ // generated from: providers/dns/exec/exec.toml
+ fmt.Fprintln(w, `Configuration for External program.`)
+ fmt.Fprintln(w, `Code: 'exec'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/exec`)
+
+ case "exoscale":
+ // generated from: providers/dns/exoscale/exoscale.toml
+ fmt.Fprintln(w, `Configuration for Exoscale.`)
+ fmt.Fprintln(w, `Code: 'exoscale'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "EXOSCALE_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "EXOSCALE_API_SECRET": API secret`)
+ fmt.Fprintln(w, ` - "EXOSCALE_ENDPOINT": API endpoint URL`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "EXOSCALE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "EXOSCALE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "EXOSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "EXOSCALE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/exoscale`)
+
+ case "fastdns":
+ // generated from: providers/dns/fastdns/fastdns.toml
+ fmt.Fprintln(w, `Configuration for FastDNS.`)
+ fmt.Fprintln(w, `Code: 'fastdns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "AKAMAI_ACCESS_TOKEN": Access token`)
+ fmt.Fprintln(w, ` - "AKAMAI_CLIENT_SECRET": Client secret`)
+ fmt.Fprintln(w, ` - "AKAMAI_CLIENT_TOKEN": Client token`)
+ fmt.Fprintln(w, ` - "AKAMAI_HOST": API host`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "AKAMAI_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "AKAMAI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "AKAMAI_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/fastdns`)
+
+ case "gandi":
+ // generated from: providers/dns/gandi/gandi.toml
+ fmt.Fprintln(w, `Configuration for Gandi.`)
+ fmt.Fprintln(w, `Code: 'gandi'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "GANDI_API_KEY": API key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "GANDI_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "GANDI_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "GANDI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "GANDI_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/gandi`)
+
+ case "gandiv5":
+ // generated from: providers/dns/gandiv5/gandiv5.toml
+ fmt.Fprintln(w, `Configuration for Gandi Live DNS (v5).`)
+ fmt.Fprintln(w, `Code: 'gandiv5'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "GANDIV5_API_KEY": API key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "GANDIV5_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "GANDIV5_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "GANDIV5_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "GANDIV5_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/gandiv5`)
+
+ case "gcloud":
+ // generated from: providers/dns/gcloud/gcloud.toml
+ fmt.Fprintln(w, `Configuration for Google Cloud.`)
+ fmt.Fprintln(w, `Code: 'gcloud'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "Application Default Credentials": [Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)`)
+ fmt.Fprintln(w, ` - "GCE_PROJECT": Project name`)
+ fmt.Fprintln(w, ` - "GCE_SERVICE_ACCOUNT": Account`)
+ fmt.Fprintln(w, ` - "GCE_SERVICE_ACCOUNT_FILE": Account file path`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "GCE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "GCE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "GCE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/gcloud`)
+
+ case "glesys":
+ // generated from: providers/dns/glesys/glesys.toml
+ fmt.Fprintln(w, `Configuration for Glesys.`)
+ fmt.Fprintln(w, `Code: 'glesys'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "GLESYS_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "GLESYS_API_USER": API user`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "GLESYS_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "GLESYS_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "GLESYS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "GLESYS_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/glesys`)
+
+ case "godaddy":
+ // generated from: providers/dns/godaddy/godaddy.toml
+ fmt.Fprintln(w, `Configuration for Go Daddy.`)
+ fmt.Fprintln(w, `Code: 'godaddy'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "GODADDY_API_KEY": **TODO**`)
+ fmt.Fprintln(w, ` - "GODADDY_API_SECRET": **TODO**`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "GODADDY_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "GODADDY_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "GODADDY_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "GODADDY_SEQUENCE_INTERVAL": Interval between iteration`)
+ fmt.Fprintln(w, ` - "GODADDY_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/godaddy`)
+
+ case "hostingde":
+ // generated from: providers/dns/hostingde/hostingde.toml
+ fmt.Fprintln(w, `Configuration for Hosting.de.`)
+ fmt.Fprintln(w, `Code: 'hostingde'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "HOSTINGDE_API_KEY": **TODO**`)
+ fmt.Fprintln(w, ` - "HOSTINGDE_ZONE_NAME": **TODO**`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "HOSTINGDE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "HOSTINGDE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "HOSTINGDE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "HOSTINGDE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/hostingde`)
+
+ case "httpreq":
+ // generated from: providers/dns/httpreq/httpreq.toml
+ fmt.Fprintln(w, `Configuration for HTTP request.`)
+ fmt.Fprintln(w, `Code: 'httpreq'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "HTTPREQ_ENDPOINT": The URL of the server`)
+ fmt.Fprintln(w, ` - "HTTPREQ_MODE": 'RAW', none`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "HTTPREQ_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "HTTPREQ_PASSWORD": **TODO**`)
+ fmt.Fprintln(w, ` - "HTTPREQ_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "HTTPREQ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "HTTPREQ_USERNAME": **TODO**`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/httpreq`)
+
+ case "iij":
+ // generated from: providers/dns/iij/iij.toml
+ fmt.Fprintln(w, `Configuration for Internet Initiative Japan.`)
+ fmt.Fprintln(w, `Code: 'iij'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "IIJ_API_ACCESS_KEY": API access key`)
+ fmt.Fprintln(w, ` - "IIJ_API_SECRET_KEY": API secret key`)
+ fmt.Fprintln(w, ` - "IIJ_DO_SERVICE_CODE": DO service code`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "IIJ_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "IIJ_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "IIJ_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/iij`)
+
+ case "inwx":
+ // generated from: providers/dns/inwx/inwx.toml
+ fmt.Fprintln(w, `Configuration for INWX.`)
+ fmt.Fprintln(w, `Code: 'inwx'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "INWX_PASSWORD": Password`)
+ fmt.Fprintln(w, ` - "INWX_USERNAME": Username`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "INWX_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "INWX_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "INWX_SANDBOX": Activate the sandbox (boolean)`)
+ fmt.Fprintln(w, ` - "INWX_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/inwx`)
+
+ case "lightsail":
+ // generated from: providers/dns/lightsail/lightsail.toml
+ fmt.Fprintln(w, `Configuration for Amazon Lightsail.`)
+ fmt.Fprintln(w, `Code: 'lightsail'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "AWS_ACCESS_KEY_ID": Access key ID`)
+ fmt.Fprintln(w, ` - "AWS_SECRET_ACCESS_KEY": Secret access key`)
+ fmt.Fprintln(w, ` - "DNS_ZONE": DNS zone`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "LIGHTSAIL_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "LIGHTSAIL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/lightsail`)
+
+ case "linode":
+ // generated from: providers/dns/linode/linode.toml
+ fmt.Fprintln(w, `Configuration for Linode (deprecated).`)
+ fmt.Fprintln(w, `Code: 'linode'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "LINODE_API_KEY": API key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "LINODE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "LINODE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "LINODE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/linode`)
+
+ case "linodev4":
+ // generated from: providers/dns/linodev4/linodev4.toml
+ fmt.Fprintln(w, `Configuration for Linode (v4).`)
+ fmt.Fprintln(w, `Code: 'linodev4'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "LINODE_TOKEN": API token`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "LINODE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "LINODE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "LINODE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/linodev4`)
+
+ case "mydnsjp":
+ // generated from: providers/dns/mydnsjp/mydnsjp.toml
+ fmt.Fprintln(w, `Configuration for MyDNS.jp.`)
+ fmt.Fprintln(w, `Code: 'mydnsjp'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "MYDNSJP_MASTER_ID": Master ID`)
+ fmt.Fprintln(w, ` - "MYDNSJP_PASSWORD": Password`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "MYDNSJP_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "MYDNSJP_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "MYDNSJP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "MYDNSJP_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/mydnsjp`)
+
+ case "namecheap":
+ // generated from: providers/dns/namecheap/namecheap.toml
+ fmt.Fprintln(w, `Configuration for Namecheap.`)
+ fmt.Fprintln(w, `Code: 'namecheap'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "NAMECHEAP_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "NAMECHEAP_API_USER": API user`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "NAMECHEAP_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "NAMECHEAP_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "NAMECHEAP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "NAMECHEAP_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/namecheap`)
+
+ case "namedotcom":
+ // generated from: providers/dns/namedotcom/namedotcom.toml
+ fmt.Fprintln(w, `Configuration for Name.com.`)
+ fmt.Fprintln(w, `Code: 'namedotcom'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "NAMECOM_API_TOKEN": API token`)
+ fmt.Fprintln(w, ` - "NAMECOM_USERNAME": Username`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "NAMECOM_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "NAMECOM_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "NAMECOM_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "NAMECOM_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/namedotcom`)
+
+ case "netcup":
+ // generated from: providers/dns/netcup/netcup.toml
+ fmt.Fprintln(w, `Configuration for Netcup.`)
+ fmt.Fprintln(w, `Code: 'netcup'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "NETCUP_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "NETCUP_API_PASSWORD": API password`)
+ fmt.Fprintln(w, ` - "NETCUP_CUSTOMER_NUMBER": Customer number`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "NETCUP_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "NETCUP_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "NETCUP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "NETCUP_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/netcup`)
+
+ case "nifcloud":
+ // generated from: providers/dns/nifcloud/nifcloud.toml
+ fmt.Fprintln(w, `Configuration for NIFCloud.`)
+ fmt.Fprintln(w, `Code: 'nifcloud'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "NIFCLOUD_ACCESS_KEY_ID": Access key`)
+ fmt.Fprintln(w, ` - "NIFCLOUD_SECRET_ACCESS_KEY": Secret access key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "NIFCLOUD_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "NIFCLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "NIFCLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "NIFCLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/nifcloud`)
+
+ case "ns1":
+ // generated from: providers/dns/ns1/ns1.toml
+ fmt.Fprintln(w, `Configuration for NS1.`)
+ fmt.Fprintln(w, `Code: 'ns1'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "NS1_API_KEY": API key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "NS1_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "NS1_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "NS1_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "NS1_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/ns1`)
+
+ case "oraclecloud":
+ // generated from: providers/dns/oraclecloud/oraclecloud.toml
+ fmt.Fprintln(w, `Configuration for Oracle Cloud.`)
+ fmt.Fprintln(w, `Code: 'oraclecloud'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "OCI_COMPARTMENT_OCID": Compartment OCID`)
+ fmt.Fprintln(w, ` - "OCI_PRIVKEY_FILE": Private key file`)
+ fmt.Fprintln(w, ` - "OCI_PRIVKEY_PASS": Private key password`)
+ fmt.Fprintln(w, ` - "OCI_PUBKEY_FINGERPRINT": Public key fingerprint`)
+ fmt.Fprintln(w, ` - "OCI_REGION": Region`)
+ fmt.Fprintln(w, ` - "OCI_TENANCY_OCID": Tenanct OCID`)
+ fmt.Fprintln(w, ` - "OCI_USER_OCID": User OCID`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "OCI_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "OCI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "OCI_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/oraclecloud`)
+
+ case "otc":
+ // generated from: providers/dns/otc/otc.toml
+ fmt.Fprintln(w, `Configuration for Open Telekom Cloud.`)
+ fmt.Fprintln(w, `Code: 'otc'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "OTC_DOMAIN_NAME": Domain name`)
+ fmt.Fprintln(w, ` - "OTC_IDENTITY_ENDPOINT": Identity endpoint URL`)
+ fmt.Fprintln(w, ` - "OTC_PASSWORD": Password`)
+ fmt.Fprintln(w, ` - "OTC_PROJECT_NAME": Project name`)
+ fmt.Fprintln(w, ` - "OTC_USER_NAME": User name`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "OTC_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "OTC_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "OTC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "OTC_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/otc`)
+
+ case "ovh":
+ // generated from: providers/dns/ovh/ovh.toml
+ fmt.Fprintln(w, `Configuration for OVH.`)
+ fmt.Fprintln(w, `Code: 'ovh'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "OVH_APPLICATION_KEY": Application key`)
+ fmt.Fprintln(w, ` - "OVH_APPLICATION_SECRET": Application secret`)
+ fmt.Fprintln(w, ` - "OVH_CONSUMER_KEY": Consumer key`)
+ fmt.Fprintln(w, ` - "OVH_ENDPOINT": Endpoint URL (ovh-eu or ovh-ca)`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "OVH_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "OVH_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "OVH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "OVH_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/ovh`)
+
+ case "pdns":
+ // generated from: providers/dns/pdns/pdns.toml
+ fmt.Fprintln(w, `Configuration for PowerDNS.`)
+ fmt.Fprintln(w, `Code: 'pdns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "PDNS_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "PDNS_API_URL": API url`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "PDNS_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "PDNS_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "PDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "PDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/pdns`)
+
+ case "rackspace":
+ // generated from: providers/dns/rackspace/rackspace.toml
+ fmt.Fprintln(w, `Configuration for Rackspace.`)
+ fmt.Fprintln(w, `Code: 'rackspace'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "RACKSPACE_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "RACKSPACE_USER": API user`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "RACKSPACE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "RACKSPACE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "RACKSPACE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "RACKSPACE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/rackspace`)
+
+ case "rfc2136":
+ // generated from: providers/dns/rfc2136/rfc2136.toml
+ fmt.Fprintln(w, `Configuration for RFC2136.`)
+ fmt.Fprintln(w, `Code: 'rfc2136'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "RFC2136_NAMESERVER": Network address in the form "host" or "host:port"`)
+ fmt.Fprintln(w, ` - "RFC2136_TSIG_ALGORITHM": TSIG algorythm. See [miekg/dns#tsig.go](https://github.com/miekg/dns/blob/master/tsig.go) for supported values. To disable TSIG authentication, leave the 'RFC2136_TSIG*' variables unset.`)
+ fmt.Fprintln(w, ` - "RFC2136_TSIG_KEY": Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the 'RFC2136_TSIG*' variables unset.`)
+ fmt.Fprintln(w, ` - "RFC2136_TSIG_SECRET": Secret key payload. To disable TSIG authentication, leave the' RFC2136_TSIG*' variables unset.`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "RFC2136_DNS_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "RFC2136_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "RFC2136_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "RFC2136_SEQUENCE_INTERVAL": Interval between iteration`)
+ fmt.Fprintln(w, ` - "RFC2136_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/rfc2136`)
+
+ case "route53":
+ // generated from: providers/dns/route53/route53.toml
+ fmt.Fprintln(w, `Configuration for Amazon Route 53.`)
+ fmt.Fprintln(w, `Code: 'route53'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "AWS_ACCESS_KEY_ID": `)
+ fmt.Fprintln(w, ` - "AWS_HOSTED_ZONE_ID": `)
+ fmt.Fprintln(w, ` - "AWS_REGION": `)
+ fmt.Fprintln(w, ` - "AWS_SECRET_ACCESS_KEY": `)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "AWS_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "AWS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "AWS_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/route53`)
+
+ case "sakuracloud":
+ // generated from: providers/dns/sakuracloud/sakuracloud.toml
+ fmt.Fprintln(w, `Configuration for Sakura Cloud.`)
+ fmt.Fprintln(w, `Code: 'sakuracloud'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "SAKURACLOUD_ACCESS_TOKEN": Access token`)
+ fmt.Fprintln(w, ` - "SAKURACLOUD_ACCESS_TOKEN_SECRET": Access token secret`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "SAKURACLOUD_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "SAKURACLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "SAKURACLOUD_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/sakuracloud`)
+
+ case "selectel":
+ // generated from: providers/dns/selectel/selectel.toml
+ fmt.Fprintln(w, `Configuration for Selectel.`)
+ fmt.Fprintln(w, `Code: 'selectel'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "SELECTEL_API_TOKEN": API token`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "SELECTEL_BASE_URL": API endpoint URL`)
+ fmt.Fprintln(w, ` - "SELECTEL_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "SELECTEL_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "SELECTEL_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "SELECTEL_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/selectel`)
+
+ case "stackpath":
+ // generated from: providers/dns/stackpath/stackpath.toml
+ fmt.Fprintln(w, `Configuration for Stackpath.`)
+ fmt.Fprintln(w, `Code: 'stackpath'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "STACKPATH_CLIENT_ID": Client ID`)
+ fmt.Fprintln(w, ` - "STACKPATH_CLIENT_SECRET": Client secret`)
+ fmt.Fprintln(w, ` - "STACKPATH_STACK_ID": Stack ID`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "STACKPATH_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "STACKPATH_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "STACKPATH_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/stackpath`)
+
+ case "transip":
+ // generated from: providers/dns/transip/transip.toml
+ fmt.Fprintln(w, `Configuration for TransIP.`)
+ fmt.Fprintln(w, `Code: 'transip'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "TRANSIP_ACCOUNT_NAME": Account name`)
+ fmt.Fprintln(w, ` - "TRANSIP_PRIVATE_KEY_PATH": Private key path`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "TRANSIP_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "TRANSIP_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "TRANSIP_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/transip`)
+
+ case "vegadns":
+ // generated from: providers/dns/vegadns/vegadns.toml
+ fmt.Fprintln(w, `Configuration for VegaDNS.`)
+ fmt.Fprintln(w, `Code: 'vegadns'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "SECRET_VEGADNS_KEY": API key`)
+ fmt.Fprintln(w, ` - "SECRET_VEGADNS_SECRET": API secret`)
+ fmt.Fprintln(w, ` - "VEGADNS_URL": API endpoint URL`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "VEGADNS_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "VEGADNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "VEGADNS_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/vegadns`)
+
+ case "vscale":
+ // generated from: providers/dns/vscale/vscale.toml
+ fmt.Fprintln(w, `Configuration for Vscale.`)
+ fmt.Fprintln(w, `Code: 'vscale'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "VSCALE_API_TOKEN": API token`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "VSCALE_BASE_URL": API enddpoint URL`)
+ fmt.Fprintln(w, ` - "VSCALE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "VSCALE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "VSCALE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "VSCALE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/vscale`)
+
+ case "vultr":
+ // generated from: providers/dns/vultr/vultr.toml
+ fmt.Fprintln(w, `Configuration for Vultr.`)
+ fmt.Fprintln(w, `Code: 'vultr'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "VULTR_API_KEY": API key`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "VULTR_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "VULTR_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "VULTR_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "VULTR_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/vultr`)
+
+ case "zoneee":
+ // generated from: providers/dns/zoneee/zoneee.toml
+ fmt.Fprintln(w, `Configuration for Zone.ee.`)
+ fmt.Fprintln(w, `Code: 'zoneee'`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Credentials:`)
+ fmt.Fprintln(w, ` - "ZONEEE_API_KEY": API key`)
+ fmt.Fprintln(w, ` - "ZONEEE_API_USER": API user`)
+ fmt.Fprintln(w)
+
+ fmt.Fprintln(w, `Additional Configuration:`)
+ fmt.Fprintln(w, ` - "ZONEEE_ENDPOINT": API endpoint URL`)
+ fmt.Fprintln(w, ` - "ZONEEE_HTTP_TIMEOUT": API request timeout`)
+ fmt.Fprintln(w, ` - "ZONEEE_POLLING_INTERVAL": Time between DNS propagation check`)
+ fmt.Fprintln(w, ` - "ZONEEE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
+ fmt.Fprintln(w, ` - "ZONEEE_TTL": The TTL of the TXT record used for the DNS challenge`)
+
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/zoneee`)
+
+ case "manual":
+ fmt.Fprintln(w, `Solving the DNS-01 challenge using CLI prompt.`)
+ default:
+ log.Fatalf("%q is not yet supported.", name)
+ }
+ w.Flush()
+}
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000..7b822970
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,2 @@
+themes/
+public/
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..09d3dad2
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+.PHONY: default clean hugo hugo-build
+
+default: hugo
+
+clean:
+ rm -rf public/
+
+
+hugo-build: clean hugo-themes
+ hugo --enableGitInfo --source .
+
+hugo:
+ hugo server --disableFastRender --enableGitInfo --watch --source .
+ # hugo server -D
+
+hugo-themes:
+ rm -rf themes
+ mkdir themes
+ git clone --depth=1 https://github.com/matcornic/hugo-theme-learn.git themes/hugo-theme-learn
+ rm -rf themes/hugo-theme-learn/.git
diff --git a/docs/archetypes/default.md b/docs/archetypes/default.md
new file mode 100644
index 00000000..00e77bd7
--- /dev/null
+++ b/docs/archetypes/default.md
@@ -0,0 +1,6 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+date: {{ .Date }}
+draft: true
+---
+
diff --git a/docs/config.toml b/docs/config.toml
new file mode 100644
index 00000000..028dcf1c
--- /dev/null
+++ b/docs/config.toml
@@ -0,0 +1,67 @@
+baseURL = "https://xenolf.github.io/lego/"
+languageCode = "en-us"
+title = "Lego"
+
+theme = "hugo-theme-learn"
+
+# Code higlighting settings
+pygmentsCodefences = true
+pygmentsCodeFencesGuesSsyntax = false
+pygmentsOptions = ""
+pygmentsStyle = "monokai"
+# The monokai stylesheet is included in the base template.
+pygmentsUseClasses = true
+
+[permalinks]
+ dns = "/dns/:slug/"
+
+[params]
+ # Prefix URL to edit current page. Will display an "Edit this page" button on top right hand corner of every page.
+ # Useful to give opportunity to people to create merge request for your doc.
+ # See the config.toml file from this documentation site to have an example.
+# editURL = ""
+ # Author of the site, will be used in meta information
+ author = "Lego Team"
+ # Description of the site, will be used in meta information
+# description = ""
+ # Shows a checkmark for visited pages on the menu
+ showVisitedLinks = true
+ # Disable search function. It will hide search bar
+# disableSearch = false
+ # Javascript and CSS cache are automatically busted when new version of site is generated.
+ # Set this to true to disable this behavior (some proxies don't handle well this optimization)
+# disableAssetsBusting = false
+ # Set this to true to disable copy-to-clipboard button for inline code.
+# disableInlineCopyToClipBoard = true
+ # A title for shortcuts in menu is set by default. Set this to true to disable it.
+# disableShortcutsTitle = false
+ # When using mulitlingual website, disable the switch language button.
+# disableLanguageSwitchingButton = false
+ # Hide breadcrumbs in the header and only show the current page title
+# disableBreadcrumb = true
+ # Hide Next and Previous page buttons normally displayed full height beside content
+# disableNextPrev = true
+ # Order sections in menu by "weight" or "title". Default to "weight"
+# ordersectionsby = "weight"
+ # Change default color scheme with a variant one. Can be "red", "blue", "green".
+ themeVariant = "blue"
+
+[Languages]
+[Languages.en]
+ title = "Let’s Encrypt client and ACME library written in Go."
+ weight = 1
+ languageName = "English"
+
+[[Languages.en.menu.shortcuts]]
+ name = " Github repo"
+ identifier = "ds"
+ url = "https://github.com/xenof/lego"
+ weight = 10
+
+[[Languages.en.menu.shortcuts]]
+ name = " Issues"
+ url = "https://github.com/xenolf/lego/issues"
+ weight = 11
+
+[outputs]
+ home = [ "HTML", "RSS", "JSON"]
diff --git a/docs/content/_index.md b/docs/content/_index.md
new file mode 100644
index 00000000..a3634dab
--- /dev/null
+++ b/docs/content/_index.md
@@ -0,0 +1,30 @@
+---
+title: "Welcome"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+chapter: true
+---
+
+# Lego
+
+Let's Encrypt client and ACME library written in Go.
+
+## Features
+
+- Register with CA
+- Obtain certificates, both from scratch or with an existing CSR
+- Renew certificates
+- Revoke certificates
+- Robust implementation of all ACME challenges
+ - HTTP (http-01)
+ - DNS (dns-01)
+ - TLS (tls-alpn-01)
+- SAN certificate support
+- Comes with multiple optional [DNS providers](dns)
+- [Custom challenge solvers](usage/library/writing-a-challenge-solver/)
+- Certificate bundling
+- OCSP helper function
+
+
+lego introduced support for ACME v2 in [v1.0.0](https://github.com/xenolf/lego/releases/tag/v1.0.0).
+If you still need to utilize ACME v1, you can do so by using the [v0.5.0](https://github.com/xenolf/lego/releases/tag/v0.5.0) version.
diff --git a/docs/content/dns/_index.md b/docs/content/dns/_index.md
new file mode 100644
index 00000000..d4a80a51
--- /dev/null
+++ b/docs/content/dns/_index.md
@@ -0,0 +1,18 @@
+---
+title: "DNS Providers"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+weight: 3
+---
+
+Credentials for DNS providers must be passed through environment variables.
+
+Here is an example bash command using the CloudFlare DNS provider:
+
+```bash
+CLOUDFLARE_EMAIL=foo@bar.com \
+CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
+lego --dns cloudflare --domains www.example.com --email me@bar.com run
+```
+
+{{%children style="h2" description="true" %}}
\ No newline at end of file
diff --git a/docs/content/dns/manual.md b/docs/content/dns/manual.md
new file mode 100644
index 00000000..8a9c125e
--- /dev/null
+++ b/docs/content/dns/manual.md
@@ -0,0 +1,59 @@
+---
+title: "Manual"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: manual
+---
+
+Solving the DNS-01 challenge using CLI prompt.
+
+
+
+## Example
+
+```txt
+Do you accept the TOS? Y/n
+
+[INFO] acme: Registering account for test@test.com
+!!!! HEADS UP !!!!
+
+ Your account credentials have been saved in your Let's Encrypt
+ configuration directory at "~/.lego/accounts".
+ You should make a secure backup of this folder now. This
+ configuration directory will also contain certificates and
+ private keys obtained from Let's Encrypt so making regular
+ backups of this folder is ideal.
+
+[INFO] [test.com] acme: Obtaining bundled SAN certificate
+[INFO] [test.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/lornkZmVYjsh5wLHpxdQcZDPekGf_TYUM-MTJk3-yrA
+[INFO] [test.com] acme: Could not find solver for: tls-alpn-01
+[INFO] [test.com] acme: Could not find solver for: http-01
+[INFO] [test.com] acme: use dns-01 solver
+[INFO] [test.com] acme: Preparing to solve DNS-01
+lego: Please create the following TXT record in your test.com. zone:
+_acme-challenge.test.com. 120 IN TXT "VP-dby1RBuUOnDZg1n9sF-cwicLsognMzJb0Vx8ttAI"
+lego: Press 'Enter' when you are done
+
+Do you accept the TOS? Y/n
+
+[INFO] acme: Registering account for test@test.com
+!!!! HEADS UP !!!!
+
+ Your account credentials have been saved in your Let's Encrypt
+ configuration directory at "~/.lego/accounts".
+ You should make a secure backup of this folder now. This
+ configuration directory will also contain certificates and
+ private keys obtained from Let's Encrypt so making regular
+ backups of this folder is ideal.
+
+[INFO] [test.com] acme: Obtaining bundled SAN certificate
+[INFO] [test.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/lornkZmVYjsh5wLHpxdQcZDPekGf_TYUM-MTJk3-yrA
+[INFO] [test.com] acme: Could not find solver for: tls-alpn-01
+[INFO] [test.com] acme: Could not find solver for: http-01
+[INFO] [test.com] acme: use dns-01 solver
+[INFO] [test.com] acme: Preparing to solve DNS-01
+lego: Please create the following TXT record in your test.com. zone:
+_acme-challenge.test.com. 120 IN TXT "VP-dby1RBuUOnDZg1n9sF-cwicLsognMzJb0Vx8ttAI"
+lego: Press 'Enter' when you are done
+
+```
\ No newline at end of file
diff --git a/docs/content/dns/zz_gen_acme-dns.md b/docs/content/dns/zz_gen_acme-dns.md
new file mode 100644
index 00000000..ed671b45
--- /dev/null
+++ b/docs/content/dns/zz_gen_acme-dns.md
@@ -0,0 +1,46 @@
+---
+title: "Joohoi's ACME-DNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: acme-dns
+---
+
+
+
+
+
+
+Configuration for [Joohoi's ACME-DNS](https://github.com/joohoi/acme-dns).
+
+
+
+
+- Code: `acme-dns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `ACME_DNS_API_BASE` | The ACME-DNS API address |
+| `ACME_DNS_STORAGE_PATH` | The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates. |
+
+
+
+
+
+
+## More information
+
+- [API documentation](https://github.com/joohoi/acme-dns#api)
+- [Go client](https://github.com/cpu/goacmedns)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_alidns.md b/docs/content/dns/zz_gen_alidns.md
new file mode 100644
index 00000000..0b1ae6fa
--- /dev/null
+++ b/docs/content/dns/zz_gen_alidns.md
@@ -0,0 +1,54 @@
+---
+title: "Alibaba Cloud DNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: alidns
+---
+
+
+
+
+
+
+Configuration for [Alibaba Cloud DNS](https://www.alibabacloud.com/product/dns).
+
+
+
+
+- Code: `alidns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `ALICLOUD_ACCESS_KEY` | Access key ID |
+| `ALICLOUD_SECRET_KEY` | Access Key secret |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `ALICLOUD_HTTP_TIMEOUT` | API request timeout |
+| `ALICLOUD_POLLING_INTERVAL` | Time between DNS propagation check |
+| `ALICLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `ALICLOUD_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.alibabacloud.com/help/doc-detail/42875.htm)
+- [Go client](https://github.com/aliyun/alibaba-cloud-sdk-go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_auroradns.md b/docs/content/dns/zz_gen_auroradns.md
new file mode 100644
index 00000000..223819bb
--- /dev/null
+++ b/docs/content/dns/zz_gen_auroradns.md
@@ -0,0 +1,54 @@
+---
+title: "Aurora DNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: auroradns
+---
+
+
+
+
+
+
+Configuration for [Aurora DNS](https://www.pcextreme.com/aurora/dns).
+
+
+
+
+- Code: `auroradns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `AURORA_ENDPOINT` | API endpoint URL |
+| `AURORA_KEY` | User API key |
+| `AURORA_USER_ID` | User ID |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `AURORA_POLLING_INTERVAL` | Time between DNS propagation check |
+| `AURORA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `AURORA_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://libcloud.readthedocs.io/en/latest/dns/drivers/auroradns.html#api-docs)
+- [Go client](https://github.com/nrdcg/auroradns)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_azure.md b/docs/content/dns/zz_gen_azure.md
new file mode 100644
index 00000000..4186dc87
--- /dev/null
+++ b/docs/content/dns/zz_gen_azure.md
@@ -0,0 +1,58 @@
+---
+title: "Azure"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: azure
+---
+
+
+
+
+
+
+Configuration for [Azure](https://azure.microsoft.com/services/dns/).
+
+
+
+
+- Code: `azure`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `AZURE_CLIENT_ID` | Client ID |
+| `AZURE_CLIENT_SECRET` | Client secret |
+| `AZURE_RESOURCE_GROUP` | Resource group |
+| `AZURE_SUBSCRIPTION_ID` | Subscription ID |
+| `AZURE_TENANT_ID` | Tenant ID |
+| `instance metadata service` | If the credentials are **not** set via the environment, then it will attempt to get a bearer token via the [instance metadata service](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service). |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `AZURE_METADATA_ENDPOINT` | Metadata Service endpoint URL |
+| `AZURE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `AZURE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `AZURE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://docs.microsoft.com/en-us/go/azure/)
+- [Go client](https://github.com/Azure/azure-sdk-for-go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_bluecat.md b/docs/content/dns/zz_gen_bluecat.md
new file mode 100644
index 00000000..64d7082d
--- /dev/null
+++ b/docs/content/dns/zz_gen_bluecat.md
@@ -0,0 +1,53 @@
+---
+title: "Bluecat"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: bluecat
+---
+
+
+
+
+
+
+Configuration for [Bluecat](https://www.bluecatnetworks.com).
+
+
+
+
+- Code: `bluecat`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `BLUECAT_CONFIG_NAME` | Configuration name |
+| `BLUECAT_DNS_VIEW` | External DNS View Name |
+| `BLUECAT_PASSWORD` | API password |
+| `BLUECAT_SERVER_URL` | The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve |
+| `BLUECAT_USER_NAME` | API username |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `BLUECAT_HTTP_TIMEOUT` | API request timeout |
+| `BLUECAT_POLLING_INTERVAL` | Time between DNS propagation check |
+| `BLUECAT_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `BLUECAT_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+
+
+
+
diff --git a/docs/content/dns/zz_gen_cloudflare.md b/docs/content/dns/zz_gen_cloudflare.md
new file mode 100644
index 00000000..34868c8a
--- /dev/null
+++ b/docs/content/dns/zz_gen_cloudflare.md
@@ -0,0 +1,58 @@
+---
+title: "Cloudflare"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: cloudflare
+---
+
+
+
+
+
+
+Configuration for [Cloudflare](https://www.cloudflare.com/dns/).
+
+
+
+
+- Code: `cloudflare`
+
+Here is an example bash command using the Cloudflare provider:
+
+```bash
+CLOUDFLARE_EMAIL=foo@bar.com \
+CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
+lego --dns cloudflare --domains my.domain.com --email my@email.com run
+```
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `CLOUDFLARE_API_KEY` | API key |
+| `CLOUDFLARE_EMAIL` | Account email |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `CLOUDFLARE_HTTP_TIMEOUT` | API request timeout |
+| `CLOUDFLARE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `CLOUDFLARE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `CLOUDFLARE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://api.cloudflare.com/)
+- [Go client](https://github.com/cloudflare/cloudflare-go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_cloudns.md b/docs/content/dns/zz_gen_cloudns.md
new file mode 100644
index 00000000..6b5f14a9
--- /dev/null
+++ b/docs/content/dns/zz_gen_cloudns.md
@@ -0,0 +1,53 @@
+---
+title: "ClouDNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: cloudns
+---
+
+
+
+
+
+
+Configuration for [ClouDNS](https://www.cloudns.net).
+
+
+
+
+- Code: `cloudns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `CLOUDNS_AUTH_ID` | The API user ID |
+| `CLOUDNS_AUTH_PASSWORD` | The password for API user ID |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `CLOUDNS_HTTP_TIMEOUT` | API request timeout |
+| `CLOUDNS_POLLING_INTERVAL` | Time between DNS propagation check |
+| `CLOUDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `CLOUDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.cloudns.net/wiki/article/42/)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_cloudxns.md b/docs/content/dns/zz_gen_cloudxns.md
new file mode 100644
index 00000000..d89a5c5e
--- /dev/null
+++ b/docs/content/dns/zz_gen_cloudxns.md
@@ -0,0 +1,53 @@
+---
+title: "CloudXNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: cloudxns
+---
+
+
+
+
+
+
+Configuration for [CloudXNS](https://www.cloudxns.net/).
+
+
+
+
+- Code: `cloudxns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `CLOUDXNS_API_KEY` | The API key |
+| `CLOUDXNS_SECRET_KEY` | THe API secret key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `CLOUDXNS_HTTP_TIMEOUT` | API request timeout |
+| `CLOUDXNS_POLLING_INTERVAL` | Time between DNS propagation check |
+| `CLOUDXNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `CLOUDXNS_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_conoha.md b/docs/content/dns/zz_gen_conoha.md
new file mode 100644
index 00000000..c37e245f
--- /dev/null
+++ b/docs/content/dns/zz_gen_conoha.md
@@ -0,0 +1,55 @@
+---
+title: "ConoHa"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: conoha
+---
+
+
+
+
+
+
+Configuration for [ConoHa](https://www.conoha.jp/).
+
+
+
+
+- Code: `conoha`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `CONOHA_API_PASSWORD` | The API password |
+| `CONOHA_API_USERNAME` | The API username |
+| `CONOHA_TENANT_ID` | Tenant ID |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `CONOHA_HTTP_TIMEOUT` | API request timeout |
+| `CONOHA_POLLING_INTERVAL` | Time between DNS propagation check |
+| `CONOHA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `CONOHA_REGION` | The region |
+| `CONOHA_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.conoha.jp/docs/)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_designate.md b/docs/content/dns/zz_gen_designate.md
new file mode 100644
index 00000000..090be577
--- /dev/null
+++ b/docs/content/dns/zz_gen_designate.md
@@ -0,0 +1,56 @@
+---
+title: "Designate DNSaaS for Openstack"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: designate
+---
+
+
+
+
+
+
+Configuration for [Designate DNSaaS for Openstack](https://docs.openstack.org/designate/latest/).
+
+
+
+
+- Code: `designate`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `OS_AUTH_URL` | Identity endpoint URL |
+| `OS_PASSWORD` | Password |
+| `OS_REGION_NAME` | Region name |
+| `OS_TENANT_NAME` | Tenant name |
+| `OS_USERNAME` | Username |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DESIGNATE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DESIGNATE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DESIGNATE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://docs.openstack.org/designate/latest/)
+- [Go client](https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_digitalocean.md b/docs/content/dns/zz_gen_digitalocean.md
new file mode 100644
index 00000000..09b31f7a
--- /dev/null
+++ b/docs/content/dns/zz_gen_digitalocean.md
@@ -0,0 +1,52 @@
+---
+title: "Digital Ocean"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: digitalocean
+---
+
+
+
+
+
+
+Configuration for [Digital Ocean](https://www.digitalocean.com/docs/networking/dns/).
+
+
+
+
+- Code: `digitalocean`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DO_AUTH_TOKEN` | Authentication token |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DO_HTTP_TIMEOUT` | API request timeout |
+| `DO_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DO_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DO_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developers.digitalocean.com/documentation/v2/#domain-records)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_dnsimple.md b/docs/content/dns/zz_gen_dnsimple.md
new file mode 100644
index 00000000..0689948b
--- /dev/null
+++ b/docs/content/dns/zz_gen_dnsimple.md
@@ -0,0 +1,53 @@
+---
+title: "DNSimple"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: dnsimple
+---
+
+
+
+
+
+
+Configuration for [DNSimple](https://dnsimple.com/).
+
+
+
+
+- Code: `dnsimple`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DNSIMPLE_BASE_URL` | API endpoint URL |
+| `DNSIMPLE_OAUTH_TOKEN` | OAuth token |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DNSIMPLE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DNSIMPLE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DNSIMPLE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developer.dnsimple.com/v2/)
+- [Go client](https://github.com/dnsimple/dnsimple-go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_dnsmadeeasy.md b/docs/content/dns/zz_gen_dnsmadeeasy.md
new file mode 100644
index 00000000..5841f210
--- /dev/null
+++ b/docs/content/dns/zz_gen_dnsmadeeasy.md
@@ -0,0 +1,54 @@
+---
+title: "DNS Made Easy"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: dnsmadeeasy
+---
+
+
+
+
+
+
+Configuration for [DNS Made Easy](https://dnsmadeeasy.com/).
+
+
+
+
+- Code: `dnsmadeeasy`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DNSMADEEASY_API_KEY` | The API key |
+| `DNSMADEEASY_API_SECRET` | The API Secret key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DNSMADEEASY_HTTP_TIMEOUT` | API request timeout |
+| `DNSMADEEASY_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DNSMADEEASY_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DNSMADEEASY_SANDBOX` | Activate the sandbox (boolean) |
+| `DNSMADEEASY_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://api-docs.dnsmadeeasy.com/)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_dnspod.md b/docs/content/dns/zz_gen_dnspod.md
new file mode 100644
index 00000000..76b0f81c
--- /dev/null
+++ b/docs/content/dns/zz_gen_dnspod.md
@@ -0,0 +1,53 @@
+---
+title: "DNSPod"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: dnspod
+---
+
+
+
+
+
+
+Configuration for [DNSPod](http://www.dnspod.com/).
+
+
+
+
+- Code: `dnspod`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DNSPOD_API_KEY` | The user token |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DNSPOD_HTTP_TIMEOUT` | API request timeout |
+| `DNSPOD_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DNSPOD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DNSPOD_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.dnspod.com/docs/index.html)
+- [Go client](https://github.com/decker502/dnspod-go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_dreamhost.md b/docs/content/dns/zz_gen_dreamhost.md
new file mode 100644
index 00000000..01f90f5f
--- /dev/null
+++ b/docs/content/dns/zz_gen_dreamhost.md
@@ -0,0 +1,52 @@
+---
+title: "DreamHost"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: dreamhost
+---
+
+
+
+
+
+
+Configuration for [DreamHost](https://www.dreamhost.com).
+
+
+
+
+- Code: `dreamhost`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DREAMHOST_API_KEY` | The API key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DREAMHOST_HTTP_TIMEOUT` | API request timeout |
+| `DREAMHOST_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DREAMHOST_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DREAMHOST_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_duckdns.md b/docs/content/dns/zz_gen_duckdns.md
new file mode 100644
index 00000000..3c891588
--- /dev/null
+++ b/docs/content/dns/zz_gen_duckdns.md
@@ -0,0 +1,53 @@
+---
+title: "Duck DNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: duckdns
+---
+
+
+
+
+
+
+Configuration for [Duck DNS](https://www.duckdns.org/).
+
+
+
+
+- Code: `duckdns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DUCKDNS_TOKEN` | Account token |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DUCKDNS_HTTP_TIMEOUT` | API request timeout |
+| `DUCKDNS_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DUCKDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DUCKDNS_SEQUENCE_INTERVAL` | Interval between iteration |
+| `DUCKDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.duckdns.org/spec.jsp)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_dyn.md b/docs/content/dns/zz_gen_dyn.md
new file mode 100644
index 00000000..10271700
--- /dev/null
+++ b/docs/content/dns/zz_gen_dyn.md
@@ -0,0 +1,54 @@
+---
+title: "Dyn"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: dyn
+---
+
+
+
+
+
+
+Configuration for [Dyn](https://dyn.com/).
+
+
+
+
+- Code: `dyn`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `DYN_CUSTOMER_NAME` | Customer name |
+| `DYN_PASSWORD` | Paswword |
+| `DYN_USER_NAME` | User name |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `DYN_HTTP_TIMEOUT` | API request timeout |
+| `DYN_POLLING_INTERVAL` | Time between DNS propagation check |
+| `DYN_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `DYN_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://help.dyn.com/rest/)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_exec.md b/docs/content/dns/zz_gen_exec.md
new file mode 100644
index 00000000..e6114e6a
--- /dev/null
+++ b/docs/content/dns/zz_gen_exec.md
@@ -0,0 +1,133 @@
+---
+title: "External program"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: exec
+---
+
+
+
+
+
+Solving the DNS-01 challenge using an external program.
+
+
+
+
+- Code: `exec`
+
+Here is an example bash command using the External program provider:
+
+```bash
+EXEC_PATH=/the/path/to/myscript.sh \
+lego --dns exec --domains my.domain.com --email my@email.com run
+```
+
+
+
+
+
+## Base Configuration
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `EXEC_MODE` | `RAW`, none |
+| `EXEC_PATH` | TODO |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `EXEC_POLLING_INTERVAL` | Time between DNS propagation check |
+| `EXEC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+
+
+## Description
+
+The file name of the external program is specified in the environment variable `EXEC_PATH`.
+
+When it is run by lego, three command-line parameters are passed to it:
+The action ("present" or "cleanup"), the fully-qualified domain name and the value for the record.
+
+For example, requesting a certificate for the domain 'foo.example.com' can be achieved by calling lego as follows:
+
+```bash
+EXEC_PATH=./update-dns.sh \
+ lego --dns exec \
+ --domains foo.example.com \
+ --email invalid@example.com run
+```
+
+It will then call the program './update-dns.sh' with like this:
+
+```bash
+./update-dns.sh "present" "_acme-challenge.foo.example.com." "MsijOYZxqyjGnFGwhjrhfg-Xgbl5r68WPda0J9EgqqI"
+```
+
+The program then needs to make sure the record is inserted.
+When it returns an error via a non-zero exit code, lego aborts.
+
+When the record is to be removed again,
+the program is called with the first command-line parameter set to `cleanup` instead of `present`.
+
+If you want to use the raw domain, token, and keyAuth values with your program, you can set `EXEC_MODE=RAW`:
+
+```bash
+EXEC_MODE=RAW \
+EXEC_PATH=./update-dns.sh \
+ lego --dns exec \
+ --domains foo.example.com \
+ --email invalid@example.com run
+```
+
+It will then call the program `./update-dns.sh` like this:
+
+```bash
+./update-dns.sh "present" "foo.example.com." "--" "some-token" "KxAy-J3NwUmg9ZQuM-gP_Mq1nStaYSaP9tYQs5_-YsE.ksT-qywTd8058G-SHHWA3RAN72Pr0yWtPYmmY5UBpQ8"
+```
+
+## Commands
+
+{{% notice note %}}
+The `--` is because the token MAY start with a `-`, and the called program may try and interpret a `-` as indicating a flag.
+In the case of urfave, which is commonly used,
+you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely.
+{{% /notice %}}
+
+### Present
+
+| Mode | Command |
+|---------|----------------------------------------------------|
+| default | `myprogram present -- ` |
+| `RAW` | `myprogram present -- ` |
+
+### Cleanup
+
+| Mode | Command |
+|---------|----------------------------------------------------|
+| default | `myprogram cleanup -- ` |
+| `RAW` | `myprogram cleanup -- ` |
+
+### Timeout
+
+The command have to display propagation timeout and polling interval into Stdout.
+
+The values must be formatted as JSON, and times are in seconds.
+Example: `{"timeout": 30, "interval": 5}`
+
+If an error occurs or if the command is not provided:
+the default display propagation timeout and polling interval are used.
+
+| Mode | Command |
+|---------|----------------------------------------------------|
+| default | `myprogram timeout` |
+| `RAW` | `myprogram timeout` |
+
+
+
+
+
+
+
+
diff --git a/docs/content/dns/zz_gen_exoscale.md b/docs/content/dns/zz_gen_exoscale.md
new file mode 100644
index 00000000..f764f3d4
--- /dev/null
+++ b/docs/content/dns/zz_gen_exoscale.md
@@ -0,0 +1,55 @@
+---
+title: "Exoscale"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: exoscale
+---
+
+
+
+
+
+
+Configuration for [Exoscale](https://www.exoscale.com/).
+
+
+
+
+- Code: `exoscale`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `EXOSCALE_API_KEY` | API key |
+| `EXOSCALE_API_SECRET` | API secret |
+| `EXOSCALE_ENDPOINT` | API endpoint URL |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `EXOSCALE_HTTP_TIMEOUT` | API request timeout |
+| `EXOSCALE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `EXOSCALE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `EXOSCALE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://community.exoscale.com/documentation/dns/api/)
+- [Go client](https://github.com/exoscale/egoscale)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_fastdns.md b/docs/content/dns/zz_gen_fastdns.md
new file mode 100644
index 00000000..c46f23b6
--- /dev/null
+++ b/docs/content/dns/zz_gen_fastdns.md
@@ -0,0 +1,55 @@
+---
+title: "FastDNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: fastdns
+---
+
+
+
+
+
+
+Configuration for [FastDNS](https://www.akamai.com/us/en/products/security/fast-dns.jsp).
+
+
+
+
+- Code: `fastdns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `AKAMAI_ACCESS_TOKEN` | Access token |
+| `AKAMAI_CLIENT_SECRET` | Client secret |
+| `AKAMAI_CLIENT_TOKEN` | Client token |
+| `AKAMAI_HOST` | API host |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `AKAMAI_POLLING_INTERVAL` | Time between DNS propagation check |
+| `AKAMAI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `AKAMAI_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developer.akamai.com/api/web_performance/fast_dns_record_management/v1.html)
+- [Go client](https://github.com/akamai/AkamaiOPEN-edgegrid-golang)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_gandi.md b/docs/content/dns/zz_gen_gandi.md
new file mode 100644
index 00000000..feb2aa4d
--- /dev/null
+++ b/docs/content/dns/zz_gen_gandi.md
@@ -0,0 +1,52 @@
+---
+title: "Gandi"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: gandi
+---
+
+
+
+
+
+
+Configuration for [Gandi](https://www.gandi.net).
+
+
+
+
+- Code: `gandi`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `GANDI_API_KEY` | API key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `GANDI_HTTP_TIMEOUT` | API request timeout |
+| `GANDI_POLLING_INTERVAL` | Time between DNS propagation check |
+| `GANDI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `GANDI_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](http://doc.rpc.gandi.net/index.html)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_gandiv5.md b/docs/content/dns/zz_gen_gandiv5.md
new file mode 100644
index 00000000..b3b5ac1b
--- /dev/null
+++ b/docs/content/dns/zz_gen_gandiv5.md
@@ -0,0 +1,52 @@
+---
+title: "Gandi Live DNS (v5)"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: gandiv5
+---
+
+
+
+
+
+
+Configuration for [Gandi Live DNS (v5)](https://www.gandi.net).
+
+
+
+
+- Code: `gandiv5`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `GANDIV5_API_KEY` | API key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `GANDIV5_HTTP_TIMEOUT` | API request timeout |
+| `GANDIV5_POLLING_INTERVAL` | Time between DNS propagation check |
+| `GANDIV5_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `GANDIV5_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](http://doc.livedns.gandi.net)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_gcloud.md b/docs/content/dns/zz_gen_gcloud.md
new file mode 100644
index 00000000..de08207a
--- /dev/null
+++ b/docs/content/dns/zz_gen_gcloud.md
@@ -0,0 +1,55 @@
+---
+title: "Google Cloud"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: gcloud
+---
+
+
+
+
+
+
+Configuration for [Google Cloud](https://cloud.google.com).
+
+
+
+
+- Code: `gcloud`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `Application Default Credentials` | [Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application) |
+| `GCE_PROJECT` | Project name |
+| `GCE_SERVICE_ACCOUNT` | Account |
+| `GCE_SERVICE_ACCOUNT_FILE` | Account file path |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `GCE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `GCE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `GCE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://community.exoscale.com/documentation/dns/api/)
+- [Go client](https://github.com/googleapis/google-api-go-client)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_glesys.md b/docs/content/dns/zz_gen_glesys.md
new file mode 100644
index 00000000..5413d90b
--- /dev/null
+++ b/docs/content/dns/zz_gen_glesys.md
@@ -0,0 +1,53 @@
+---
+title: "Glesys"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: glesys
+---
+
+
+
+
+
+
+Configuration for [Glesys](https://glesys.com/).
+
+
+
+
+- Code: `glesys`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `GLESYS_API_KEY` | API key |
+| `GLESYS_API_USER` | API user |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `GLESYS_HTTP_TIMEOUT` | API request timeout |
+| `GLESYS_POLLING_INTERVAL` | Time between DNS propagation check |
+| `GLESYS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `GLESYS_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://github.com/GleSYS/API/wiki/API-Documentation)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_godaddy.md b/docs/content/dns/zz_gen_godaddy.md
new file mode 100644
index 00000000..1458ed15
--- /dev/null
+++ b/docs/content/dns/zz_gen_godaddy.md
@@ -0,0 +1,54 @@
+---
+title: "Go Daddy"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: godaddy
+---
+
+
+
+
+
+
+Configuration for [Go Daddy](https://godaddy.com).
+
+
+
+
+- Code: `godaddy`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `GODADDY_API_KEY` | **TODO** |
+| `GODADDY_API_SECRET` | **TODO** |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `GODADDY_HTTP_TIMEOUT` | API request timeout |
+| `GODADDY_POLLING_INTERVAL` | Time between DNS propagation check |
+| `GODADDY_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `GODADDY_SEQUENCE_INTERVAL` | Interval between iteration |
+| `GODADDY_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developer.godaddy.com/doc/endpoint/domains)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_hostingde.md b/docs/content/dns/zz_gen_hostingde.md
new file mode 100644
index 00000000..5b927633
--- /dev/null
+++ b/docs/content/dns/zz_gen_hostingde.md
@@ -0,0 +1,53 @@
+---
+title: "Hosting.de"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: hostingde
+---
+
+
+
+
+
+
+Configuration for [Hosting.de](https://www.hosting.de/).
+
+
+
+
+- Code: `hostingde`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `HOSTINGDE_API_KEY` | **TODO** |
+| `HOSTINGDE_ZONE_NAME` | **TODO** |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `HOSTINGDE_HTTP_TIMEOUT` | API request timeout |
+| `HOSTINGDE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `HOSTINGDE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `HOSTINGDE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.hosting.de/api/#dns)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_httpreq.md b/docs/content/dns/zz_gen_httpreq.md
new file mode 100644
index 00000000..05c1cb63
--- /dev/null
+++ b/docs/content/dns/zz_gen_httpreq.md
@@ -0,0 +1,91 @@
+---
+title: "HTTP request"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: httpreq
+---
+
+
+
+
+
+
+Configuration for [HTTP request](/dns/httpreq/).
+
+
+
+
+- Code: `httpreq`
+
+Here is an example bash command using the HTTP request provider:
+
+```bash
+HTTPREQ_ENDPOINT=http://my.server.com:9090 \
+lego --dns httpreq --domains my.domain.com --email my@email.com run
+```
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `HTTPREQ_ENDPOINT` | The URL of the server |
+| `HTTPREQ_MODE` | `RAW`, none |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `HTTPREQ_HTTP_TIMEOUT` | API request timeout |
+| `HTTPREQ_PASSWORD` | **TODO** |
+| `HTTPREQ_POLLING_INTERVAL` | Time between DNS propagation check |
+| `HTTPREQ_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `HTTPREQ_USERNAME` | **TODO** |
+
+## Description
+
+The server must provide:
+
+- `POST` `/present`
+- `POST` `/cleanup`
+
+The URL of the server must be define by `HTTPREQ_ENDPOINT`.
+
+### Mode
+
+There are 2 modes (`HTTPREQ_MODE`):
+
+- default mode:
+```json
+{
+ "fqdn": "_acme-challenge.domain.",
+ "value": "LHDhK3oGRvkiefQnx7OOczTY5Tic_xZ6HcMOc_gmtoM"
+}
+```
+
+- `RAW`
+```json
+{
+ "domain": "domain",
+ "token": "token",
+ "keyAuth": "key"
+}
+```
+
+### Authentication
+
+Basic authentication (optional) can be set with some environment variables:
+
+- `HTTPREQ_USERNAME` and `HTTPREQ_PASSWORD`
+- both values must be set, otherwise basic authentication is not defined.
+
+
+
+
+
+
+
+
diff --git a/docs/content/dns/zz_gen_iij.md b/docs/content/dns/zz_gen_iij.md
new file mode 100644
index 00000000..d1daea31
--- /dev/null
+++ b/docs/content/dns/zz_gen_iij.md
@@ -0,0 +1,54 @@
+---
+title: "Internet Initiative Japan"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: iij
+---
+
+
+
+
+
+
+Configuration for [Internet Initiative Japan](https://www.iij.ad.jp/en/).
+
+
+
+
+- Code: `iij`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `IIJ_API_ACCESS_KEY` | API access key |
+| `IIJ_API_SECRET_KEY` | API secret key |
+| `IIJ_DO_SERVICE_CODE` | DO service code |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `IIJ_POLLING_INTERVAL` | Time between DNS propagation check |
+| `IIJ_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `IIJ_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](http://manual.iij.jp/p2/pubapi/http://manual.iij.jp/p2/pubapi/)
+- [Go client](https://github.com/iij/doapi)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_inwx.md b/docs/content/dns/zz_gen_inwx.md
new file mode 100644
index 00000000..917a7e97
--- /dev/null
+++ b/docs/content/dns/zz_gen_inwx.md
@@ -0,0 +1,54 @@
+---
+title: "INWX"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: inwx
+---
+
+
+
+
+
+
+Configuration for [INWX](https://www.inwx.de/en).
+
+
+
+
+- Code: `inwx`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `INWX_PASSWORD` | Password |
+| `INWX_USERNAME` | Username |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `INWX_POLLING_INTERVAL` | Time between DNS propagation check |
+| `INWX_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `INWX_SANDBOX` | Activate the sandbox (boolean) |
+| `INWX_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.inwx.de/en/help/apidoc)
+- [Go client](https://github.com/nrdcg/goinwx)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_lightsail.md b/docs/content/dns/zz_gen_lightsail.md
new file mode 100644
index 00000000..4e904bf4
--- /dev/null
+++ b/docs/content/dns/zz_gen_lightsail.md
@@ -0,0 +1,53 @@
+---
+title: "Amazon Lightsail"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: lightsail
+---
+
+
+
+
+
+
+Configuration for [Amazon Lightsail](https://aws.amazon.com/lightsail/).
+
+
+
+
+- Code: `lightsail`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `AWS_ACCESS_KEY_ID` | Access key ID |
+| `AWS_SECRET_ACCESS_KEY` | Secret access key |
+| `DNS_ZONE` | DNS zone |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `LIGHTSAIL_POLLING_INTERVAL` | Time between DNS propagation check |
+| `LIGHTSAIL_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+
+
+
+
+## More information
+
+
+- [Go client](https://github.com/aws/aws-sdk-go/aws)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_linode.md b/docs/content/dns/zz_gen_linode.md
new file mode 100644
index 00000000..6b6113b1
--- /dev/null
+++ b/docs/content/dns/zz_gen_linode.md
@@ -0,0 +1,52 @@
+---
+title: "Linode (deprecated)"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: linode
+---
+
+
+
+
+
+
+Configuration for [Linode (deprecated)](https://www.linode.com/).
+
+
+
+
+- Code: `linode`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `LINODE_API_KEY` | API key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `LINODE_HTTP_TIMEOUT` | API request timeout |
+| `LINODE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `LINODE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.linode.com/api/dns)
+- [Go client](https://github.com/timewasted/linode)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_linodev4.md b/docs/content/dns/zz_gen_linodev4.md
new file mode 100644
index 00000000..317b701a
--- /dev/null
+++ b/docs/content/dns/zz_gen_linodev4.md
@@ -0,0 +1,52 @@
+---
+title: "Linode (v4)"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: linodev4
+---
+
+
+
+
+
+
+Configuration for [Linode (v4)](https://www.linode.com/).
+
+
+
+
+- Code: `linodev4`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `LINODE_TOKEN` | API token |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `LINODE_HTTP_TIMEOUT` | API request timeout |
+| `LINODE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `LINODE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developers.linode.com/api/v4)
+- [Go client](https://github.com/linode/linodego)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_mydnsjp.md b/docs/content/dns/zz_gen_mydnsjp.md
new file mode 100644
index 00000000..ac996c02
--- /dev/null
+++ b/docs/content/dns/zz_gen_mydnsjp.md
@@ -0,0 +1,53 @@
+---
+title: "MyDNS.jp"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: mydnsjp
+---
+
+
+
+
+
+
+Configuration for [MyDNS.jp](https://www.mydns.jp).
+
+
+
+
+- Code: `mydnsjp`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `MYDNSJP_MASTER_ID` | Master ID |
+| `MYDNSJP_PASSWORD` | Password |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `MYDNSJP_HTTP_TIMEOUT` | API request timeout |
+| `MYDNSJP_POLLING_INTERVAL` | Time between DNS propagation check |
+| `MYDNSJP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `MYDNSJP_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.mydns.jp/?MENU=030)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_namecheap.md b/docs/content/dns/zz_gen_namecheap.md
new file mode 100644
index 00000000..ba4197dd
--- /dev/null
+++ b/docs/content/dns/zz_gen_namecheap.md
@@ -0,0 +1,53 @@
+---
+title: "Namecheap"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: namecheap
+---
+
+
+
+
+
+
+Configuration for [Namecheap](https://www.namecheap.com).
+
+
+
+
+- Code: `namecheap`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `NAMECHEAP_API_KEY` | API key |
+| `NAMECHEAP_API_USER` | API user |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `NAMECHEAP_HTTP_TIMEOUT` | API request timeout |
+| `NAMECHEAP_POLLING_INTERVAL` | Time between DNS propagation check |
+| `NAMECHEAP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `NAMECHEAP_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.namecheap.com/support/api/methods.aspx)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_namedotcom.md b/docs/content/dns/zz_gen_namedotcom.md
new file mode 100644
index 00000000..7f7ba304
--- /dev/null
+++ b/docs/content/dns/zz_gen_namedotcom.md
@@ -0,0 +1,54 @@
+---
+title: "Name.com"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: namedotcom
+---
+
+
+
+
+
+
+Configuration for [Name.com](https://www.name.com).
+
+
+
+
+- Code: `namedotcom`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `NAMECOM_API_TOKEN` | API token |
+| `NAMECOM_USERNAME` | Username |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `NAMECOM_HTTP_TIMEOUT` | API request timeout |
+| `NAMECOM_POLLING_INTERVAL` | Time between DNS propagation check |
+| `NAMECOM_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `NAMECOM_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.name.com/api-docs/DNS)
+- [Go client](https://github.com/namedotcom/go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_netcup.md b/docs/content/dns/zz_gen_netcup.md
new file mode 100644
index 00000000..4c42da06
--- /dev/null
+++ b/docs/content/dns/zz_gen_netcup.md
@@ -0,0 +1,54 @@
+---
+title: "Netcup"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: netcup
+---
+
+
+
+
+
+
+Configuration for [Netcup](https://www.netcup.eu/).
+
+
+
+
+- Code: `netcup`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `NETCUP_API_KEY` | API key |
+| `NETCUP_API_PASSWORD` | API password |
+| `NETCUP_CUSTOMER_NUMBER` | Customer number |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `NETCUP_HTTP_TIMEOUT` | API request timeout |
+| `NETCUP_POLLING_INTERVAL` | Time between DNS propagation check |
+| `NETCUP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `NETCUP_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.netcup-wiki.de/wiki/DNS_API)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_nifcloud.md b/docs/content/dns/zz_gen_nifcloud.md
new file mode 100644
index 00000000..253b4f7a
--- /dev/null
+++ b/docs/content/dns/zz_gen_nifcloud.md
@@ -0,0 +1,53 @@
+---
+title: "NIFCloud"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: nifcloud
+---
+
+
+
+
+
+
+Configuration for [NIFCloud](https://www.nifcloud.com/).
+
+
+
+
+- Code: `nifcloud`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `NIFCLOUD_ACCESS_KEY_ID` | Access key |
+| `NIFCLOUD_SECRET_ACCESS_KEY` | Secret access key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `NIFCLOUD_HTTP_TIMEOUT` | API request timeout |
+| `NIFCLOUD_POLLING_INTERVAL` | Time between DNS propagation check |
+| `NIFCLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `NIFCLOUD_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://mbaas.nifcloud.com/doc/current/rest/common/format.html)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_ns1.md b/docs/content/dns/zz_gen_ns1.md
new file mode 100644
index 00000000..609b1e41
--- /dev/null
+++ b/docs/content/dns/zz_gen_ns1.md
@@ -0,0 +1,53 @@
+---
+title: "NS1"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: ns1
+---
+
+
+
+
+
+
+Configuration for [NS1](https://ns1.com).
+
+
+
+
+- Code: `ns1`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `NS1_API_KEY` | API key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `NS1_HTTP_TIMEOUT` | API request timeout |
+| `NS1_POLLING_INTERVAL` | Time between DNS propagation check |
+| `NS1_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `NS1_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://ns1.com/api)
+- [Go client](https://github.com/ns1/ns1-go)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_oraclecloud.md b/docs/content/dns/zz_gen_oraclecloud.md
new file mode 100644
index 00000000..42bad2ea
--- /dev/null
+++ b/docs/content/dns/zz_gen_oraclecloud.md
@@ -0,0 +1,67 @@
+---
+title: "Oracle Cloud"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: oraclecloud
+---
+
+
+
+
+
+
+Configuration for [Oracle Cloud](https://cloud.oracle.com/home).
+
+
+
+
+- Code: `oraclecloud`
+
+Here is an example bash command using the Oracle Cloud provider:
+
+```bash
+OCI_PRIVKEY_FILE="~/.oci/oci_api_key.pem" \
+OCI_PRIVKEY_PASS="secret" \
+OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" \
+OCI_USER_OCID="ocid1.user.oc1..secret" \
+OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \
+OCI_REGION="us-phoenix-1" \
+OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" \
+lego --dns oraclecloud --domains my.domain.com --email my@email.com run
+```
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `OCI_COMPARTMENT_OCID` | Compartment OCID |
+| `OCI_PRIVKEY_FILE` | Private key file |
+| `OCI_PRIVKEY_PASS` | Private key password |
+| `OCI_PUBKEY_FINGERPRINT` | Public key fingerprint |
+| `OCI_REGION` | Region |
+| `OCI_TENANCY_OCID` | Tenanct OCID |
+| `OCI_USER_OCID` | User OCID |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `OCI_POLLING_INTERVAL` | Time between DNS propagation check |
+| `OCI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `OCI_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm)
+- [Go client](https://github.com/oracle/oci-go-sdk)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_otc.md b/docs/content/dns/zz_gen_otc.md
new file mode 100644
index 00000000..119bcfc5
--- /dev/null
+++ b/docs/content/dns/zz_gen_otc.md
@@ -0,0 +1,56 @@
+---
+title: "Open Telekom Cloud"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: otc
+---
+
+
+
+
+
+
+Configuration for [Open Telekom Cloud](https://cloud.telekom.de/en).
+
+
+
+
+- Code: `otc`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `OTC_DOMAIN_NAME` | Domain name |
+| `OTC_IDENTITY_ENDPOINT` | Identity endpoint URL |
+| `OTC_PASSWORD` | Password |
+| `OTC_PROJECT_NAME` | Project name |
+| `OTC_USER_NAME` | User name |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `OTC_HTTP_TIMEOUT` | API request timeout |
+| `OTC_POLLING_INTERVAL` | Time between DNS propagation check |
+| `OTC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `OTC_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://docs.otc.t-systems.com/en-us/dns/index.html)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_ovh.md b/docs/content/dns/zz_gen_ovh.md
new file mode 100644
index 00000000..5b897df4
--- /dev/null
+++ b/docs/content/dns/zz_gen_ovh.md
@@ -0,0 +1,56 @@
+---
+title: "OVH"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: ovh
+---
+
+
+
+
+
+
+Configuration for [OVH](https://www.ovh.com/).
+
+
+
+
+- Code: `ovh`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `OVH_APPLICATION_KEY` | Application key |
+| `OVH_APPLICATION_SECRET` | Application secret |
+| `OVH_CONSUMER_KEY` | Consumer key |
+| `OVH_ENDPOINT` | Endpoint URL (ovh-eu or ovh-ca) |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `OVH_HTTP_TIMEOUT` | API request timeout |
+| `OVH_POLLING_INTERVAL` | Time between DNS propagation check |
+| `OVH_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `OVH_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://eu.api.ovh.com/)
+- [Go client](https://github.com/ovh/go-ovh)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_pdns.md b/docs/content/dns/zz_gen_pdns.md
new file mode 100644
index 00000000..f8834e9e
--- /dev/null
+++ b/docs/content/dns/zz_gen_pdns.md
@@ -0,0 +1,60 @@
+---
+title: "PowerDNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: pdns
+---
+
+
+
+
+
+
+Configuration for [PowerDNS](https://www.powerdns.com/).
+
+
+
+
+- Code: `pdns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `PDNS_API_KEY` | API key |
+| `PDNS_API_URL` | API url |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `PDNS_HTTP_TIMEOUT` | API request timeout |
+| `PDNS_POLLING_INTERVAL` | Time between DNS propagation check |
+| `PDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `PDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+## Information
+
+Tested and confirmed to work with PowerDNS authoritative server 3.4.8 and 4.0.1. Refer to [PowerDNS documentation](https://doc.powerdns.com/md/httpapi/README/) instructions on how to enable the built-in API interface.
+
+PowerDNS Notes:
+- PowerDNS API does not currently support SSL, therefore you should take care to ensure that traffic between lego and the PowerDNS API is over a trusted network, VPN etc.
+- In order to have the SOA serial automatically increment each time the `_acme-challenge` record is added/modified via the API, set `SOA-EDIT-API` to `INCEPTION-INCREMENT` for the zone in the `domainmetadata` table
+
+
+
+## More information
+
+- [API documentation](https://doc.powerdns.com/md/httpapi/README/)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_rackspace.md b/docs/content/dns/zz_gen_rackspace.md
new file mode 100644
index 00000000..67113418
--- /dev/null
+++ b/docs/content/dns/zz_gen_rackspace.md
@@ -0,0 +1,53 @@
+---
+title: "Rackspace"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: rackspace
+---
+
+
+
+
+
+
+Configuration for [Rackspace](https://www.rackspace.com/).
+
+
+
+
+- Code: `rackspace`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `RACKSPACE_API_KEY` | API key |
+| `RACKSPACE_USER` | API user |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `RACKSPACE_HTTP_TIMEOUT` | API request timeout |
+| `RACKSPACE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `RACKSPACE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `RACKSPACE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developer.rackspace.com/docs/cloud-dns/v1/)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_rfc2136.md b/docs/content/dns/zz_gen_rfc2136.md
new file mode 100644
index 00000000..22140092
--- /dev/null
+++ b/docs/content/dns/zz_gen_rfc2136.md
@@ -0,0 +1,56 @@
+---
+title: "RFC2136"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: rfc2136
+---
+
+
+
+
+
+
+Configuration for [RFC2136](https://tools.ietf.org/html/rfc2136).
+
+
+
+
+- Code: `rfc2136`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `RFC2136_NAMESERVER` | Network address in the form "host" or "host:port" |
+| `RFC2136_TSIG_ALGORITHM` | TSIG algorythm. See [miekg/dns#tsig.go](https://github.com/miekg/dns/blob/master/tsig.go) for supported values. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset. |
+| `RFC2136_TSIG_KEY` | Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset. |
+| `RFC2136_TSIG_SECRET` | Secret key payload. To disable TSIG authentication, leave the` RFC2136_TSIG*` variables unset. |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `RFC2136_DNS_TIMEOUT` | API request timeout |
+| `RFC2136_POLLING_INTERVAL` | Time between DNS propagation check |
+| `RFC2136_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `RFC2136_SEQUENCE_INTERVAL` | Interval between iteration |
+| `RFC2136_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://tools.ietf.org/html/rfc2136)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_route53.md b/docs/content/dns/zz_gen_route53.md
new file mode 100644
index 00000000..48ae58b7
--- /dev/null
+++ b/docs/content/dns/zz_gen_route53.md
@@ -0,0 +1,98 @@
+---
+title: "Amazon Route 53"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: route53
+---
+
+
+
+
+
+
+Configuration for [Amazon Route 53](https://aws.amazon.com/route53/).
+
+
+
+
+- Code: `route53`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `AWS_ACCESS_KEY_ID` | |
+| `AWS_HOSTED_ZONE_ID` | |
+| `AWS_REGION` | |
+| `AWS_SECRET_ACCESS_KEY` | |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `AWS_POLLING_INTERVAL` | Time between DNS propagation check |
+| `AWS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `AWS_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+## Description
+
+AWS Credentials are automatically detected in the following locations and prioritized in the following order:
+
+1. Environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, [`AWS_SESSION_TOKEN`]
+2. Shared credentials file (defaults to `~/.aws/credentials`)
+3. Amazon EC2 IAM role
+
+If `AWS_HOSTED_ZONE_ID` is not set, Lego tries to determine the correct public hosted zone via the FQDN.
+
+See also: [configuring-sdk](https://github.com/aws/aws-sdk-go/wiki/configuring-sdk)
+
+## Policy
+
+The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge.
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "",
+ "Effect": "Allow",
+ "Action": [
+ "route53:GetChange",
+ "route53:ChangeResourceRecordSets",
+ "route53:ListResourceRecordSets"
+ ],
+ "Resource": [
+ "arn:aws:route53:::hostedzone/*",
+ "arn:aws:route53:::change/*"
+ ]
+ },
+ {
+ "Sid": "",
+ "Effect": "Allow",
+ "Action": "route53:ListHostedZonesByName",
+ "Resource": "*"
+ }
+ ]
+}
+```
+
+
+
+
+## More information
+
+- [API documentation](https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html)
+- [Go client](https://github.com/aws/aws-sdk-go/aws)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_sakuracloud.md b/docs/content/dns/zz_gen_sakuracloud.md
new file mode 100644
index 00000000..69b734d3
--- /dev/null
+++ b/docs/content/dns/zz_gen_sakuracloud.md
@@ -0,0 +1,53 @@
+---
+title: "Sakura Cloud"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: sakuracloud
+---
+
+
+
+
+
+
+Configuration for [Sakura Cloud](https://cloud.sakura.ad.jp/).
+
+
+
+
+- Code: `sakuracloud`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `SAKURACLOUD_ACCESS_TOKEN` | Access token |
+| `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Access token secret |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `SAKURACLOUD_POLLING_INTERVAL` | Time between DNS propagation check |
+| `SAKURACLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `SAKURACLOUD_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developer.sakura.ad.jp/cloud/api/1.1/)
+- [Go client](https://github.com/sacloud/libsacloud)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_selectel.md b/docs/content/dns/zz_gen_selectel.md
new file mode 100644
index 00000000..ba452036
--- /dev/null
+++ b/docs/content/dns/zz_gen_selectel.md
@@ -0,0 +1,53 @@
+---
+title: "Selectel"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: selectel
+---
+
+
+
+
+
+
+Configuration for [Selectel](https://kb.selectel.com/).
+
+
+
+
+- Code: `selectel`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `SELECTEL_API_TOKEN` | API token |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `SELECTEL_BASE_URL` | API endpoint URL |
+| `SELECTEL_HTTP_TIMEOUT` | API request timeout |
+| `SELECTEL_POLLING_INTERVAL` | Time between DNS propagation check |
+| `SELECTEL_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `SELECTEL_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://kb.selectel.com/23136054.html)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_stackpath.md b/docs/content/dns/zz_gen_stackpath.md
new file mode 100644
index 00000000..04047729
--- /dev/null
+++ b/docs/content/dns/zz_gen_stackpath.md
@@ -0,0 +1,53 @@
+---
+title: "Stackpath"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: stackpath
+---
+
+
+
+
+
+
+Configuration for [Stackpath](https://www.stackpath.com/).
+
+
+
+
+- Code: `stackpath`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `STACKPATH_CLIENT_ID` | Client ID |
+| `STACKPATH_CLIENT_SECRET` | Client secret |
+| `STACKPATH_STACK_ID` | Stack ID |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `STACKPATH_POLLING_INTERVAL` | Time between DNS propagation check |
+| `STACKPATH_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `STACKPATH_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developer.stackpath.com/en/api/dns/#tag/Zone)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_transip.md b/docs/content/dns/zz_gen_transip.md
new file mode 100644
index 00000000..ad3457b1
--- /dev/null
+++ b/docs/content/dns/zz_gen_transip.md
@@ -0,0 +1,53 @@
+---
+title: "TransIP"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: transip
+---
+
+
+
+
+
+
+Configuration for [TransIP](https://www.transip.nl/).
+
+
+
+
+- Code: `transip`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `TRANSIP_ACCOUNT_NAME` | Account name |
+| `TRANSIP_PRIVATE_KEY_PATH` | Private key path |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `TRANSIP_POLLING_INTERVAL` | Time between DNS propagation check |
+| `TRANSIP_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `TRANSIP_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://api.transip.nl/docs/transip.nl/package-Transip.html)
+- [Go client](https://github.com/transip/gotransip)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_vegadns.md b/docs/content/dns/zz_gen_vegadns.md
new file mode 100644
index 00000000..3958de8f
--- /dev/null
+++ b/docs/content/dns/zz_gen_vegadns.md
@@ -0,0 +1,54 @@
+---
+title: "VegaDNS"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: vegadns
+---
+
+
+
+
+
+
+Configuration for [VegaDNS](https://github.com/shupp/VegaDNS-API).
+
+
+
+
+- Code: `vegadns`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `SECRET_VEGADNS_KEY` | API key |
+| `SECRET_VEGADNS_SECRET` | API secret |
+| `VEGADNS_URL` | API endpoint URL |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `VEGADNS_POLLING_INTERVAL` | Time between DNS propagation check |
+| `VEGADNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `VEGADNS_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://github.com/shupp/VegaDNS-API)
+- [Go client](https://github.com/OpenDNS/vegadns2client)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_vscale.md b/docs/content/dns/zz_gen_vscale.md
new file mode 100644
index 00000000..76e09c94
--- /dev/null
+++ b/docs/content/dns/zz_gen_vscale.md
@@ -0,0 +1,53 @@
+---
+title: "Vscale"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: vscale
+---
+
+
+
+
+
+
+Configuration for [Vscale](https://vscale.io/).
+
+
+
+
+- Code: `vscale`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `VSCALE_API_TOKEN` | API token |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `VSCALE_BASE_URL` | API enddpoint URL |
+| `VSCALE_HTTP_TIMEOUT` | API request timeout |
+| `VSCALE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `VSCALE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `VSCALE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://developers.vscale.io/documentation/api/v1/#api-Domains_Records)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_vultr.md b/docs/content/dns/zz_gen_vultr.md
new file mode 100644
index 00000000..8d7ad724
--- /dev/null
+++ b/docs/content/dns/zz_gen_vultr.md
@@ -0,0 +1,53 @@
+---
+title: "Vultr"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: vultr
+---
+
+
+
+
+
+
+Configuration for [Vultr](https://www.vultr.com/).
+
+
+
+
+- Code: `vultr`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `VULTR_API_KEY` | API key |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `VULTR_HTTP_TIMEOUT` | API request timeout |
+| `VULTR_POLLING_INTERVAL` | Time between DNS propagation check |
+| `VULTR_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `VULTR_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://www.vultr.com/api/#dns)
+- [Go client](https://github.com/JamesClonk/vultr)
+
+
+
+
diff --git a/docs/content/dns/zz_gen_zoneee.md b/docs/content/dns/zz_gen_zoneee.md
new file mode 100644
index 00000000..a04398fc
--- /dev/null
+++ b/docs/content/dns/zz_gen_zoneee.md
@@ -0,0 +1,54 @@
+---
+title: "Zone.ee"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: zoneee
+---
+
+
+
+
+
+
+Configuration for [Zone.ee](https://www.zone.ee/).
+
+
+
+
+- Code: `zoneee`
+
+{{% notice note %}}
+_Please contribute by adding a CLI example._
+{{% /notice %}}
+
+
+
+
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `ZONEEE_API_KEY` | API key |
+| `ZONEEE_API_USER` | API user |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `ZONEEE_ENDPOINT` | API endpoint URL |
+| `ZONEEE_HTTP_TIMEOUT` | API request timeout |
+| `ZONEEE_POLLING_INTERVAL` | Time between DNS propagation check |
+| `ZONEEE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
+| `ZONEEE_TTL` | The TTL of the TXT record used for the DNS challenge |
+
+
+
+
+## More information
+
+- [API documentation](https://api.zone.eu/v2)
+
+
+
+
diff --git a/docs/content/installation/_index.md b/docs/content/installation/_index.md
new file mode 100644
index 00000000..4858945a
--- /dev/null
+++ b/docs/content/installation/_index.md
@@ -0,0 +1,35 @@
+---
+title: "Installation"
+date: 2019-03-03T16:39:46+01:00
+weight: 1
+draft: false
+---
+
+## Binaries
+
+To get the binary just download the latest release for your OS/Arch from [the release page](https://github.com/xenolf/lego/releases) and put the binary somewhere convenient.
+lego does not assume anything about the location you run it from.
+
+## From Docker
+
+```bash
+docker run xenolf/lego -h
+```
+
+## From package managers
+
+- [ArchLinux (AUR)](https://aur.archlinux.org/packages/lego):
+
+```bash
+yay -S lego
+```
+
+**Note**: only the package manager for Arch Linux is officially supported by the lego team.
+
+## From sources
+
+To install from sources, just run:
+
+```bash
+go get -u github.com/xenolf/lego/cmd/lego
+```
diff --git a/docs/content/usage/_index.md b/docs/content/usage/_index.md
new file mode 100644
index 00000000..7a4b3f48
--- /dev/null
+++ b/docs/content/usage/_index.md
@@ -0,0 +1,8 @@
+---
+title: "Usage"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+weight: 2
+---
+
+{{%children style="h2" description="true" %}}
diff --git a/docs/content/usage/cli/_index.md b/docs/content/usage/cli/_index.md
new file mode 100644
index 00000000..3b3c0e83
--- /dev/null
+++ b/docs/content/usage/cli/_index.md
@@ -0,0 +1,94 @@
+---
+title: "CLI"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+---
+
+Lego can be use as a CLI.
+
+
+
+## Usage
+
+{{%expand "CLI help" %}}
+```slim
+NAME:
+ lego - Let's Encrypt client written in Go
+
+USAGE:
+ lego [global options] command [command options] [arguments...]
+
+COMMANDS:
+ run Register an account, then create and install a certificate
+ revoke Revoke a certificate
+ renew Renew a certificate
+ dnshelp Shows additional help for the --dns global option
+ list Display certificates and accounts information.
+ help, h Shows a list of commands or help for one command
+
+GLOBAL OPTIONS:
+ --domains value, -d value Add a domain to the process. Can be specified multiple times.
+ --server value, -s value CA hostname (and optionally :port). The server certificate must be trusted in order to avoid further modifications to the client. (default: "https://acme-v02.api.letsencrypt.org/directory")
+ --accept-tos, -a By setting this flag to true you indicate that you accept the current Let's Encrypt terms of service.
+ --email value, -m value Email used for registration and recovery contact.
+ --csr value, -c value Certificate signing request filename, if an external CSR is to be used.
+ --eab Use External Account Binding for account registration. Requires --kid and --hmac.
+ --kid value Key identifier from External CA. Used for External Account Binding.
+ --hmac value MAC key from External CA. Should be in Base64 URL Encoding without padding format. Used for External Account Binding.
+ --key-type value, -k value Key type to use for private keys. Supported: rsa2048, rsa4096, rsa8192, ec256, ec384. (default: "rsa2048")
+ --filename value (deprecated) Filename of the generated certificate.
+ --path value Directory to use for storing the data. (default: "./.lego")
+ --http Use the HTTP challenge to solve challenges. Can be mixed with other types of challenges.
+ --http.port value Set the port and interface to use for HTTP based challenges to listen on.Supported: interface:port or :port. (default: ":80")
+ --http.webroot value Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge.
+ --http.memcached-host value Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.
+ --tls Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.
+ --tls.port value Set the port and interface to use for TLS based challenges to listen on. Supported: interface:port or :port. (default: ":443")
+ --dns value Solve a DNS challenge using the specified provider. Can be mixed with other types of challenges. Run 'lego dnshelp' for help on usage.
+ --dns.disable-cp By setting this flag to true, disables the need to wait the propagation of the TXT record to all authoritative name servers.
+ --dns.resolvers value Set the resolvers to use for performing recursive DNS queries. Supported: host:port. The default is to use the system resolvers, or Google's DNS resolvers if the system's cannot be determined.
+ --http-timeout value Set the HTTP timeout value to a specific value in seconds. (default: 0)
+ --dns-timeout value Set the DNS timeout value to a specific value in seconds. Used only when performing authoritative name servers queries. (default: 10)
+ --pem Generate a .pem file by concatenating the .key and .crt files together.
+ --cert.timeout value Set the certificate timeout value to a specific value in seconds. Only used when obtaining certificates. (default: 30)
+ --help, -h show help
+ --version, -v print the version
+```
+{{% /expand%}}
+
+
+When using the standard `--path` option, all certificates and account configurations are saved to a folder `.lego` in the current working directory.
+
+
+## Let's Encrypt ACME server
+
+lego defaults to communicating with the production Let's Encrypt ACME server.
+If you'd like to test something without issuing real certificates, consider using the staging endpoint instead:
+
+```bash
+lego --server=https://acme-staging-v02.api.letsencrypt.org/directory …
+```
+
+## Sudo
+
+The CLI does not require root permissions but needs to bind to port 80 and 443 for certain challenges.
+To run the CLI without sudo, you have four options:
+
+- Use setcap 'cap_net_bind_service=+ep' /path/to/program
+- Pass the `--http.port` or/and the `--tls.port` option and specify a custom port to bind to. In this case you have to forward port 80/443 to these custom ports (see [Port Usage](usage/cli#port-usage)).
+- Pass the `--http.webroot` option and specify the path to your webroot folder. In this case the challenge will be written in a file in `.well-known/acme-challenge/` inside your webroot.
+- Pass the `--dns` option and specify a DNS provider.
+
+## Port Usage
+
+By default lego assumes it is able to bind to ports 80 and 443 to solve challenges.
+If this is not possible in your environment, you can use the `--http.port` and `--tls.port` options to instruct
+lego to listen on that interface:port for any incoming challenges.
+
+If you are using this option, make sure you proxy all of the following traffic to these ports.
+
+**HTTP Port:** All plaintext HTTP requests to port **80** which begin with a request path of `/.well-known/acme-challenge/` for the HTTP challenge.
+
+**TLS Port:** All TLS handshakes on port **443** for the TLS-ALPN challenge.
+
+This traffic redirection is only needed as long as lego solves challenges. As soon as you have received your certificates you can deactivate the forwarding.
diff --git a/docs/content/usage/cli/examples.md b/docs/content/usage/cli/examples.md
new file mode 100644
index 00000000..9b6c6072
--- /dev/null
+++ b/docs/content/usage/cli/examples.md
@@ -0,0 +1,48 @@
+---
+title: "Examples"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+---
+
+## CLI Examples
+
+Assumes the `lego` binary has permission to bind to ports 80 and 443.
+You can get a pre-built binary from the [releases](https://github.com/xenolf/lego/releases) page.
+If your environment does not allow you to bind to these ports, please read [Port Usage](usage/cli#port-usage).
+
+### Obtain a certificate
+
+```bash
+lego --email="foo@bar.com" --domains="example.com" --http run
+```
+
+(Find your certificate in the `.lego` folder of current working directory.)
+
+### To renew the certificate
+
+```bash
+lego --email="foo@bar.com" --domains="example.com" --http renew
+```
+
+### To renew the certificate only if it expires within 45 days
+
+```bash
+lego --email="foo@bar.com" --domains="example.com" --http renew --days 45
+```
+
+### Obtain a certificate using the DNS challenge
+
+```bash
+AWS_REGION=us-east-1 \
+AWS_ACCESS_KEY_ID=my_id \
+AWS_SECRET_ACCESS_KEY=my_key \
+lego --email="foo@bar.com" --domains="example.com" --dns="route53" run
+```
+
+### Obtain a certificate given a certificate signing request (CSR) generated by something else
+
+```bash
+lego --email="foo@bar.com" --http --csr=/path/to/csr.pem run
+```
+
+(lego will infer the domains to be validated based on the contents of the CSR, so make sure the CSR's Common Name and optional SubjectAltNames are set correctly.)
diff --git a/docs/content/usage/library/Writing-a-Challenge-Solver.md b/docs/content/usage/library/Writing-a-Challenge-Solver.md
new file mode 100644
index 00000000..f9edae2a
--- /dev/null
+++ b/docs/content/usage/library/Writing-a-Challenge-Solver.md
@@ -0,0 +1,103 @@
+---
+title: "Writing a Challenge Solver"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+---
+
+Lego can solve multiple ACME challenge types out of the box, but sometimes you have custom requirements.
+
+
+
+For example, you may want to write a solver for the DNS-01 challenge that works with a different DNS provider (lego already supports CloudFlare, AWS, DigitalOcean, and others).
+
+The DNS-01 challenge is advantageous when other challenge types are impossible.
+For example, the HTTP-01 challenge doesn't work well behind a load balancer or CDN and the TLS-ALPN-01 challenge breaks behind TLS termination.
+
+But even if using HTTP-01 or TLS-ALPN-01 challenges, you may have specific needs that lego does not consider by default.
+
+You can write something called a `challenge.Provider` that implements [this interface](https://godoc.org/github.com/xenolf/lego/challenge#Provider):
+
+```go
+type Provider interface {
+ Present(domain, token, keyAuth string) error
+ CleanUp(domain, token, keyAuth string) error
+}
+```
+
+This provides the means to solve a challenge.
+First you present a token to the ACME server in a way defined by the challenge type you're solving for, then you "clean up" after the challenge finishes.
+
+## Writing a challenge.Provider
+
+Pretend we want to write our own DNS-01 challenge provider (other challenge types have different requirements but the same principles apply).
+
+This will let us prove ownership of domain names parked at a new, imaginary DNS service called BestDNS without having to start our own HTTP server.
+BestDNS has an API that, given an authentication token, allows us to manipulate DNS records.
+
+This simplistic example has only one field to store the auth token, but in reality you may need to keep more state.
+
+```go
+type DNSProviderBestDNS struct {
+ apiAuthToken string
+}
+```
+
+We should provide a constructor that returns a *pointer* to the `struct`.
+This is important in case we need to maintain state in the `struct`.
+
+```go
+func NewDNSProviderBestDNS(apiAuthToken string) (*DNSProviderBestDNS, error) {
+ return &DNSProviderBestDNS{apiAuthToken: apiAuthToken}, nil
+}
+```
+
+Now we need to implement the interface.
+We'll start with the `Present` method.
+You'll be passed the `domain` name for which you're proving ownership, a `token`, and a `keyAuth` string.
+How your provider uses `token` and `keyAuth`, or if you even use them at all, depends on the challenge type.
+For DNS-01, we'll just use `domain` and `keyAuth`.
+
+```go
+func (d *DNSProviderBestDNS) Present(domain, token, keyAuth string) error {
+ fqdn, value := dns01.GetRecord(domain, keyAuth)
+ // make API request to set a TXT record on fqdn with value and ttl
+ return nil
+}
+```
+
+After calling `dns01.GetRecord(domain, keyAuth)`, we now have the information we need to make our API request and set the TXT record:
+- `fqdn` is the fully qualified domain name on which to set the TXT record.
+- `value` is the record's value to set on the record.
+
+So then you make an API request to the DNS service according to their docs.
+Once the TXT record is set on the domain, you may return and the challenge will proceed.
+
+The ACME server will then verify that you did what it required you to do, and once it is finished, lego will call your `CleanUp` method.
+In our case, we want to remove the TXT record we just created.
+
+```go
+func (d *DNSProviderBestDNS) CleanUp(domain, token, keyAuth string) error {
+ // clean up any state you created in Present, like removing the TXT record
+}
+```
+
+In our case, we'd just make another API request to have the DNS record deleted; no need to keep it and clutter the zone file.
+
+## Using your new challenge.Provider
+
+To use your new challenge provider, call [`client.Challenge.SetDNS01Provider`](https://godoc.org/github.com/xenolf/lego/challenge/resolver#SolverManager.SetDNS01Provider) to tell lego, "For this challenge, use this provider".
+In our case:
+
+```go
+bestDNS, err := NewDNSProviderBestDNS("my-auth-token")
+if err != nil {
+ return err
+}
+
+client.Challenge.SetDNS01Provider(bestDNS)
+```
+
+Then, when this client tries to solve the DNS-01 challenge, it will use our new provider, which sets TXT records on a domain name hosted by BestDNS.
+
+That's really all there is to it.
+Go make awesome things!
diff --git a/docs/content/usage/library/_index.md b/docs/content/usage/library/_index.md
new file mode 100644
index 00000000..a697511b
--- /dev/null
+++ b/docs/content/usage/library/_index.md
@@ -0,0 +1,115 @@
+---
+title: "Library"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+---
+
+Lego can be use as a Go Library.
+
+
+
+## GoDoc
+
+The GoDoc can be found here: [GoDoc](https://godoc.org/github.com/xenolf/lego/acme)
+
+## Usage
+
+A valid, but bare-bones example use of the acme package:
+
+```go
+package main
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "fmt"
+ "log"
+
+ "github.com/xenolf/lego/certcrypto"
+ "github.com/xenolf/lego/certificate"
+ "github.com/xenolf/lego/challenge/http01"
+ "github.com/xenolf/lego/challenge/tlsalpn01"
+ "github.com/xenolf/lego/lego"
+ "github.com/xenolf/lego/registration"
+)
+
+// You'll need a user or account type that implements acme.User
+type MyUser struct {
+ Email string
+ Registration *registration.Resource
+ key crypto.PrivateKey
+}
+
+func (u *MyUser) GetEmail() string {
+ return u.Email
+}
+func (u MyUser) GetRegistration() *registration.Resource {
+ return u.Registration
+}
+func (u *MyUser) GetPrivateKey() crypto.PrivateKey {
+ return u.key
+}
+
+func main() {
+
+ // Create a user. New accounts need an email and private key to start.
+ privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ myUser := MyUser{
+ Email: "you@yours.com",
+ key: privateKey,
+ }
+
+ config := lego.NewConfig(&myUser)
+
+ // This CA URL is configured for a local dev instance of Boulder running in Docker in a VM.
+ config.CADirURL = "http://192.168.99.100:4000/directory"
+ config.Certificate.KeyType = certcrypto.RSA2048
+
+ // A client facilitates communication with the CA server.
+ client, err := lego.NewClient(config)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // We specify an http port of 5002 and an tls port of 5001 on all interfaces
+ // because we aren't running as root and can't bind a listener to port 80 and 443
+ // (used later when we attempt to pass challenges). Keep in mind that you still
+ // need to proxy challenge traffic to port 5002 and 5001.
+ err = client.Challenge.SetHTTP01Provider(http01.NewProviderServer("", "5002"))
+ if err != nil {
+ log.Fatal(err)
+ }
+ err = client.Challenge.SetTLSALPN01Provider(tlsalpn01.NewProviderServer("", "5001"))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // New users will need to register
+ reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
+ if err != nil {
+ log.Fatal(err)
+ }
+ myUser.Registration = reg
+
+ request := certificate.ObtainRequest{
+ Domains: []string{"mydomain.com"},
+ Bundle: true,
+ }
+ certificates, err := client.Certificate.Obtain(request)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Each certificate comes back with the cert bytes, the bytes of the client's
+ // private key, and a certificate URL. SAVE THESE TO DISK.
+ fmt.Printf("%#v\n", certificates)
+
+ // ... all done.
+}
+```
diff --git a/docs/layouts/partials/logo.html b/docs/layouts/partials/logo.html
new file mode 100644
index 00000000..f91d1ddd
--- /dev/null
+++ b/docs/layouts/partials/logo.html
@@ -0,0 +1 @@
+
diff --git a/docs/static/images/logo-white.png b/docs/static/images/logo-white.png
new file mode 100644
index 00000000..598c5da7
Binary files /dev/null and b/docs/static/images/logo-white.png differ
diff --git a/docs/static/images/logo.png b/docs/static/images/logo.png
new file mode 100644
index 00000000..bee263a4
Binary files /dev/null and b/docs/static/images/logo.png differ
diff --git a/internal/dnsdocs/dns.go.tmpl b/internal/dnsdocs/dns.go.tmpl
new file mode 100644
index 00000000..6527775d
--- /dev/null
+++ b/internal/dnsdocs/dns.go.tmpl
@@ -0,0 +1,53 @@
+package cmd
+
+// CODE GENERATED AUTOMATICALLY
+// THIS FILE MUST NOT BE EDITED BY HAND
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/xenolf/lego/log"
+)
+
+func allDNSCodes() string {
+ return strings.Join([]string{
+{{- range $provider := .Providers }}
+ "{{ $provider.Code }}",
+{{- end}}
+ }, ", ")
+}
+
+func displayDNSHelp(name string) {
+ w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
+ switch name {
+{{ range $provider := .Providers }}
+ case "{{ $provider.Code }}":
+ // generated from: {{ .GeneratedFrom }}
+ fmt.Fprintln(w, `Configuration for {{ $provider.Name }}.`)
+ fmt.Fprintln(w, `Code: '{{ $provider.Code }}'`)
+ fmt.Fprintln(w)
+{{if $provider.Configuration }}{{if $provider.Configuration.Credentials }}
+ fmt.Fprintln(w, `Credentials:`)
+{{- range $k, $v := $provider.Configuration.Credentials }}
+ fmt.Fprintln(w,` - "{{ $k }}": {{ safe $v }}`)
+{{- end}}
+ fmt.Fprintln(w)
+{{end}}{{if $provider.Configuration.Additional }}
+ fmt.Fprintln(w, `Additional Configuration:`)
+{{- range $k, $v := $provider.Configuration.Additional }}
+ fmt.Fprintln(w, ` - "{{ $k }}": {{ safe $v }}`)
+{{- end}}
+{{end}}{{end}}
+ fmt.Fprintln(w)
+ fmt.Fprintln(w, `More information: https://xenolf.github.io/lego/dns/{{ $provider.Code }}`)
+{{end}}
+ case "manual":
+ fmt.Fprintln(w, `Solving the DNS-01 challenge using CLI prompt.`)
+ default:
+ log.Fatalf("%q is not yet supported.", name)
+ }
+ w.Flush()
+}
\ No newline at end of file
diff --git a/internal/dnsdocs/dns.md.tmpl b/internal/dnsdocs/dns.md.tmpl
new file mode 100644
index 00000000..4f01412c
--- /dev/null
+++ b/internal/dnsdocs/dns.md.tmpl
@@ -0,0 +1,73 @@
+---
+title: "{{ .Name }}"
+date: 2019-03-03T16:39:46+01:00
+draft: false
+slug: {{ .Code }}
+---
+
+
+
+
+
+{{if .Description -}}
+{{ .Description }}
+{{else}}
+Configuration for [{{ .Name }}]({{ .URL }}).
+{{end}}
+
+
+
+- Code: `{{ .Code }}`
+{{if .Example }}
+Here is an example bash command using the {{ .Name }} provider:
+
+```bash
+{{ .Example -}}
+```
+{{else}}
+{{ "{{" }}% notice note %}}
+_Please contribute by adding a CLI example._
+{{ "{{" }}% /notice %}}
+{{end}}
+
+{{if .Configuration }}
+{{if .Configuration.Credentials }}
+## Credentials
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+{{- range $k, $v := .Configuration.Credentials }}
+| `{{$k}}` | {{$v}} |
+{{- end}}
+
+{{- end}}
+
+{{if .Configuration.Additional }}
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+{{- range $k, $v := .Configuration.Additional }}
+| `{{$k}}` | {{$v}} |
+{{- end}}
+
+{{- end}}
+{{- end}}
+
+{{ .Additional }}
+
+{{if .Links }}
+## More information
+
+{{if .Links.API -}}
+- [API documentation]({{ .Links.API }})
+{{- end}}
+{{- if .Links.GoClient }}
+- [Go client]({{ .Links.GoClient }})
+{{- end}}
+
+{{- end}}
+
+
+
+
diff --git a/internal/dnsdocs/generator.go b/internal/dnsdocs/generator.go
new file mode 100644
index 00000000..a2d1742e
--- /dev/null
+++ b/internal/dnsdocs/generator.go
@@ -0,0 +1,136 @@
+package main
+
+//go:generate go run .
+
+import (
+ "bytes"
+ "go/format"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+ "text/template"
+
+ "github.com/BurntSushi/toml"
+)
+
+const (
+ root = "../../"
+ dnsPackage = root + "providers/dns"
+ mdTemplate = root + "internal/dnsdocs/dns.md.tmpl"
+ cliTemplate = root + "internal/dnsdocs/dns.go.tmpl"
+ cliOutput = root + "cmd/zz_gen_cmd_dnshelp.go"
+ docOutput = root + "docs/content/dns"
+)
+
+type Model struct {
+ Name string // Real name of the DNS provider
+ Code string // DNS code
+ URL string // DNS provider URL
+ Description string // Provider summary
+ Example string // CLI example
+ Configuration *Configuration // Environment variables
+ Links *Links // Links
+ Additional string // Extra documentation
+ GeneratedFrom string // Source file
+}
+
+type Configuration struct {
+ Credentials map[string]string
+ Additional map[string]string
+}
+
+type Links struct {
+ API string
+ GoClient string
+}
+
+type Providers struct {
+ Providers []Model
+}
+
+func main() {
+ models := &Providers{}
+
+ err := filepath.Walk(dnsPackage, walker(models))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // generate CLI help
+ err = generateCLIHelp(models)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func walker(prs *Providers) func(string, os.FileInfo, error) error {
+ return func(path string, _ os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ if filepath.Ext(path) == ".toml" {
+ m := Model{}
+
+ m.GeneratedFrom, err = filepath.Rel(root, path)
+ if err != nil {
+ return err
+ }
+
+ _, err := toml.DecodeFile(path, &m)
+ if err != nil {
+ return err
+ }
+
+ prs.Providers = append(prs.Providers, m)
+
+ // generate documentation
+ return generateDocumentation(m)
+ }
+
+ return nil
+ }
+}
+
+func generateDocumentation(m Model) error {
+ filename := filepath.Join(docOutput, "zz_gen_"+m.Code+".md")
+
+ file, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+
+ return template.Must(template.ParseFiles(mdTemplate)).Execute(file, m)
+}
+
+func generateCLIHelp(models *Providers) error {
+ filename := filepath.Join(cliOutput)
+
+ file, err := os.Create(filename)
+ if err != nil {
+ return err
+ }
+
+ tlt := template.New(filepath.Base(cliTemplate)).Funcs(map[string]interface{}{
+ "safe": func(src string) string {
+ return strings.ReplaceAll(src, "`", "'")
+ },
+ })
+
+ b := &bytes.Buffer{}
+ err = template.Must(tlt.ParseFiles(cliTemplate)).Execute(b, models)
+ if err != nil {
+ return err
+ }
+
+ // gofmt
+ source, err := format.Source(b.Bytes())
+ if err != nil {
+ return err
+ }
+
+ _, err = file.Write(source)
+ return err
+
+}
diff --git a/providers/dns/acmedns/acmedns.toml b/providers/dns/acmedns/acmedns.toml
new file mode 100644
index 00000000..da0372c0
--- /dev/null
+++ b/providers/dns/acmedns/acmedns.toml
@@ -0,0 +1,15 @@
+Name = "Joohoi's ACME-DNS"
+Description = ''''''
+URL = "https://github.com/joohoi/acme-dns"
+Code = "acme-dns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ ACME_DNS_API_BASE = "The ACME-DNS API address"
+ ACME_DNS_STORAGE_PATH = "The ACME-DNS JSON account data file. A per-domain account will be registered/persisted to this file and used for TXT updates."
+
+[Links]
+ API = "https://github.com/joohoi/acme-dns#api"
+ GoClient = "https://github.com/cpu/goacmedns"
diff --git a/providers/dns/alidns/alidns.toml b/providers/dns/alidns/alidns.toml
new file mode 100644
index 00000000..c3c9fb87
--- /dev/null
+++ b/providers/dns/alidns/alidns.toml
@@ -0,0 +1,20 @@
+Name = "Alibaba Cloud DNS"
+Description = ''''''
+URL = "https://www.alibabacloud.com/product/dns"
+Code = "alidns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ ALICLOUD_ACCESS_KEY = "Access key ID"
+ ALICLOUD_SECRET_KEY = "Access Key secret"
+ [Configuration.Additional]
+ ALICLOUD_POLLING_INTERVAL = "Time between DNS propagation check"
+ ALICLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ ALICLOUD_TTL = "The TTL of the TXT record used for the DNS challenge"
+ ALICLOUD_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.alibabacloud.com/help/doc-detail/42875.htm"
+ GoClient = "https://github.com/aliyun/alibaba-cloud-sdk-go"
diff --git a/providers/dns/auroradns/auroradns.toml b/providers/dns/auroradns/auroradns.toml
new file mode 100644
index 00000000..d9e065ab
--- /dev/null
+++ b/providers/dns/auroradns/auroradns.toml
@@ -0,0 +1,20 @@
+Name = "Aurora DNS"
+Description = ''''''
+URL = "https://www.pcextreme.com/aurora/dns"
+Code = "auroradns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ AURORA_USER_ID = "User ID"
+ AURORA_KEY = "User API key"
+ AURORA_ENDPOINT = "API endpoint URL"
+ [Configuration.Additional]
+ AURORA_POLLING_INTERVAL = "Time between DNS propagation check"
+ AURORA_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ AURORA_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://libcloud.readthedocs.io/en/latest/dns/drivers/auroradns.html#api-docs"
+ GoClient = "https://github.com/nrdcg/auroradns"
diff --git a/providers/dns/azure/azure.toml b/providers/dns/azure/azure.toml
new file mode 100644
index 00000000..0923d1b4
--- /dev/null
+++ b/providers/dns/azure/azure.toml
@@ -0,0 +1,24 @@
+Name = "Azure"
+Description = ''''''
+URL = "https://azure.microsoft.com/services/dns/"
+Code = "azure"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ AZURE_CLIENT_ID = "Client ID"
+ AZURE_CLIENT_SECRET = "Client secret"
+ AZURE_SUBSCRIPTION_ID = "Subscription ID"
+ AZURE_TENANT_ID = "Tenant ID"
+ AZURE_RESOURCE_GROUP = "Resource group"
+ 'instance metadata service' = "If the credentials are **not** set via the environment, then it will attempt to get a bearer token via the [instance metadata service](https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service)."
+ [Configuration.Additional]
+ AZURE_POLLING_INTERVAL = "Time between DNS propagation check"
+ AZURE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ AZURE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ AZURE_METADATA_ENDPOINT = "Metadata Service endpoint URL"
+
+[Links]
+ API = "https://docs.microsoft.com/en-us/go/azure/"
+ GoClient = "https://github.com/Azure/azure-sdk-for-go"
diff --git a/providers/dns/bluecat/bluecat.toml b/providers/dns/bluecat/bluecat.toml
new file mode 100644
index 00000000..c8f43ce6
--- /dev/null
+++ b/providers/dns/bluecat/bluecat.toml
@@ -0,0 +1,19 @@
+Name = "Bluecat"
+Description = ''''''
+URL = "https://www.bluecatnetworks.com"
+Code = "bluecat"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ BLUECAT_SERVER_URL = "The server URL, should have scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve"
+ BLUECAT_USER_NAME = "API username"
+ BLUECAT_PASSWORD = "API password"
+ BLUECAT_CONFIG_NAME = "Configuration name"
+ BLUECAT_DNS_VIEW = "External DNS View Name"
+ [Configuration.Additional]
+ BLUECAT_POLLING_INTERVAL = "Time between DNS propagation check"
+ BLUECAT_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ BLUECAT_TTL = "The TTL of the TXT record used for the DNS challenge"
+ BLUECAT_HTTP_TIMEOUT = "API request timeout"
diff --git a/providers/dns/cloudflare/cloudflare.toml b/providers/dns/cloudflare/cloudflare.toml
new file mode 100644
index 00000000..c60f4999
--- /dev/null
+++ b/providers/dns/cloudflare/cloudflare.toml
@@ -0,0 +1,24 @@
+Name = "Cloudflare"
+Description = ''''''
+URL = "https://www.cloudflare.com/dns/"
+Code = "cloudflare"
+
+Example = '''
+CLOUDFLARE_EMAIL=foo@bar.com \
+CLOUDFLARE_API_KEY=b9841238feb177a84330febba8a83208921177bffe733 \
+lego --dns cloudflare --domains my.domain.com --email my@email.com run
+'''
+
+[Configuration]
+ [Configuration.Credentials]
+ CLOUDFLARE_EMAIL = "Account email"
+ CLOUDFLARE_API_KEY = "API key"
+ [Configuration.Additional]
+ CLOUDFLARE_POLLING_INTERVAL = "Time between DNS propagation check"
+ CLOUDFLARE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ CLOUDFLARE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ CLOUDFLARE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://api.cloudflare.com/"
+ GoClient = "https://github.com/cloudflare/cloudflare-go"
diff --git a/providers/dns/cloudns/cloudns.toml b/providers/dns/cloudns/cloudns.toml
new file mode 100644
index 00000000..f8c2b83a
--- /dev/null
+++ b/providers/dns/cloudns/cloudns.toml
@@ -0,0 +1,19 @@
+Name = "ClouDNS"
+Description = ''''''
+URL = "https://www.cloudns.net"
+Code = "cloudns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ CLOUDNS_AUTH_ID = "The API user ID"
+ CLOUDNS_AUTH_PASSWORD = "The password for API user ID"
+ [Configuration.Additional]
+ CLOUDNS_POLLING_INTERVAL = "Time between DNS propagation check"
+ CLOUDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ CLOUDNS_TTL = "The TTL of the TXT record used for the DNS challenge"
+ CLOUDNS_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.cloudns.net/wiki/article/42/"
diff --git a/providers/dns/cloudxns/cloudxns.toml b/providers/dns/cloudxns/cloudxns.toml
new file mode 100644
index 00000000..58695142
--- /dev/null
+++ b/providers/dns/cloudxns/cloudxns.toml
@@ -0,0 +1,19 @@
+Name = "CloudXNS"
+Description = """"""
+URL = "https://www.cloudxns.net/"
+Code = "cloudxns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ CLOUDXNS_API_KEY = "The API key"
+ CLOUDXNS_SECRET_KEY = "THe API secret key"
+ [Configuration.Additional]
+ CLOUDXNS_POLLING_INTERVAL = "Time between DNS propagation check"
+ CLOUDXNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ CLOUDXNS_TTL = "The TTL of the TXT record used for the DNS challenge"
+ CLOUDXNS_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip"
diff --git a/providers/dns/conoha/conoha.toml b/providers/dns/conoha/conoha.toml
new file mode 100644
index 00000000..c5f003da
--- /dev/null
+++ b/providers/dns/conoha/conoha.toml
@@ -0,0 +1,21 @@
+Name = "ConoHa"
+Description = ''''''
+URL = "https://www.conoha.jp/"
+Code = "conoha"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ CONOHA_TENANT_ID = "Tenant ID"
+ CONOHA_API_USERNAME = "The API username"
+ CONOHA_API_PASSWORD = "The API password"
+ [Configuration.Additional]
+ CONOHA_POLLING_INTERVAL = "Time between DNS propagation check"
+ CONOHA_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ CONOHA_TTL = "The TTL of the TXT record used for the DNS challenge"
+ CONOHA_HTTP_TIMEOUT = "API request timeout"
+ CONOHA_REGION = "The region"
+
+[Links]
+ API = "https://www.conoha.jp/docs/"
diff --git a/providers/dns/designate/designate.toml b/providers/dns/designate/designate.toml
new file mode 100644
index 00000000..7c82d216
--- /dev/null
+++ b/providers/dns/designate/designate.toml
@@ -0,0 +1,22 @@
+Name = "Designate DNSaaS for Openstack"
+Description = ''''''
+URL = "https://docs.openstack.org/designate/latest/"
+Code = "designate"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ OS_AUTH_URL = "Identity endpoint URL"
+ OS_USERNAME = "Username"
+ OS_PASSWORD = "Password"
+ OS_TENANT_NAME = "Tenant name"
+ OS_REGION_NAME = "Region name"
+ [Configuration.Additional]
+ DESIGNATE_POLLING_INTERVAL = "Time between DNS propagation check"
+ DESIGNATE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DESIGNATE_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://docs.openstack.org/designate/latest/"
+ GoClient = "https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2"
diff --git a/providers/dns/digitalocean/digitalocean.toml b/providers/dns/digitalocean/digitalocean.toml
new file mode 100644
index 00000000..f4aaf9ee
--- /dev/null
+++ b/providers/dns/digitalocean/digitalocean.toml
@@ -0,0 +1,18 @@
+Name = "Digital Ocean"
+Description = ''''''
+URL = "https://www.digitalocean.com/docs/networking/dns/"
+Code = "digitalocean"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ DO_AUTH_TOKEN = "Authentication token"
+ [Configuration.Additional]
+ DO_POLLING_INTERVAL = "Time between DNS propagation check"
+ DO_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DO_TTL = "The TTL of the TXT record used for the DNS challenge"
+ DO_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://developers.digitalocean.com/documentation/v2/#domain-records"
diff --git a/providers/dns/dnsimple/dnsimple.toml b/providers/dns/dnsimple/dnsimple.toml
new file mode 100644
index 00000000..a817e598
--- /dev/null
+++ b/providers/dns/dnsimple/dnsimple.toml
@@ -0,0 +1,19 @@
+Name = "DNSimple"
+Description = ''''''
+URL = "https://dnsimple.com/"
+Code = "dnsimple"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ DNSIMPLE_OAUTH_TOKEN = "OAuth token"
+ DNSIMPLE_BASE_URL = "API endpoint URL"
+ [Configuration.Additional]
+ DNSIMPLE_POLLING_INTERVAL = "Time between DNS propagation check"
+ DNSIMPLE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DNSIMPLE_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://developer.dnsimple.com/v2/"
+ GoClient = "https://github.com/dnsimple/dnsimple-go"
diff --git a/providers/dns/dnsmadeeasy/dnsmadeeasy.toml b/providers/dns/dnsmadeeasy/dnsmadeeasy.toml
new file mode 100644
index 00000000..53ec6014
--- /dev/null
+++ b/providers/dns/dnsmadeeasy/dnsmadeeasy.toml
@@ -0,0 +1,20 @@
+Name = "DNS Made Easy"
+Description = ''''''
+URL = "https://dnsmadeeasy.com/"
+Code = "dnsmadeeasy"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ DNSMADEEASY_API_KEY = "The API key"
+ DNSMADEEASY_API_SECRET = "The API Secret key"
+ [Configuration.Additional]
+ DNSMADEEASY_SANDBOX = "Activate the sandbox (boolean)"
+ DNSMADEEASY_POLLING_INTERVAL = "Time between DNS propagation check"
+ DNSMADEEASY_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DNSMADEEASY_TTL = "The TTL of the TXT record used for the DNS challenge"
+ DNSMADEEASY_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://api-docs.dnsmadeeasy.com/"
diff --git a/providers/dns/dnspod/dnspod.toml b/providers/dns/dnspod/dnspod.toml
new file mode 100644
index 00000000..7b468466
--- /dev/null
+++ b/providers/dns/dnspod/dnspod.toml
@@ -0,0 +1,19 @@
+Name = "DNSPod"
+Description = ''''''
+URL = "http://www.dnspod.com/"
+Code = "dnspod"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ DNSPOD_API_KEY = "The user token"
+ [Configuration.Additional]
+ DNSPOD_POLLING_INTERVAL = "Time between DNS propagation check"
+ DNSPOD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DNSPOD_TTL = "The TTL of the TXT record used for the DNS challenge"
+ DNSPOD_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.dnspod.com/docs/index.html"
+ GoClient = "https://github.com/decker502/dnspod-go"
diff --git a/providers/dns/dreamhost/dreamhost.toml b/providers/dns/dreamhost/dreamhost.toml
new file mode 100644
index 00000000..7bfeff10
--- /dev/null
+++ b/providers/dns/dreamhost/dreamhost.toml
@@ -0,0 +1,18 @@
+Name = "DreamHost"
+Description = ''''''
+URL = "https://www.dreamhost.com"
+Code = "dreamhost"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ DREAMHOST_API_KEY = "The API key"
+ [Configuration.Additional]
+ DREAMHOST_POLLING_INTERVAL = "Time between DNS propagation check"
+ DREAMHOST_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DREAMHOST_TTL = "The TTL of the TXT record used for the DNS challenge"
+ DREAMHOST_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://help.dreamhost.com/hc/en-us/articles/217560167-API_overview"
diff --git a/providers/dns/duckdns/duckdns.toml b/providers/dns/duckdns/duckdns.toml
new file mode 100644
index 00000000..f99eb0ab
--- /dev/null
+++ b/providers/dns/duckdns/duckdns.toml
@@ -0,0 +1,19 @@
+Name = "Duck DNS"
+Description = ''''''
+URL = "https://www.duckdns.org/"
+Code = "duckdns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ DUCKDNS_TOKEN = "Account token"
+ [Configuration.Additional]
+ DUCKDNS_POLLING_INTERVAL = "Time between DNS propagation check"
+ DUCKDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DUCKDNS_TTL = "The TTL of the TXT record used for the DNS challenge"
+ DUCKDNS_HTTP_TIMEOUT = "API request timeout"
+ DUCKDNS_SEQUENCE_INTERVAL = "Interval between iteration"
+
+[Links]
+ API = "https://www.duckdns.org/spec.jsp"
diff --git a/providers/dns/dyn/dyn.toml b/providers/dns/dyn/dyn.toml
new file mode 100644
index 00000000..c2a4b6b8
--- /dev/null
+++ b/providers/dns/dyn/dyn.toml
@@ -0,0 +1,20 @@
+Name = "Dyn"
+Description = ''''''
+URL = "https://dyn.com/"
+Code = "dyn"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ DYN_CUSTOMER_NAME = "Customer name"
+ DYN_USER_NAME = "User name"
+ DYN_PASSWORD = "Paswword"
+ [Configuration.Additional]
+ DYN_POLLING_INTERVAL = "Time between DNS propagation check"
+ DYN_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ DYN_TTL = "The TTL of the TXT record used for the DNS challenge"
+ DYN_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://help.dyn.com/rest/"
diff --git a/providers/dns/exec/readme.md b/providers/dns/exec/exec.toml
similarity index 78%
rename from providers/dns/exec/readme.md
rename to providers/dns/exec/exec.toml
index 3fa2f7cf..c44a4c93 100644
--- a/providers/dns/exec/readme.md
+++ b/providers/dns/exec/exec.toml
@@ -1,6 +1,30 @@
-# Execute an external program
+Name = "External program"
+Description = "Solving the DNS-01 challenge using an external program."
+URL = "/dns/exec"
+Code = "exec"
+
+Example = '''
+EXEC_PATH=/the/path/to/myscript.sh \
+lego --dns exec --domains my.domain.com --email my@email.com run
+'''
+
+Additional = '''
+
+## Base Configuration
+
+| Environment Variable Name | Description |
+|-----------------------|-------------|
+| `EXEC_MODE` | `RAW`, none |
+| `EXEC_PATH` | TODO |
+
+
+## Additional Configuration
+
+| Environment Variable Name | Description |
+|--------------------------------|-------------|
+| `EXEC_POLLING_INTERVAL` | Time between DNS propagation check |
+| `EXEC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
-Solving the DNS-01 challenge using an external program.
## Description
@@ -48,6 +72,12 @@ It will then call the program `./update-dns.sh` like this:
## Commands
+{{% notice note %}}
+The `--` is because the token MAY start with a `-`, and the called program may try and interpret a `-` as indicating a flag.
+In the case of urfave, which is commonly used,
+you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely.
+{{% /notice %}}
+
### Present
| Mode | Command |
@@ -77,10 +107,4 @@ the default display propagation timeout and polling interval are used.
| default | `myprogram timeout` |
| `RAW` | `myprogram timeout` |
-
-## NOTE
-
-The `--` is because the token MAY start with a `-`, and the called program may try and interpret a - as indicating a flag.
-
-In the case of urfave, which is commonly used,
-you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely.
+'''
diff --git a/providers/dns/exoscale/exoscale.toml b/providers/dns/exoscale/exoscale.toml
new file mode 100644
index 00000000..0e992aba
--- /dev/null
+++ b/providers/dns/exoscale/exoscale.toml
@@ -0,0 +1,21 @@
+Name = "Exoscale"
+Description = ''''''
+URL = "https://www.exoscale.com/"
+Code = "exoscale"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ EXOSCALE_API_KEY = "API key"
+ EXOSCALE_API_SECRET = "API secret"
+ EXOSCALE_ENDPOINT = "API endpoint URL"
+ [Configuration.Additional]
+ EXOSCALE_POLLING_INTERVAL = "Time between DNS propagation check"
+ EXOSCALE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ EXOSCALE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ EXOSCALE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://community.exoscale.com/documentation/dns/api/"
+ GoClient = "https://github.com/exoscale/egoscale"
diff --git a/providers/dns/fastdns/fastdns.toml b/providers/dns/fastdns/fastdns.toml
new file mode 100644
index 00000000..196994ba
--- /dev/null
+++ b/providers/dns/fastdns/fastdns.toml
@@ -0,0 +1,23 @@
+Name = "FastDNS"
+Description = ''''''
+URL = "https://www.akamai.com/us/en/products/security/fast-dns.jsp"
+Code = "fastdns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ AKAMAI_HOST = "API host"
+ AKAMAI_CLIENT_TOKEN = "Client token"
+ AKAMAI_CLIENT_SECRET = "Client secret"
+ AKAMAI_ACCESS_TOKEN = "Access token"
+ [Configuration.Additional]
+ AKAMAI_POLLING_INTERVAL = "Time between DNS propagation check"
+ AKAMAI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ AKAMAI_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://developer.akamai.com/api/web_performance/fast_dns_record_management/v1.html"
+ GoClient = "https://github.com/akamai/AkamaiOPEN-edgegrid-golang"
+
+
diff --git a/providers/dns/gandi/gandi.toml b/providers/dns/gandi/gandi.toml
new file mode 100644
index 00000000..15db58ca
--- /dev/null
+++ b/providers/dns/gandi/gandi.toml
@@ -0,0 +1,18 @@
+Name = "Gandi"
+Description = """"""
+URL = "https://www.gandi.net"
+Code = "gandi"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ GANDI_API_KEY = "API key"
+ [Configuration.Additional]
+ GANDI_POLLING_INTERVAL = "Time between DNS propagation check"
+ GANDI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ GANDI_TTL = "The TTL of the TXT record used for the DNS challenge"
+ GANDI_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "http://doc.rpc.gandi.net/index.html"
diff --git a/providers/dns/gandiv5/gandiv5.toml b/providers/dns/gandiv5/gandiv5.toml
new file mode 100644
index 00000000..9eaa567e
--- /dev/null
+++ b/providers/dns/gandiv5/gandiv5.toml
@@ -0,0 +1,18 @@
+Name = "Gandi Live DNS (v5)"
+Description = ''''''
+URL = "https://www.gandi.net"
+Code = "gandiv5"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ GANDIV5_API_KEY = "API key"
+ [Configuration.Additional]
+ GANDIV5_POLLING_INTERVAL = "Time between DNS propagation check"
+ GANDIV5_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ GANDIV5_TTL = "The TTL of the TXT record used for the DNS challenge"
+ GANDIV5_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "http://doc.livedns.gandi.net"
diff --git a/providers/dns/gcloud/gcloud.toml b/providers/dns/gcloud/gcloud.toml
new file mode 100644
index 00000000..ae43c965
--- /dev/null
+++ b/providers/dns/gcloud/gcloud.toml
@@ -0,0 +1,21 @@
+Name = "Google Cloud"
+Description = ''''''
+URL = "https://cloud.google.com"
+Code = "gcloud"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ GCE_PROJECT = "Project name"
+ 'Application Default Credentials' = "[Documentation](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)"
+ GCE_SERVICE_ACCOUNT_FILE = "Account file path"
+ GCE_SERVICE_ACCOUNT = "Account"
+ [Configuration.Additional]
+ GCE_POLLING_INTERVAL = "Time between DNS propagation check"
+ GCE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ GCE_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://community.exoscale.com/documentation/dns/api/"
+ GoClient = "https://github.com/googleapis/google-api-go-client"
diff --git a/providers/dns/glesys/glesys.go b/providers/dns/glesys/glesys.go
index 98667d8d..c647a741 100644
--- a/providers/dns/glesys/glesys.go
+++ b/providers/dns/glesys/glesys.go
@@ -13,8 +13,6 @@ import (
"github.com/xenolf/lego/platform/config/env"
)
-// GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation
-
const (
// defaultBaseURL is the GleSYS API endpoint used by Present and CleanUp.
defaultBaseURL = "https://api.glesys.com/domain"
diff --git a/providers/dns/glesys/glesys.toml b/providers/dns/glesys/glesys.toml
new file mode 100644
index 00000000..daba9d7b
--- /dev/null
+++ b/providers/dns/glesys/glesys.toml
@@ -0,0 +1,19 @@
+Name = "Glesys"
+Description = ''''''
+URL = "https://glesys.com/"
+Code = "glesys"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ GLESYS_API_USER = "API user"
+ GLESYS_API_KEY = "API key"
+ [Configuration.Additional]
+ GLESYS_POLLING_INTERVAL = "Time between DNS propagation check"
+ GLESYS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ GLESYS_TTL = "The TTL of the TXT record used for the DNS challenge"
+ GLESYS_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://github.com/GleSYS/API/wiki/API-Documentation"
diff --git a/providers/dns/godaddy/godaddy.toml b/providers/dns/godaddy/godaddy.toml
new file mode 100644
index 00000000..688bc665
--- /dev/null
+++ b/providers/dns/godaddy/godaddy.toml
@@ -0,0 +1,18 @@
+Name = "Go Daddy"
+Description = ''''''
+URL = "https://godaddy.com"
+Code = "godaddy"
+
+[Configuration]
+ [Configuration.Credentials]
+ GODADDY_API_KEY = "**TODO**"
+ GODADDY_API_SECRET = "**TODO**"
+ [Configuration.Additional]
+ GODADDY_POLLING_INTERVAL = "Time between DNS propagation check"
+ GODADDY_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ GODADDY_TTL = "The TTL of the TXT record used for the DNS challenge"
+ GODADDY_HTTP_TIMEOUT = "API request timeout"
+ GODADDY_SEQUENCE_INTERVAL = "Interval between iteration"
+
+[Links]
+ API = "https://developer.godaddy.com/doc/endpoint/domains"
diff --git a/providers/dns/hostingde/hostingde.toml b/providers/dns/hostingde/hostingde.toml
new file mode 100644
index 00000000..1bf91b92
--- /dev/null
+++ b/providers/dns/hostingde/hostingde.toml
@@ -0,0 +1,19 @@
+Name = "Hosting.de"
+Description = ''''''
+URL = "https://www.hosting.de/"
+Code = "hostingde"
+
+[Configuration]
+ [Configuration.Credentials]
+ HOSTINGDE_API_KEY = "**TODO**"
+ HOSTINGDE_ZONE_NAME = "**TODO**"
+ [Configuration.Additional]
+ HOSTINGDE_POLLING_INTERVAL = "Time between DNS propagation check"
+ HOSTINGDE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ HOSTINGDE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ HOSTINGDE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.hosting.de/api/#dns"
+
+
diff --git a/providers/dns/httpreq/httpreq.toml b/providers/dns/httpreq/httpreq.toml
new file mode 100644
index 00000000..bf7892d3
--- /dev/null
+++ b/providers/dns/httpreq/httpreq.toml
@@ -0,0 +1,60 @@
+Name = "HTTP request"
+Description = ''''''
+URL = "/dns/httpreq/"
+Code = "httpreq"
+
+Example = '''
+HTTPREQ_ENDPOINT=http://my.server.com:9090 \
+lego --dns httpreq --domains my.domain.com --email my@email.com run
+'''
+
+Additional = '''
+## Description
+
+The server must provide:
+
+- `POST` `/present`
+- `POST` `/cleanup`
+
+The URL of the server must be define by `HTTPREQ_ENDPOINT`.
+
+### Mode
+
+There are 2 modes (`HTTPREQ_MODE`):
+
+- default mode:
+```json
+{
+ "fqdn": "_acme-challenge.domain.",
+ "value": "LHDhK3oGRvkiefQnx7OOczTY5Tic_xZ6HcMOc_gmtoM"
+}
+```
+
+- `RAW`
+```json
+{
+ "domain": "domain",
+ "token": "token",
+ "keyAuth": "key"
+}
+```
+
+### Authentication
+
+Basic authentication (optional) can be set with some environment variables:
+
+- `HTTPREQ_USERNAME` and `HTTPREQ_PASSWORD`
+- both values must be set, otherwise basic authentication is not defined.
+
+'''
+
+[Configuration]
+ [Configuration.Credentials]
+ HTTPREQ_MODE = "`RAW`, none"
+ HTTPREQ_ENDPOINT = "The URL of the server"
+ [Configuration.Additional]
+ HTTPREQ_USERNAME = "**TODO**"
+ HTTPREQ_PASSWORD = "**TODO**"
+ HTTPREQ_POLLING_INTERVAL = "Time between DNS propagation check"
+ HTTPREQ_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ HTTPREQ_HTTP_TIMEOUT = "API request timeout"
diff --git a/providers/dns/httpreq/readme.md b/providers/dns/httpreq/readme.md
deleted file mode 100644
index 51bc9a0b..00000000
--- a/providers/dns/httpreq/readme.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# HTTP request
-
-The server must provide:
-- `POST` `/present`
-- `POST` `/cleanup`
-
-The URL of the server must be define by `HTTPREQ_ENDPOINT`.
-
-## Mode
-
-There are 2 modes (`HTTPREQ_MODE`):
-- default mode:
-```json
-{
- "fqdn": "_acme-challenge.domain.",
- "value": "LHDhK3oGRvkiefQnx7OOczTY5Tic_xZ6HcMOc_gmtoM"
-}
-```
-
-- `RAW`
-```json
-{
- "domain": "domain",
- "token": "token",
- "keyAuth": "key"
-}
-```
-
-## Authentication
-
-Basic authentication (optional) can be set with some environment variables:
-- `HTTPREQ_USERNAME` and `HTTPREQ_PASSWORD`
-- both values must be set, otherwise basic authentication is not defined.
diff --git a/providers/dns/iij/iij.toml b/providers/dns/iij/iij.toml
new file mode 100644
index 00000000..0beb5587
--- /dev/null
+++ b/providers/dns/iij/iij.toml
@@ -0,0 +1,20 @@
+Name = "Internet Initiative Japan"
+Description = ''''''
+URL = "https://www.iij.ad.jp/en/"
+Code = "iij"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ IIJ_API_ACCESS_KEY = "API access key"
+ IIJ_API_SECRET_KEY = "API secret key"
+ IIJ_DO_SERVICE_CODE = "DO service code"
+ [Configuration.Additional]
+ IIJ_POLLING_INTERVAL = "Time between DNS propagation check"
+ IIJ_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ IIJ_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "http://manual.iij.jp/p2/pubapi/http://manual.iij.jp/p2/pubapi/"
+ GoClient = "https://github.com/iij/doapi"
diff --git a/providers/dns/inwx/inwx.toml b/providers/dns/inwx/inwx.toml
new file mode 100644
index 00000000..f77265fc
--- /dev/null
+++ b/providers/dns/inwx/inwx.toml
@@ -0,0 +1,20 @@
+Name = "INWX"
+Description = ''''''
+URL = "https://www.inwx.de/en"
+Code = "inwx"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ INWX_USERNAME = "Username"
+ INWX_PASSWORD = "Password"
+ [Configuration.Additional]
+ INWX_POLLING_INTERVAL = "Time between DNS propagation check"
+ INWX_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ INWX_TTL = "The TTL of the TXT record used for the DNS challenge"
+ INWX_SANDBOX = "Activate the sandbox (boolean)"
+
+[Links]
+ API = "https://www.inwx.de/en/help/apidoc"
+ GoClient = "https://github.com/nrdcg/goinwx"
diff --git a/providers/dns/lightsail/lightsail.toml b/providers/dns/lightsail/lightsail.toml
new file mode 100644
index 00000000..191cb1d3
--- /dev/null
+++ b/providers/dns/lightsail/lightsail.toml
@@ -0,0 +1,18 @@
+Name = "Amazon Lightsail"
+Description = ''''''
+URL = "https://aws.amazon.com/lightsail/"
+Code = "lightsail"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ AWS_ACCESS_KEY_ID = "Access key ID"
+ AWS_SECRET_ACCESS_KEY = "Secret access key"
+ DNS_ZONE = "DNS zone"
+ [Configuration.Additional]
+ LIGHTSAIL_POLLING_INTERVAL = "Time between DNS propagation check"
+ LIGHTSAIL_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+
+[Links]
+ GoClient = "https://github.com/aws/aws-sdk-go/aws"
diff --git a/providers/dns/linode/linode.toml b/providers/dns/linode/linode.toml
new file mode 100644
index 00000000..d11021c0
--- /dev/null
+++ b/providers/dns/linode/linode.toml
@@ -0,0 +1,18 @@
+Name = "Linode (deprecated)"
+Description = ''''''
+URL = "https://www.linode.com/"
+Code = "linode"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ LINODE_API_KEY = "API key"
+ [Configuration.Additional]
+ LINODE_POLLING_INTERVAL = "Time between DNS propagation check"
+ LINODE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ LINODE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.linode.com/api/dns"
+ GoClient = "https://github.com/timewasted/linode"
diff --git a/providers/dns/linodev4/linodev4.toml b/providers/dns/linodev4/linodev4.toml
new file mode 100644
index 00000000..49e853cf
--- /dev/null
+++ b/providers/dns/linodev4/linodev4.toml
@@ -0,0 +1,18 @@
+Name = "Linode (v4)"
+Description = ''''''
+URL = "https://www.linode.com/"
+Code = "linodev4"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ LINODE_TOKEN = "API token"
+ [Configuration.Additional]
+ LINODE_POLLING_INTERVAL = "Time between DNS propagation check"
+ LINODE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ LINODE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://developers.linode.com/api/v4"
+ GoClient = "https://github.com/linode/linodego"
diff --git a/providers/dns/mydnsjp/mydnsjp.toml b/providers/dns/mydnsjp/mydnsjp.toml
new file mode 100644
index 00000000..ec5392e2
--- /dev/null
+++ b/providers/dns/mydnsjp/mydnsjp.toml
@@ -0,0 +1,19 @@
+Name = "MyDNS.jp"
+Description = ''''''
+URL = "https://www.mydns.jp"
+Code = "mydnsjp"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ MYDNSJP_MASTER_ID = "Master ID"
+ MYDNSJP_PASSWORD = "Password"
+ [Configuration.Additional]
+ MYDNSJP_POLLING_INTERVAL = "Time between DNS propagation check"
+ MYDNSJP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ MYDNSJP_TTL = "The TTL of the TXT record used for the DNS challenge"
+ MYDNSJP_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.mydns.jp/?MENU=030"
diff --git a/providers/dns/namecheap/namecheap.toml b/providers/dns/namecheap/namecheap.toml
new file mode 100644
index 00000000..341ac6ad
--- /dev/null
+++ b/providers/dns/namecheap/namecheap.toml
@@ -0,0 +1,19 @@
+Name = "Namecheap"
+Description = ''''''
+URL = "https://www.namecheap.com"
+Code = "namecheap"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ NAMECHEAP_API_USER = "API user"
+ NAMECHEAP_API_KEY = "API key"
+ [Configuration.Additional]
+ NAMECHEAP_POLLING_INTERVAL = "Time between DNS propagation check"
+ NAMECHEAP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ NAMECHEAP_TTL = "The TTL of the TXT record used for the DNS challenge"
+ NAMECHEAP_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.namecheap.com/support/api/methods.aspx"
diff --git a/providers/dns/namedotcom/namedotcom.toml b/providers/dns/namedotcom/namedotcom.toml
new file mode 100644
index 00000000..c69c45e7
--- /dev/null
+++ b/providers/dns/namedotcom/namedotcom.toml
@@ -0,0 +1,21 @@
+Name = "Name.com"
+Description = ''''''
+URL = "https://www.name.com"
+Code = "namedotcom"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ NAMECOM_USERNAME = "Username"
+ NAMECOM_API_TOKEN = "API token"
+ [Configuration.Additional]
+ NAMECOM_POLLING_INTERVAL = "Time between DNS propagation check"
+ NAMECOM_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ NAMECOM_TTL = "The TTL of the TXT record used for the DNS challenge"
+ NAMECOM_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.name.com/api-docs/DNS"
+ GoClient = "https://github.com/namedotcom/go"
+
diff --git a/providers/dns/netcup/netcup.toml b/providers/dns/netcup/netcup.toml
new file mode 100644
index 00000000..6ade9cf3
--- /dev/null
+++ b/providers/dns/netcup/netcup.toml
@@ -0,0 +1,20 @@
+Name = "Netcup"
+Description = ''''''
+URL = "https://www.netcup.eu/"
+Code = "netcup"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ NETCUP_CUSTOMER_NUMBER = "Customer number"
+ NETCUP_API_KEY = "API key"
+ NETCUP_API_PASSWORD = "API password"
+ [Configuration.Additional]
+ NETCUP_POLLING_INTERVAL = "Time between DNS propagation check"
+ NETCUP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ NETCUP_TTL = "The TTL of the TXT record used for the DNS challenge"
+ NETCUP_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.netcup-wiki.de/wiki/DNS_API"
diff --git a/providers/dns/nifcloud/nifcloud.toml b/providers/dns/nifcloud/nifcloud.toml
new file mode 100644
index 00000000..c4b6465d
--- /dev/null
+++ b/providers/dns/nifcloud/nifcloud.toml
@@ -0,0 +1,19 @@
+Name = "NIFCloud"
+Description = ''''''
+URL = "https://www.nifcloud.com/"
+Code = "nifcloud"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ NIFCLOUD_ACCESS_KEY_ID = "Access key"
+ NIFCLOUD_SECRET_ACCESS_KEY = "Secret access key"
+ [Configuration.Additional]
+ NIFCLOUD_POLLING_INTERVAL = "Time between DNS propagation check"
+ NIFCLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ NIFCLOUD_TTL = "The TTL of the TXT record used for the DNS challenge"
+ NIFCLOUD_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://mbaas.nifcloud.com/doc/current/rest/common/format.html"
diff --git a/providers/dns/ns1/ns1.toml b/providers/dns/ns1/ns1.toml
new file mode 100644
index 00000000..74807974
--- /dev/null
+++ b/providers/dns/ns1/ns1.toml
@@ -0,0 +1,19 @@
+Name = "NS1"
+Description = ''''''
+URL = "https://ns1.com"
+Code = "ns1"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ NS1_API_KEY = "API key"
+ [Configuration.Additional]
+ NS1_POLLING_INTERVAL = "Time between DNS propagation check"
+ NS1_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ NS1_TTL = "The TTL of the TXT record used for the DNS challenge"
+ NS1_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://ns1.com/api"
+ GoClient = "https://github.com/ns1/ns1-go"
diff --git a/providers/dns/oraclecloud/README.md b/providers/dns/oraclecloud/README.md
deleted file mode 100644
index 476882e0..00000000
--- a/providers/dns/oraclecloud/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Export shell-env for OracleCloud
-
-## In Bash
-
-```
-export OCI_PRIVKEY_FILE="/.oci/oci_api_key.pem"
-export OCI_PRIVKEY_PASS="secret"
-export OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret"
-export OCI_USER_OCID="ocid1.user.oc1..secret"
-export OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"
-export OCI_REGION="us-phoenix-1"
-export OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret"
-```
-
-```
-export OCI_PRIVKEY=`base64 ~/.oci/oci_api_key.pem`
-export OCI_PRIVKEY_PASS="secret"
-export OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret"
-export OCI_USER_OCID="ocid1.user.oc1..secret"
-export OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"
-export OCI_REGION="us-phoenix-1"
-export OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret"
-```
-
-## In Fish
-
-```
-set -x OCI_PRIVKEY_FILE '~/.oci/oci_api_key.pem'
-set -x OCI_PRIVKEY_PASS 'secret'
-set -x OCI_TENANCY_OCID 'ocid1.tenancy.oc1..secret'
-set -x OCI_USER_OCID 'ocid1.user.oc1..secret'
-set -x OCI_PUBKEY_FINGERPRINT '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'
-set -x OCI_REGION 'us-phoenix-1'
-set -x OCI_COMPARTMENT_OCID 'ocid1.tenancy.oc1..secret'
-```
-
-```
-set IFS
-set -x OCI_PRIVKEY (base64 ~/.oci/oci_api_key.pem)
-set IFS \n" "\t
-
-set -x OCI_PRIVKEY_PASS 'secret'
-set -x OCI_TENANCY_OCID 'ocid1.tenancy.oc1..secret'
-set -x OCI_USER_OCID 'ocid1.user.oc1..secret'
-set -x OCI_PUBKEY_FINGERPRINT '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'
-set -x OCI_REGION 'us-phoenix-1'
-set -x OCI_COMPARTMENT_OCID 'ocid1.tenancy.oc1..secret'
-```
diff --git a/providers/dns/oraclecloud/oraclecloud.toml b/providers/dns/oraclecloud/oraclecloud.toml
new file mode 100644
index 00000000..60786c3c
--- /dev/null
+++ b/providers/dns/oraclecloud/oraclecloud.toml
@@ -0,0 +1,34 @@
+Name = "Oracle Cloud"
+Description = ''''''
+URL = "https://cloud.oracle.com/home"
+Code = "oraclecloud"
+
+Example = '''
+OCI_PRIVKEY_FILE="~/.oci/oci_api_key.pem" \
+OCI_PRIVKEY_PASS="secret" \
+OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" \
+OCI_USER_OCID="ocid1.user.oc1..secret" \
+OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \
+OCI_REGION="us-phoenix-1" \
+OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" \
+lego --dns oraclecloud --domains my.domain.com --email my@email.com run
+'''
+
+[Configuration]
+ [Configuration.Credentials]
+ OCI_PRIVKEY_FILE = "Private key file"
+ OCI_PRIVKEY_PASS = "Private key password"
+ OCI_TENANCY_OCID = "Tenanct OCID"
+ OCI_USER_OCID = "User OCID"
+ OCI_PUBKEY_FINGERPRINT = "Public key fingerprint"
+ OCI_REGION = "Region"
+ OCI_COMPARTMENT_OCID = "Compartment OCID"
+ [Configuration.Additional]
+ OCI_POLLING_INTERVAL = "Time between DNS propagation check"
+ OCI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ OCI_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://docs.cloud.oracle.com/iaas/Content/DNS/Concepts/dnszonemanagement.htm"
+ GoClient = "https://github.com/oracle/oci-go-sdk"
+
diff --git a/providers/dns/otc/otc.toml b/providers/dns/otc/otc.toml
new file mode 100644
index 00000000..24bb5008
--- /dev/null
+++ b/providers/dns/otc/otc.toml
@@ -0,0 +1,23 @@
+Name = "Open Telekom Cloud"
+Description = ''''''
+URL = "https://cloud.telekom.de/en"
+Code = "otc"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ OTC_USER_NAME = "User name"
+ OTC_PASSWORD = "Password"
+ OTC_PROJECT_NAME = "Project name"
+ OTC_DOMAIN_NAME = "Domain name"
+ OTC_IDENTITY_ENDPOINT = "Identity endpoint URL"
+ [Configuration.Additional]
+ OTC_POLLING_INTERVAL = "Time between DNS propagation check"
+ OTC_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ OTC_TTL = "The TTL of the TXT record used for the DNS challenge"
+ OTC_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://docs.otc.t-systems.com/en-us/dns/index.html"
+
diff --git a/providers/dns/ovh/ovh.toml b/providers/dns/ovh/ovh.toml
new file mode 100644
index 00000000..3c65e71a
--- /dev/null
+++ b/providers/dns/ovh/ovh.toml
@@ -0,0 +1,22 @@
+Name = "OVH"
+Description = ''''''
+URL = "https://www.ovh.com/"
+Code = "ovh"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ OVH_ENDPOINT = "Endpoint URL (ovh-eu or ovh-ca)"
+ OVH_APPLICATION_KEY = "Application key"
+ OVH_APPLICATION_SECRET = "Application secret"
+ OVH_CONSUMER_KEY = "Consumer key"
+ [Configuration.Additional]
+ OVH_POLLING_INTERVAL = "Time between DNS propagation check"
+ OVH_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ OVH_TTL = "The TTL of the TXT record used for the DNS challenge"
+ OVH_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://eu.api.ovh.com/"
+ GoClient = "https://github.com/ovh/go-ovh"
diff --git a/providers/dns/pdns/README.md b/providers/dns/pdns/pdns.toml
similarity index 51%
rename from providers/dns/pdns/README.md
rename to providers/dns/pdns/pdns.toml
index c13e7f9d..393f52a6 100644
--- a/providers/dns/pdns/README.md
+++ b/providers/dns/pdns/pdns.toml
@@ -1,7 +1,29 @@
-## PowerDNS provider
+Name = "PowerDNS"
+Description = ''''''
+URL = "https://www.powerdns.com/"
+Code = "pdns"
+
+Example = ''''''
+
+Additional = '''
+## Information
Tested and confirmed to work with PowerDNS authoritative server 3.4.8 and 4.0.1. Refer to [PowerDNS documentation](https://doc.powerdns.com/md/httpapi/README/) instructions on how to enable the built-in API interface.
PowerDNS Notes:
- PowerDNS API does not currently support SSL, therefore you should take care to ensure that traffic between lego and the PowerDNS API is over a trusted network, VPN etc.
- In order to have the SOA serial automatically increment each time the `_acme-challenge` record is added/modified via the API, set `SOA-EDIT-API` to `INCEPTION-INCREMENT` for the zone in the `domainmetadata` table
+'''
+
+[Configuration]
+ [Configuration.Credentials]
+ PDNS_API_KEY = "API key"
+ PDNS_API_URL = "API url"
+ [Configuration.Additional]
+ PDNS_POLLING_INTERVAL = "Time between DNS propagation check"
+ PDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ PDNS_TTL = "The TTL of the TXT record used for the DNS challenge"
+ PDNS_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://doc.powerdns.com/md/httpapi/README/"
diff --git a/providers/dns/rackspace/rackspace.toml b/providers/dns/rackspace/rackspace.toml
new file mode 100644
index 00000000..caec8f72
--- /dev/null
+++ b/providers/dns/rackspace/rackspace.toml
@@ -0,0 +1,19 @@
+Name = "Rackspace"
+Description = ''''''
+URL = "https://www.rackspace.com/"
+Code = "rackspace"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ RACKSPACE_USER = "API user"
+ RACKSPACE_API_KEY = "API key"
+ [Configuration.Additional]
+ RACKSPACE_POLLING_INTERVAL = "Time between DNS propagation check"
+ RACKSPACE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ RACKSPACE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ RACKSPACE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://developer.rackspace.com/docs/cloud-dns/v1/"
diff --git a/providers/dns/rfc2136/rfc2136.go b/providers/dns/rfc2136/rfc2136.go
index 395e7dc9..b49daa4f 100644
--- a/providers/dns/rfc2136/rfc2136.go
+++ b/providers/dns/rfc2136/rfc2136.go
@@ -51,7 +51,7 @@ type DNSProvider struct {
// See https://github.com/miekg/dns/blob/master/tsig.go for supported values.
// RFC2136_TSIG_KEY: Name of the secret key as defined in DNS server configuration.
// RFC2136_TSIG_SECRET: Secret key payload.
-// RFC2136_TIMEOUT: DNS propagation timeout in time.ParseDuration format. (60s)
+// RFC2136_PROPAGATION_TIMEOUT: DNS propagation timeout in time.ParseDuration format. (60s)
// To disable TSIG authentication, leave the RFC2136_TSIG* variables unset.
func NewDNSProvider() (*DNSProvider, error) {
values, err := env.Get("RFC2136_NAMESERVER")
diff --git a/providers/dns/rfc2136/rfc2136.toml b/providers/dns/rfc2136/rfc2136.toml
new file mode 100644
index 00000000..183f8ef5
--- /dev/null
+++ b/providers/dns/rfc2136/rfc2136.toml
@@ -0,0 +1,22 @@
+Name = "RFC2136"
+Description = ''''''
+URL = "https://tools.ietf.org/html/rfc2136"
+Code = "rfc2136"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ RFC2136_TSIG_KEY = "Name of the secret key as defined in DNS server configuration. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset."
+ RFC2136_TSIG_SECRET = "Secret key payload. To disable TSIG authentication, leave the` RFC2136_TSIG*` variables unset."
+ RFC2136_TSIG_ALGORITHM = "TSIG algorythm. See [miekg/dns#tsig.go](https://github.com/miekg/dns/blob/master/tsig.go) for supported values. To disable TSIG authentication, leave the `RFC2136_TSIG*` variables unset."
+ RFC2136_NAMESERVER = 'Network address in the form "host" or "host:port"'
+ [Configuration.Additional]
+ RFC2136_POLLING_INTERVAL = "Time between DNS propagation check"
+ RFC2136_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ RFC2136_TTL = "The TTL of the TXT record used for the DNS challenge"
+ RFC2136_DNS_TIMEOUT = "API request timeout"
+ RFC2136_SEQUENCE_INTERVAL = "Interval between iteration"
+
+[Links]
+ API = "https://tools.ietf.org/html/rfc2136"
diff --git a/providers/dns/route53/route53.toml b/providers/dns/route53/route53.toml
new file mode 100644
index 00000000..39b17223
--- /dev/null
+++ b/providers/dns/route53/route53.toml
@@ -0,0 +1,67 @@
+Name = "Amazon Route 53"
+Description = ''''''
+URL = "https://aws.amazon.com/route53/"
+Code = "route53"
+
+Example = ''''''
+
+Additional = '''
+## Description
+
+AWS Credentials are automatically detected in the following locations and prioritized in the following order:
+
+1. Environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, [`AWS_SESSION_TOKEN`]
+2. Shared credentials file (defaults to `~/.aws/credentials`)
+3. Amazon EC2 IAM role
+
+If `AWS_HOSTED_ZONE_ID` is not set, Lego tries to determine the correct public hosted zone via the FQDN.
+
+See also: [configuring-sdk](https://github.com/aws/aws-sdk-go/wiki/configuring-sdk)
+
+## Policy
+
+The following AWS IAM policy document describes the permissions required for lego to complete the DNS challenge.
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "",
+ "Effect": "Allow",
+ "Action": [
+ "route53:GetChange",
+ "route53:ChangeResourceRecordSets",
+ "route53:ListResourceRecordSets"
+ ],
+ "Resource": [
+ "arn:aws:route53:::hostedzone/*",
+ "arn:aws:route53:::change/*"
+ ]
+ },
+ {
+ "Sid": "",
+ "Effect": "Allow",
+ "Action": "route53:ListHostedZonesByName",
+ "Resource": "*"
+ }
+ ]
+}
+```
+
+'''
+
+[Configuration]
+ [Configuration.Credentials]
+ AWS_ACCESS_KEY_ID = ""
+ AWS_SECRET_ACCESS_KEY = ""
+ AWS_REGION = ""
+ AWS_HOSTED_ZONE_ID = ""
+ [Configuration.Additional]
+ AWS_POLLING_INTERVAL = "Time between DNS propagation check"
+ AWS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ AWS_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://docs.aws.amazon.com/Route53/latest/APIReference/API_Operations_Amazon_Route_53.html"
+ GoClient = "https://github.com/aws/aws-sdk-go/aws"
diff --git a/providers/dns/sakuracloud/sakuracloud.toml b/providers/dns/sakuracloud/sakuracloud.toml
new file mode 100644
index 00000000..b49506a8
--- /dev/null
+++ b/providers/dns/sakuracloud/sakuracloud.toml
@@ -0,0 +1,19 @@
+Name = "Sakura Cloud"
+Description = ''''''
+URL = "https://cloud.sakura.ad.jp/"
+Code = "sakuracloud"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ SAKURACLOUD_ACCESS_TOKEN = "Access token"
+ SAKURACLOUD_ACCESS_TOKEN_SECRET = "Access token secret"
+ [Configuration.Additional]
+ SAKURACLOUD_POLLING_INTERVAL = "Time between DNS propagation check"
+ SAKURACLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ SAKURACLOUD_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://developer.sakura.ad.jp/cloud/api/1.1/"
+ GoClient = "https://github.com/sacloud/libsacloud"
diff --git a/providers/dns/selectel/selectel.toml b/providers/dns/selectel/selectel.toml
new file mode 100644
index 00000000..2e756e9c
--- /dev/null
+++ b/providers/dns/selectel/selectel.toml
@@ -0,0 +1,19 @@
+Name = "Selectel"
+Description = ''''''
+URL = "https://kb.selectel.com/"
+Code = "selectel"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ SELECTEL_API_TOKEN = "API token"
+ [Configuration.Additional]
+ SELECTEL_BASE_URL = "API endpoint URL"
+ SELECTEL_POLLING_INTERVAL = "Time between DNS propagation check"
+ SELECTEL_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ SELECTEL_TTL = "The TTL of the TXT record used for the DNS challenge"
+ SELECTEL_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://kb.selectel.com/23136054.html"
diff --git a/providers/dns/stackpath/stackpath.toml b/providers/dns/stackpath/stackpath.toml
new file mode 100644
index 00000000..81b94191
--- /dev/null
+++ b/providers/dns/stackpath/stackpath.toml
@@ -0,0 +1,19 @@
+Name = "Stackpath"
+Description = ''''''
+URL = "https://www.stackpath.com/"
+Code = "stackpath"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ STACKPATH_CLIENT_ID = "Client ID"
+ STACKPATH_CLIENT_SECRET = "Client secret"
+ STACKPATH_STACK_ID = "Stack ID"
+ [Configuration.Additional]
+ STACKPATH_POLLING_INTERVAL = "Time between DNS propagation check"
+ STACKPATH_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ STACKPATH_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://developer.stackpath.com/en/api/dns/#tag/Zone"
diff --git a/providers/dns/transip/transip.toml b/providers/dns/transip/transip.toml
new file mode 100644
index 00000000..913806c3
--- /dev/null
+++ b/providers/dns/transip/transip.toml
@@ -0,0 +1,20 @@
+Name = "TransIP"
+Description = ''''''
+URL = "https://www.transip.nl/"
+Code = "transip"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ TRANSIP_ACCOUNT_NAME = "Account name"
+ TRANSIP_PRIVATE_KEY_PATH = "Private key path"
+ [Configuration.Additional]
+ TRANSIP_POLLING_INTERVAL = "Time between DNS propagation check"
+ TRANSIP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ TRANSIP_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://api.transip.nl/docs/transip.nl/package-Transip.html"
+ GoClient = "https://github.com/transip/gotransip"
+
diff --git a/providers/dns/vegadns/vegadns.toml b/providers/dns/vegadns/vegadns.toml
new file mode 100644
index 00000000..a223b6e6
--- /dev/null
+++ b/providers/dns/vegadns/vegadns.toml
@@ -0,0 +1,21 @@
+Name = "VegaDNS"
+Description = ''''''
+URL = "https://github.com/shupp/VegaDNS-API"
+Code = "vegadns"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ SECRET_VEGADNS_KEY = "API key"
+ SECRET_VEGADNS_SECRET = "API secret"
+ VEGADNS_URL = "API endpoint URL"
+ [Configuration.Additional]
+ VEGADNS_POLLING_INTERVAL = "Time between DNS propagation check"
+ VEGADNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ VEGADNS_TTL = "The TTL of the TXT record used for the DNS challenge"
+
+[Links]
+ API = "https://github.com/shupp/VegaDNS-API"
+ GoClient = "https://github.com/OpenDNS/vegadns2client"
+
diff --git a/providers/dns/vscale/vscale.toml b/providers/dns/vscale/vscale.toml
new file mode 100644
index 00000000..71a3350a
--- /dev/null
+++ b/providers/dns/vscale/vscale.toml
@@ -0,0 +1,19 @@
+Name = "Vscale"
+Description = ''''''
+URL = "https://vscale.io/"
+Code = "vscale"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ VSCALE_API_TOKEN = "API token"
+ [Configuration.Additional]
+ VSCALE_BASE_URL = "API enddpoint URL"
+ VSCALE_POLLING_INTERVAL = "Time between DNS propagation check"
+ VSCALE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ VSCALE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ VSCALE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://developers.vscale.io/documentation/api/v1/#api-Domains_Records"
diff --git a/providers/dns/vultr/vultr.toml b/providers/dns/vultr/vultr.toml
new file mode 100644
index 00000000..23d4243e
--- /dev/null
+++ b/providers/dns/vultr/vultr.toml
@@ -0,0 +1,19 @@
+Name = "Vultr"
+Description = ''''''
+URL = "https://www.vultr.com/"
+Code = "vultr"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ VULTR_API_KEY = "API key"
+ [Configuration.Additional]
+ VULTR_POLLING_INTERVAL = "Time between DNS propagation check"
+ VULTR_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ VULTR_TTL = "The TTL of the TXT record used for the DNS challenge"
+ VULTR_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://www.vultr.com/api/#dns"
+ GoClient = "https://github.com/JamesClonk/vultr"
diff --git a/providers/dns/zoneee/zoneee.toml b/providers/dns/zoneee/zoneee.toml
new file mode 100644
index 00000000..fc564ce7
--- /dev/null
+++ b/providers/dns/zoneee/zoneee.toml
@@ -0,0 +1,20 @@
+Name = "Zone.ee"
+Description = ''''''
+URL = "https://www.zone.ee/"
+Code = "zoneee"
+
+Example = ''''''
+
+[Configuration]
+ [Configuration.Credentials]
+ ZONEEE_API_USER = "API user"
+ ZONEEE_API_KEY = "API key"
+ [Configuration.Additional]
+ ZONEEE_ENDPOINT = "API endpoint URL"
+ ZONEEE_POLLING_INTERVAL = "Time between DNS propagation check"
+ ZONEEE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
+ ZONEEE_TTL = "The TTL of the TXT record used for the DNS challenge"
+ ZONEEE_HTTP_TIMEOUT = "API request timeout"
+
+[Links]
+ API = "https://api.zone.eu/v2"
diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING
new file mode 100644
index 00000000..01b57432
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/COPYING
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 TOML authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
new file mode 100644
index 00000000..01b57432
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-decoder/COPYING
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 TOML authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
new file mode 100644
index 00000000..01b57432
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/cmd/toml-test-encoder/COPYING
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 TOML authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
new file mode 100644
index 00000000..01b57432
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/cmd/tomlv/COPYING
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 TOML authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go
new file mode 100644
index 00000000..b0fd51d5
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/decode.go
@@ -0,0 +1,509 @@
+package toml
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math"
+ "reflect"
+ "strings"
+ "time"
+)
+
+func e(format string, args ...interface{}) error {
+ return fmt.Errorf("toml: "+format, args...)
+}
+
+// Unmarshaler is the interface implemented by objects that can unmarshal a
+// TOML description of themselves.
+type Unmarshaler interface {
+ UnmarshalTOML(interface{}) error
+}
+
+// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
+func Unmarshal(p []byte, v interface{}) error {
+ _, err := Decode(string(p), v)
+ return err
+}
+
+// Primitive is a TOML value that hasn't been decoded into a Go value.
+// When using the various `Decode*` functions, the type `Primitive` may
+// be given to any value, and its decoding will be delayed.
+//
+// A `Primitive` value can be decoded using the `PrimitiveDecode` function.
+//
+// The underlying representation of a `Primitive` value is subject to change.
+// Do not rely on it.
+//
+// N.B. Primitive values are still parsed, so using them will only avoid
+// the overhead of reflection. They can be useful when you don't know the
+// exact type of TOML data until run time.
+type Primitive struct {
+ undecoded interface{}
+ context Key
+}
+
+// DEPRECATED!
+//
+// Use MetaData.PrimitiveDecode instead.
+func PrimitiveDecode(primValue Primitive, v interface{}) error {
+ md := MetaData{decoded: make(map[string]bool)}
+ return md.unify(primValue.undecoded, rvalue(v))
+}
+
+// PrimitiveDecode is just like the other `Decode*` functions, except it
+// decodes a TOML value that has already been parsed. Valid primitive values
+// can *only* be obtained from values filled by the decoder functions,
+// including this method. (i.e., `v` may contain more `Primitive`
+// values.)
+//
+// Meta data for primitive values is included in the meta data returned by
+// the `Decode*` functions with one exception: keys returned by the Undecoded
+// method will only reflect keys that were decoded. Namely, any keys hidden
+// behind a Primitive will be considered undecoded. Executing this method will
+// update the undecoded keys in the meta data. (See the example.)
+func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
+ md.context = primValue.context
+ defer func() { md.context = nil }()
+ return md.unify(primValue.undecoded, rvalue(v))
+}
+
+// Decode will decode the contents of `data` in TOML format into a pointer
+// `v`.
+//
+// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
+// used interchangeably.)
+//
+// TOML arrays of tables correspond to either a slice of structs or a slice
+// of maps.
+//
+// TOML datetimes correspond to Go `time.Time` values.
+//
+// All other TOML types (float, string, int, bool and array) correspond
+// to the obvious Go types.
+//
+// An exception to the above rules is if a type implements the
+// encoding.TextUnmarshaler interface. In this case, any primitive TOML value
+// (floats, strings, integers, booleans and datetimes) will be converted to
+// a byte string and given to the value's UnmarshalText method. See the
+// Unmarshaler example for a demonstration with time duration strings.
+//
+// Key mapping
+//
+// TOML keys can map to either keys in a Go map or field names in a Go
+// struct. The special `toml` struct tag may be used to map TOML keys to
+// struct fields that don't match the key name exactly. (See the example.)
+// A case insensitive match to struct names will be tried if an exact match
+// can't be found.
+//
+// The mapping between TOML values and Go values is loose. That is, there
+// may exist TOML values that cannot be placed into your representation, and
+// there may be parts of your representation that do not correspond to
+// TOML values. This loose mapping can be made stricter by using the IsDefined
+// and/or Undecoded methods on the MetaData returned.
+//
+// This decoder will not handle cyclic types. If a cyclic type is passed,
+// `Decode` will not terminate.
+func Decode(data string, v interface{}) (MetaData, error) {
+ rv := reflect.ValueOf(v)
+ if rv.Kind() != reflect.Ptr {
+ return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
+ }
+ if rv.IsNil() {
+ return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
+ }
+ p, err := parse(data)
+ if err != nil {
+ return MetaData{}, err
+ }
+ md := MetaData{
+ p.mapping, p.types, p.ordered,
+ make(map[string]bool, len(p.ordered)), nil,
+ }
+ return md, md.unify(p.mapping, indirect(rv))
+}
+
+// DecodeFile is just like Decode, except it will automatically read the
+// contents of the file at `fpath` and decode it for you.
+func DecodeFile(fpath string, v interface{}) (MetaData, error) {
+ bs, err := ioutil.ReadFile(fpath)
+ if err != nil {
+ return MetaData{}, err
+ }
+ return Decode(string(bs), v)
+}
+
+// DecodeReader is just like Decode, except it will consume all bytes
+// from the reader and decode it for you.
+func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
+ bs, err := ioutil.ReadAll(r)
+ if err != nil {
+ return MetaData{}, err
+ }
+ return Decode(string(bs), v)
+}
+
+// unify performs a sort of type unification based on the structure of `rv`,
+// which is the client representation.
+//
+// Any type mismatch produces an error. Finding a type that we don't know
+// how to handle produces an unsupported type error.
+func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
+
+ // Special case. Look for a `Primitive` value.
+ if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
+ // Save the undecoded data and the key context into the primitive
+ // value.
+ context := make(Key, len(md.context))
+ copy(context, md.context)
+ rv.Set(reflect.ValueOf(Primitive{
+ undecoded: data,
+ context: context,
+ }))
+ return nil
+ }
+
+ // Special case. Unmarshaler Interface support.
+ if rv.CanAddr() {
+ if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
+ return v.UnmarshalTOML(data)
+ }
+ }
+
+ // Special case. Handle time.Time values specifically.
+ // TODO: Remove this code when we decide to drop support for Go 1.1.
+ // This isn't necessary in Go 1.2 because time.Time satisfies the encoding
+ // interfaces.
+ if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) {
+ return md.unifyDatetime(data, rv)
+ }
+
+ // Special case. Look for a value satisfying the TextUnmarshaler interface.
+ if v, ok := rv.Interface().(TextUnmarshaler); ok {
+ return md.unifyText(data, v)
+ }
+ // BUG(burntsushi)
+ // The behavior here is incorrect whenever a Go type satisfies the
+ // encoding.TextUnmarshaler interface but also corresponds to a TOML
+ // hash or array. In particular, the unmarshaler should only be applied
+ // to primitive TOML values. But at this point, it will be applied to
+ // all kinds of values and produce an incorrect error whenever those values
+ // are hashes or arrays (including arrays of tables).
+
+ k := rv.Kind()
+
+ // laziness
+ if k >= reflect.Int && k <= reflect.Uint64 {
+ return md.unifyInt(data, rv)
+ }
+ switch k {
+ case reflect.Ptr:
+ elem := reflect.New(rv.Type().Elem())
+ err := md.unify(data, reflect.Indirect(elem))
+ if err != nil {
+ return err
+ }
+ rv.Set(elem)
+ return nil
+ case reflect.Struct:
+ return md.unifyStruct(data, rv)
+ case reflect.Map:
+ return md.unifyMap(data, rv)
+ case reflect.Array:
+ return md.unifyArray(data, rv)
+ case reflect.Slice:
+ return md.unifySlice(data, rv)
+ case reflect.String:
+ return md.unifyString(data, rv)
+ case reflect.Bool:
+ return md.unifyBool(data, rv)
+ case reflect.Interface:
+ // we only support empty interfaces.
+ if rv.NumMethod() > 0 {
+ return e("unsupported type %s", rv.Type())
+ }
+ return md.unifyAnything(data, rv)
+ case reflect.Float32:
+ fallthrough
+ case reflect.Float64:
+ return md.unifyFloat64(data, rv)
+ }
+ return e("unsupported type %s", rv.Kind())
+}
+
+func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
+ tmap, ok := mapping.(map[string]interface{})
+ if !ok {
+ if mapping == nil {
+ return nil
+ }
+ return e("type mismatch for %s: expected table but found %T",
+ rv.Type().String(), mapping)
+ }
+
+ for key, datum := range tmap {
+ var f *field
+ fields := cachedTypeFields(rv.Type())
+ for i := range fields {
+ ff := &fields[i]
+ if ff.name == key {
+ f = ff
+ break
+ }
+ if f == nil && strings.EqualFold(ff.name, key) {
+ f = ff
+ }
+ }
+ if f != nil {
+ subv := rv
+ for _, i := range f.index {
+ subv = indirect(subv.Field(i))
+ }
+ if isUnifiable(subv) {
+ md.decoded[md.context.add(key).String()] = true
+ md.context = append(md.context, key)
+ if err := md.unify(datum, subv); err != nil {
+ return err
+ }
+ md.context = md.context[0 : len(md.context)-1]
+ } else if f.name != "" {
+ // Bad user! No soup for you!
+ return e("cannot write unexported field %s.%s",
+ rv.Type().String(), f.name)
+ }
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
+ tmap, ok := mapping.(map[string]interface{})
+ if !ok {
+ if tmap == nil {
+ return nil
+ }
+ return badtype("map", mapping)
+ }
+ if rv.IsNil() {
+ rv.Set(reflect.MakeMap(rv.Type()))
+ }
+ for k, v := range tmap {
+ md.decoded[md.context.add(k).String()] = true
+ md.context = append(md.context, k)
+
+ rvkey := indirect(reflect.New(rv.Type().Key()))
+ rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
+ if err := md.unify(v, rvval); err != nil {
+ return err
+ }
+ md.context = md.context[0 : len(md.context)-1]
+
+ rvkey.SetString(k)
+ rv.SetMapIndex(rvkey, rvval)
+ }
+ return nil
+}
+
+func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ if !datav.IsValid() {
+ return nil
+ }
+ return badtype("slice", data)
+ }
+ sliceLen := datav.Len()
+ if sliceLen != rv.Len() {
+ return e("expected array length %d; got TOML array of length %d",
+ rv.Len(), sliceLen)
+ }
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ if !datav.IsValid() {
+ return nil
+ }
+ return badtype("slice", data)
+ }
+ n := datav.Len()
+ if rv.IsNil() || rv.Cap() < n {
+ rv.Set(reflect.MakeSlice(rv.Type(), n, n))
+ }
+ rv.SetLen(n)
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
+ sliceLen := data.Len()
+ for i := 0; i < sliceLen; i++ {
+ v := data.Index(i).Interface()
+ sliceval := indirect(rv.Index(i))
+ if err := md.unify(v, sliceval); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error {
+ if _, ok := data.(time.Time); ok {
+ rv.Set(reflect.ValueOf(data))
+ return nil
+ }
+ return badtype("time.Time", data)
+}
+
+func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
+ if s, ok := data.(string); ok {
+ rv.SetString(s)
+ return nil
+ }
+ return badtype("string", data)
+}
+
+func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
+ if num, ok := data.(float64); ok {
+ switch rv.Kind() {
+ case reflect.Float32:
+ fallthrough
+ case reflect.Float64:
+ rv.SetFloat(num)
+ default:
+ panic("bug")
+ }
+ return nil
+ }
+ return badtype("float", data)
+}
+
+func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
+ if num, ok := data.(int64); ok {
+ if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int64:
+ // No bounds checking necessary.
+ case reflect.Int8:
+ if num < math.MinInt8 || num > math.MaxInt8 {
+ return e("value %d is out of range for int8", num)
+ }
+ case reflect.Int16:
+ if num < math.MinInt16 || num > math.MaxInt16 {
+ return e("value %d is out of range for int16", num)
+ }
+ case reflect.Int32:
+ if num < math.MinInt32 || num > math.MaxInt32 {
+ return e("value %d is out of range for int32", num)
+ }
+ }
+ rv.SetInt(num)
+ } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
+ unum := uint64(num)
+ switch rv.Kind() {
+ case reflect.Uint, reflect.Uint64:
+ // No bounds checking necessary.
+ case reflect.Uint8:
+ if num < 0 || unum > math.MaxUint8 {
+ return e("value %d is out of range for uint8", num)
+ }
+ case reflect.Uint16:
+ if num < 0 || unum > math.MaxUint16 {
+ return e("value %d is out of range for uint16", num)
+ }
+ case reflect.Uint32:
+ if num < 0 || unum > math.MaxUint32 {
+ return e("value %d is out of range for uint32", num)
+ }
+ }
+ rv.SetUint(unum)
+ } else {
+ panic("unreachable")
+ }
+ return nil
+ }
+ return badtype("integer", data)
+}
+
+func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
+ if b, ok := data.(bool); ok {
+ rv.SetBool(b)
+ return nil
+ }
+ return badtype("boolean", data)
+}
+
+func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
+ rv.Set(reflect.ValueOf(data))
+ return nil
+}
+
+func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error {
+ var s string
+ switch sdata := data.(type) {
+ case TextMarshaler:
+ text, err := sdata.MarshalText()
+ if err != nil {
+ return err
+ }
+ s = string(text)
+ case fmt.Stringer:
+ s = sdata.String()
+ case string:
+ s = sdata
+ case bool:
+ s = fmt.Sprintf("%v", sdata)
+ case int64:
+ s = fmt.Sprintf("%d", sdata)
+ case float64:
+ s = fmt.Sprintf("%f", sdata)
+ default:
+ return badtype("primitive (string-like)", data)
+ }
+ if err := v.UnmarshalText([]byte(s)); err != nil {
+ return err
+ }
+ return nil
+}
+
+// rvalue returns a reflect.Value of `v`. All pointers are resolved.
+func rvalue(v interface{}) reflect.Value {
+ return indirect(reflect.ValueOf(v))
+}
+
+// indirect returns the value pointed to by a pointer.
+// Pointers are followed until the value is not a pointer.
+// New values are allocated for each nil pointer.
+//
+// An exception to this rule is if the value satisfies an interface of
+// interest to us (like encoding.TextUnmarshaler).
+func indirect(v reflect.Value) reflect.Value {
+ if v.Kind() != reflect.Ptr {
+ if v.CanSet() {
+ pv := v.Addr()
+ if _, ok := pv.Interface().(TextUnmarshaler); ok {
+ return pv
+ }
+ }
+ return v
+ }
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ return indirect(reflect.Indirect(v))
+}
+
+func isUnifiable(rv reflect.Value) bool {
+ if rv.CanSet() {
+ return true
+ }
+ if _, ok := rv.Interface().(TextUnmarshaler); ok {
+ return true
+ }
+ return false
+}
+
+func badtype(expected string, data interface{}) error {
+ return e("cannot load TOML value of type %T into a Go %s", data, expected)
+}
diff --git a/vendor/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/BurntSushi/toml/decode_meta.go
new file mode 100644
index 00000000..b9914a67
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/decode_meta.go
@@ -0,0 +1,121 @@
+package toml
+
+import "strings"
+
+// MetaData allows access to meta information about TOML data that may not
+// be inferrable via reflection. In particular, whether a key has been defined
+// and the TOML type of a key.
+type MetaData struct {
+ mapping map[string]interface{}
+ types map[string]tomlType
+ keys []Key
+ decoded map[string]bool
+ context Key // Used only during decoding.
+}
+
+// IsDefined returns true if the key given exists in the TOML data. The key
+// should be specified hierarchially. e.g.,
+//
+// // access the TOML key 'a.b.c'
+// IsDefined("a", "b", "c")
+//
+// IsDefined will return false if an empty key given. Keys are case sensitive.
+func (md *MetaData) IsDefined(key ...string) bool {
+ if len(key) == 0 {
+ return false
+ }
+
+ var hash map[string]interface{}
+ var ok bool
+ var hashOrVal interface{} = md.mapping
+ for _, k := range key {
+ if hash, ok = hashOrVal.(map[string]interface{}); !ok {
+ return false
+ }
+ if hashOrVal, ok = hash[k]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// Type returns a string representation of the type of the key specified.
+//
+// Type will return the empty string if given an empty key or a key that
+// does not exist. Keys are case sensitive.
+func (md *MetaData) Type(key ...string) string {
+ fullkey := strings.Join(key, ".")
+ if typ, ok := md.types[fullkey]; ok {
+ return typ.typeString()
+ }
+ return ""
+}
+
+// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
+// to get values of this type.
+type Key []string
+
+func (k Key) String() string {
+ return strings.Join(k, ".")
+}
+
+func (k Key) maybeQuotedAll() string {
+ var ss []string
+ for i := range k {
+ ss = append(ss, k.maybeQuoted(i))
+ }
+ return strings.Join(ss, ".")
+}
+
+func (k Key) maybeQuoted(i int) string {
+ quote := false
+ for _, c := range k[i] {
+ if !isBareKeyChar(c) {
+ quote = true
+ break
+ }
+ }
+ if quote {
+ return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
+ }
+ return k[i]
+}
+
+func (k Key) add(piece string) Key {
+ newKey := make(Key, len(k)+1)
+ copy(newKey, k)
+ newKey[len(k)] = piece
+ return newKey
+}
+
+// Keys returns a slice of every key in the TOML data, including key groups.
+// Each key is itself a slice, where the first element is the top of the
+// hierarchy and the last is the most specific.
+//
+// The list will have the same order as the keys appeared in the TOML data.
+//
+// All keys returned are non-empty.
+func (md *MetaData) Keys() []Key {
+ return md.keys
+}
+
+// Undecoded returns all keys that have not been decoded in the order in which
+// they appear in the original TOML document.
+//
+// This includes keys that haven't been decoded because of a Primitive value.
+// Once the Primitive value is decoded, the keys will be considered decoded.
+//
+// Also note that decoding into an empty interface will result in no decoding,
+// and so no keys will be considered decoded.
+//
+// In this sense, the Undecoded keys correspond to keys in the TOML document
+// that do not have a concrete type in your representation.
+func (md *MetaData) Undecoded() []Key {
+ undecoded := make([]Key, 0, len(md.keys))
+ for _, key := range md.keys {
+ if !md.decoded[key.String()] {
+ undecoded = append(undecoded, key)
+ }
+ }
+ return undecoded
+}
diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go
new file mode 100644
index 00000000..b371f396
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/doc.go
@@ -0,0 +1,27 @@
+/*
+Package toml provides facilities for decoding and encoding TOML configuration
+files via reflection. There is also support for delaying decoding with
+the Primitive type, and querying the set of keys in a TOML document with the
+MetaData type.
+
+The specification implemented: https://github.com/toml-lang/toml
+
+The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
+whether a file is a valid TOML document. It can also be used to print the
+type of each key in a TOML document.
+
+Testing
+
+There are two important types of tests used for this package. The first is
+contained inside '*_test.go' files and uses the standard Go unit testing
+framework. These tests are primarily devoted to holistically testing the
+decoder and encoder.
+
+The second type of testing is used to verify the implementation's adherence
+to the TOML specification. These tests have been factored into their own
+project: https://github.com/BurntSushi/toml-test
+
+The reason the tests are in a separate project is so that they can be used by
+any implementation of TOML. Namely, it is language agnostic.
+*/
+package toml
diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go
new file mode 100644
index 00000000..d905c21a
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/encode.go
@@ -0,0 +1,568 @@
+package toml
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type tomlEncodeError struct{ error }
+
+var (
+ errArrayMixedElementTypes = errors.New(
+ "toml: cannot encode array with mixed element types")
+ errArrayNilElement = errors.New(
+ "toml: cannot encode array with nil element")
+ errNonString = errors.New(
+ "toml: cannot encode a map with non-string key type")
+ errAnonNonStruct = errors.New(
+ "toml: cannot encode an anonymous field that is not a struct")
+ errArrayNoTable = errors.New(
+ "toml: TOML array element cannot contain a table")
+ errNoKey = errors.New(
+ "toml: top-level values must be Go maps or structs")
+ errAnything = errors.New("") // used in testing
+)
+
+var quotedReplacer = strings.NewReplacer(
+ "\t", "\\t",
+ "\n", "\\n",
+ "\r", "\\r",
+ "\"", "\\\"",
+ "\\", "\\\\",
+)
+
+// Encoder controls the encoding of Go values to a TOML document to some
+// io.Writer.
+//
+// The indentation level can be controlled with the Indent field.
+type Encoder struct {
+ // A single indentation level. By default it is two spaces.
+ Indent string
+
+ // hasWritten is whether we have written any output to w yet.
+ hasWritten bool
+ w *bufio.Writer
+}
+
+// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
+// given. By default, a single indentation level is 2 spaces.
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{
+ w: bufio.NewWriter(w),
+ Indent: " ",
+ }
+}
+
+// Encode writes a TOML representation of the Go value to the underlying
+// io.Writer. If the value given cannot be encoded to a valid TOML document,
+// then an error is returned.
+//
+// The mapping between Go values and TOML values should be precisely the same
+// as for the Decode* functions. Similarly, the TextMarshaler interface is
+// supported by encoding the resulting bytes as strings. (If you want to write
+// arbitrary binary data then you will need to use something like base64 since
+// TOML does not have any binary types.)
+//
+// When encoding TOML hashes (i.e., Go maps or structs), keys without any
+// sub-hashes are encoded first.
+//
+// If a Go map is encoded, then its keys are sorted alphabetically for
+// deterministic output. More control over this behavior may be provided if
+// there is demand for it.
+//
+// Encoding Go values without a corresponding TOML representation---like map
+// types with non-string keys---will cause an error to be returned. Similarly
+// for mixed arrays/slices, arrays/slices with nil elements, embedded
+// non-struct types and nested slices containing maps or structs.
+// (e.g., [][]map[string]string is not allowed but []map[string]string is OK
+// and so is []map[string][]string.)
+func (enc *Encoder) Encode(v interface{}) error {
+ rv := eindirect(reflect.ValueOf(v))
+ if err := enc.safeEncode(Key([]string{}), rv); err != nil {
+ return err
+ }
+ return enc.w.Flush()
+}
+
+func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if terr, ok := r.(tomlEncodeError); ok {
+ err = terr.error
+ return
+ }
+ panic(r)
+ }
+ }()
+ enc.encode(key, rv)
+ return nil
+}
+
+func (enc *Encoder) encode(key Key, rv reflect.Value) {
+ // Special case. Time needs to be in ISO8601 format.
+ // Special case. If we can marshal the type to text, then we used that.
+ // Basically, this prevents the encoder for handling these types as
+ // generic structs (or whatever the underlying type of a TextMarshaler is).
+ switch rv.Interface().(type) {
+ case time.Time, TextMarshaler:
+ enc.keyEqElement(key, rv)
+ return
+ }
+
+ k := rv.Kind()
+ switch k {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64,
+ reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
+ enc.keyEqElement(key, rv)
+ case reflect.Array, reflect.Slice:
+ if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
+ enc.eArrayOfTables(key, rv)
+ } else {
+ enc.keyEqElement(key, rv)
+ }
+ case reflect.Interface:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Map:
+ if rv.IsNil() {
+ return
+ }
+ enc.eTable(key, rv)
+ case reflect.Ptr:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Struct:
+ enc.eTable(key, rv)
+ default:
+ panic(e("unsupported type for key '%s': %s", key, k))
+ }
+}
+
+// eElement encodes any value that can be an array element (primitives and
+// arrays).
+func (enc *Encoder) eElement(rv reflect.Value) {
+ switch v := rv.Interface().(type) {
+ case time.Time:
+ // Special case time.Time as a primitive. Has to come before
+ // TextMarshaler below because time.Time implements
+ // encoding.TextMarshaler, but we need to always use UTC.
+ enc.wf(v.UTC().Format("2006-01-02T15:04:05Z"))
+ return
+ case TextMarshaler:
+ // Special case. Use text marshaler if it's available for this value.
+ if s, err := v.MarshalText(); err != nil {
+ encPanic(err)
+ } else {
+ enc.writeQuoted(string(s))
+ }
+ return
+ }
+ switch rv.Kind() {
+ case reflect.Bool:
+ enc.wf(strconv.FormatBool(rv.Bool()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64:
+ enc.wf(strconv.FormatInt(rv.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16,
+ reflect.Uint32, reflect.Uint64:
+ enc.wf(strconv.FormatUint(rv.Uint(), 10))
+ case reflect.Float32:
+ enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
+ case reflect.Float64:
+ enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
+ case reflect.Array, reflect.Slice:
+ enc.eArrayOrSliceElement(rv)
+ case reflect.Interface:
+ enc.eElement(rv.Elem())
+ case reflect.String:
+ enc.writeQuoted(rv.String())
+ default:
+ panic(e("unexpected primitive type: %s", rv.Kind()))
+ }
+}
+
+// By the TOML spec, all floats must have a decimal with at least one
+// number on either side.
+func floatAddDecimal(fstr string) string {
+ if !strings.Contains(fstr, ".") {
+ return fstr + ".0"
+ }
+ return fstr
+}
+
+func (enc *Encoder) writeQuoted(s string) {
+ enc.wf("\"%s\"", quotedReplacer.Replace(s))
+}
+
+func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
+ length := rv.Len()
+ enc.wf("[")
+ for i := 0; i < length; i++ {
+ elem := rv.Index(i)
+ enc.eElement(elem)
+ if i != length-1 {
+ enc.wf(", ")
+ }
+ }
+ enc.wf("]")
+}
+
+func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
+ if len(key) == 0 {
+ encPanic(errNoKey)
+ }
+ for i := 0; i < rv.Len(); i++ {
+ trv := rv.Index(i)
+ if isNil(trv) {
+ continue
+ }
+ panicIfInvalidKey(key)
+ enc.newline()
+ enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
+ enc.newline()
+ enc.eMapOrStruct(key, trv)
+ }
+}
+
+func (enc *Encoder) eTable(key Key, rv reflect.Value) {
+ panicIfInvalidKey(key)
+ if len(key) == 1 {
+ // Output an extra newline between top-level tables.
+ // (The newline isn't written if nothing else has been written though.)
+ enc.newline()
+ }
+ if len(key) > 0 {
+ enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
+ enc.newline()
+ }
+ enc.eMapOrStruct(key, rv)
+}
+
+func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
+ switch rv := eindirect(rv); rv.Kind() {
+ case reflect.Map:
+ enc.eMap(key, rv)
+ case reflect.Struct:
+ enc.eStruct(key, rv)
+ default:
+ panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
+ }
+}
+
+func (enc *Encoder) eMap(key Key, rv reflect.Value) {
+ rt := rv.Type()
+ if rt.Key().Kind() != reflect.String {
+ encPanic(errNonString)
+ }
+
+ // Sort keys so that we have deterministic output. And write keys directly
+ // underneath this key first, before writing sub-structs or sub-maps.
+ var mapKeysDirect, mapKeysSub []string
+ for _, mapKey := range rv.MapKeys() {
+ k := mapKey.String()
+ if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
+ mapKeysSub = append(mapKeysSub, k)
+ } else {
+ mapKeysDirect = append(mapKeysDirect, k)
+ }
+ }
+
+ var writeMapKeys = func(mapKeys []string) {
+ sort.Strings(mapKeys)
+ for _, mapKey := range mapKeys {
+ mrv := rv.MapIndex(reflect.ValueOf(mapKey))
+ if isNil(mrv) {
+ // Don't write anything for nil fields.
+ continue
+ }
+ enc.encode(key.add(mapKey), mrv)
+ }
+ }
+ writeMapKeys(mapKeysDirect)
+ writeMapKeys(mapKeysSub)
+}
+
+func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
+ // Write keys for fields directly under this key first, because if we write
+ // a field that creates a new table, then all keys under it will be in that
+ // table (not the one we're writing here).
+ rt := rv.Type()
+ var fieldsDirect, fieldsSub [][]int
+ var addFields func(rt reflect.Type, rv reflect.Value, start []int)
+ addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
+ for i := 0; i < rt.NumField(); i++ {
+ f := rt.Field(i)
+ // skip unexported fields
+ if f.PkgPath != "" && !f.Anonymous {
+ continue
+ }
+ frv := rv.Field(i)
+ if f.Anonymous {
+ t := f.Type
+ switch t.Kind() {
+ case reflect.Struct:
+ // Treat anonymous struct fields with
+ // tag names as though they are not
+ // anonymous, like encoding/json does.
+ if getOptions(f.Tag).name == "" {
+ addFields(t, frv, f.Index)
+ continue
+ }
+ case reflect.Ptr:
+ if t.Elem().Kind() == reflect.Struct &&
+ getOptions(f.Tag).name == "" {
+ if !frv.IsNil() {
+ addFields(t.Elem(), frv.Elem(), f.Index)
+ }
+ continue
+ }
+ // Fall through to the normal field encoding logic below
+ // for non-struct anonymous fields.
+ }
+ }
+
+ if typeIsHash(tomlTypeOfGo(frv)) {
+ fieldsSub = append(fieldsSub, append(start, f.Index...))
+ } else {
+ fieldsDirect = append(fieldsDirect, append(start, f.Index...))
+ }
+ }
+ }
+ addFields(rt, rv, nil)
+
+ var writeFields = func(fields [][]int) {
+ for _, fieldIndex := range fields {
+ sft := rt.FieldByIndex(fieldIndex)
+ sf := rv.FieldByIndex(fieldIndex)
+ if isNil(sf) {
+ // Don't write anything for nil fields.
+ continue
+ }
+
+ opts := getOptions(sft.Tag)
+ if opts.skip {
+ continue
+ }
+ keyName := sft.Name
+ if opts.name != "" {
+ keyName = opts.name
+ }
+ if opts.omitempty && isEmpty(sf) {
+ continue
+ }
+ if opts.omitzero && isZero(sf) {
+ continue
+ }
+
+ enc.encode(key.add(keyName), sf)
+ }
+ }
+ writeFields(fieldsDirect)
+ writeFields(fieldsSub)
+}
+
+// tomlTypeName returns the TOML type name of the Go value's type. It is
+// used to determine whether the types of array elements are mixed (which is
+// forbidden). If the Go value is nil, then it is illegal for it to be an array
+// element, and valueIsNil is returned as true.
+
+// Returns the TOML type of a Go value. The type may be `nil`, which means
+// no concrete TOML type could be found.
+func tomlTypeOfGo(rv reflect.Value) tomlType {
+ if isNil(rv) || !rv.IsValid() {
+ return nil
+ }
+ switch rv.Kind() {
+ case reflect.Bool:
+ return tomlBool
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64:
+ return tomlInteger
+ case reflect.Float32, reflect.Float64:
+ return tomlFloat
+ case reflect.Array, reflect.Slice:
+ if typeEqual(tomlHash, tomlArrayType(rv)) {
+ return tomlArrayHash
+ }
+ return tomlArray
+ case reflect.Ptr, reflect.Interface:
+ return tomlTypeOfGo(rv.Elem())
+ case reflect.String:
+ return tomlString
+ case reflect.Map:
+ return tomlHash
+ case reflect.Struct:
+ switch rv.Interface().(type) {
+ case time.Time:
+ return tomlDatetime
+ case TextMarshaler:
+ return tomlString
+ default:
+ return tomlHash
+ }
+ default:
+ panic("unexpected reflect.Kind: " + rv.Kind().String())
+ }
+}
+
+// tomlArrayType returns the element type of a TOML array. The type returned
+// may be nil if it cannot be determined (e.g., a nil slice or a zero length
+// slize). This function may also panic if it finds a type that cannot be
+// expressed in TOML (such as nil elements, heterogeneous arrays or directly
+// nested arrays of tables).
+func tomlArrayType(rv reflect.Value) tomlType {
+ if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
+ return nil
+ }
+ firstType := tomlTypeOfGo(rv.Index(0))
+ if firstType == nil {
+ encPanic(errArrayNilElement)
+ }
+
+ rvlen := rv.Len()
+ for i := 1; i < rvlen; i++ {
+ elem := rv.Index(i)
+ switch elemType := tomlTypeOfGo(elem); {
+ case elemType == nil:
+ encPanic(errArrayNilElement)
+ case !typeEqual(firstType, elemType):
+ encPanic(errArrayMixedElementTypes)
+ }
+ }
+ // If we have a nested array, then we must make sure that the nested
+ // array contains ONLY primitives.
+ // This checks arbitrarily nested arrays.
+ if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
+ nest := tomlArrayType(eindirect(rv.Index(0)))
+ if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
+ encPanic(errArrayNoTable)
+ }
+ }
+ return firstType
+}
+
+type tagOptions struct {
+ skip bool // "-"
+ name string
+ omitempty bool
+ omitzero bool
+}
+
+func getOptions(tag reflect.StructTag) tagOptions {
+ t := tag.Get("toml")
+ if t == "-" {
+ return tagOptions{skip: true}
+ }
+ var opts tagOptions
+ parts := strings.Split(t, ",")
+ opts.name = parts[0]
+ for _, s := range parts[1:] {
+ switch s {
+ case "omitempty":
+ opts.omitempty = true
+ case "omitzero":
+ opts.omitzero = true
+ }
+ }
+ return opts
+}
+
+func isZero(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return rv.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return rv.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return rv.Float() == 0.0
+ }
+ return false
+}
+
+func isEmpty(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+ return rv.Len() == 0
+ case reflect.Bool:
+ return !rv.Bool()
+ }
+ return false
+}
+
+func (enc *Encoder) newline() {
+ if enc.hasWritten {
+ enc.wf("\n")
+ }
+}
+
+func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
+ if len(key) == 0 {
+ encPanic(errNoKey)
+ }
+ panicIfInvalidKey(key)
+ enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
+ enc.eElement(val)
+ enc.newline()
+}
+
+func (enc *Encoder) wf(format string, v ...interface{}) {
+ if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
+ encPanic(err)
+ }
+ enc.hasWritten = true
+}
+
+func (enc *Encoder) indentStr(key Key) string {
+ return strings.Repeat(enc.Indent, len(key)-1)
+}
+
+func encPanic(err error) {
+ panic(tomlEncodeError{err})
+}
+
+func eindirect(v reflect.Value) reflect.Value {
+ switch v.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ return eindirect(v.Elem())
+ default:
+ return v
+ }
+}
+
+func isNil(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return rv.IsNil()
+ default:
+ return false
+ }
+}
+
+func panicIfInvalidKey(key Key) {
+ for _, k := range key {
+ if len(k) == 0 {
+ encPanic(e("Key '%s' is not a valid table name. Key names "+
+ "cannot be empty.", key.maybeQuotedAll()))
+ }
+ }
+}
+
+func isValidKeyName(s string) bool {
+ return len(s) != 0
+}
diff --git a/vendor/github.com/BurntSushi/toml/encoding_types.go b/vendor/github.com/BurntSushi/toml/encoding_types.go
new file mode 100644
index 00000000..d36e1dd6
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/encoding_types.go
@@ -0,0 +1,19 @@
+// +build go1.2
+
+package toml
+
+// In order to support Go 1.1, we define our own TextMarshaler and
+// TextUnmarshaler types. For Go 1.2+, we just alias them with the
+// standard library interfaces.
+
+import (
+ "encoding"
+)
+
+// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
+// so that Go 1.1 can be supported.
+type TextMarshaler encoding.TextMarshaler
+
+// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
+// here so that Go 1.1 can be supported.
+type TextUnmarshaler encoding.TextUnmarshaler
diff --git a/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
new file mode 100644
index 00000000..e8d503d0
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
@@ -0,0 +1,18 @@
+// +build !go1.2
+
+package toml
+
+// These interfaces were introduced in Go 1.2, so we add them manually when
+// compiling for Go 1.1.
+
+// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
+// so that Go 1.1 can be supported.
+type TextMarshaler interface {
+ MarshalText() (text []byte, err error)
+}
+
+// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
+// here so that Go 1.1 can be supported.
+type TextUnmarshaler interface {
+ UnmarshalText(text []byte) error
+}
diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go
new file mode 100644
index 00000000..e0a742a8
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/lex.go
@@ -0,0 +1,953 @@
+package toml
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+type itemType int
+
+const (
+ itemError itemType = iota
+ itemNIL // used in the parser to indicate no type
+ itemEOF
+ itemText
+ itemString
+ itemRawString
+ itemMultilineString
+ itemRawMultilineString
+ itemBool
+ itemInteger
+ itemFloat
+ itemDatetime
+ itemArray // the start of an array
+ itemArrayEnd
+ itemTableStart
+ itemTableEnd
+ itemArrayTableStart
+ itemArrayTableEnd
+ itemKeyStart
+ itemCommentStart
+ itemInlineTableStart
+ itemInlineTableEnd
+)
+
+const (
+ eof = 0
+ comma = ','
+ tableStart = '['
+ tableEnd = ']'
+ arrayTableStart = '['
+ arrayTableEnd = ']'
+ tableSep = '.'
+ keySep = '='
+ arrayStart = '['
+ arrayEnd = ']'
+ commentStart = '#'
+ stringStart = '"'
+ stringEnd = '"'
+ rawStringStart = '\''
+ rawStringEnd = '\''
+ inlineTableStart = '{'
+ inlineTableEnd = '}'
+)
+
+type stateFn func(lx *lexer) stateFn
+
+type lexer struct {
+ input string
+ start int
+ pos int
+ line int
+ state stateFn
+ items chan item
+
+ // Allow for backing up up to three runes.
+ // This is necessary because TOML contains 3-rune tokens (""" and ''').
+ prevWidths [3]int
+ nprev int // how many of prevWidths are in use
+ // If we emit an eof, we can still back up, but it is not OK to call
+ // next again.
+ atEOF bool
+
+ // A stack of state functions used to maintain context.
+ // The idea is to reuse parts of the state machine in various places.
+ // For example, values can appear at the top level or within arbitrarily
+ // nested arrays. The last state on the stack is used after a value has
+ // been lexed. Similarly for comments.
+ stack []stateFn
+}
+
+type item struct {
+ typ itemType
+ val string
+ line int
+}
+
+func (lx *lexer) nextItem() item {
+ for {
+ select {
+ case item := <-lx.items:
+ return item
+ default:
+ lx.state = lx.state(lx)
+ }
+ }
+}
+
+func lex(input string) *lexer {
+ lx := &lexer{
+ input: input,
+ state: lexTop,
+ line: 1,
+ items: make(chan item, 10),
+ stack: make([]stateFn, 0, 10),
+ }
+ return lx
+}
+
+func (lx *lexer) push(state stateFn) {
+ lx.stack = append(lx.stack, state)
+}
+
+func (lx *lexer) pop() stateFn {
+ if len(lx.stack) == 0 {
+ return lx.errorf("BUG in lexer: no states to pop")
+ }
+ last := lx.stack[len(lx.stack)-1]
+ lx.stack = lx.stack[0 : len(lx.stack)-1]
+ return last
+}
+
+func (lx *lexer) current() string {
+ return lx.input[lx.start:lx.pos]
+}
+
+func (lx *lexer) emit(typ itemType) {
+ lx.items <- item{typ, lx.current(), lx.line}
+ lx.start = lx.pos
+}
+
+func (lx *lexer) emitTrim(typ itemType) {
+ lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line}
+ lx.start = lx.pos
+}
+
+func (lx *lexer) next() (r rune) {
+ if lx.atEOF {
+ panic("next called after EOF")
+ }
+ if lx.pos >= len(lx.input) {
+ lx.atEOF = true
+ return eof
+ }
+
+ if lx.input[lx.pos] == '\n' {
+ lx.line++
+ }
+ lx.prevWidths[2] = lx.prevWidths[1]
+ lx.prevWidths[1] = lx.prevWidths[0]
+ if lx.nprev < 3 {
+ lx.nprev++
+ }
+ r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
+ lx.prevWidths[0] = w
+ lx.pos += w
+ return r
+}
+
+// ignore skips over the pending input before this point.
+func (lx *lexer) ignore() {
+ lx.start = lx.pos
+}
+
+// backup steps back one rune. Can be called only twice between calls to next.
+func (lx *lexer) backup() {
+ if lx.atEOF {
+ lx.atEOF = false
+ return
+ }
+ if lx.nprev < 1 {
+ panic("backed up too far")
+ }
+ w := lx.prevWidths[0]
+ lx.prevWidths[0] = lx.prevWidths[1]
+ lx.prevWidths[1] = lx.prevWidths[2]
+ lx.nprev--
+ lx.pos -= w
+ if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
+ lx.line--
+ }
+}
+
+// accept consumes the next rune if it's equal to `valid`.
+func (lx *lexer) accept(valid rune) bool {
+ if lx.next() == valid {
+ return true
+ }
+ lx.backup()
+ return false
+}
+
+// peek returns but does not consume the next rune in the input.
+func (lx *lexer) peek() rune {
+ r := lx.next()
+ lx.backup()
+ return r
+}
+
+// skip ignores all input that matches the given predicate.
+func (lx *lexer) skip(pred func(rune) bool) {
+ for {
+ r := lx.next()
+ if pred(r) {
+ continue
+ }
+ lx.backup()
+ lx.ignore()
+ return
+ }
+}
+
+// errorf stops all lexing by emitting an error and returning `nil`.
+// Note that any value that is a character is escaped if it's a special
+// character (newlines, tabs, etc.).
+func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
+ lx.items <- item{
+ itemError,
+ fmt.Sprintf(format, values...),
+ lx.line,
+ }
+ return nil
+}
+
+// lexTop consumes elements at the top level of TOML data.
+func lexTop(lx *lexer) stateFn {
+ r := lx.next()
+ if isWhitespace(r) || isNL(r) {
+ return lexSkip(lx, lexTop)
+ }
+ switch r {
+ case commentStart:
+ lx.push(lexTop)
+ return lexCommentStart
+ case tableStart:
+ return lexTableStart
+ case eof:
+ if lx.pos > lx.start {
+ return lx.errorf("unexpected EOF")
+ }
+ lx.emit(itemEOF)
+ return nil
+ }
+
+ // At this point, the only valid item can be a key, so we back up
+ // and let the key lexer do the rest.
+ lx.backup()
+ lx.push(lexTopEnd)
+ return lexKeyStart
+}
+
+// lexTopEnd is entered whenever a top-level item has been consumed. (A value
+// or a table.) It must see only whitespace, and will turn back to lexTop
+// upon a newline. If it sees EOF, it will quit the lexer successfully.
+func lexTopEnd(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == commentStart:
+ // a comment will read to a newline for us.
+ lx.push(lexTop)
+ return lexCommentStart
+ case isWhitespace(r):
+ return lexTopEnd
+ case isNL(r):
+ lx.ignore()
+ return lexTop
+ case r == eof:
+ lx.emit(itemEOF)
+ return nil
+ }
+ return lx.errorf("expected a top-level item to end with a newline, "+
+ "comment, or EOF, but got %q instead", r)
+}
+
+// lexTable lexes the beginning of a table. Namely, it makes sure that
+// it starts with a character other than '.' and ']'.
+// It assumes that '[' has already been consumed.
+// It also handles the case that this is an item in an array of tables.
+// e.g., '[[name]]'.
+func lexTableStart(lx *lexer) stateFn {
+ if lx.peek() == arrayTableStart {
+ lx.next()
+ lx.emit(itemArrayTableStart)
+ lx.push(lexArrayTableEnd)
+ } else {
+ lx.emit(itemTableStart)
+ lx.push(lexTableEnd)
+ }
+ return lexTableNameStart
+}
+
+func lexTableEnd(lx *lexer) stateFn {
+ lx.emit(itemTableEnd)
+ return lexTopEnd
+}
+
+func lexArrayTableEnd(lx *lexer) stateFn {
+ if r := lx.next(); r != arrayTableEnd {
+ return lx.errorf("expected end of table array name delimiter %q, "+
+ "but got %q instead", arrayTableEnd, r)
+ }
+ lx.emit(itemArrayTableEnd)
+ return lexTopEnd
+}
+
+func lexTableNameStart(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.peek(); {
+ case r == tableEnd || r == eof:
+ return lx.errorf("unexpected end of table name " +
+ "(table names cannot be empty)")
+ case r == tableSep:
+ return lx.errorf("unexpected table separator " +
+ "(table names cannot be empty)")
+ case r == stringStart || r == rawStringStart:
+ lx.ignore()
+ lx.push(lexTableNameEnd)
+ return lexValue // reuse string lexing
+ default:
+ return lexBareTableName
+ }
+}
+
+// lexBareTableName lexes the name of a table. It assumes that at least one
+// valid character for the table has already been read.
+func lexBareTableName(lx *lexer) stateFn {
+ r := lx.next()
+ if isBareKeyChar(r) {
+ return lexBareTableName
+ }
+ lx.backup()
+ lx.emit(itemText)
+ return lexTableNameEnd
+}
+
+// lexTableNameEnd reads the end of a piece of a table name, optionally
+// consuming whitespace.
+func lexTableNameEnd(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.next(); {
+ case isWhitespace(r):
+ return lexTableNameEnd
+ case r == tableSep:
+ lx.ignore()
+ return lexTableNameStart
+ case r == tableEnd:
+ return lx.pop()
+ default:
+ return lx.errorf("expected '.' or ']' to end table name, "+
+ "but got %q instead", r)
+ }
+}
+
+// lexKeyStart consumes a key name up until the first non-whitespace character.
+// lexKeyStart will ignore whitespace.
+func lexKeyStart(lx *lexer) stateFn {
+ r := lx.peek()
+ switch {
+ case r == keySep:
+ return lx.errorf("unexpected key separator %q", keySep)
+ case isWhitespace(r) || isNL(r):
+ lx.next()
+ return lexSkip(lx, lexKeyStart)
+ case r == stringStart || r == rawStringStart:
+ lx.ignore()
+ lx.emit(itemKeyStart)
+ lx.push(lexKeyEnd)
+ return lexValue // reuse string lexing
+ default:
+ lx.ignore()
+ lx.emit(itemKeyStart)
+ return lexBareKey
+ }
+}
+
+// lexBareKey consumes the text of a bare key. Assumes that the first character
+// (which is not whitespace) has not yet been consumed.
+func lexBareKey(lx *lexer) stateFn {
+ switch r := lx.next(); {
+ case isBareKeyChar(r):
+ return lexBareKey
+ case isWhitespace(r):
+ lx.backup()
+ lx.emit(itemText)
+ return lexKeyEnd
+ case r == keySep:
+ lx.backup()
+ lx.emit(itemText)
+ return lexKeyEnd
+ default:
+ return lx.errorf("bare keys cannot contain %q", r)
+ }
+}
+
+// lexKeyEnd consumes the end of a key and trims whitespace (up to the key
+// separator).
+func lexKeyEnd(lx *lexer) stateFn {
+ switch r := lx.next(); {
+ case r == keySep:
+ return lexSkip(lx, lexValue)
+ case isWhitespace(r):
+ return lexSkip(lx, lexKeyEnd)
+ default:
+ return lx.errorf("expected key separator %q, but got %q instead",
+ keySep, r)
+ }
+}
+
+// lexValue starts the consumption of a value anywhere a value is expected.
+// lexValue will ignore whitespace.
+// After a value is lexed, the last state on the next is popped and returned.
+func lexValue(lx *lexer) stateFn {
+ // We allow whitespace to precede a value, but NOT newlines.
+ // In array syntax, the array states are responsible for ignoring newlines.
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexValue)
+ case isDigit(r):
+ lx.backup() // avoid an extra state and use the same as above
+ return lexNumberOrDateStart
+ }
+ switch r {
+ case arrayStart:
+ lx.ignore()
+ lx.emit(itemArray)
+ return lexArrayValue
+ case inlineTableStart:
+ lx.ignore()
+ lx.emit(itemInlineTableStart)
+ return lexInlineTableValue
+ case stringStart:
+ if lx.accept(stringStart) {
+ if lx.accept(stringStart) {
+ lx.ignore() // Ignore """
+ return lexMultilineString
+ }
+ lx.backup()
+ }
+ lx.ignore() // ignore the '"'
+ return lexString
+ case rawStringStart:
+ if lx.accept(rawStringStart) {
+ if lx.accept(rawStringStart) {
+ lx.ignore() // Ignore """
+ return lexMultilineRawString
+ }
+ lx.backup()
+ }
+ lx.ignore() // ignore the "'"
+ return lexRawString
+ case '+', '-':
+ return lexNumberStart
+ case '.': // special error case, be kind to users
+ return lx.errorf("floats must start with a digit, not '.'")
+ }
+ if unicode.IsLetter(r) {
+ // Be permissive here; lexBool will give a nice error if the
+ // user wrote something like
+ // x = foo
+ // (i.e. not 'true' or 'false' but is something else word-like.)
+ lx.backup()
+ return lexBool
+ }
+ return lx.errorf("expected value but found %q instead", r)
+}
+
+// lexArrayValue consumes one value in an array. It assumes that '[' or ','
+// have already been consumed. All whitespace and newlines are ignored.
+func lexArrayValue(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r) || isNL(r):
+ return lexSkip(lx, lexArrayValue)
+ case r == commentStart:
+ lx.push(lexArrayValue)
+ return lexCommentStart
+ case r == comma:
+ return lx.errorf("unexpected comma")
+ case r == arrayEnd:
+ // NOTE(caleb): The spec isn't clear about whether you can have
+ // a trailing comma or not, so we'll allow it.
+ return lexArrayEnd
+ }
+
+ lx.backup()
+ lx.push(lexArrayValueEnd)
+ return lexValue
+}
+
+// lexArrayValueEnd consumes everything between the end of an array value and
+// the next value (or the end of the array): it ignores whitespace and newlines
+// and expects either a ',' or a ']'.
+func lexArrayValueEnd(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r) || isNL(r):
+ return lexSkip(lx, lexArrayValueEnd)
+ case r == commentStart:
+ lx.push(lexArrayValueEnd)
+ return lexCommentStart
+ case r == comma:
+ lx.ignore()
+ return lexArrayValue // move on to the next value
+ case r == arrayEnd:
+ return lexArrayEnd
+ }
+ return lx.errorf(
+ "expected a comma or array terminator %q, but got %q instead",
+ arrayEnd, r,
+ )
+}
+
+// lexArrayEnd finishes the lexing of an array.
+// It assumes that a ']' has just been consumed.
+func lexArrayEnd(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemArrayEnd)
+ return lx.pop()
+}
+
+// lexInlineTableValue consumes one key/value pair in an inline table.
+// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
+func lexInlineTableValue(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexInlineTableValue)
+ case isNL(r):
+ return lx.errorf("newlines not allowed within inline tables")
+ case r == commentStart:
+ lx.push(lexInlineTableValue)
+ return lexCommentStart
+ case r == comma:
+ return lx.errorf("unexpected comma")
+ case r == inlineTableEnd:
+ return lexInlineTableEnd
+ }
+ lx.backup()
+ lx.push(lexInlineTableValueEnd)
+ return lexKeyStart
+}
+
+// lexInlineTableValueEnd consumes everything between the end of an inline table
+// key/value pair and the next pair (or the end of the table):
+// it ignores whitespace and expects either a ',' or a '}'.
+func lexInlineTableValueEnd(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexInlineTableValueEnd)
+ case isNL(r):
+ return lx.errorf("newlines not allowed within inline tables")
+ case r == commentStart:
+ lx.push(lexInlineTableValueEnd)
+ return lexCommentStart
+ case r == comma:
+ lx.ignore()
+ return lexInlineTableValue
+ case r == inlineTableEnd:
+ return lexInlineTableEnd
+ }
+ return lx.errorf("expected a comma or an inline table terminator %q, "+
+ "but got %q instead", inlineTableEnd, r)
+}
+
+// lexInlineTableEnd finishes the lexing of an inline table.
+// It assumes that a '}' has just been consumed.
+func lexInlineTableEnd(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemInlineTableEnd)
+ return lx.pop()
+}
+
+// lexString consumes the inner contents of a string. It assumes that the
+// beginning '"' has already been consumed and ignored.
+func lexString(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == eof:
+ return lx.errorf("unexpected EOF")
+ case isNL(r):
+ return lx.errorf("strings cannot contain newlines")
+ case r == '\\':
+ lx.push(lexString)
+ return lexStringEscape
+ case r == stringEnd:
+ lx.backup()
+ lx.emit(itemString)
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ return lexString
+}
+
+// lexMultilineString consumes the inner contents of a string. It assumes that
+// the beginning '"""' has already been consumed and ignored.
+func lexMultilineString(lx *lexer) stateFn {
+ switch lx.next() {
+ case eof:
+ return lx.errorf("unexpected EOF")
+ case '\\':
+ return lexMultilineStringEscape
+ case stringEnd:
+ if lx.accept(stringEnd) {
+ if lx.accept(stringEnd) {
+ lx.backup()
+ lx.backup()
+ lx.backup()
+ lx.emit(itemMultilineString)
+ lx.next()
+ lx.next()
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ lx.backup()
+ }
+ }
+ return lexMultilineString
+}
+
+// lexRawString consumes a raw string. Nothing can be escaped in such a string.
+// It assumes that the beginning "'" has already been consumed and ignored.
+func lexRawString(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == eof:
+ return lx.errorf("unexpected EOF")
+ case isNL(r):
+ return lx.errorf("strings cannot contain newlines")
+ case r == rawStringEnd:
+ lx.backup()
+ lx.emit(itemRawString)
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ return lexRawString
+}
+
+// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
+// a string. It assumes that the beginning "'''" has already been consumed and
+// ignored.
+func lexMultilineRawString(lx *lexer) stateFn {
+ switch lx.next() {
+ case eof:
+ return lx.errorf("unexpected EOF")
+ case rawStringEnd:
+ if lx.accept(rawStringEnd) {
+ if lx.accept(rawStringEnd) {
+ lx.backup()
+ lx.backup()
+ lx.backup()
+ lx.emit(itemRawMultilineString)
+ lx.next()
+ lx.next()
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ lx.backup()
+ }
+ }
+ return lexMultilineRawString
+}
+
+// lexMultilineStringEscape consumes an escaped character. It assumes that the
+// preceding '\\' has already been consumed.
+func lexMultilineStringEscape(lx *lexer) stateFn {
+ // Handle the special case first:
+ if isNL(lx.next()) {
+ return lexMultilineString
+ }
+ lx.backup()
+ lx.push(lexMultilineString)
+ return lexStringEscape(lx)
+}
+
+func lexStringEscape(lx *lexer) stateFn {
+ r := lx.next()
+ switch r {
+ case 'b':
+ fallthrough
+ case 't':
+ fallthrough
+ case 'n':
+ fallthrough
+ case 'f':
+ fallthrough
+ case 'r':
+ fallthrough
+ case '"':
+ fallthrough
+ case '\\':
+ return lx.pop()
+ case 'u':
+ return lexShortUnicodeEscape
+ case 'U':
+ return lexLongUnicodeEscape
+ }
+ return lx.errorf("invalid escape character %q; only the following "+
+ "escape characters are allowed: "+
+ `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
+}
+
+func lexShortUnicodeEscape(lx *lexer) stateFn {
+ var r rune
+ for i := 0; i < 4; i++ {
+ r = lx.next()
+ if !isHexadecimal(r) {
+ return lx.errorf(`expected four hexadecimal digits after '\u', `+
+ "but got %q instead", lx.current())
+ }
+ }
+ return lx.pop()
+}
+
+func lexLongUnicodeEscape(lx *lexer) stateFn {
+ var r rune
+ for i := 0; i < 8; i++ {
+ r = lx.next()
+ if !isHexadecimal(r) {
+ return lx.errorf(`expected eight hexadecimal digits after '\U', `+
+ "but got %q instead", lx.current())
+ }
+ }
+ return lx.pop()
+}
+
+// lexNumberOrDateStart consumes either an integer, a float, or datetime.
+func lexNumberOrDateStart(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexNumberOrDate
+ }
+ switch r {
+ case '_':
+ return lexNumber
+ case 'e', 'E':
+ return lexFloat
+ case '.':
+ return lx.errorf("floats must start with a digit, not '.'")
+ }
+ return lx.errorf("expected a digit but got %q", r)
+}
+
+// lexNumberOrDate consumes either an integer, float or datetime.
+func lexNumberOrDate(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexNumberOrDate
+ }
+ switch r {
+ case '-':
+ return lexDatetime
+ case '_':
+ return lexNumber
+ case '.', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexDatetime consumes a Datetime, to a first approximation.
+// The parser validates that it matches one of the accepted formats.
+func lexDatetime(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexDatetime
+ }
+ switch r {
+ case '-', 'T', ':', '.', 'Z', '+':
+ return lexDatetime
+ }
+
+ lx.backup()
+ lx.emit(itemDatetime)
+ return lx.pop()
+}
+
+// lexNumberStart consumes either an integer or a float. It assumes that a sign
+// has already been read, but that *no* digits have been consumed.
+// lexNumberStart will move to the appropriate integer or float states.
+func lexNumberStart(lx *lexer) stateFn {
+ // We MUST see a digit. Even floats have to start with a digit.
+ r := lx.next()
+ if !isDigit(r) {
+ if r == '.' {
+ return lx.errorf("floats must start with a digit, not '.'")
+ }
+ return lx.errorf("expected a digit but got %q", r)
+ }
+ return lexNumber
+}
+
+// lexNumber consumes an integer or a float after seeing the first digit.
+func lexNumber(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexNumber
+ }
+ switch r {
+ case '_':
+ return lexNumber
+ case '.', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexFloat consumes the elements of a float. It allows any sequence of
+// float-like characters, so floats emitted by the lexer are only a first
+// approximation and must be validated by the parser.
+func lexFloat(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexFloat
+ }
+ switch r {
+ case '_', '.', '-', '+', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemFloat)
+ return lx.pop()
+}
+
+// lexBool consumes a bool string: 'true' or 'false.
+func lexBool(lx *lexer) stateFn {
+ var rs []rune
+ for {
+ r := lx.next()
+ if !unicode.IsLetter(r) {
+ lx.backup()
+ break
+ }
+ rs = append(rs, r)
+ }
+ s := string(rs)
+ switch s {
+ case "true", "false":
+ lx.emit(itemBool)
+ return lx.pop()
+ }
+ return lx.errorf("expected value but found %q instead", s)
+}
+
+// lexCommentStart begins the lexing of a comment. It will emit
+// itemCommentStart and consume no characters, passing control to lexComment.
+func lexCommentStart(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemCommentStart)
+ return lexComment
+}
+
+// lexComment lexes an entire comment. It assumes that '#' has been consumed.
+// It will consume *up to* the first newline character, and pass control
+// back to the last state on the stack.
+func lexComment(lx *lexer) stateFn {
+ r := lx.peek()
+ if isNL(r) || r == eof {
+ lx.emit(itemText)
+ return lx.pop()
+ }
+ lx.next()
+ return lexComment
+}
+
+// lexSkip ignores all slurped input and moves on to the next state.
+func lexSkip(lx *lexer, nextState stateFn) stateFn {
+ return func(lx *lexer) stateFn {
+ lx.ignore()
+ return nextState
+ }
+}
+
+// isWhitespace returns true if `r` is a whitespace character according
+// to the spec.
+func isWhitespace(r rune) bool {
+ return r == '\t' || r == ' '
+}
+
+func isNL(r rune) bool {
+ return r == '\n' || r == '\r'
+}
+
+func isDigit(r rune) bool {
+ return r >= '0' && r <= '9'
+}
+
+func isHexadecimal(r rune) bool {
+ return (r >= '0' && r <= '9') ||
+ (r >= 'a' && r <= 'f') ||
+ (r >= 'A' && r <= 'F')
+}
+
+func isBareKeyChar(r rune) bool {
+ return (r >= 'A' && r <= 'Z') ||
+ (r >= 'a' && r <= 'z') ||
+ (r >= '0' && r <= '9') ||
+ r == '_' ||
+ r == '-'
+}
+
+func (itype itemType) String() string {
+ switch itype {
+ case itemError:
+ return "Error"
+ case itemNIL:
+ return "NIL"
+ case itemEOF:
+ return "EOF"
+ case itemText:
+ return "Text"
+ case itemString, itemRawString, itemMultilineString, itemRawMultilineString:
+ return "String"
+ case itemBool:
+ return "Bool"
+ case itemInteger:
+ return "Integer"
+ case itemFloat:
+ return "Float"
+ case itemDatetime:
+ return "DateTime"
+ case itemTableStart:
+ return "TableStart"
+ case itemTableEnd:
+ return "TableEnd"
+ case itemKeyStart:
+ return "KeyStart"
+ case itemArray:
+ return "Array"
+ case itemArrayEnd:
+ return "ArrayEnd"
+ case itemCommentStart:
+ return "CommentStart"
+ }
+ panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype)))
+}
+
+func (item item) String() string {
+ return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val)
+}
diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go
new file mode 100644
index 00000000..50869ef9
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/parse.go
@@ -0,0 +1,592 @@
+package toml
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+type parser struct {
+ mapping map[string]interface{}
+ types map[string]tomlType
+ lx *lexer
+
+ // A list of keys in the order that they appear in the TOML data.
+ ordered []Key
+
+ // the full key for the current hash in scope
+ context Key
+
+ // the base key name for everything except hashes
+ currentKey string
+
+ // rough approximation of line number
+ approxLine int
+
+ // A map of 'key.group.names' to whether they were created implicitly.
+ implicits map[string]bool
+}
+
+type parseError string
+
+func (pe parseError) Error() string {
+ return string(pe)
+}
+
+func parse(data string) (p *parser, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ var ok bool
+ if err, ok = r.(parseError); ok {
+ return
+ }
+ panic(r)
+ }
+ }()
+
+ p = &parser{
+ mapping: make(map[string]interface{}),
+ types: make(map[string]tomlType),
+ lx: lex(data),
+ ordered: make([]Key, 0),
+ implicits: make(map[string]bool),
+ }
+ for {
+ item := p.next()
+ if item.typ == itemEOF {
+ break
+ }
+ p.topLevel(item)
+ }
+
+ return p, nil
+}
+
+func (p *parser) panicf(format string, v ...interface{}) {
+ msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
+ p.approxLine, p.current(), fmt.Sprintf(format, v...))
+ panic(parseError(msg))
+}
+
+func (p *parser) next() item {
+ it := p.lx.nextItem()
+ if it.typ == itemError {
+ p.panicf("%s", it.val)
+ }
+ return it
+}
+
+func (p *parser) bug(format string, v ...interface{}) {
+ panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
+}
+
+func (p *parser) expect(typ itemType) item {
+ it := p.next()
+ p.assertEqual(typ, it.typ)
+ return it
+}
+
+func (p *parser) assertEqual(expected, got itemType) {
+ if expected != got {
+ p.bug("Expected '%s' but got '%s'.", expected, got)
+ }
+}
+
+func (p *parser) topLevel(item item) {
+ switch item.typ {
+ case itemCommentStart:
+ p.approxLine = item.line
+ p.expect(itemText)
+ case itemTableStart:
+ kg := p.next()
+ p.approxLine = kg.line
+
+ var key Key
+ for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
+ key = append(key, p.keyString(kg))
+ }
+ p.assertEqual(itemTableEnd, kg.typ)
+
+ p.establishContext(key, false)
+ p.setType("", tomlHash)
+ p.ordered = append(p.ordered, key)
+ case itemArrayTableStart:
+ kg := p.next()
+ p.approxLine = kg.line
+
+ var key Key
+ for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
+ key = append(key, p.keyString(kg))
+ }
+ p.assertEqual(itemArrayTableEnd, kg.typ)
+
+ p.establishContext(key, true)
+ p.setType("", tomlArrayHash)
+ p.ordered = append(p.ordered, key)
+ case itemKeyStart:
+ kname := p.next()
+ p.approxLine = kname.line
+ p.currentKey = p.keyString(kname)
+
+ val, typ := p.value(p.next())
+ p.setValue(p.currentKey, val)
+ p.setType(p.currentKey, typ)
+ p.ordered = append(p.ordered, p.context.add(p.currentKey))
+ p.currentKey = ""
+ default:
+ p.bug("Unexpected type at top level: %s", item.typ)
+ }
+}
+
+// Gets a string for a key (or part of a key in a table name).
+func (p *parser) keyString(it item) string {
+ switch it.typ {
+ case itemText:
+ return it.val
+ case itemString, itemMultilineString,
+ itemRawString, itemRawMultilineString:
+ s, _ := p.value(it)
+ return s.(string)
+ default:
+ p.bug("Unexpected key type: %s", it.typ)
+ panic("unreachable")
+ }
+}
+
+// value translates an expected value from the lexer into a Go value wrapped
+// as an empty interface.
+func (p *parser) value(it item) (interface{}, tomlType) {
+ switch it.typ {
+ case itemString:
+ return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
+ case itemMultilineString:
+ trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
+ return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
+ case itemRawString:
+ return it.val, p.typeOfPrimitive(it)
+ case itemRawMultilineString:
+ return stripFirstNewline(it.val), p.typeOfPrimitive(it)
+ case itemBool:
+ switch it.val {
+ case "true":
+ return true, p.typeOfPrimitive(it)
+ case "false":
+ return false, p.typeOfPrimitive(it)
+ }
+ p.bug("Expected boolean value, but got '%s'.", it.val)
+ case itemInteger:
+ if !numUnderscoresOK(it.val) {
+ p.panicf("Invalid integer %q: underscores must be surrounded by digits",
+ it.val)
+ }
+ val := strings.Replace(it.val, "_", "", -1)
+ num, err := strconv.ParseInt(val, 10, 64)
+ if err != nil {
+ // Distinguish integer values. Normally, it'd be a bug if the lexer
+ // provides an invalid integer, but it's possible that the number is
+ // out of range of valid values (which the lexer cannot determine).
+ // So mark the former as a bug but the latter as a legitimate user
+ // error.
+ if e, ok := err.(*strconv.NumError); ok &&
+ e.Err == strconv.ErrRange {
+
+ p.panicf("Integer '%s' is out of the range of 64-bit "+
+ "signed integers.", it.val)
+ } else {
+ p.bug("Expected integer value, but got '%s'.", it.val)
+ }
+ }
+ return num, p.typeOfPrimitive(it)
+ case itemFloat:
+ parts := strings.FieldsFunc(it.val, func(r rune) bool {
+ switch r {
+ case '.', 'e', 'E':
+ return true
+ }
+ return false
+ })
+ for _, part := range parts {
+ if !numUnderscoresOK(part) {
+ p.panicf("Invalid float %q: underscores must be "+
+ "surrounded by digits", it.val)
+ }
+ }
+ if !numPeriodsOK(it.val) {
+ // As a special case, numbers like '123.' or '1.e2',
+ // which are valid as far as Go/strconv are concerned,
+ // must be rejected because TOML says that a fractional
+ // part consists of '.' followed by 1+ digits.
+ p.panicf("Invalid float %q: '.' must be followed "+
+ "by one or more digits", it.val)
+ }
+ val := strings.Replace(it.val, "_", "", -1)
+ num, err := strconv.ParseFloat(val, 64)
+ if err != nil {
+ if e, ok := err.(*strconv.NumError); ok &&
+ e.Err == strconv.ErrRange {
+
+ p.panicf("Float '%s' is out of the range of 64-bit "+
+ "IEEE-754 floating-point numbers.", it.val)
+ } else {
+ p.panicf("Invalid float value: %q", it.val)
+ }
+ }
+ return num, p.typeOfPrimitive(it)
+ case itemDatetime:
+ var t time.Time
+ var ok bool
+ var err error
+ for _, format := range []string{
+ "2006-01-02T15:04:05Z07:00",
+ "2006-01-02T15:04:05",
+ "2006-01-02",
+ } {
+ t, err = time.ParseInLocation(format, it.val, time.Local)
+ if err == nil {
+ ok = true
+ break
+ }
+ }
+ if !ok {
+ p.panicf("Invalid TOML Datetime: %q.", it.val)
+ }
+ return t, p.typeOfPrimitive(it)
+ case itemArray:
+ array := make([]interface{}, 0)
+ types := make([]tomlType, 0)
+
+ for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
+ if it.typ == itemCommentStart {
+ p.expect(itemText)
+ continue
+ }
+
+ val, typ := p.value(it)
+ array = append(array, val)
+ types = append(types, typ)
+ }
+ return array, p.typeOfArray(types)
+ case itemInlineTableStart:
+ var (
+ hash = make(map[string]interface{})
+ outerContext = p.context
+ outerKey = p.currentKey
+ )
+
+ p.context = append(p.context, p.currentKey)
+ p.currentKey = ""
+ for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
+ if it.typ != itemKeyStart {
+ p.bug("Expected key start but instead found %q, around line %d",
+ it.val, p.approxLine)
+ }
+ if it.typ == itemCommentStart {
+ p.expect(itemText)
+ continue
+ }
+
+ // retrieve key
+ k := p.next()
+ p.approxLine = k.line
+ kname := p.keyString(k)
+
+ // retrieve value
+ p.currentKey = kname
+ val, typ := p.value(p.next())
+ // make sure we keep metadata up to date
+ p.setType(kname, typ)
+ p.ordered = append(p.ordered, p.context.add(p.currentKey))
+ hash[kname] = val
+ }
+ p.context = outerContext
+ p.currentKey = outerKey
+ return hash, tomlHash
+ }
+ p.bug("Unexpected value type: %s", it.typ)
+ panic("unreachable")
+}
+
+// numUnderscoresOK checks whether each underscore in s is surrounded by
+// characters that are not underscores.
+func numUnderscoresOK(s string) bool {
+ accept := false
+ for _, r := range s {
+ if r == '_' {
+ if !accept {
+ return false
+ }
+ accept = false
+ continue
+ }
+ accept = true
+ }
+ return accept
+}
+
+// numPeriodsOK checks whether every period in s is followed by a digit.
+func numPeriodsOK(s string) bool {
+ period := false
+ for _, r := range s {
+ if period && !isDigit(r) {
+ return false
+ }
+ period = r == '.'
+ }
+ return !period
+}
+
+// establishContext sets the current context of the parser,
+// where the context is either a hash or an array of hashes. Which one is
+// set depends on the value of the `array` parameter.
+//
+// Establishing the context also makes sure that the key isn't a duplicate, and
+// will create implicit hashes automatically.
+func (p *parser) establishContext(key Key, array bool) {
+ var ok bool
+
+ // Always start at the top level and drill down for our context.
+ hashContext := p.mapping
+ keyContext := make(Key, 0)
+
+ // We only need implicit hashes for key[0:-1]
+ for _, k := range key[0 : len(key)-1] {
+ _, ok = hashContext[k]
+ keyContext = append(keyContext, k)
+
+ // No key? Make an implicit hash and move on.
+ if !ok {
+ p.addImplicit(keyContext)
+ hashContext[k] = make(map[string]interface{})
+ }
+
+ // If the hash context is actually an array of tables, then set
+ // the hash context to the last element in that array.
+ //
+ // Otherwise, it better be a table, since this MUST be a key group (by
+ // virtue of it not being the last element in a key).
+ switch t := hashContext[k].(type) {
+ case []map[string]interface{}:
+ hashContext = t[len(t)-1]
+ case map[string]interface{}:
+ hashContext = t
+ default:
+ p.panicf("Key '%s' was already created as a hash.", keyContext)
+ }
+ }
+
+ p.context = keyContext
+ if array {
+ // If this is the first element for this array, then allocate a new
+ // list of tables for it.
+ k := key[len(key)-1]
+ if _, ok := hashContext[k]; !ok {
+ hashContext[k] = make([]map[string]interface{}, 0, 5)
+ }
+
+ // Add a new table. But make sure the key hasn't already been used
+ // for something else.
+ if hash, ok := hashContext[k].([]map[string]interface{}); ok {
+ hashContext[k] = append(hash, make(map[string]interface{}))
+ } else {
+ p.panicf("Key '%s' was already created and cannot be used as "+
+ "an array.", keyContext)
+ }
+ } else {
+ p.setValue(key[len(key)-1], make(map[string]interface{}))
+ }
+ p.context = append(p.context, key[len(key)-1])
+}
+
+// setValue sets the given key to the given value in the current context.
+// It will make sure that the key hasn't already been defined, account for
+// implicit key groups.
+func (p *parser) setValue(key string, value interface{}) {
+ var tmpHash interface{}
+ var ok bool
+
+ hash := p.mapping
+ keyContext := make(Key, 0)
+ for _, k := range p.context {
+ keyContext = append(keyContext, k)
+ if tmpHash, ok = hash[k]; !ok {
+ p.bug("Context for key '%s' has not been established.", keyContext)
+ }
+ switch t := tmpHash.(type) {
+ case []map[string]interface{}:
+ // The context is a table of hashes. Pick the most recent table
+ // defined as the current hash.
+ hash = t[len(t)-1]
+ case map[string]interface{}:
+ hash = t
+ default:
+ p.bug("Expected hash to have type 'map[string]interface{}', but "+
+ "it has '%T' instead.", tmpHash)
+ }
+ }
+ keyContext = append(keyContext, key)
+
+ if _, ok := hash[key]; ok {
+ // Typically, if the given key has already been set, then we have
+ // to raise an error since duplicate keys are disallowed. However,
+ // it's possible that a key was previously defined implicitly. In this
+ // case, it is allowed to be redefined concretely. (See the
+ // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
+ //
+ // But we have to make sure to stop marking it as an implicit. (So that
+ // another redefinition provokes an error.)
+ //
+ // Note that since it has already been defined (as a hash), we don't
+ // want to overwrite it. So our business is done.
+ if p.isImplicit(keyContext) {
+ p.removeImplicit(keyContext)
+ return
+ }
+
+ // Otherwise, we have a concrete key trying to override a previous
+ // key, which is *always* wrong.
+ p.panicf("Key '%s' has already been defined.", keyContext)
+ }
+ hash[key] = value
+}
+
+// setType sets the type of a particular value at a given key.
+// It should be called immediately AFTER setValue.
+//
+// Note that if `key` is empty, then the type given will be applied to the
+// current context (which is either a table or an array of tables).
+func (p *parser) setType(key string, typ tomlType) {
+ keyContext := make(Key, 0, len(p.context)+1)
+ for _, k := range p.context {
+ keyContext = append(keyContext, k)
+ }
+ if len(key) > 0 { // allow type setting for hashes
+ keyContext = append(keyContext, key)
+ }
+ p.types[keyContext.String()] = typ
+}
+
+// addImplicit sets the given Key as having been created implicitly.
+func (p *parser) addImplicit(key Key) {
+ p.implicits[key.String()] = true
+}
+
+// removeImplicit stops tagging the given key as having been implicitly
+// created.
+func (p *parser) removeImplicit(key Key) {
+ p.implicits[key.String()] = false
+}
+
+// isImplicit returns true if the key group pointed to by the key was created
+// implicitly.
+func (p *parser) isImplicit(key Key) bool {
+ return p.implicits[key.String()]
+}
+
+// current returns the full key name of the current context.
+func (p *parser) current() string {
+ if len(p.currentKey) == 0 {
+ return p.context.String()
+ }
+ if len(p.context) == 0 {
+ return p.currentKey
+ }
+ return fmt.Sprintf("%s.%s", p.context, p.currentKey)
+}
+
+func stripFirstNewline(s string) string {
+ if len(s) == 0 || s[0] != '\n' {
+ return s
+ }
+ return s[1:]
+}
+
+func stripEscapedWhitespace(s string) string {
+ esc := strings.Split(s, "\\\n")
+ if len(esc) > 1 {
+ for i := 1; i < len(esc); i++ {
+ esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
+ }
+ }
+ return strings.Join(esc, "")
+}
+
+func (p *parser) replaceEscapes(str string) string {
+ var replaced []rune
+ s := []byte(str)
+ r := 0
+ for r < len(s) {
+ if s[r] != '\\' {
+ c, size := utf8.DecodeRune(s[r:])
+ r += size
+ replaced = append(replaced, c)
+ continue
+ }
+ r += 1
+ if r >= len(s) {
+ p.bug("Escape sequence at end of string.")
+ return ""
+ }
+ switch s[r] {
+ default:
+ p.bug("Expected valid escape code after \\, but got %q.", s[r])
+ return ""
+ case 'b':
+ replaced = append(replaced, rune(0x0008))
+ r += 1
+ case 't':
+ replaced = append(replaced, rune(0x0009))
+ r += 1
+ case 'n':
+ replaced = append(replaced, rune(0x000A))
+ r += 1
+ case 'f':
+ replaced = append(replaced, rune(0x000C))
+ r += 1
+ case 'r':
+ replaced = append(replaced, rune(0x000D))
+ r += 1
+ case '"':
+ replaced = append(replaced, rune(0x0022))
+ r += 1
+ case '\\':
+ replaced = append(replaced, rune(0x005C))
+ r += 1
+ case 'u':
+ // At this point, we know we have a Unicode escape of the form
+ // `uXXXX` at [r, r+5). (Because the lexer guarantees this
+ // for us.)
+ escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
+ replaced = append(replaced, escaped)
+ r += 5
+ case 'U':
+ // At this point, we know we have a Unicode escape of the form
+ // `uXXXX` at [r, r+9). (Because the lexer guarantees this
+ // for us.)
+ escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
+ replaced = append(replaced, escaped)
+ r += 9
+ }
+ }
+ return string(replaced)
+}
+
+func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
+ s := string(bs)
+ hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
+ if err != nil {
+ p.bug("Could not parse '%s' as a hexadecimal number, but the "+
+ "lexer claims it's OK: %s", s, err)
+ }
+ if !utf8.ValidRune(rune(hex)) {
+ p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
+ }
+ return rune(hex)
+}
+
+func isStringType(ty itemType) bool {
+ return ty == itemString || ty == itemMultilineString ||
+ ty == itemRawString || ty == itemRawMultilineString
+}
diff --git a/vendor/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/BurntSushi/toml/type_check.go
new file mode 100644
index 00000000..c73f8afc
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/type_check.go
@@ -0,0 +1,91 @@
+package toml
+
+// tomlType represents any Go type that corresponds to a TOML type.
+// While the first draft of the TOML spec has a simplistic type system that
+// probably doesn't need this level of sophistication, we seem to be militating
+// toward adding real composite types.
+type tomlType interface {
+ typeString() string
+}
+
+// typeEqual accepts any two types and returns true if they are equal.
+func typeEqual(t1, t2 tomlType) bool {
+ if t1 == nil || t2 == nil {
+ return false
+ }
+ return t1.typeString() == t2.typeString()
+}
+
+func typeIsHash(t tomlType) bool {
+ return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
+}
+
+type tomlBaseType string
+
+func (btype tomlBaseType) typeString() string {
+ return string(btype)
+}
+
+func (btype tomlBaseType) String() string {
+ return btype.typeString()
+}
+
+var (
+ tomlInteger tomlBaseType = "Integer"
+ tomlFloat tomlBaseType = "Float"
+ tomlDatetime tomlBaseType = "Datetime"
+ tomlString tomlBaseType = "String"
+ tomlBool tomlBaseType = "Bool"
+ tomlArray tomlBaseType = "Array"
+ tomlHash tomlBaseType = "Hash"
+ tomlArrayHash tomlBaseType = "ArrayHash"
+)
+
+// typeOfPrimitive returns a tomlType of any primitive value in TOML.
+// Primitive values are: Integer, Float, Datetime, String and Bool.
+//
+// Passing a lexer item other than the following will cause a BUG message
+// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
+func (p *parser) typeOfPrimitive(lexItem item) tomlType {
+ switch lexItem.typ {
+ case itemInteger:
+ return tomlInteger
+ case itemFloat:
+ return tomlFloat
+ case itemDatetime:
+ return tomlDatetime
+ case itemString:
+ return tomlString
+ case itemMultilineString:
+ return tomlString
+ case itemRawString:
+ return tomlString
+ case itemRawMultilineString:
+ return tomlString
+ case itemBool:
+ return tomlBool
+ }
+ p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
+ panic("unreachable")
+}
+
+// typeOfArray returns a tomlType for an array given a list of types of its
+// values.
+//
+// In the current spec, if an array is homogeneous, then its type is always
+// "Array". If the array is not homogeneous, an error is generated.
+func (p *parser) typeOfArray(types []tomlType) tomlType {
+ // Empty arrays are cool.
+ if len(types) == 0 {
+ return tomlArray
+ }
+
+ theType := types[0]
+ for _, t := range types[1:] {
+ if !typeEqual(theType, t) {
+ p.panicf("Array contains values of type '%s' and '%s', but "+
+ "arrays must be homogeneous.", theType, t)
+ }
+ }
+ return tomlArray
+}
diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go
new file mode 100644
index 00000000..608997c2
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/type_fields.go
@@ -0,0 +1,242 @@
+package toml
+
+// Struct field handling is adapted from code in encoding/json:
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the Go distribution.
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+)
+
+// A field represents a single field found in a struct.
+type field struct {
+ name string // the name of the field (`toml` tag included)
+ tag bool // whether field has a `toml` tag
+ index []int // represents the depth of an anonymous field
+ typ reflect.Type // the type of the field
+}
+
+// byName sorts field by name, breaking ties with depth,
+// then breaking ties with "name came from toml tag", then
+// breaking ties with index sequence.
+type byName []field
+
+func (x byName) Len() int { return len(x) }
+
+func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (x byName) Less(i, j int) bool {
+ if x[i].name != x[j].name {
+ return x[i].name < x[j].name
+ }
+ if len(x[i].index) != len(x[j].index) {
+ return len(x[i].index) < len(x[j].index)
+ }
+ if x[i].tag != x[j].tag {
+ return x[i].tag
+ }
+ return byIndex(x).Less(i, j)
+}
+
+// byIndex sorts field by index sequence.
+type byIndex []field
+
+func (x byIndex) Len() int { return len(x) }
+
+func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (x byIndex) Less(i, j int) bool {
+ for k, xik := range x[i].index {
+ if k >= len(x[j].index) {
+ return false
+ }
+ if xik != x[j].index[k] {
+ return xik < x[j].index[k]
+ }
+ }
+ return len(x[i].index) < len(x[j].index)
+}
+
+// typeFields returns a list of fields that TOML should recognize for the given
+// type. The algorithm is breadth-first search over the set of structs to
+// include - the top struct and then any reachable anonymous structs.
+func typeFields(t reflect.Type) []field {
+ // Anonymous fields to explore at the current level and the next.
+ current := []field{}
+ next := []field{{typ: t}}
+
+ // Count of queued names for current level and the next.
+ count := map[reflect.Type]int{}
+ nextCount := map[reflect.Type]int{}
+
+ // Types already visited at an earlier level.
+ visited := map[reflect.Type]bool{}
+
+ // Fields found.
+ var fields []field
+
+ for len(next) > 0 {
+ current, next = next, current[:0]
+ count, nextCount = nextCount, map[reflect.Type]int{}
+
+ for _, f := range current {
+ if visited[f.typ] {
+ continue
+ }
+ visited[f.typ] = true
+
+ // Scan f.typ for fields to include.
+ for i := 0; i < f.typ.NumField(); i++ {
+ sf := f.typ.Field(i)
+ if sf.PkgPath != "" && !sf.Anonymous { // unexported
+ continue
+ }
+ opts := getOptions(sf.Tag)
+ if opts.skip {
+ continue
+ }
+ index := make([]int, len(f.index)+1)
+ copy(index, f.index)
+ index[len(f.index)] = i
+
+ ft := sf.Type
+ if ft.Name() == "" && ft.Kind() == reflect.Ptr {
+ // Follow pointer.
+ ft = ft.Elem()
+ }
+
+ // Record found field and index sequence.
+ if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
+ tagged := opts.name != ""
+ name := opts.name
+ if name == "" {
+ name = sf.Name
+ }
+ fields = append(fields, field{name, tagged, index, ft})
+ if count[f.typ] > 1 {
+ // If there were multiple instances, add a second,
+ // so that the annihilation code will see a duplicate.
+ // It only cares about the distinction between 1 or 2,
+ // so don't bother generating any more copies.
+ fields = append(fields, fields[len(fields)-1])
+ }
+ continue
+ }
+
+ // Record new anonymous struct to explore in next round.
+ nextCount[ft]++
+ if nextCount[ft] == 1 {
+ f := field{name: ft.Name(), index: index, typ: ft}
+ next = append(next, f)
+ }
+ }
+ }
+ }
+
+ sort.Sort(byName(fields))
+
+ // Delete all fields that are hidden by the Go rules for embedded fields,
+ // except that fields with TOML tags are promoted.
+
+ // The fields are sorted in primary order of name, secondary order
+ // of field index length. Loop over names; for each name, delete
+ // hidden fields by choosing the one dominant field that survives.
+ out := fields[:0]
+ for advance, i := 0, 0; i < len(fields); i += advance {
+ // One iteration per name.
+ // Find the sequence of fields with the name of this first field.
+ fi := fields[i]
+ name := fi.name
+ for advance = 1; i+advance < len(fields); advance++ {
+ fj := fields[i+advance]
+ if fj.name != name {
+ break
+ }
+ }
+ if advance == 1 { // Only one field with this name
+ out = append(out, fi)
+ continue
+ }
+ dominant, ok := dominantField(fields[i : i+advance])
+ if ok {
+ out = append(out, dominant)
+ }
+ }
+
+ fields = out
+ sort.Sort(byIndex(fields))
+
+ return fields
+}
+
+// dominantField looks through the fields, all of which are known to
+// have the same name, to find the single field that dominates the
+// others using Go's embedding rules, modified by the presence of
+// TOML tags. If there are multiple top-level fields, the boolean
+// will be false: This condition is an error in Go and we skip all
+// the fields.
+func dominantField(fields []field) (field, bool) {
+ // The fields are sorted in increasing index-length order. The winner
+ // must therefore be one with the shortest index length. Drop all
+ // longer entries, which is easy: just truncate the slice.
+ length := len(fields[0].index)
+ tagged := -1 // Index of first tagged field.
+ for i, f := range fields {
+ if len(f.index) > length {
+ fields = fields[:i]
+ break
+ }
+ if f.tag {
+ if tagged >= 0 {
+ // Multiple tagged fields at the same level: conflict.
+ // Return no field.
+ return field{}, false
+ }
+ tagged = i
+ }
+ }
+ if tagged >= 0 {
+ return fields[tagged], true
+ }
+ // All remaining fields have the same length. If there's more than one,
+ // we have a conflict (two fields named "X" at the same level) and we
+ // return no field.
+ if len(fields) > 1 {
+ return field{}, false
+ }
+ return fields[0], true
+}
+
+var fieldCache struct {
+ sync.RWMutex
+ m map[reflect.Type][]field
+}
+
+// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
+func cachedTypeFields(t reflect.Type) []field {
+ fieldCache.RLock()
+ f := fieldCache.m[t]
+ fieldCache.RUnlock()
+ if f != nil {
+ return f
+ }
+
+ // Compute fields without lock.
+ // Might duplicate effort but won't hold other computations back.
+ f = typeFields(t)
+ if f == nil {
+ f = []field{}
+ }
+
+ fieldCache.Lock()
+ if fieldCache.m == nil {
+ fieldCache.m = map[reflect.Type][]field{}
+ }
+ fieldCache.m[t] = f
+ fieldCache.Unlock()
+ return f
+}