This is the full, technical path from an email sent to post@emino.app to a published post, including DNS/TLS, SMTP/IMAP, filtering/ validation, and the importer internals. The flowchart now includes sender checks and parsing.
Pipeline at a Glance
- DNS: A record emino.app → 188.34.194.25; MX record 0 emino.app.
- TLS: Let’s Encrypt cert at /etc/letsencrypt/live/emino.app/ used by Nginx, Postfix (STARTTLS), Dovecot (IMAPS); certbot.timer auto- renews.
- SMTP inbound: Postfix on :25, virtual mailbox post@emino.app → Maildir /var/mail/vhosts/emino.app/post/.
- IMAP access (for importer/clients): Dovecot on :993 with the same LE cert.
- Firewall: ufw open for 22, 80, 443, 25; 993 if IMAP access is needed.
- Importer: polls Maildir/IMAP, validates sender, parses/sanitizes, converts to Markdown, writes to posts/ (or your content dir), triggers build/deploy.
- Web: Nginx serves the static blog on HTTPS.
Importer: Technical Behavior
- Polling: runs on a timer (systemd timer/cron) or long-lived watcher; reads Maildir new/ and cur/ or IMAP inbox.
- Sender validation: checks From: against an allowlist (e.g., your addresses). Unknown senders are skipped/logged.
- Parsing:
- Subject → title/slug; body → Markdown body.
- Attachments: can be ignored or saved; filter to text/image types if enabled.
- Sanitization: strip dangerous HTML, normalize encodings, optional link/emoji cleanup.
- Conversion: build frontmatter (title, date, tags, author) + body in Markdown; filename into content/posts/ (or configured path).
- Publishing: optionally runs a build/deploy hook (static site generator, cache refresh) after writing the file.
- Logging: importer logs to its own file; failures should log and leave messages in Maildir for retry.
Configuration Checklist
- DNS
- A emino.app 188.34.194.25
- MX 0 emino.app.
- TLS
- certbot –nginx -d emino.app -d www.emino.app –redirect
- Verify certbot.timer (systemctl list-timers | grep certbot)
- Postfix (virtual mailbox)
/etc/postfix/vmailbox:
post@emino.app emino.app/post/
postmap /etc/postfix/vmailbox
postconf -e “virtual_mailbox_domains=emino.app”
postconf -e “virtual_mailbox_maps=hash:/etc/postfix/vmailbox”
TLS:
postconf -e “smtpd_tls_cert_file=/etc/letsencrypt/live/ emino.app/fullchain.pem” postconf -e “smtpd_tls_key_file=/etc/letsencrypt/live/ emino.app/privkey.pem” systemctl reload postfix
- Dovecot (IMAP/IMAPS)
/etc/dovecot/conf.d/10-ssl.conf:
ssl_cert = </etc/letsencrypt/live/emino.app/fullchain.pem ssl_key = </etc/letsencrypt/live/emino.app/privkey.pem
systemctl reload dovecot
- Maildir permissions
- /var/mail/vhosts/emino.app/post/ owned by vmail:vmail, mode 700/600.
- Firewall (ufw)
- ufw allow 25/tcp
- ufw allow 993/tcp (if IMAP access needed)
- ufw allow 80,443/tcp
- Importer job
- Read from Maildir or IMAP for post@emino.app.
- Allowlist sender addresses.
- Write posts into your blog content path (e.g., content/posts/ or posts/).
- Run via systemd timer/cron; log to a dedicated file; trigger build/deploy if required.
- Nginx
- Port 80 → return 301 https://$host$request_uri;
- Port 443 with LE cert paths; root at your blog directory; serve static site.
Monitoring & Reliability
- TLS expiry: openssl x509 -in /etc/letsencrypt/live/ emino.app/fullchain.pem -noout -enddate
- Mail flow: /var/log/mail.log (Postfix/Dovecot)
- Importer: its dedicated log; alert on failures
- Renewals: journalctl -u certbot if renewals misbehave
- Health checks: periodic test mail to post@emino.app, confirm file lands in Maildir and importer publishes.
Common Failure Modes (and fixes)
- Expired cert → use nginx HTTP challenge + certbot.timer (already in place).
- Port 25 blocked → open in ufw (done).
- Importer down → ensure timer/service is active; check logs.
- Maildir perms → keep vmail ownership and 700/600 modes.
- Unknown sender → email skipped; add to allowlist if desired.
This covers the complete, reproducible “email to blog” setup on emino.app—from DNS/TLS through SMTP/IMAP and sender validation to Markdown generation and publishing.
Post created via email from emin@nuri.com