A pure-Go (no cgo) reimplementation of the surface of Ruby's
acme-client gem — the ACME /
Let's Encrypt (RFC 8555) protocol client. It mirrors the ergonomics of
Acme::Client — accounts, orders, authorizations, challenges, finalization and
certificate download — while delegating the ACME/JWS wire protocol itself to the
standard, pure-Go golang.org/x/crypto/acme
client, so no part of RFC 8555 is reimplemented here.
It is the ACME backend for go-embedded-ruby, but is a standalone, reusable module — a sibling of go-ruby-jwt and go-ruby-oauth2.
The Ruby object graph is reproduced with idiomatic Go types:
- Client —
NewClient(privateKey, directory)≙Acme::Client.new(private_key:, directory:) - Account —
NewAccount(ctx, contact, tosAgreed)≙#new_account,Account(ctx)for lookup - Order —
NewOrder(ctx, identifiers)≙#new_order;Order#Authorizations,#Finalize(csr),#Certificate(PEM chain),#Status,#Reload - Authorization —
#Challenges,#HTTP01/#DNS01/#TLSALPN01,#Domain,#Status - Challenge —
#KeyAuthorization(token.thumbprint),#RequestValidation,#Status,#Error,#Reload - Errors — an error tree rooted at
*Errorwith*Unauthorized,*RateLimited,*BadNonceand*Malformed, mapping ACME problem documents onto theAcme::Client::Errorsubclasses
import "github.com/go-ruby-acme/acme"
client, _ := acme.NewClient(accountKey, "https://acme-v02.api.letsencrypt.org/directory")
account, _ := client.NewAccount(ctx, []string{"mailto:me@example.com"}, true)
order, _ := client.NewOrder(ctx, []string{"example.com"})
authzs, _ := order.Authorizations(ctx)
chal := authzs[0].HTTP01()
ka, _ := chal.KeyAuthorization() // serve at /.well-known/acme-challenge/<token>
_ = chal.RequestValidation(ctx)
csr, _ := acme.NewCSR(certKey, "example.com")
_ = order.Finalize(ctx, csr)
pemChain, _ := order.Certificate(ctx)The ACME transport is injectable. WithHTTPClient swaps the *http.Client
(the whole flow runs against an in-process mock ACME server with no live
network) and WithRetryBackoff tunes the retry policy. The test suite drives
the full account → order → authz → http-01 → finalize → certificate flow, plus
the badNonce retry, rate-limit, unauthorized and invalid-challenge error
paths, against a mock CA — deterministic, and with no real Let's Encrypt.
go test ./... runs the full flow against the mock ACME server. The CI gate
enforces 100% statement coverage under -race and builds/tests on all six
64-bit Go targets — amd64, arm64, riscv64, loong64, ppc64le, s390x
(big-endian).
BSD-3-Clause. Copyright (c) the go-ruby-acme/acme authors.
Being pure Go (CGO=0), this library also compiles to WebAssembly — both
GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI).
CI builds both targets on every push, alongside the six 64-bit native/qemu arches.
GOOS=js GOARCH=wasm go build ./... # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./... # WASI (wasmtime, wasmer, wasmedge, …)