Prompts used since previous commit: - Kun je ook een webinterface maken waarmee ik de regels kan beheren? - Ik wil dat de webinterface benaderbaar is vanaf mijn hele interne netwerk. En ik wil alle regels kunnen beheren. Dus ook de bestaande ingebouwde routes. - 1. basic auth - 2. 4321 Special observations: - Domain routes moved from Python source to domain_routes.json so the web UI can manage all previous built-in domain routes. - web_config.json remains ignored; live sorter processes must be restarted after route edits to pick up changed rules.
9.7 KiB
Restart Prompt
You are continuing the mailcat project in /Users/hanswienen/Documents/Development/Vibes/mailcat.
Project Goal
Build a reliable mailbox cleanup and automation toolkit for australius.nl.
The operational direction is Python over IMAP/SMTP because Sieve is not viable with the provider. The current test target is backup@australius.nl. The user wants to test always-on automation first on a local QNAP-like NAS named hades, not on vps.australius.nl.
Do not run mailbox-affecting scripts yourself unless the user explicitly authorizes it. The user prefers to run those scripts personally.
Collaboration Rules
- Follow the git workflow from
AGENTS.md. - Work on branch
codexunless the user says otherwise. - Before every commit, rewrite this file from scratch as the current reconstruction prompt.
- Commit after each user-prompted code change using the required three-section commit message format.
- The user asked: first provide a plan and wait for approval before doing future work.
- Do not stage local runtime output such as
verplaats_log.jsonunless the user explicitly asks.
Local Data And Secrets
Ignored or local-only data includes:
mailbox/**/*.emlconfig.jsonweb_config.json__pycache__/- bytecode files
*.log.DS_Store- generated runtime state/log JSON files
Do not commit credentials.
Routing Policy
Sorting policy:
INBOX.Facturen - verwerktis the only folder treated as already sorted.Sent,Drafts,Trash, andSpamare excluded from sorting.- Every other mailbox folder is treated as a source.
- Do not use
Archiefas a final destination. - Existing
Archief.*folders are source folders. Matching messages should move out into functional folders. - If an archived/source message does not match a rule, leave it in place.
Destination policy:
- Final destination folders are functional folders such as
Financieel,Werk,Diensten,Nieuwsbrieven,Bestellingen,Mobiliteit,Administratie, andTechnisch. TechnischandTechnisch.DMARCare valid destinations.Administratie.KvKis the destination for KvK messages.- Former assignment senders route under
Werk.Opdrachten.*. zuiderzee.netand genericgmail.comintentionally remain unmatched.- AI providers route under
Diensten.AI; confirmed AI providers include Canva, Claude/Anthropic, Cursor, Huckr, Jamie, Mistral, OpenAI, and Perplexity. - Microsoft, Zapier, Envato, reMarkable, Nord, and ExpressVPN are not treated as AI providers unless the user reclassifies them.
Current Implementation
Shared modules:
imap_utils.pycentralizes IMAP modified UTF-7 folder encoding/decoding, quoted mailbox names, LIST parsing, and folder listing.mail_routes.pycentralizes routing behavior: invoice keyword detection, mailing-list routing, domain routing, source-folder exclusions, already-sorted folders, invoice quarter routing, and destination folder generation.mail_imap_ops.pycentralizes message header extraction, destination computation, destination mailbox creation, and UID COPY plus UID STORE move semantics.
Domain routes:
- Hardcoded domain routes have been moved out of Python into
domain_routes.json. mail_routes.domain_routes()loads and validatesdomain_routes.json.mail_routes.domain_lookup()builds the cached domain lookup.DOMAIN_ROUTESandDOMAIN_LOOKUPstill exist for compatibility, but active routing usesdomain_lookup().- If a long-running sorter process is already running, route edits on disk require restarting that process before it sees the new rules.
Mailing-list routes:
mailinglist_routes.jsonis still separate from domain routes.mail_routes.pychecks mailing-list routes before domain routes.mailinglist_routes.jsoncovers normalized policy fromdecisions.json, includingAfmelden.*destinations.- Mailing-list routing supports domain and
Fromsubstring matching; it does not yet fetch or matchList-ID.
Historical sorting:
verplaats_bestaand.pydefaults to dry-run. Real moves require--uitvoerenand interactiveJA.- Account selection is explicit with
--account <naam>, for example--account backup. --auditis available in dry-run mode.- It logs to
verplaats_log.json, retries IMAP aborts where implemented, and parses FETCH responses defensively.
Folder creation:
maak_mappen.pycreates destination folders generated frommail_routes.destination_folders().- It creates parent folders as needed and avoids
Archief.*destinations.
Backup mirror:
kopieer_naar_backup.pymirrors configured IMAP accounts, deduplicating byMessage-IDper destination folder.- It uses
backup_log.jsonfor progress and does not mark a folder complete if failures occurred.
Live daemon:
sort_mail_daemon.pydefaults to--account backup --folder INBOX.- It uses shared routing through
mail_imap_ops.destination_from_header(). - It maintains processed UID state and resets when UIDVALIDITY changes.
- It supports
--onceand--dry-run. - It uses IMAP IDLE where available, falls back conservatively, reconnects on failures, and logs to
sort_mail_daemon.logby default.
Daily report:
dagelijks_overzicht.pybuilds an HTML digest for a selected date, defaulting to yesterday.- It supports
--accountand--mail-to. - The NAS daily report target is
hans@australius.nl. - It sends a report even when there are zero messages.
Web Route Management
The route-management web interface is implemented in manage_routes_web.py.
Current decisions:
- LAN binding:
0.0.0.0 - Port:
4321 - Authentication: HTTP Basic Auth
- Credentials come from ignored
web_config.jsonor environment variablesMAILCAT_WEB_USERandMAILCAT_WEB_PASSWORD. - Example config is in
web_config.example.json.
Scope:
- The web UI manages all domain routes in
domain_routes.json, including the previous built-in routes. - Mailing-list routes remain in
mailinglist_routes.jsonand are not yet managed by the web UI.
Behavior:
GET /serves a dense table/form UI.GET /api/routesreturns all routes.POST /api/routesappends a route.PUT /api/routes/<index>updates a route.DELETE /api/routes/<index>deletes a route.- Domains and mailbox names are validated server-side.
- Duplicate domains across routes are rejected.
- Writes are atomic and create
domain_routes.json.bak.
Security note:
- This is Basic Auth on the internal network. It is not HTTPS by itself. If exposed beyond the LAN, put it behind TLS or a VPN.
NAS Deployment
deploy_vps.sh is host-neutral despite its historical filename.
For the QNAP-like NAS, use:
SERVICE_MANAGER=plain
MAILCAT_HOME=/share/homes/mailcat
PYTHON=/opt/bin/python3
APP_USER=mailcat
Known NAS command paths supplied by the user:
SUDO=/usr/bin/sudoUSERADD=/usr/local/bin/useraddPYTHON=/opt/bin/python3INSTALL=/usr/bin/installSED=/bin/sedTAR=/bin/tarMKDIR=/bin/mkdirCHOWN=/bin/chownCHMOD=/bin/chmodRM=/bin/rm
In SERVICE_MANAGER=plain mode:
- Default
APP_DIRis/share/homes/mailcat/mailcat. - Default
STATE_DIRis$APP_DIR/state. - Default
LOG_DIRis$APP_DIR/log. - Default
RUN_DIRis$APP_DIR/run. - The sorter PID file is
$RUN_DIR/mailcat.pid. - The web PID file is
$RUN_DIR/mailcat_web.pid. - The deploy script installs:
$APP_DIR/bin/start_mailcat.sh$APP_DIR/bin/stop_mailcat.sh$APP_DIR/bin/status_mailcat.sh$APP_DIR/bin/run_daily_report.sh$APP_DIR/bin/start_web.sh$APP_DIR/bin/stop_web.sh$APP_DIR/bin/status_web.sh
- The sorter appends wrapper stdout/stderr to
$LOG_DIR/daemon.out. - The web UI appends stdout/stderr to
$LOG_DIR/web.out. - The daily report wrapper appends stdout/stderr to
$LOG_DIR/daily_report.out.
Before starting services on the NAS:
- Create
/share/homes/mailcat/mailcat/config.jsonwith mode600. - Create
/share/homes/mailcat/mailcat/web_config.jsonwith mode600, for example:
{
"username": "mailcat",
"password": "use-a-real-password"
}
After route edits through the web UI, restart start_mailcat.sh/stop_mailcat.sh if the live sorter should use the new rules.
Systemd mode still exists for a future VPS deployment using systemd/mailcat-sort.service.
Current Backup Test Status
The backup mirror completed earlier:
- Source eligible mirror scope: 34 folders, 11,203 messages.
- Script copied: 11,190 messages.
- Duplicate skips: 13.
- Backup account after mirror: 159 folders, 11,190 messages.
The user later ran the backup historical sorter successfully and reported:
Totaal gepland: 0 | Totaal geen match: 2218 | Totaal overgeslagen: 8219 | Totaal fouten: 0
Treat that as a successful backup historical-sorter run with no immediate recovery work needed.
Verification Commands
Latest local verification should include:
python3 -m py_compile *.py tests/*.py
bash -n deploy_vps.sh
python3 -m unittest discover -s tests
git diff --check
Useful routing sanity check:
python3 - <<'PY'
from mail_routes import destination_folders, domain_routes, route_from_subject
print("routes", len(domain_routes()))
print("folders", len(destination_folders()))
print("openai", route_from_subject("noreply@openai.com", "update"))
print("huckr", route_from_subject("hello@huckr.ai", "update"))
PY
Next Expected Work
Likely next steps:
- Deploy the updated code to the NAS in
SERVICE_MANAGER=plainmode. - Create remote
web_config.json. - Start the web UI with
/share/homes/mailcat/mailcat/bin/start_web.sh. - Open
http://<nas-host-or-ip>:4321/from the internal network and verify Basic Auth. - Make a harmless route edit, verify
domain_routes.jsonanddomain_routes.json.bak, then restart the sorter if the live daemon is running.