LastSignal is a self-hosted, email-first dead man's switch. You write encrypted messages for the people you care about. If you stop responding to email check-ins, LastSignal delivers those messages automatically.
Website: lastsignal.app
- System emails you periodic check-ins.
- If you miss them, you receive reminder attempts at a fixed interval.
- The final reminder triggers the trusted contact ping (if configured).
- If you still don't respond, messages are delivered by email.
- End-to-end encrypted - Server never sees plaintext messages
- Zero-knowledge architecture - Even the operator can't read your data
- Modern cryptography - Argon2id (256MB) + XChaCha20-Poly1305 + X25519
- Auditable - Audit the code yourself
LastSignal uses a server-generated KDF salt stored alongside recipient public keys. This is a deliberate architectural trade-off that enables deterministic key regeneration from passphrases, but it introduces a specific threat: if an attacker gains access to the database (via server compromise, data breach, malicious operator, or law enforcement request), they obtain the salt and can attempt offline brute-force attacks against recipient passphrases without any rate limiting.
All timing settings are configurable per user in Account Settings.
| Setting | Default |
|---|---|
| Check-in interval | 30 days |
| Reminder attempts | 3 (includes the first reminder) |
| Attempt interval | 7 days |
| Trusted contact pause | 15 days |
Example timeline:
| Event | Date | State |
|---|---|---|
| Last check-in | Apr 1 | 🟢 Active |
| Reminder #1 | May 1 | 🟢 Active |
| Reminder #2 | May 8 | 🟡 Grace |
| Reminder #3 (final + trusted contact ping) | May 15 | 🟠 Cooldown |
| Delivery (if no response) | May 22 | 🔴 Delivered |
If the trusted contact confirms on May 16:
| Event | Date | State |
|---|---|---|
| Delivery paused until | May 31 | 🟠 Cooldown (paused) |
| New trusted contact ping | May 31 | 🟠 Cooldown |
| Delivery unless the user checks in or the trusted contact confirms again | Jun 7 | 🔴 Delivered |
Recipient-specific delay: You can also set a delay (in days) per recipient. This delays when the recipient can decrypt the message after delivery—useful for staggered access or time-sensitive information.
This runs a local dev stack and opens emails in your browser using letter_opener.
- Ruby 3.4+
- SQLite 3 (sqlite3 gem >= 2.1)
- Node.js (for Tailwind)
Example for a fresh Ubuntu install:
apt-get update && apt-get install -y \
git \
ruby \
bundler \
libyaml-devgit clone https://github.com/giovantenne/lastsignal.git
cd lastsignal
bundle install
cp .env.example .env
bin/setup
bin/devThen open http://localhost:3000 and request a magic link.
The email opens in your browser automatically via letter_opener.
If you want to try the app without installing Ruby locally, use the dev compose stack with Mailhog:
docker compose -f docker-compose.dev.yaml up --buildThen open:
- App: http://localhost:3000
- Mailhog inbox: http://localhost:8025
This lets you test the full check-in -> delivery flow quickly, without waiting days.
Prereqs:
- Start the stack.
- Log in, add a recipient, accept the invite with a passphrase, and create a message for that recipient. Check-ins are skipped unless there is at least one message linked to an accepted recipient. Dev commands:
bin/rails demo:checkins:status EMAIL=you@example.com
bin/rails demo:checkins:advance EMAIL=you@example.com
bin/rails demo:checkins:advance_days EMAIL=you@example.com DAYS=7
bin/rails demo:checkins:deliver EMAIL=you@example.com`Docker commands:
docker compose -f docker-compose.dev.yaml exec app bin/rails demo:checkins:status EMAIL=you@example.com
docker compose -f docker-compose.dev.yaml exec app bin/rails demo:checkins:advance EMAIL=you@example.com
docker compose -f docker-compose.dev.yaml exec app bin/rails demo:checkins:advance_days EMAIL=you@example.com DAYS=7
docker compose -f docker-compose.dev.yaml exec app bin/rails demo:checkins:deliver EMAIL=you@example.comNotes:
- Each
advancesends the next email in the sequence (reminder -> grace -> cooldown -> delivery). advance_dayssimulates time passing by N days and runs the check-in job.- To skip straight to delivery:
bin/rails demo:checkins:deliver EMAIL=you@example.com - Emails open in letter_opener (Dev) or Mailhog (Docker).
- Demo helpers only run in development/test.
# Full suite
bin/test
# Targeted specs
bin/test spec/models/user_spec.rb
bin/test spec/jobs/process_checkins_job_spec.rb
bin/test spec/requests/auth_spec.rbYou only need Docker, SSH access, and a reliable SMTP provider.
Kamal docs: https://kamal-deploy.org
- Provision a Linux host (Ubuntu 22.04+ recommended)
- Install Docker and open ports 80/443
- Point DNS to the server IP (A/AAAA records)
Copy the template and fill in the required values:
cp .env.production.example .env.productionYou must set:
KAMAL_*(image, registry, server, domain)APP_BASE_URLandAPP_HOSTSMTP_*(your provider credentials)ALLOWED_EMAILS(optional allowlist for private instances)
Generate a master key if you don't have one:
bin/rails credentials:editbin/kamal setup
bin/kamal deploybin/kamal app exec --interactive --reuse "bin/rails db:prepare"
bin/kamal app exec --interactive --reuse "bin/rails db:prepare DATABASE=cache"
bin/kamal app exec --interactive --reuse "bin/rails db:prepare DATABASE=queue"bin/kamal logsHealth check: https://YOUR_DOMAIN/up
If you prefer not to use Kamal, you can deploy with docker-compose using the provided docker-compose.prod.yaml.
# Copy and configure environment
cp .env.production.example .env.production
# Edit .env.production with your values (you can ignore KAMAL_* variables)
# Start the stack
docker compose -f docker-compose.prod.yaml --env-file .env.production up -d --build
# Prepare databases (first run only)
docker compose -f docker-compose.prod.yaml exec app bin/rails db:prepare
docker compose -f docker-compose.prod.yaml exec app bin/rails db:prepare DATABASE=cache
docker compose -f docker-compose.prod.yaml exec app bin/rails db:prepare DATABASE=queue
# View logs
docker compose -f docker-compose.prod.yaml logs -fFor production, place a reverse proxy (nginx, Caddy, Traefik) in front of the app to handle HTTPS. Example with Caddy:
yourdomain.com {
reverse_proxy localhost:80
}
Caddy automatically provisions Let's Encrypt certificates.
Email delivery is mission-critical for LastSignal. If your SMTP setup is misconfigured, messages may never arrive.
Most transactional email providers (such as Postmark, SendGrid, or similar services) guide you through this process and provide the required DNS records and configuration details.
- SPF: authorize your SMTP provider to send on your domain
- DKIM: enable DKIM signing and add the DNS record
- DMARC: start with
p=none, then tighten toquarantineorreject - From address: use a domain you control (matches
SMTP_FROM_EMAIL)
The default deployment stores the SQLite database and Active Storage files in the Docker volume lastsignal_storage. Back it up regularly.
Backup:
docker run --rm -v lastsignal_storage:/data -v "$PWD":/backup alpine \
sh -c "cd /data && tar -czf /backup/lastsignal_storage.tgz ."Restore:
docker run --rm -v lastsignal_storage:/data -v "$PWD":/backup alpine \
sh -c "cd /data && tar -xzf /backup/lastsignal_storage.tgz"Timing, rate-limit, and crypto defaults live in config/initializers/app_config.rb.
Contributions welcome! Please open an issue first to discuss changes.
This project is licensed under the MIT License.
See LICENSE for details.
LastSignal is provided "as is" without warranty of any kind. The authors provide only the source code and do not host, operate or monitor the server on your behalf. All email delivery is performed by your self-hosted instance and your chosen SMTP provider.
By using this project, you accept full responsibility for configuration, security, backups, content, recipients, compliance obligations, and the consequences of any delivery or non-delivery. The authors disclaim all liability for damages, data loss, missed or premature delivery, misuse, or any other outcome, whether arising from bugs, misconfiguration, third-party outages, or operational errors.
LastSignal is not a substitute for a will, trust, power of attorney, or any other legal instrument. It is not legally binding and should not be relied upon to transfer rights, property, or obligations. If you need legal certainty, consult a qualified attorney and use appropriate legal documents.
If you find LastSignal useful and want to support its development, you can donate via ₿itcoin:
bc1qt6z0e5ttcjx0cnwjdl8mua2srt0lamah5lnnvm
Donations help support ongoing maintenance, security reviews, and long-term sustainability of the project. Thank you 🙏