Email-ingest automation for Codex tasks
Project description
Quick Navigation
- Why Codemail
- Requirements
- Install & First Run
- How It Works
- Email Flow
- Configuration Cheat Sheet
- Development
- Troubleshooting & FAQ
- License
Why Codemail
Codemail turns an email inbox into a Codex automation trigger. Any message you send to the configured address is parsed, mapped to the right Codex session, executed, and answered with a single HTML recap (complete with headings, bullet lists, and Reply-To pointing back at the task pipeline). No more juggling CLI windows when you just want to fire off a quick instruction from your phone.
Highlights:
- Drop-in Postfix hook – point an alias or
.forwardat the runner and you are live. - Markdown-driven reports – agent output is converted to a dark, shareable status card.
- Session smart – references and message IDs keep threads tied to Codex sessions.
- Operator friendly – logs, state, and transcripts stay in the same places you already use.
Requirements
- Python 3.10+
- Codex CLI available on
PATH(codex exec) - Postfix (or another MTA) capable of piping mail to shell commands
- Access to the inbox you want Codemail to reply from (IMAP/SMTP credentials)
Install & First Run
- Clone & install
cd ~/tools git clone https://github.com/BranchManager69/codemail.git cd codemail python3 -m venv .venv source .venv/bin/activate pip install -e .
- Copy the environment template
cp .env.example .env # edit .env with your SMTP user, password, and desired paths
- Create the wrapper used by Postfix
cat > bin/codemail-wrapper <<'EOF_WRAPPER' #!/bin/bash set -a source /home/branchmanager/tools/codemail/.env set +a exec /home/branchmanager/tools/codemail/.venv/bin/codemail-runner EOF_WRAPPER chmod +x bin/codemail-wrapper
- Point your trigger address at the wrapper
.forward+tasks|/home/branchmanager/tools/codemail/bin/codemail-wrapper
- or
/etc/aliasestasks: "|/home/branchmanager/tools/codemail/bin/codemail-wrapper"
After editing aliases runnewaliases.
- Send a smoke test
Email
tasks@branch.bet(or your alias) with a short request. You should receive a single HTML reply summarising the work, and~/.codex/task-mail-runner.logwill show theSTART/SUMMARYentries.
How It Works
email → Postfix alias/.forward → codemail-wrapper → codemail-runner → Codex CLI
↓
HTML summary email
runner.pyhandles parsing, prompt construction, Codex invocation, and reply delivery.state.pykeeps a JSON map of message-id → session across runs.markdown_render.pyturns Markdown summaries into the dark status card.config.pycentralises env lookups so you can override paths or credentials.
Email Flow
- Incoming mail hits Postfix and is piped into
codemail-wrapper. - The wrapper exports your
.env, then executescodemail-runner. - The runner parses the message, finds any previous session, and shells out to
codex execin JSON streaming mode. - Codex emits Markdown summary text; Codemail renders it to HTML, sets
Reply-Toto the task address, and emails the sender. - Logs and state are appended so the next message in the thread resumes the same session.
Configuration Cheat Sheet
| Variable | Default | Purpose |
|---|---|---|
CODEMAIL_MAIL_USER |
codex@branch.bet |
From/BCC address for replies |
CODEMAIL_MAIL_PASS |
(required) | SMTP password (or app password) |
CODEMAIL_PASSWORD_FILE |
~/.codex_mail_pass |
Optional file-based password fallback |
CODEMAIL_SMTP_HOST |
mail.branch.bet |
SMTP host |
CODEMAIL_SMTP_PORT |
587 |
SMTP port |
CODEMAIL_STATE_PATH |
~/.codex/task_mail_map.json |
Session mapping file |
CODEMAIL_LOG_PATH |
~/.codex/task-mail-runner.log |
Runner log |
CODEMAIL_SESSION_ROOT |
~/.codex/sessions |
Codex transcript directory |
CODEMAIL_REPLY_TO |
tasks@branch.bet |
Reply-To header used on summaries |
CODEMAIL_FALLBACK_RECIPIENT |
branch@branch.bet |
Where to send reports if no recipient headers are found |
CODEMAIL_CODEX_BIN |
codex |
Override Codex CLI binary name/path |
CODEMAIL_ALLOWED_SENDERS |
(unset) | Comma-separated list of email addresses allowed to trigger tasks; when set, Codemail rejects other senders and emails them a notice |
All variables can be set in the .env file or exported before the wrapper executes.
Mail Security Hardening
Codemail assumes you already run a responsible MTA. To keep task traffic from being spoofed (or flagged as spam) you should wire in the following before inviting other operators to email your automation inbox.
1. SPF
- Publish an SPF TXT record for the domain you use in
CODEMAIL_MAIL_USER. - Example (Cloudflare → DNS):
v=spf1 mx include:_spf.google.com -all– swap in the MTAs that are actually allowed to send on your behalf.
2. DKIM
- Install and enable OpenDKIM (or your platform’s equivalent) and generate a key pair:
sudo opendkim-genkey -D /etc/opendkim/keys/yourdomain -d yourdomain -s mail sudo chown opendkim:opendkim /etc/opendkim/keys/yourdomain/mail.private
- Add a DNS TXT record named
mail._domainkey.yourdomainwhose value is the contents ofmail.txtproduced by the command above. - Wire the key into Postfix by adding to
/etc/opendkim/KeyTableand/etc/opendkim/SigningTable, then set in/etc/opendkim.conf:Domain yourdomain KeyFile /etc/opendkim/keys/yourdomain/mail.private Selector mail Socket local:/var/spool/postfix/opendkim/opendkim.sock
- Restart OpenDKIM and Postfix, then verify DNS propagation:
sudo systemctl restart opendkim postfix sudo opendkim-testkey -d yourdomain -s mail -k /etc/opendkim/keys/yourdomain/mail.private
3. DMARC
- Publish a DMARC TXT record (again via DNS) such as:
_dmarc.yourdomain IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain; ruf=mailto:dmarc@yourdomain"
Start withp=noneif you want reports before enforcing policy. - Install and enable OpenDMARC (optional but recommended). Postfix main.cf should include:
smtpd_milters = inet:localhost:8893, inet:localhost:8891 non_smtpd_milters = inet:localhost:8893, inet:localhost:8891
(with 8893 as OpenDMARC and 8891 as OpenDKIM, adjust to match your install). - Watch the reports sent to the
ruaaddress—you can move top=rejectonce you are satisfied that only authorized hosts are sending mail.
4. Allowed Sender List (Codemail)
- Use
CODEMAIL_ALLOWED_SENDERSto whitelist trusted operators (example:branch@branch.bet,nrsander@gmail.com). Codemail politely rejects everyone else so you are aware of attempted usage.
Tip: if you deploy Codemail on a fresh host, put the DKIM key, DMARC TXT, and allowed sender list into infrastructure-as-code or scripts so future reinstalls stay consistent.
Development
source .venv/bin/activate
pip install -e .
ruff format src tests
ruff check src tests
pytest
Troubleshooting & FAQ
- Getting two emails per task? Make sure the prompt hasn’t been modified—Codemail’s default instructs the agent not to send extra mail unless the task explicitly requires it.
- Runner exits with status 75? Check that the wrapper can read
.envand that the SMTP credentials are valid. - Want multiple configurations? Create separate
.envfiles and wrapper scripts, then point different aliases at each.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codemail-0.1.1.tar.gz.
File metadata
- Download URL: codemail-0.1.1.tar.gz
- Upload date:
- Size: 16.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dfca1a7e104bdc593d1f44b0412ceeeec5baf0b514f03312727ff29f86a60301
|
|
| MD5 |
9119262cc6ec6b5722cb62b5aa0ac356
|
|
| BLAKE2b-256 |
304e67e1ad70e05eba99cf04e5e0f960b371c2ecefe3ce8ff962d39d6f8483e9
|
File details
Details for the file codemail-0.1.1-py3-none-any.whl.
File metadata
- Download URL: codemail-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fdbe83c574748719a5e3ab685bd8abd97aae3633e31603b8bbc2ad8d4a4e95d
|
|
| MD5 |
92fbcb27aa4467b67344eab5bd5d1a74
|
|
| BLAKE2b-256 |
41d80011e269dd0a246931032ef87390582c588ee4a030cdfbaf2129276760fe
|