Task manager to help remember periodic or upcoming tasks with Telegram front-end.
Go to file
2026-06-04 21:23:24 -04:00
bin Finished Webhooks and Telegram bindings 2026-06-04 15:44:16 -04:00
config Initial scaffolding 2026-06-04 13:57:46 -04:00
lib/Mnemosyne Fixed a couple message routing errors 2026-06-04 21:23:24 -04:00
nginx Initial scaffolding 2026-06-04 13:57:46 -04:00
scripts Initial completion 2026-06-04 19:33:35 -04:00
share Initial scaffolding 2026-06-04 13:57:46 -04:00
systemd Initial completion 2026-06-04 19:33:35 -04:00
t Finished Schedule.pm 2026-06-04 14:52:49 -04:00
cpanfile Initial scaffolding 2026-06-04 13:57:46 -04:00
LICENSE Initial commit 2026-06-04 14:56:16 +00:00
README.md Fixed a couple message routing errors 2026-06-04 21:23:24 -04:00

Mnemosyne

A self-hosted, single-user task manager built around long-timeframe recurring tasks. Its job is to keep things on the radar — recurring obligations, periodic maintenance, and someday/maybe items — without the bloat of a full project management system.

The primary interface is a Telegram bot. Every morning it delivers a Day at a Glance digest: what's overdue, what's due today, what's coming up in the next week, and a rotation of floating reminders. Everything else is query-on-demand. No mid-day push notifications, ever.

The name is Mnemosyne — the Greek personification of memory. The app remembers so you don't have to.


Task classes

Five scheduling models cover most recurring-task patterns:

Class Description Example
monthly_date Fixed day of the month Pay rent on the 1st
monthly_weekday Nth weekday of the month First Thursday, last Friday
every_n_period Every N days/weeks/months from an anchor date Every 2 weeks from 2026-01-06
interval Every N days, clock resets on completion Change oil every 90 days
floating No fixed date; surfaces by priority rotation Call parents, learn Spanish

Missed dated tasks roll into an Overdue section rather than disappearing. Slippage is a feature, not an error.


Telegram commands

Command What it does
/today or /glance Day at a Glance digest on demand
/list [filter] Browse tasks; filter by class, priority, or inactive
/add Create a task via guided wizard
/done <id|title> Mark a task complete
/edit <id> <field> <value> Update a single field
/disable <id|title> Hide a task without deleting it
/delete <id|title> Permanently delete (with confirmation)
/settime HH:MM Change the morning digest delivery time
/help List commands

Actionable tasks in digests and list results get inline Mark Done buttons. Tapping one completes the task immediately and replaces the button with Undo in case of misclicks.


Stack

  • Perl — application logic
  • SQLite — single-file database via DBD::SQLite; WAL mode
  • Mojolicious — webhook receiver and outbound Telegram API calls (Mojo::UserAgent)
  • nginx — TLS termination and reverse proxy; bot binds to localhost only
  • systemd — long-running webhook service with Restart=always
  • cron — morning digest (runs every 5 minutes; the script checks the configured time)

Prerequisites

  • A Linux server with a public IP (AlmaLinux 10 / RHEL family recommended; Debian/Ubuntu also supported)
  • A domain or subdomain with an A record pointing at the server
  • A Telegram bot token from @BotFather
  • Your Telegram chat ID (@userinfobot will tell you)
  • Perl 5.26+, nginx, certbot, sqlite3

Deployment

1. Clone the repo

git clone https://git.castlehollow.com/rodger/mnemosyne.git
cd mnemosyne

2. Run the install script

Must be run as root. Safe to re-run — it will not overwrite an existing database or config file.

sudo bash scripts/install.sh

This will:

  • Install system packages (Perl, cpanminus, sqlite3, gcc) via dnf or apt-get
  • Install CPAN dependencies from cpanfile via cpanm
  • Create a mnemosyne system user and group
  • Deploy application files to /opt/mnemosyne
  • Install the systemd service (enabled but not started until you configure it)
  • Install a cron job at /etc/cron.d/mnemosyne for the digest and daily backup

Default paths (override with flags if needed):

Flag Default
--app-dir /opt/mnemosyne
--config-dir /etc/mnemosyne
--data-dir /var/lib/mnemosyne

3. Edit the config file

sudo nano /etc/mnemosyne/mnemosyne.conf

Fields to fill in:

[bot]
token = YOUR_BOT_TOKEN_HERE          # from @BotFather
webhook_secret = ...                  # openssl rand -hex 32
webhook_url = https://bot.example.com/hook/YOUR_RANDOM_PATH
listen_port = 8765
allowed_chat_ids = 123456789          # your Telegram chat ID

[db]
path = /var/lib/mnemosyne/mnemosyne.db

