a self-hosted personal command center PWA for tasks, notes, reminders, and daily focus.
2.4K
A self-hosted personal command center PWA for tasks, notes, reminders, events, bookmarks, and daily focus. Built with Flask, HTMX, PostgreSQL, and Docker.
| Layer | Technology |
|---|---|
| Backend | Python 3.14, Flask 3.1 |
| Database | PostgreSQL 16 (production), SQLite (development/testing) |
| ORM | SQLAlchemy 2.0 + Alembic migrations |
| Frontend | Jinja2 templates, HTMX 1.9, Vanilla JS |
| Auth | Flask-Login, bcrypt (work factor 12), PyOTP (TOTP) |
| Security | Flask-WTF (CSRF), Flask-Limiter (rate limiting) |
| Server | Gunicorn 25 (2 workers) |
| Deployment | Docker, Docker Compose |
# 1. Clone the repository
git clone https://github.com/whahn1983/helmhub.git
cd helmhub
# 2. Copy the example environment file
cp .env.example .env
# 3. Edit .env and set strong secrets and admin credentials
nano .env
# 4. Start the application
docker-compose up -d
# Application is available at http://localhost:8080
Log in with the admin credentials you set in .env. On first run, the admin account is created automatically if no users exist in the database.
# 1. Clone and enter the repository
git clone https://github.com/whahn1983/helmhub.git
cd helmhub
# 2. Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. Set environment variables (or create a .env file)
export SECRET_KEY="your-secret-key-here"
export TOTP_ENCRYPTION_KEY="$(python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())')"
export DATABASE_URL="postgresql://user:password@localhost:5432/helmhub"
export FLASK_ENV="development"
export DEFAULT_ADMIN_USERNAME="admin"
export DEFAULT_ADMIN_PASSWORD="changeme"
# 5. Apply database migrations
flask db upgrade
# 6. Start the development server
flask run --port 8080
All configuration is done via environment variables. Copy .env.example to .env and fill in your values.
| Variable | Description | Default |
|---|---|---|
SECRET_KEY | Flask session and CSRF secret — must be changed in production | (required) |
TOTP_ENCRYPTION_KEY | Fernet key used to encrypt TOTP secrets at rest | (required) |
DATABASE_URL | Database connection URI | sqlite:///helmhub.db |
APP_PORT | Host port mapped to the container | 8080 |
POSTGRES_PASSWORD | PostgreSQL password (used by docker-compose) | helmhub_secret |
DEFAULT_ADMIN_USERNAME | Username for the auto-created admin account | admin |
DEFAULT_ADMIN_PASSWORD | Password for the auto-created admin account | changeme |
TZ | Timezone for date/time display | America/New_York |
FLASK_ENV | Runtime environment (production, development, testing) | production |
SESSION_COOKIE_SECURE | Restrict session cookies to HTTPS | True |
PROXY_FIX_X_FOR | Trusted X-Forwarded-For proxy hop count | 0 |
PROXY_FIX_X_PROTO | Trusted X-Forwarded-Proto proxy hop count | 0 |
For production deployments, always set:
SECRET_KEYTOTP_ENCRYPTION_KEY (generate with python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")DEFAULT_ADMIN_PASSWORD (or change the password immediately after first login)SESSION_COOKIE_SECURE=True if serving over HTTPShelmhub/
├── app/
│ ├── __init__.py # Application factory (create_app)
│ ├── config.py # Config classes (Dev / Prod / Test)
│ ├── extensions.py # Flask extension instances
│ ├── models/ # SQLAlchemy models (user, task, note, reminder, event, bookmark, setting)
│ ├── routes/ # Flask blueprints per feature
│ │ ├── api.py # JSON REST API
│ │ ├── auth.py # Login, TOTP, logout
│ │ ├── dashboard.py # Main dashboard
│ │ ├── tasks.py
│ │ ├── notes.py
│ │ ├── reminders.py
│ │ ├── events.py
│ │ ├── bookmarks.py
│ │ ├── focus.py
│ │ └── settings.py
│ ├── services/ # Auth and TOTP helpers
│ ├── static/ # CSS, JS, service worker, PWA manifest, icons
│ └── templates/ # Jinja2 HTML templates
├── migrations/ # Alembic migration versions
├── tests/ # pytest test suite
├── docker-compose.yml
├── Dockerfile
├── gunicorn.conf.py
├── entrypoint.sh
├── requirements.txt
└── .env.example
All endpoints require an authenticated session. Responses are JSON.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/dashboard-data | Summary of tasks, reminders, events, and notes |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/tasks | List tasks; supports view, priority, and search query params |
view options: today, upcoming, overdue, completed, all
priority options: low, medium, high
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/reminders/due | List currently due or snoozed reminders |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/quick-capture | Create a task, note, reminder, or event from JSON |
Example request body:
{
"type": "task",
"title": "Review pull request",
"priority": "high",
"due_at": "2025-01-15T17:00:00"
}
HelmHub supports browser-compatible bookmark import and export using the Netscape bookmark HTML format (the same format exported by Chrome, Firefox, Edge, and many other browsers).
helmhub-bookmarks.html.http, https, ftp) are imported; invalid or unsafe entries are skipped.SESSION_COOKIE_SECURE=True for HTTPS deployments# Install dependencies (if not already done)
pip install -r requirements.txt
# Run the full test suite
pytest tests/
# Verbose output
pytest tests/ -v
# Run a specific test file
pytest tests/test_bookmarks.py
pytest tests/test_tasks.py
Tests use an in-memory SQLite database with CSRF and rate limiting disabled. No external services are required.
| Module | Test file | Areas covered |
|---|---|---|
| Auth | tests/test_auth.py | Login, logout, TOTP 2FA, recovery codes |
| Dashboard | tests/test_dashboard.py | Page render, widget data, API endpoint |
| Tasks | tests/test_tasks.py | CRUD, completion toggle, pin, filtering |
| Notes | tests/test_notes.py | CRUD, pin, search, scratchpad |
| Bookmarks | tests/test_bookmarks.py | Model properties, CRUD, pin toggle, search/filter, HTMX responses |
HelmHub is a Progressive Web App and can be installed as a standalone application on desktop and mobile.
The service worker caches static assets for offline access and uses a network-first strategy for HTML and API responses.
If running behind nginx or a similar proxy, set SESSION_COOKIE_SECURE=True and ensure the proxy forwards X-Forwarded-For and X-Forwarded-Proto headers so Flask can determine the correct scheme for CSRF and session security.
Migrations run automatically via entrypoint.sh when the Docker container starts. For manual deployments, run:
flask db upgrade
To create a new migration after changing models:
flask db migrate -m "describe the change"
flask db upgrade
Default settings (gunicorn.conf.py):
Content type
Image
Digest
sha256:7448e0a14…
Size
171.8 MB
Last updated
6 months ago
docker pull whahn1983/helmhub