Lastsignal
A self-hosted dead man's switch for delivering encrypted messages (E2EE) to your loved ones — when you're gone or unresponsive.
Install / Use
/learn @giovantenne/LastsignalREADME
<img src="https://lastsignal.app/logo-mark.svg" alt="LastSignal" width="50" height="50" align="absmiddle" /> LastSignal
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
[!IMPORTANT] LastSignal is open source and self-hosted. The authors do not offer any hosted service, SaaS, paid subscription, or commercial instance of LastSignal. If you see LastSignal presented elsewhere as a paid hosted product, it is not an official LastSignal service.
📬 Email-First Flow (Quick Overview)
- 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.
🔒 Security Model
- 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
⚠️ Critical: Strong Passphrases Required
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.
⏱️ Default Timing (Days)
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.
🧪 Development
This runs a local dev stack and opens emails in your browser using letter_opener.
Requirements
- 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-dev
Run the stack
git clone https://github.com/giovantenne/lastsignal.git
cd lastsignal
bundle install
cp .env.example .env
bin/setup
bin/dev
Then open http://localhost:3000 and request a magic link.
The email opens in your browser automatically via letter_opener.
Docker (Quick trial with Mailhog)
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 --build
Then open:
- App: http://localhost:3000
- Mailhog inbox: http://localhost:8025
E2EE demo flow (Dev / Docker)
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.com
Notes:
- 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.
Tests
# 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.rb
🚀 Production Deployment (Kamal)
You only need Docker, SSH access, and a reliable SMTP provider.
Kamal docs: https://kamal-deploy.org
1) Prepare the server
- Provision a Linux host (Ubuntu 22.04+ recommended)
- Install Docker and open ports 80/443
- Point DNS to the server IP (A/AAAA records)
2) Configure environment
Copy the template and fill in the required values:
cp .env.production.example .env.production
You 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:edit
3) Deploy
bin/kamal setup
bin/kamal deploy
4) Prepare the databases
bin/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"
5) Verify
bin/kamal logs
Health check: https://YOUR_DOMAIN/up
🐳 Production Deployment (docker-compose)
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 -f
Reverse Proxy (SSL/TLS)
For 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 Deliverability Checklist
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)
💾 Storage Backups
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"
⚙️ Defaults
Timing, rate-limit, and crypto defaults live in config/initializers/app_config.rb.
🤝 Contributing
Contributions welcome! Please open an issue first to discuss changes.
📄 License
This project is licensed under the MIT License.
See LICENSE for details.
⚠️ Disclaimer
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 re
Related Skills
node-connect
335.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
335.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.5kCommit, push, and open a PR
