certbotbot is a noninteractive certificate-management producer for
existing Let's Encrypt/Certbot accounts. It uses Route53 DNS-01 to
issue or renew a selected certificate, persists the complete validated
Certbot account and certificate state in an existing Amazon S3 object,
and can resume on a fresh Linux runner without relying on that runner's
previous /etc/letsencrypt directory.
An explicitly invoked certbotbot register operation can now provision and
persist a Let's Encrypt account without requesting a certificate. Ordinary
issuance and renewal never register an account implicitly. This new path
has only mocked acceptance coverage pending milestone #4 live staging checks.
It is designed for scheduled or manually initiated, one-shot Bash execution. Docker and rootless Podman are optional ways to run the same producer; neither is required.
Releases · Complete lifecycle · Configuration and CLI · Operator examples · Architectural decisions · Issues
RENEW_ALL=true is a separate opt-in to check every
existing complete archived lineage, without implicit issuance or deletion.The current producer supports S3 storage, gzip-compressed GNU tar state archives (optionally inside an authenticated GPG envelope), and Certbot with Route53 DNS-01. It relies on a previously registered Let's Encrypt account for the selected CA (production by default, or explicit staging) and an already seeded S3 archive. Never mix staging and production account state within one archive or S3 object key.
The ordinary certificate lifecycle does not create Let's Encrypt accounts,
S3 buckets, IAM roles, hosted zones, or an initial archive. The separate,
explicit register command is the only path permitted to create a native
account-only archive at a positively missing S3 key. The tool does not
provision a web server, install or reload target certificates, implicitly
renew every stored lineage, delete orphaned lineages without an explicitly
complete desired-lineage set and destructive opt-in, provide distributed
locking, or implicitly migrate encrypted/plaintext formats. Other storage backends,
DNS providers and archive formats are not supported by the current
producer. A container image is a distribution choice, not additional
account-provisioning or certificate-serving functionality.
Opt-in bulk maintenance is deliberately bounded: --renew-all true checks
all validated existing archived lineages once, using their stored
renewal configurations and a preexisting account. A domainless run is
permitted only with a nonempty complete restored lineage inventory.
Explicit selected issuance remains possible, while unrelated SANs are not
reconciled. Only a fully verified complete result may publish to the existing
current key. See ADR-058
and the bulk renewal scope matrix.
Opt-in --remove-orphans true reconciles the shared archive against a
separate, complete, explicitly supplied set of stable certificate
lineage names. Use repeatable --desired-cert-name NAME inputs (or the
literal DESIRED_CERT_NAMES collection); selecting a domain or SAN does
not implicitly authorize deletion. An invalid, missing, or partially
specified desired set fails before mutation, and RENEW_ALL=true
cannot be combined with removal. Only already validated orphan paths
inside the isolated workspace are pruned after the ordinary selected
operation succeeds. See ADR-059
and the orphan removal contract.
See How certbotbot works for the complete lifecycle and the threat model for specific trust boundaries and failure assumptions.
The one-shot producer restores validated Certbot account and certificate state from S3, runs the selected Route53 DNS-01 operation, verifies the candidate state, and conditionally republishes the current archive. Installation and TLS reloads remain operator responsibilities. See the architecture diagram and lifecycle walkthrough and full lifecycle contract.
Use the separate certbotbot register command to initialize native
account-only state in an existing S3 bucket. It requires an email address,
affirmative subscriber-terms consent, and a selected CA; it does not request
a certificate. Keep staging and production account state in different keys.
See the registration walkthrough
and registration contract.
Ordinary issuance requires an existing compatible Let's Encrypt account, a seeded S3 archive, an existing Route53 zone, correctly scoped AWS access, and a trusted, independently verified release executable. Use the operator quick start for the complete prerequisites, immutable release download and attestation checks, and failure/recovery guidance.
Once those prerequisites are independently established, an example invocation is:
./certbotbot.bash \
--s3-bucket my-existing-certificates \
--s3-key latest.tar.gz \
example.com
These are illustrative names, not provisioned resources. By default, the selected DNS name also requests its complementary apex/wildcard SAN. A successful command does not install or deploy the resulting certificate.
Settings resolve CLI > explicitly exported environment >
literal selected .env > built-in defaults. The presence-only
--no-env flag disables producer application environment and file
configuration while preserving explicit CLI settings and external AWS
credential mechanisms. It also suppresses environment-only GPG secrets;
use it only when the selected archive and encryption mode are compatible.
Without an explicit --config FILE, the optional ./.env file is read
as strict data, never sourced as shell code. Unknown or
duplicate entries and shell expansion are rejected.
Optional --aws-args, --gpg-args and --certbot-args flags accept
bounded JSON argument arrays; the same collections can come from the
exported AWS_EXTRA_ARGS, GPG_EXTRA_ARGS and CERTBOT_EXTRA_ARGS
variables. They are deliberately restricted to the documented safe
upstream options and require jq and base64 only when configured.
See ADR-056.
The full configuration reference
is authoritative for values, edge cases, precedence and all
public flags. The source-checked producer/register option inventory
additionally distinguishes registration-only options, repeatable
whole-collection settings, environment-only provider JSON vectors,
secret-only values, and implemented/destructive bulk-mode support.
| CLI | Environment / literal .env | Default or purpose |
|---|---|---|
--acme-environment | ACME_ENVIRONMENT | production by default; opt into staging for a matching test archive |
DNS_NAME or --domain | DOMAIN | Required primary DNS name |
--san (repeatable) | ADDITIONAL_SANS | Optional additional names; CLI replaces lower-tier SAN collection |
--include-subdomains | INCLUDE_SUBDOMAINS | true; add complementary apex/wildcard for each name |
--cert-name | CERT_NAME | Derive shortest valid SAN name unless explicitly set |
--s3-bucket | S3_BUCKET | Required existing bucket |
--s3-key or --s3-key-template | S3_KEY or S3_KEY_TEMPLATE | Current object; default literal latest.tar.gz |
--upload-no-renewal | UPLOAD_NO_RENEWAL | false; do not republish an unchanged verified no-op |
--renew-all | RENEW_ALL | false; opt in to checked due-renewal of every original validated archived lineage, without implicit issuance |
--remove-orphans | REMOVE_ORPHANS | false; destructive opt-in, requires an explicit complete desired lineage set and selected certificate |
--desired-cert-name (repeatable) | DESIRED_CERT_NAMES | No default; complete desired set for orphan pruning, CLI list or comma-separated environment/file list |
--dated-backup-enabled | DATED_BACKUP_ENABLED | false; opt in to an additional dated recovery object |
--backup-key-template | BACKUP_KEY_TEMPLATE | archive-{YEAR}{MONTH}{DOM}.tar.gz; naming alone does not enable publishing |
--backup-retention-count | BACKUP_RETENTION_COUNT | 0; no deletion unless positive and dated backups enabled |
--archive-encryption | ARCHIVE_ENCRYPTION | auto: plaintext without key, GPG OCB with exported key |
--archive-migration | ARCHIVE_MIGRATION | none; explicit one-way migration only |
--archive-migration-backup-confirmed | ARCHIVE_MIGRATION_BACKUP_CONFIRMED | false; independent recoverable-backup acknowledgment |
The secret GPG_SYMMETRIC_KEY is accepted only as an
exported process-environment variable, never as a CLI option
or literal .env key. Use a trusted secret manager and ensure
it is absent from command arguments, build inputs, files and
logs. Selecting ARCHIVE_ENCRYPTION=none while supplying a
nonempty key fails closed; migration requires explicit mode,
key and independently confirmed backup.
The current key and a separately selected date-derived
current-key template conflict only when both are set at the
winning precedence tier. Templates use a frozen UTC date
and the supported {YEAR}, {MONTH}, {DOM} tokens.
Positive retention additionally requires stricter managed
date-shaped backup keys and can delete eligible older
objects. No option automatically creates a recovery backup
or restores it on failure.
Run ./certbotbot.bash --help or
./certbotbot.bash --version without cloud access.
The full reference covers exact Boolean syntax, SAN limits,
status codes, invalid combinations and encryption migration
safety.
The separate certbotbot-client.bash release artifact retrieves
one current selected S3 object, validates it, and extracts
the complete supported Certbot state into a new, absent
operator-selected destination. It does not issue or renew
certificates, write to S3, delete backups, install certificate
files into a service or reload that service. A target system
does not need Certbot to use previously issued material.
Download its matching .sha256 companion, independently
verify the release and attestations as above, then invoke
the client with an existing read-only AWS identity and
a fresh private destination:
./certbotbot-client.bash \
--s3-bucket my-existing-certificates \
--s3-key latest.tar.gz \
--destination "$HOME/certificate-states/current"
The destination must not already exist, and its parent must satisfy the client's private-directory requirements. For encrypted objects, the client also needs compatible GnuPG and a matching exported secret. See client settings, client examples and the separate-client contract before adoption. The client is shipped separately and is not embedded in the producer's OCI image.
The optional Alpine OCI producer executes the same one-shot CLI in Docker or rootless Podman. Its reviewed scope is linux/amd64, nonroot UID/GID 65532, a private ephemeral state directory, a read-only root filesystem and no baked credentials or ACME material. The image does not schedule itself or automatically deploy certificates. AWS workload identity or securely injected runtime credentials must be provided by the operator.
After a successfully verified release, the publisher targets GHCR and Docker Hub. To pull and inspect the current image from either registry:
docker pull --platform linux/amd64 ghcr.io/wesley-dean/certbotbot:latest
docker image inspect ghcr.io/wesley-dean/certbotbot:latest \
--format '{{.Os}}/{{.Architecture}} {{.Config.User}}'
podman pull --arch amd64 --os linux docker.io/wesleydean/certbotbot:latest
podman image inspect docker.io/wesleydean/certbotbot:latest \
--format '{{.Os}}/{{.Architecture}} {{.Config.User}}'
latest follows the most recently accepted release and can change.
For reproducible deployments, pinning a particular release or an
independently verified image digest is encouraged. See the deployment
guide for those options and for secure runtime examples. These commands
do not establish that a release image has already been published or
accepted in either registry.
Operators starting without an S3 bucket can consult the manual AWS bucket-bootstrap and least-privilege runtime IAM guide; the accompanying IAM policy template is intentionally uninstantiated and is not run by this repository. The account must then be explicitly registered before ordinary issuance.
The Docker/rootless Podman published-image quick start
includes direct docker/podman pull, label inspection, nonroot
read-only run examples, and the additional prerequisites for real
issuance. See the full container deployment guide,
OCI publication and operator guide, and
publication contract for
trust, credentials, registry recovery, upgrades, and release boundaries.
Local image build/runtime tests do not prove live registry
publication or certificate issuance in your environment.
Start with the from-zero-S3 first-certificate guide before activating any certificate-maintenance automation.
Examples under doc/examples/ are references, not active
production automation:
In particular, the GitHub Actions example is deliberately
outside .github/workflows/. An operator must separately
review and activate a protected workflow, credentials, IAM
trust policy, release pin and schedule. Examples do not
provision accounts or buckets, run live cloud operations on
PRs, or deploy certificates to consumer servers.
The local protocol CI workflow exercises real Certbot, Pebble, and SeaweedFS using isolated test fixtures. DNS-01 validation is intentionally bypassed, and Route53 is stubbed; success does not prove live AWS or Let's Encrypt integration. See testing and CI.
Real Let's Encrypt staging and AWS acceptance requires a separately authorized, operator-triggered test. See the staging acceptance overview and manual staging workflow.
The maintained Bash source lives in src/ and lib/.
GNU Make is the canonical build and test entrypoint.
Bash dependencies (including the pinned mktext
library used for safe filename templates) are prepared
before building; the resulting standalone scripts have
no runtime dependency on the repository's vendor/ tree.
make deps
make deps-check
make build
make check
make test
make deps can access the network; make deps-check
and make build are network-free once dependencies
are prepared. make test-report writes JUnit output.
make check validates maintained source; make test exercises
all three verified producer flavors. CI also checks the independent
client, OCI runtime, release provenance, and local protocol lifecycle.
See the testing and CI overview,
detailed testing contracts, and
release verification.
make format applies the shfmt profile; make adr-index and
make docs generate ignored reference output.
The eight release assets comprise the three producer
flavors (certbotbot.dev.bash, certbotbot.bash,
certbotbot.min.bash) plus the distinct
certbotbot-client.bash, each with its .sha256
companion. Release CI independently verifies the
producer variants, required Bash compatibility and
authenticated standalone client, then attests the
exact files before releasing them. Read
release verification
before pinning executable versions or adopting a
production runner.
For contributor onboarding, start with AGENTS.md, testing guidance, the engineering philosophy, the architecture decision map, accepted ADRs, the Bash documentation standard, CONTRIBUTING.md, and the security disclosure policy. The generated ADR index and Doxygen reference output are not maintained source.
This repository uses an opt-in,
three-phase approved development workflow.
A current issue's approved label records its own
authorization, not blanket permission for related
tasks; substantive architecture changes must respect
Accepted ADRs. Issues and PRs are reviewed separately
from CI success.
For support and project status, use
SUPPORT.md,
GitHub Issues,
and GitHub Actions.
The historical research record is in
doc/research/.
The project adopts a pinned released snapshot of
coding standards
through .codingstandardrc.
Dedicated to the public domain under CC0 1.0 Universal. See LICENSE, CONTRIBUTING.md, and CODE_OF_CONDUCT.md.
Native issue relationships and optional GitHub Projects v2 status are maintained by separate repository administration scripts, outside the certificate-management product. See the GitHub issue and Kanban automation guide for authentication, commands, safety checks, and governance.
Dependency synchronization remains disabled in Actions pending an audit. See the documented safety boundary.
See how to render the issue-derived board.
Content type
Image
Digest
sha256:960e36ea9…
Size
78.3 MB
Last updated
2 days ago
docker pull wesleydean/certbotbot