Standalone command-line client for the Eliware Mail service.
The package is intentionally separate from the mail server so it can be installed globally. It connects directly to MariaDB and RabbitMQ and reads attachment objects from the configured shared storage path; it does not use the web API or SMTP.
npm install --global @eliware/mailctlCreate ~/.config/mailctl/.env and set the direct MariaDB and RabbitMQ
connection URLs. MAIL_STORAGE_PATH must point to the shared attachment
volume. Existing environment variables take precedence over values in that
file. The configuration file is local-only and must never be committed.
Configuration contract:
| Variable | Required | Default | Format and effect | Sensitive |
|---|---|---|---|---|
MYSQL_URL |
For database commands | None | mysql://USER:PASSWORD@HOST/DATABASE; selects the MariaDB endpoint and schema |
Yes |
RABBITMQ_URL |
For send, retry, and cancel |
None | amqp://USER:PASSWORD@HOST/VHOST; selects the RabbitMQ vhost used for outbound work |
Yes |
MAIL_STORAGE_PATH |
For attachment commands | None | Absolute readable/writable directory containing hashed attachment objects | No, but deployment-specific |
The CLI validates configuration when a command opens the relevant dependency. Values may be supplied by the process environment or the per-user dotenv file; process environment values take precedence. Never print these URLs or commit them.
The CLI is designed for one-shot operator and AI-agent use. It never starts a
consumer, prompts for input, or connects to RabbitMQ for read/delete commands.
Add --json to every command for machine-readable output. Message bodies and
attachment metadata are stored in MariaDB; attachment bytes are read and
written under MAIL_STORAGE_PATH.
Help is the default when no command is supplied. Every command also has focused usage, flags, safety notes, and an agent-oriented example:
mailctl
mailctl list --help
mailctl send --help
mailctl health --jsonFor a typical agent workflow, run health --json, discover IDs with
list --json or sent --json, use read/sent-read for complete records,
and use send --json --idempotency KEY ... followed by sent --json to verify
delivery state. Destructive operations require --yes; use --dry-run to
preview them safely.
mailctl list
mailctl headers MESSAGE_ID...
mailctl read MESSAGE_ID...
mailctl sent
mailctl sent-read OUTBOUND_ID...
mailctl search QUERY
mailctl thread MESSAGE_ID
mailctl retry OUTBOUND_ID... --yes
mailctl cancel OUTBOUND_ID... --yes
mailctl health
mailctl attachments MESSAGE_ID
mailctl save-attachments MESSAGE_ID DIRECTORY
mailctl send
mailctl delete MESSAGE_ID_OR_OUTBOUND_ID... --yes
mailctl migrate --yes
mailctl domains
Text and HTML bodies can be supplied inline or from a file with @path.
Outbound attachments are SHA-256 hashed, gzip-compressed, deduplicated, and
queued through the durable mail.outbound.submit RabbitMQ queue. Destructive
commands require --yes. --dry-run previews send and delete operations
without changing MariaDB, RabbitMQ, or shared storage.
sent lists outbound messages with aggregate and per-recipient delivery
status. Use --status queued, --status retryable, --status failed, or
--status sent to find work needing attention. sent-read returns the full
outbound message, headers, body, attachments, delivery records, and SMTP
attempt history. These commands are read-only and are suitable for agent
monitoring and reconciliation.
delete soft-deletes inbound or outbound messages by setting deleted_at;
it never removes message records or relational data. Deleted messages are
excluded from listing, reading, searching, threading, attachment export,
retry, and cancellation commands.
migrate is the schema authority for the mail service. It applies eligible
semver-prefixed ESM migrations while holding a MariaDB advisory lock. Run it
deliberately with --yes or MIGRATE_CONFIRM=apply; it never runs as a side
effect of another command.
Use mailctl outbound-status OUTBOUND_ID --json to inspect each recipient's
delivery state, latest recorded SMTP attempt, elapsed attempt age, and whether
the delivery is stale. This read-only diagnostic uses the
MAIL_OUTBOUND_STALE_DELIVERY_MS threshold, five minutes by default.
search searches inbound and outbound headers and bodies. Results include a
MariaDB FULLTEXT relevance score and rank content matches ahead of fallback
sender, subject, recipient, and header matches. thread follows
stored message-reference headers. retry republishes retryable deliveries;
cancel prevents queued deliveries from being sent. Both require --yes or
support --dry-run. health returns component status and exits with code 2
when degraded. With --json, failures are emitted as one JSON object on
stderr with a stable error and code shape.
The per-user ~/.config/mailctl/.env file supplies direct MariaDB, RabbitMQ,
and storage configuration and is intentionally outside the package.
npm ci
npm test
npm run lint
npm run packLive service checks are deliberately separate from the default test command:
npm run test:integrationThat command requires disposable MariaDB, RabbitMQ, and attachment-storage
resources configured as described in docs/integration-testing.md.
It is not run by CI unless explicitly enabled.
The v* tag workflow publishes the package to npm with provenance after
tests, lint, and package validation pass. Publishing requires the repository's
NPM_TOKEN secret.
The CLI connects directly to database, queue, and shared storage resources.
Keep credentials in environment files or secret injection, use --json only
when its output is safe for the receiving agent, and never include passwords or
credential-bearing URLs in command output or bug reports.