The get_parser() loop that scanned globals() for *_cmd names was fragile
and forced # noqa: F401 on all lxc imports (ruff couldn't see they were
used dynamically).
Replace it with an explicit SUBCOMMANDS list of
(cmd_func, options_func, needs_config) tuples. This makes the full set
of subcommands visible at a glance, their registration order defined,
and the imports unconditionally used (no more noqa suppressions).
Code:
- Fix check_ssh_config_include() to do a case-insensitive line match
(read_text().splitlines() instead of filter(None, map(..., open()))).
- Drop the default help_text= from _add_name_args(); callers now must
supply an explicit string.
- Expand the DNSContainer class docstring.
- Fix sysctl comment: note that incus provides net.* virtualization
so the sysctl only affects the container's network namespace.
Docs (doc/source/lxc.rst):
- Remove double blank line after page title; fix missing comma.
- Replace the plain-text root-access note with a .. caution:: block.
- Tighten the Quick-start section and lxc-test CLI entry.
Replace the two-function find_relay_image / _find_relay_image pair
with Incus.find_image(aliases), which returns the first alias
that exists in the local image store, or None.
Container.launch() passes [RELAY_IMAGE_ALIAS, BASE_IMAGE_ALIAS]
and ensure_base_image() passes [BASE_IMAGE_ALIAS].
Move the policy-rc.d install/remove boilerplate into a shared context
manager in basedeploy.py so both UnboundDeployer and DovecotDeployer use
the same abstraction, and the DNS container's _install_powerdns() inline
shell uses the same pattern.
DovecotDeployer now wraps its three package installs in
blocked_service_startup() to prevent Dovecot from auto-starting on
initial install — avoiding bind conflicts on IPv4-only systems.
Move the Out output-printer class to cmdeploy/util.py so it is shared
across CLI modules. All print/shell calls in lxc/cli.py, lxc/incus.py,
and dns.py now route through Out instead of bare print().
Key additions:
- Out.section() / Out.section_line(): coloured section headers scaled
to the current terminal width (or $_CMDEPLOY_WIDTH for sub-processes).
- Out.shell(): merges stdout/stderr, prefixes each output line, and
prints a red error line with the exit code on failure.
- Out.new_prefixed_out(): indented sub-printer that shares section_timings.
- 'cmdeploy -v / -vv' exposes the verbosity levels.
- Tests for Out added to test_util.py.
After restarting pdns/pdns-recursor, wait up to 10 s for the recursor
to actually answer a query before proceeding. Likewise, after
configure_dns(), poll from inside the relay container until the
configured DNS IP responds.
On --destroy-all, unset the incusbr0 dns.mode and raw.dnsmasq network
options so the next lxc-start starts from a clean bridge state.
DNSConfigurationError (caught in main()) is raised on timeout so the
CLI prints a clean error instead of failing later with a cryptic message.
Add cmdeploy "lxc-test" command to run cmdeploy against local containers,
with supplementary lxc-start, lxc-stop and lxc-status subcommands.
See doc/source/lxc.rst for full documentation including prerequisites,
DNS setup, TLS handling, DNS-free testing, and known limitations.
Apart from adding lxc-specific docs, tests, and implementation files in the cmdeploy/lxc directory,
this PR adds the --ssh-config option to cmdeploy run/dns/status/test commands and pyinfra invocations,
and also to sshexec (Execnet) handling. This allows for the host to need no DNS entries for a relay,
and route all resolution through ssh-config. This is used by the "lxc-test" command, which performs
a completely local setup -- again, see docs for more details.
While working on DNS/SSH things i also unified all zone-file handling
to use actual BIND format as it is easy enough to parse back.
Adds a new tls_external_cert_and_key config option for chatmail servers
that manage their own TLS certificates (e.g. via an external ACME client
or a load balancer).
A systemd path unit (tls-cert-reload.path) watches the certificate file
via inotify and automatically reloads dovecot and nginx when it changes.
Postfix reads certs per TLS handshake so needs no reload.
Also extracts openssl_selfsigned_args() so cert generation parameters
are shared between SelfSignedTlsDeployer and the e2e test.
* cleanup: remove CFFI deltachat bindings usage, and consolidate test support with rpc-bindings
major simplification: all chatmail fixtures used in the test are now created inside the cmdeploy plugin,
and do not inherit anything from other fixture machineries, let alone the legacy deltachat CFFI ones.
also fix that pytest report headers show correct chatmail domains under test
feat: support self-signed TLS via underscore domain convention
Domains starting with "_" (e.g. _chat.example.org) automatically use
self-signed TLS certificates instead of ACME/Let's Encrypt. The TLS
mode is derived from the domain name — no separate config option needed.
Internally, when config.tls_cert_mode is "self" (underscore domain):
- Generate self-signed certificates via openssl
- Set Postfix smtp_tls_security_level to "encrypt" (opportunistic TLS)
- Add smtp_tls_policy_map entry for underscore domains
- Skip ACME, MTA-STS and www CNAME checks in `cmdeploy dns`
- Serve /new via GET (not redirect to dcaccount:) with rate-limiting
(nginx limit_req, 2r/s burst=5)
- Return dclogin: URLs with ic=3 (AcceptInvalidCertificates) from /new
- Render QR codes client-side via JavaScript and qrcode-svg
- Use config.tls_cert_path/tls_key_path in Postfix, Dovecot and nginx
templates instead of hardcoded ACME paths
* docs: update migration guide after nine migration
* use $OLD_IP4 and $NEW_IP4 to make docs more readable. Also streamline "set TTL to 5 minute" phrasing a bit.
* fix tar commands
* refactor: streamline and refactor the migration guide to provide more clarity and focus
* recommend a "higher TTL" concrete value
Co-authored-by: missytake <missytake@systemli.org>
* scriptify another location
---------
Co-authored-by: missytake <missytake@systemli.org>
refactor README.rst and architecture file into sphinx doc project, automatically deploying on main merges and PRs.
* add FAQs from https://chatmail.at/relays landing page
* fix links, and streamline postfix/dovecot mentioning
* add linkcheck to CI, fix several links and streamlihne DKIM section while at it
* some streamlining, rename to "overview"
* ci: upload documentation to chatmail.at/doc/relay
* ci: main should be uploaded when docs.yaml changes
* ci: fix typo
* Update .github/workflows/docs-preview.yaml
Co-authored-by: missytake <missytake@systemli.org>
* use "relay" instead of "server" and "address" instead of "account"
* consistent Dovecot capitalization and striking relay in two places
* address missytake's comments: use "chatmail relay servers" sometimes -- it's still fine to talk about relays being, or running on, servers
* draft blocking of incoming non-encrypted mail
* create a new enforceE2EE file in address dirs by default and only accept incoming cleartext file if the enforceE2EE file is missing
* Update cmdeploy/src/cmdeploy/service/filtermail.service.f
Co-authored-by: l <link2xt@testrun.org>
* fix benchmark so they setup encryption
* hack around limitations of aiosmtpd's handliung of RCPTO options
* add tests, and split incoming/outgoing handlers for clarity
* document mailbox directory structure, some streamlining of features/E2EE in intro
* use SMTP response code "523 Encryption Needed"
* filtermail: care for the case that the recipient does not exist
Co-authored-by: missytake <missytake@systemli.org>
* Update chatmaild/src/chatmaild/filtermail.py
Co-authored-by: l <link2xt@testrun.org>
* Update chatmaild/src/chatmaild/filtermail.py
Co-authored-by: l <link2xt@testrun.org>
* remove debug info print
* ensure multipart/report type for mailer-daemon messages
* Allow sending out Autocrypt Setup Messages
---------
Co-authored-by: l <link2xt@testrun.org>
Co-authored-by: missytake <missytake@systemli.org>
* enforce encryption for in-server mails
* make tests work with chatmail server only support e2ee internally
* fix echobot test
* simplify quota-exceeded test
* work around rpc-server fixture changes