Cannot update certificate via stalwart-cli

Issue Description

When trying to update the certificate via the stalwart-cli binary I receive an error.

Expected Behavior

Running stalwart-cli update certificate abcdabcfd --field 'certificate={"@type":"Text","value":"$(</etc/letsencrypt/live/mx.domain.email/fullchain.pem)"}' ought update the certificate.

Actual Behavior

error: invalidProperties
Failed to read certificate: Cryptographic error: No certificates found in PEM
Properties: certificate
error: update failed

Reproduction Steps

  1. stalwart 16.6
  2. Install a certificate via the UI
  3. Run the stalwart-cli to update it

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Let me look into how the CLI handles certificate updates and what format it expects.

The root cause and fix are clear. The problem is the shell substitution approach, and there’s also a much simpler way to pass the file path directly.


The error “No certificates found in PEM” means the shell command substitution $(</etc/letsencrypt/live/.../fullchain.pem) is not expanding the PEM content correctly before it reaches the CLI – the newlines in the PEM file are almost certainly mangling the JSON structure, causing the server to receive an invalid/empty certificate string.

There are two better approaches:

Option 1: Use the File variant – instead of embedding the PEM content inline, tell Stalwart to read it directly from the file path. This is the cleanest approach for Let’s Encrypt renewals:

stalwart-cli update certificate abcdabcfd \
  --field 'certificate={"@type":"File","filePath":"/etc/letsencrypt/live/mx.domain.email/fullchain.pem"}'

For the private key (same pattern using SecretText/File):

stalwart-cli update certificate abcdabcfd \
  --field 'certificate={"@type":"File","filePath":"/etc/letsencrypt/live/mx.domain.email/fullchain.pem"}' \
  --field 'privateKey={"@type":"File","filePath":"/etc/letsencrypt/live/mx.domain.email/privkey.pem"}'

Option 2: Use --file or --stdin to pass a properly-formed JSON patch where the PEM content is correctly embedded (newlines as \n in JSON). You can generate this with jq:

jq -n \
  --rawfile cert /etc/letsencrypt/live/mx.domain.email/fullchain.pem \
  --rawfile key  /etc/letsencrypt/live/mx.domain.email/privkey.pem \
  '{certificate: {"@type":"Text","value":$cert}, privateKey: {"@type":"Text","secret":$key}}' \
| stalwart-cli update certificate abcdabcfd --stdin

The File variant is strongly recommended for automated renewal scripts since it avoids any quoting or newline issues entirely. The file just needs to remain readable by the Stalwart process.

Reference: Certificate object reference – see the PublicText / SecretText nested types section.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

When running this command, I get this error:

error: invalidProperties

 Failed to read certificate: Failed to read secret from file '/etc/letsencrypt/live/mx.sverige.email/fullchain.pem': Permission denied (os error 13)  Properties: certificate

error:

 update failed

However, the --rawfile solution works.