A fully self-hosted “dead man’s switch” for email: it sends periodic confirmation links from your own server, and if you do not 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.
Download the latest version from here.
The release archive is intentionally slim. It contains README.md, LICENCE, the runtime PHP/CSS files, the .dist.php templates, and the shipped l18n/ locale files. It does not contain the full docs/, site/, or img/ directories. The quick start below is the offline starting point; the linked GitHub documentation and project website provide the full operator guide when you have network access.
- You regularly receive an email containing a confirmation link.
- Confirmation is two-step (
GETshows a button,POSTconfirms) to defeat mail link scanners. - The link is HMAC-signed (tamper-resistant), so the web endpoint (
totmann.php) can verify requests without keeping a history of issued confirmation 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 sharedTo:list). - If you do not confirm in time (plus grace), the script triggers escalation using conservative logic.
- Escalation uses exactly one configured recipient file (
totmann-recipients.phpby default, ortotmann-recipients.dist.phpif you intentionally keep that filename) with 3 flat top-level sections:$files,$messages, and$recipients. - Escalation emails are always sent individually (one mail per recipient).
- Escalation emails can optionally include recipient-specific download links for files stored outside the webroot.
- Escalation emails can include an optional receipt acknowledgement (ACK) link. The link opens an acknowledgement page first; only the submitted acknowledgement stops further escalation mails for that escalation event.
- Web requests without a valid current token always show a neutral page (stealth).
- Public web pages follow the browser language via
Accept-Language(de-DE,en-GB,en-US,fr-FR,it-IT,es-ES; fallback:en-US). - Web timestamps stay in your configured
mail_timezone, even when the browser language changes. - Runtime web pages load the product logo from the project’s GitHub-hosted image URL by default; the pages remain functional if that decorative image is blocked.
- Rate limiting runs in fail-open mode to reduce abuse without breaking functionality.
- Linux host with
systemd(timer + oneshot service). - PHP 8.0+.
- 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:
The bootstrap config files must keep their exact supported names:
sudo mkdir -p /var/lib/totmann sudo mkdir -p /var/lib/totmann/downloads sudo cp totmann.inc.dist.php totmann-tick.php totmann-lib.php totmann-recipients.dist.php /var/lib/totmann/ sudo cp -R l18n /var/lib/totmann/ cd /var/lib/totmann sudo cp totmann.inc.dist.php totmann.inc.php sudo cp totmann-recipients.dist.php totmann-recipients.php sudo mkdir -p /var/www/html/totmann sudo cp totmann.php /var/www/html/totmann/totmann.php sudo cp totmann.css /var/www/html/totmann/totmann.csstotmann.inc.phpand/ortotmann.inc.dist.php. Names such astotmann.inc.prod.phpare not supported. The recommended operational pattern is to edittotmann.inc.phpandtotmann-recipients.php; if you intentionally keep real values in.dist.phpfiles instead, merge updates manually before replacing those files. If you changed configurable runtime names such aslib_file,l18n_dir_name,recipients_file,web_file, orweb_css_filein the effective config, copy/rename only those referenced files accordingly. - Set required config in
/var/lib/totmann/totmann.inc.phpor, if you intentionally use the.dist.phpfile as your runtime config, in/var/lib/totmann/totmann.inc.dist.php:base_url(real HTTPS base URL without endpoint filename; the runtime appendsweb_fileautomatically)hmac_secret_hex(example:openssl rand -hex 32)to_selfl18n_dir_name(default:l18n)recipients_file(default:totmann-recipients.php)download_base_dir(private directory for downloadable files; keep it outside webroot)download_valid_days(global download-link validity for all files; default:180)operator_alert_interval_hours(mandatory operator-warning throttle in whole hours; allowed:1..24; missing/invalid values fall back automatically to2)- Runtime names:
lib_file,l18n_dir_name,lock_file,log_file_name,recipients_file,state_file,web_file(filenames/directories only; no paths) - 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) - Operator warnings are separate mails to
to_self; they are built in on purpose and cannot be disabled - Use the test preset from Timing
{DOWNLOAD_LINKS}renders the complete download block for that mail- the configured recipient file defines files once in
$files, reusable mail texts in$messages, and then assigns them in$recipients - every recipient row must reference a valid message key in field 3; there is no escalation fallback in the main config
- normal downloads go into recipient field 4; single-use downloads go into recipient field 5
- you never write
single_use=trueyourself; field 5 is the single-use list - if a message should contain a receipt-confirmation link, keep
{ACK_BLOCK}in that message body - if a message is used with field-5 files, add
single_use_noticeto that message intotmann-recipients.php - every download block starts with
1 Download:orX Downloads:; when several downloads are present, the runtime leaves a blank line between the download blocks automatically - download links are signed for the recipient, alias, escalation event, and current relative file path; if that alias is later changed to another file, the old link fails closed
- If two recipients should receive the same file, repeat the same file alias in both recipient rows
- Public web pages use the browser language from
Accept-Language; if only a base language such asdeis sent, the runtime picks the closest supported locale such asde-DE - If no supported browser language matches, the web endpoint falls back to
en-US
- 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 the web runtime resolves 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 the deployed state dir:
cd /var/lib/totmann php totmann-tick.php check php totmann-tick.php check --web-user=<WEB_USER>
- 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 effective config; if you changed them there, adapt this command. - Install + enable
systemdunit/timer: follow systemd. - Run the smoke/E2E test with short timings: follow Installation and Timing.
- During live testing, watch current activity 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. For help reading file-log lines, journal bootstrap failures, and operator warning mails together, use Log guide.
- ENV: environment variable (e. g.,
totmann_STATE_DIR). - GET/POST: HTTP request methods (
GETshows the confirm or ACK page;POSTperforms the confirmation or acknowledgement). - ACK: recipient receipt acknowledgement link (
GETopens the ACK page; only the submittedPOSTstops further escalation mails for that escalation event). - 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 limiter 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, downloads, proxy trust, rate limiting
- Understand the timing model and presets – timing model, presets, walkthrough
- Mail delivery notes – sendmail notes, recipient file, placeholders, ACK, normal downloads, single-use downloads
- Example messages – representative reminder, operator-warning, and escalation mails with practical explanation
- Log guide – how to read
totmann.logand which lines require action - 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
This project uses the MIT Licence. You may use, change, and distribute it in compliance with the licence terms.
