Cadence is a lightweight Flask app for tracking recurring tasks, grouped by category, with completion history and simple backup/restore controls.
- Create task groups (for example: Health, Home, Work)
- Add tasks into groups
- Mark tasks complete and store full completion history
- Show human-friendly "last completed" timing
- Provide a hidden admin route for task deletion, full wipe, and restore from JSON backups
- Persist data in SQLite
- Python
- Flask
- SQLite
- Gunicorn (for container runtime)
- Docker / Docker Compose (optional)
- Python 3.10+ (3.12 recommended)
pip- Docker + Docker Compose plugin (optional, for containerized run)
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtCreate an environment file:
cp .env.example .envThen edit .env and set at least:
ADMIN_SLUG(long random slug)SECRET_KEY(recommended: 64 hex chars)USER_TIMEZONE(IANA timezone likeAmerica/New_YorkorUTC)
Generate a strong SECRET_KEY if needed:
python3 -c 'import secrets; print(secrets.token_hex(32))'Run:
python app.pyOpen: http://127.0.0.1:5000
Notes:
ADMIN_SLUGis optional, but without it all/admin/...routes return404.- If
SECRET_KEYis not set, the app generates one at startup (sessions are then invalidated on restart).
Create and configure your env file:
cp .env.example .envThen update ADMIN_SLUG, SECRET_KEY, and USER_TIMEZONE in .env.
Start:
docker compose up --build -dView logs:
docker compose logs -f cadenceStop:
docker compose downOpen: http://127.0.0.1:5000
Environment variables used by the app:
| Variable | Default | Purpose |
|---|---|---|
ADMIN_SLUG |
unset | Secret slug for admin route (/admin/<slug>). If unset, admin route is disabled (404). |
SECRET_KEY |
random at process start | Flask session and CSRF signing key. Set this explicitly in real deployments. |
USER_TIMEZONE |
UTC |
Timezone used for displayed completion times (IANA format, e.g. America/Chicago). Invalid values fall back to UTC. |
DATABASE_PATH |
./tasks.db |
SQLite database file path. |
BACKUP_DIR |
./backups |
Directory for JSON backups. |
MAX_NAME_LENGTH |
120 |
Maximum length for group/task names. |
SESSION_COOKIE_SECURE |
false |
Set to true when serving over HTTPS. |
FLASK_DEBUG |
false |
Enables Flask debug mode when running python app.py. |
- Database:
tasks.db - Backups:
backups/backup-YYYYMMDD-HHMMSS.json
- In container: database at
/data/tasks.db, backups at/data/backups - Persisted via named volume:
cadence_data
Admin URL format:
http://127.0.0.1:5000/admin/<your-admin-slug>
Available admin actions:
- Delete a task (also removes its completion history)
- Full wipe
- Creates a backup first
- Removes all groups, tasks, completions
- Re-creates default
Generalgroup
- Restore from an existing backup file in the backup directory
- All
POSTforms require CSRF token validation - Admin slug checks use constant-time comparison
- Response headers include:
Content-Security-PolicyX-Frame-Options: DENYX-Content-Type-Options: nosniffReferrer-Policy: no-referrer
- Session cookies are
HttpOnlyandSameSite=Lax
app.py: Flask app and SQLite logictemplates/index.html: main task UItemplates/admin.html: admin UIdocker-compose.yml: local container orchestrationDockerfile: production-style app image.env.example: example environment file for local and Compose runs
docker composefails withset ADMIN_SLUGorset SECRET_KEY:- Ensure
.envexists (cp .env.example .env) and contains both values.
- Ensure
- Timezone still shows as UTC in Docker:
- Ensure
USER_TIMEZONEis set in.envwith an IANA value (for exampleAmerica/New_York). - Rebuild after changing timezone config:
docker compose up --build -d.
- Ensure
- Admin page returns
404:- Ensure
ADMIN_SLUGis set and URL slug matches exactly.
- Ensure
- Data appears reset after container restart:
- Confirm the container uses the
cadence_datavolume and you did not remove volumes.
- Confirm the container uses the