[app]
timezone = America/New_York           # any IANA timezone name
digest_time = 06:30

The webhook_url path segment (/hook/YOUR_RANDOM_PATH) should be a hard-to-guess string — it acts as a second layer of security alongside the webhook_secret header. Generate one with openssl rand -hex 16.

4. Configure nginx

Copy the example config and adjust the server name and port:

sudo cp /opt/mnemosyne/nginx/mnemosyne.conf.example \
        /etc/nginx/conf.d/mnemosyne.conf
sudo nano /etc/nginx/conf.d/mnemosyne.conf
# Replace mnemosyne.example.com with your subdomain
# Replace 8765 with your listen_port if you changed it
sudo nginx -t && sudo systemctl reload nginx

5. Obtain a TLS certificate

Telegram requires HTTPS. Use certbot:

sudo certbot --nginx -d bot.example.com

Verify the nginx config still works after certbot modifies it:

sudo nginx -t && sudo systemctl reload nginx

6. Start the bot

sudo systemctl start mnemosyne-bot
sudo systemctl status mnemosyne-bot
sudo journalctl -u mnemosyne-bot -f

7. Register the Telegram webhook

This tells Telegram where to send updates. Run it once, and again any time the webhook_url changes:

sudo -u mnemosyne /opt/mnemosyne/bin/mnemosyne-webhook \
    --config /etc/mnemosyne/mnemosyne.conf

To remove the webhook (e.g. before switching servers):

sudo -u mnemosyne /opt/mnemosyne/bin/mnemosyne-webhook \
    --config /etc/mnemosyne/mnemosyne.conf --delete

8. Verify

Send /today to your bot. You should receive a digest (or an "all clear" message if no tasks are due).


AlmaLinux / RHEL-specific notes

IPv6 not routed (common on Linode/VPS) — if the bot logs "Connect timeout" when trying to reach api.telegram.org, IPv6 is configured on the interface but not actually routed. Fix by telling the system resolver to prefer IPv4:

echo 'precedence ::ffff:0:0/96  100' | sudo tee -a /etc/gai.conf
sudo systemctl restart mnemosyne-bot

SELinux — if nginx cannot connect to the local bot port, allow it:

sudo setsebool -P httpd_can_network_connect 1

Firewall — the bot port (8765 by default) does not need to be open to the internet; nginx proxies to it locally. Only ports 80 and 443 need to be reachable.

Perl modules — some CPAN modules require perl-devel and gcc for XS compilation. The install script handles this, but if you install manually:

sudo dnf install -y perl-devel gcc make
sudo cpanm --notest --installdeps .

Backup and restore

The install script sets up a daily backup cron at 02:15. You can also run it manually:

sudo -u mnemosyne /opt/mnemosyne/scripts/backup.sh \
    --config /etc/mnemosyne/mnemosyne.conf

Backups land in /var/backups/mnemosyne/ and are retained for 30 days by default. Override with --keep-days N.

The backup uses SQLite's Online Backup API, so it's safe to run while the bot is live — no downtime required.

Restore:

sudo systemctl stop mnemosyne-bot
sudo cp /var/backups/mnemosyne/mnemosyne-YYYYMMDD-HHMMSS.db \
        /var/lib/mnemosyne/mnemosyne.db
sudo chown mnemosyne:mnemosyne /var/lib/mnemosyne/mnemosyne.db
sudo systemctl start mnemosyne-bot

Updating

cd /path/to/mnemosyne-repo
git pull
sudo bash scripts/install.sh   # re-runs safely; does not touch config or database
sudo systemctl restart mnemosyne-bot

Repository layout

mnemosyne/
├── bin/
│   ├── mnemosyne-bot         # webhook receiver (Mojolicious app, run by systemd)
│   ├── mnemosyne-digest      # morning digest sender (run by cron)
│   └── mnemosyne-webhook     # one-shot webhook registration helper
├── lib/Mnemosyne/
│   ├── Config.pm             # INI config loader
│   ├── DB.pm                 # SQLite connection, schema, migration
│   ├── Digest.pm             # Day at a Glance builder and renderer
│   ├── Schedule.pm           # per-class due/overdue/upcoming resolver
│   ├── Task.pm               # task CRUD + completions
│   ├── Telegram.pm           # Bot API client and keyboard factories
│   └── Webhook.pm            # update routing, command and callback handlers
├── config/
│   └── mnemosyne.conf.example
├── nginx/
│   └── mnemosyne.conf.example
├── scripts/
│   ├── backup.sh
│   ├── install.sh
│   └── print-digest          # debug: render a digest to the terminal
├── share/
│   └── schema.sql
├── systemd/
│   └── mnemosyne-bot.service
└── t/                        # test suite

License

MIT