A fully self-hosted “dead man’s switch” for email: it sends periodic confirmation links from your own server, and if you don’t confirm within a defined window (plus grace), it escalates to predefined recipients. No third-party services, no vendor lock-in – just systemd, PHP, and sendmail on infrastructure you control.
- You regularly receive an email containing a confirmation link.
- Confirmation is two-step (GET shows a button, POST confirms) to defeat mail link scanners.
- The link is HMAC-signed (tamper-resistant), so the web endpoint (
totmann.php) can verify requests without storing a history of issued links. - You must open the link within a configured time window.
- A successful confirmation resets the cycle.
- The script sends reminder emails as one email per
to_selfrecipient (no shared To list). - If you do not confirm in time (plus grace), the script triggers escalation using conservative logic.
- The script sends escalation as one email per recipient (no shared To list), with optional recipient-specific plain-text subject/body pairs via external template IDs.
- Escalation emails can include an optional receipt acknowledgement (ACK) link. Once any recipient acknowledges, further ACK reminders stop.
- If you configure an ACK reminder limit, the script logs one final marker when it reaches the limit and then stays quiet for this escalation state (until ACK or reset) to avoid irrelevant log noise.
- Web requests without a valid current token always show a neutral page (stealth).
- Rate limiting runs in fail-open mode to reduce abuse without breaking functionality.
- Linux host with
systemd(timer + oneshot service). - PHP 8.0+ (uses
str_contains()). - A sendmail-compatible MTA (e. g., Postfix/Exim) available via
sendmail_path.
One rule that matters: The tick runs as
root, but the web request does not. This means the state directory must be writable by bothrootand your real web user/group – and it must not be inside your webroot.
- Identify your real PHP runtime identity (
<WEB_USER>:<WEB_GROUP>): Installation guide, section “Before you start”. - Create state dir + place files:
If you changed
sudo mkdir -p /var/lib/totmann sudo cp totmann.inc.php totmann-tick.php totmann-lib.php totmann-messages.php /var/lib/totmann/ # Place the web endpoint in your webroot (example): # sudo cp totmann.php /var/www/html/totmann/totmann.php # Optional but recommended for styled web pages: # sudo cp totmann.css /var/www/html/totmann/totmann.css
lib_file,web_file,web_css_file, ormail_fileintotmann.inc.php, copy/rename files accordingly. - Set required config in
/var/lib/totmann/totmann.inc.php:base_url(real HTTPS base URL without endpoint filename; the script appendsweb_fileautomatically)hmac_secret_hex(example:openssl rand -hex 32)to_selfto_recipientsas entries in format[address]or[address, id]body_escalate(mandatory fallback escalation body)mail_file(optional ID-to-subject/body map in external PHP file)- Runtime filenames:
lib_file,lock_file,log_file_name,mail_file,state_file,web_file(filenames only) - Optional web stylesheet filename:
web_css_file(same webroot folder asweb_file; empty disables link) - Logging target via
log_mode:none,syslog,file,both(recommended:both) - Use the test preset from Timing
- Important fail-safe: if recipient ID mapping is invalid/missing, or template loading fails at runtime, escalation still sends with
subject_escalate+body_escalate
- Set permissions:
sudo chown -R root:<WEB_GROUP> /var/lib/totmann sudo find /var/lib/totmann -type d -exec chmod 2770 {} \; sudo find /var/lib/totmann -type f -exec chmod 0660 {} \;
- Ensure web runtime can resolve the same state dir:
- Prefer ENV
TOTMANN_STATE_DIR=/var/lib/totmannin your PHP runtime. totmann.phpin this repo ships withdefine('TOTMANN_STATE_DIR', '/var/lib/totmann')enabled by default. Adjust this value if your state dir differs.
- Prefer ENV
- Run preflight in deployed state dir:
Preflight now validates timing/interval values strictly (integer type + minimum bounds) and reports warning-level hints for suspicious but allowed combinations (e. g.,
cd /var/lib/totmann php totmann-tick.php check php totmann-tick.php check --web-user=<WEB_USER>
confirm_window_seconds > check_interval_seconds). - Clean initialise once:
The
sudo rm -f /var/lib/totmann/totmann.json /var/lib/totmann/totmann.lock /var/lib/totmann/totmann.log sudo sh -c 'umask 0007; /usr/bin/php /var/lib/totmann/totmann-tick.php tick'rmline uses the filenames shown in the template config; if you changed them intotmann.inc.php, adapt this command. - Install + enable
systemdunit/timer: Follow the instructions in systemd. - Run smoke/E2E test with short timings: Follow the “Smoke test” in the installation guide and the checklist in Timing.
- During live testing, watch script decisions in real time:
If you changed
tail -f /var/lib/totmann/totmann.log
log_file_nameorlog_file, use that effective path instead. Iflog_modeissyslogornone, usejournalctlinstead oftail.
- ENV: environment variable (e. g.,
TOTMANN_STATE_DIR). - GET/POST: HTTP request methods (GET shows the confirm page; POST performs the confirmation).
- ACK: recipient receipt acknowledgement link (stops further ACK reminders once any recipient clicks).
- HMAC: keyed hash used to sign tokens (tamper-resistant).
- MTA: Mail Transfer Agent (server-side mailer, e. g., Postfix/Exim).
- PHP-FPM: PHP FastCGI Process Manager (common PHP runtime behind nginx).
- setgid: directory bit so new files inherit the directory group.
- umask: process permission mask controlling default file modes.
- fail-open: on internal failure, allow the request (used for rate limiting to avoid accidental lockouts).
- Read the installation guide – layout, permissions, clean initialise, smoke test
- Configure
systemd– service/timer units + operational checks - Configure the web endpoint – state dir resolution, stealth responses, rate limiting
- Understand the timing model and presets – timing model, presets, walkthrough
- Mail delivery notes – sendmail notes, SMTPUTF8, deliverability tips
- Troubleshooting – neutral page, missing mails, permissions, common failure modes
- Changelog – release notes and version history
- Roadmap – planned next features
- Contribution guide – contribution workflow, quality checks, PR checklist
Contributions are welcome! Please follow these steps:
- Fork this repository
- Create a feature branch:
git checkout -b feature-branch - Commit your changes:
git commit -m "Add feature" - Push the branch:
git push origin feature-branch - Submit a pull request
Please ensure all changes are well-documented and tested.
This project uses the MIT Licence. You may use, change, and distribute it in compliance with the licence terms.