sugarcraft / candy-mail
Email sender for PHP, modeled on the charmbracelet/pop Go tool — send via the Resend API or direct SMTP. Compose from stdin, attach files, supports CC/BCC, HTML body.
Requires
- php: ^8.3
- sugarcraft/sugar-async: dev-master
- sugarcraft/sugar-core: dev-master
Requires (Dev)
- phpstan/phpstan: ^2.2
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-17 07:23:25 UTC
README
CandyMail
Formerly published as
sugarcraft/sugar-post(renamed 2026-10). Namespace changed:SugarCraft\Post\→SugarCraft\Mail\.
candy-mail — an email sender for PHP 8.3+, via the Resend API or direct SMTP.
Features
- Dual transport — send via Resend HTTP API or raw SMTP
- Email value object — from, to, cc, bcc, subject, body, HTML body, reply-to, attachments
- File attachments — attach files from paths or raw content with MIME detection
- Inline attachments — embed images inline (CID references)
- CC/BCC support — full carbon-copy / blind carbon-copy routing
- STDIN compose — read email body from STDIN for shell pipeline use
- Environment config —
RESEND_API_KEY,CANDY_MAIL_SMTP_*,CANDY_MAIL_FROM,CANDY_MAIL_SIGNATURE - PHP 8.3+ — pure PHP, no extensions required beyond cURL (for Resend transport)
Install
composer require sugarcraft/candy-mail
Quick Start
Resend API
use SugarCraft\Mail\{Email, Mailer, ResendTransport}; $transport = new ResendTransport('re_xxxxxxxxxxxxx'); $mailer = new Mailer($transport); $email = new Email( from: 'you@example.com', to: ['them@example.com'], subject: 'Hello from CandyMail', body: 'Sent via the Resend API.', ); $mailer->send($email);
SMTP
use SugarCraft\Mail\{Email, Mailer, SmtpTransport}; $transport = new SmtpTransport('smtp.gmail.com', 587, 'username', 'password'); $mailer = new Mailer($transport); $mailer->send(new Email( from: 'you@gmail.com', to: ['them@gmail.com'], subject: 'Hello via SMTP', body: 'Sent directly via SMTP.', ));
Attachment
$email = new Email(/* ... */); $email = $email->withAttachment('invoice.pdf', '/path/to/invoice.pdf'); $mailer->send($email);
Note:
--attach/withAttachment()read arbitrary local paths. The caller is responsible for validating that passed paths are trusted. No path allow-list is enforced by design.
CLI
candy-mail --from "me@example.com" --to "you@example.com" --subject "Hello" # Body read from STDIN
Environment variables:
export RESEND_API_KEY=re_xxxxx # Resend API key export CANDY_MAIL_SMTP_HOST=smtp.gmail.com # SMTP host export CANDY_MAIL_SMTP_PORT=587 # SMTP port (default: 587) export CANDY_MAIL_SMTP_USERNAME=user # SMTP username export CANDY_MAIL_SMTP_PASSWORD=pass # SMTP password export CANDY_MAIL_FROM=me@example.com # Pre-fill From address export CANDY_MAIL_SIGNATURE="Sent with CandyMail" # Appended to body # Pre-rename POP_*/POP_SMTP_* exports are still read, with a deprecation warning. // @rename-keep
Architecture
Email— immutable email message value objectAttachment— immutable file attachment (path or inline content)Transport— interface for sending implementationsResendTransport— sends via Resend REST API (HTTPS)SmtpTransport— sends via direct SMTP (TCP/TLS)Mailer— high-level API wrapping a Transport
Shared foundations
candy-mail uses sugar-async for best-effort cancellation on SMTP sends via CancellationToken (pre-write isCancelled() check + onCancel callback).
License
MIT
Credits & inspiration
Originally inspired by the Go Charm ecosystem; SugarCraft is developed as a native PHP project.