Grylli is a secure, self-hosted message delivery platform that automatically sends pre-configured notifications if a user fails to check in within a defined time period.
- Self-hosted Flask web application with user authentication
- Responsive UI built with Tailwind CSS
- Frontend interactivity powered by Stimulus.js for minimal, CSP-compliant JavaScript
- Light and dark mode support
- Local timezone support
- Multilingual interface
- Automatic translation support using GPT-PO Translator for rapid language expansion
- Status endpoint for healthcheck and version
- Integrated version check with GitHub release comparison with automated scheduler updates
- Language selection toggle with per-user preference and locale-aware interface
- Fixed sidebar and header layout with scrollable main content for consistent UX
- Extensive internal logging for traceable execution and graceful failure handling
- Detects and applies browser language on first visit before user login
- Context-sensitive help panels for each major module
- Shared UI components and actions for consistent interactions across modules
- Progressive Web App (PWA) support with installable manifest, service worker registration, offline metadata caching, and home screen integration on mobile and desktop
- Seamless Navigation with HTMX. All sidebar and profile dropdown links now use HTMX for dynamic navigation. Prevents full-page reloads and avoids sidebar collapse/reflow
- Grylli has undergone a thorough accessibility audit using pa11y and manual contrast verification.
- Main pages (login, signup, dashboard, reminders, emails, messages, settings) have been remediated for WCAG 2.1 AA compliance
- All forms include semantic labels, ARIA feedback, and keyboard-accessible controls
- High-contrast themes and visible focus styles are built-in
- No inline JavaScript or event handlers (CSP-compliant)
- No CAPTCHAs or visual-only barriers; public routes are rate-limited instead
β οΈ Minor, non-blocking color contrast issues (e.g. emoji/icons) are acknowledged
- Configurable notification and check-in system
- Send notifications via apprise Apprise destinations
- Execute webhooks when notification grace period for checkin has expired
- Customizable emails with optional attachments
- Attach multiple files to emails
- Files are loaded from disk at send time (edit outside Grylli)
- Create reminders with labels, subjects, and rich scheduling options
- Assign email, webhook, and Apprise destinations to each reminder
- Optional test-send for validation of all linked services
- Schedule single or recurring reminders (daily, weekly, monthly, etc.)
- Toggle reminders on/off dynamically from the UI
- Multi-admin and multi-user support
- Role-based access control (RBAC) with user/admin privileges
- MFA using TOTP apps (e.g. Google Authenticator) with recovery codes
- MFA reset and recovery options for both users and admins
- Admin protection from self-demotion and critical privilege changes
- Sign-up with registration code
- Forgot username and password recovery flows
- User actions to export their data and delete their account
- Global SMTP settings for system-level notifications
- User-specific SMTP settings for personalized delivery
- Sensitive credentials (e.g., SMTP passwords, Apprise tokens) are encrypted at rest using Fernet symmetric encryption
- Fully Content Security Policy (CSP) compliant: dynamic nonces, no inline scripts or handlers, no
.innerHTML
- Frontend entirely refactored to use Stimulus.js controllers instead of Alpine.js or inline JavaScript
- Password and token reveal functionality is CSP-safe with strict event handling
- Admin routes are tightly permission-controlled with automatic role enforcement
- All public forms and inputs validated server-side using secure WTForms
- App-level logging captures all sensitive operations, errors, and admin events without exposing secrets
- Enforces complex passwords
- Runs as non-root using PUID/PGID
- Reverse proxy ready (
base_url
support) - Runs in a minimal distroless container for production, reducing attack surface and image size
- Python sources are precompiled to
.pyc
files for faster startup and to reduce accidental code exposure in the image - Rate limiting on failed logins and sign up
- Additional HTTP security headers: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, frame-ancestors: 'none' to mitigate common web vulnerabilities
- Account lockout enforced after repeated failed login attempts, with automatic unlock after a delay
- Modular view architecture using partials for cleaner maintenance and CSP compliance
- CAPTCHA-free signup flow with soft rate limiting for better UX and accessibility
- Grylli is designed with privacy and control in mind.
- Users retain full control over their check-in schedules, messages, and delivery methods.
- All sensitive user data β including email passwords, Apprise tokens, and webhook URLs β is encrypted before being stored.
- Administrators cannot view stored credentials or plaintext tokens.
- No external telemetry, analytics, or phone-home behavior is present.
- Users can export or delete their data at any time.
- Passwords and secrets encrypted at rest
- CSP-compliant templates and JavaScript (no inline scripts or handlers)
- Rate-limited login, signup, and reset flows
- MFA with TOTP and recovery support
- Role-based route protections (admin vs. user)
- Admin safeguards (no self-demotion)
- Secure form validation with CSRF and ARIA feedback
- Optional backup and deletion workflows
- Automatic account lockout after repeated failed logins
- CSP Violation Reporting: Server now captures and logs CSP violations
Grylli has been tested with Google Lighthouse across all major modules, achieving:
Metric | Average Score |
---|---|
Performance | 96 |
Accessibility | 99 |
Best Practices | 93 |
SEO | 100 |
All primary user-facing and administrative pages meet or exceed Lighthouse guidelines.
Installability and offline support were validated using Lighthouse PWA audits.
β Minor performance warnings on public login and signup pages are acknowledged (e.g. layout shift, unauthenticated CSS/JS) and do not affect core UX.
Grylli has a comprehensive test suite covering the platform's core features, ensuring the stability, security, and functionality of all workflows.
Module | Coverage |
---|---|
Auth Routes | 65% |
Admin Panels | 68% |
Reminder Logic | 83% |
Email Workflows | 68% |
Apprise & Webhooks | 76% |
Backup & Restore | 78% |
MFA Setup & Reset | 80% |
Account Management | 74% |
CSP Compliance | 100% |
Public Pages | 100% |
Version Metadata | 100% |
Route Permissions | 100% |
Total Coverage: 78% (4017 statements, 893 currently not covered)
- Written using pytest
- Uses SQLAlchemy 2.x-style
db.session.get()
methods - Mocks all external calls (email, encryption, login state) for safe, fast test runs
- Enforces role-based access control through simulated admin/user scenarios
- Coverage includes flash messages, status codes, and failure paths
- Increase coverage for email workflows, scheduler logic, and admin panels to ensure complete reliability across the application.
- Expand tests for webhooks and Apprise notifications, which are vital for communication flows.
- Automated daily database backups (7-day retention)
- On-demand backup option
If you prefer not to use Docker Compose, you can run Grylli with a single command:
Show Docker Run Command
docker run -d \
--name grylli \
-p 5069:5069 \
-v $(pwd)/grylli/data:/data \
-v $(pwd)/grylli/uploads:/uploads \
-e TZ=America/Chicago \
-e PUID=1000 \
-e PGID=1000 \
-e GRYLLI_DATA_DIR=/data \
-e DEBUG=False \
-e FQDN=http://your.domain.com:5069 \
-e BASE_URL=/grylli \
-e FLASK_APP_PORT=5069 \
-e FLASK_APP_KEY=changeme-supersecret-key \
-e FERRET_KEY=changeme-fernet-key \
-e SIGNUP_CODE=YourSuperSecretCode123! \
-e DEFAULT_LANGUAGE=en \
-e SMTP_HOST=smtp.example.com \
-e SMTP_PORT=587 \
-e SMTP_USE_TLS=1 \
-e EMAIL_FROM=you@example.com \
-e SMTP_USER=you@example.com \
-e SMTP_PASS=your_password_or_app_token \
--restart unless-stopped \
ghcr.io/samcro1967/grylli
Show Environment Variables
Variable | Description | Example/Notes |
---|---|---|
TZ |
Timezone for the container | America/Chicago |
PUID |
User ID for container process (for volume permissions) | 1000 |
PGID |
Group ID for container process | 1000 |
GRYLLI_DATA_DIR |
Directory for persistent data inside the container | /data |
DEBUG |
Enable or disable debug mode | False (use True for debugging) |
FQDN |
Public base URL of your Grylli instance | http://your.domain.com:5069 |
BASE_URL |
Base URL path for Grylli (use /grylli or / ) |
/grylli |
FLASK_APP_PORT |
Port Grylli listens on inside the container | 5069 |
FLASK_APP_KEY |
Secret key for Flask session security | (generate a secure random string) |
FERRET_KEY |
Encryption key for sensitive data (Fernet, 32-byte base64 string) | (generate with Fernet) |
SIGNUP_CODE |
Registration code required for new sign-ups | YourSuperSecretCode123! |
DEFAULT_LANGUAGE |
Default language code | en |
SMTP_HOST |
SMTP server hostname | smtp.example.com |
SMTP_PORT |
SMTP server port | 587 (for TLS), 465 (for SSL) |
SMTP_USE_TLS |
Use TLS for SMTP connection (1 for yes, 0 for no) | 1 |
EMAIL_FROM |
Default sender email address | you@example.com |
SMTP_USER |
SMTP authentication username | you@example.com |
SMTP_PASS |
SMTP authentication password or app token | your_password_or_app_token |
Note: See
docker-compose.sample.yml
for instructions on how to generate your ownFLASK_APP_KEY
andFERRET_KEY
.
- Flask β Python web framework
- Flask-WTF β Secure web forms with CSRF protection
- Flask-Login β User session and authentication management
- Flask-Migrate β Database migrations powered by Alembic
- Flask-Babel β Internationalization (i18n) and localization
- APScheduler β Advanced Python scheduling
- email-validator β Email address validation
- cryptography β Secure encryption for sensitive data at rest
- PyOTP β Time-based one-time passwords for MFA
- python-dateutil β Advanced datetime parsing and timezone handling
- polib β PO file management for localization workflows
- GPT-PO Translator β Automated PO file translation with GPT
- Stimulus β Lightweight JavaScript framework for CSP-compliant interactivity
- HTMX β Dynamic HTML-over-the-wire interactivity without custom JavaScript
- Tailwind CSS β Utility-first CSS framework for responsive, accessible UI
- PostCSS β CSS transformation engine used in Tailwindβs build pipeline
- Autoprefixer β Adds vendor prefixes to CSS rules automatically
- Apprise β Notification delivery to dozens of services
- htmx-extensions: safe-nonce β HTMX extension for CSP nonce propagation
- Critical β Extracts and inlines critical-path CSS for faster first paint
- Pa11y β Automated accessibility testing and validation
- gunicorn β Production Python WSGI server
- Docker β Containerized application deployment (distroless)
- Clean-CSS CLI β CSS minifier for optimized production output
- Terser β JavaScript minifier used in production builds
- pytest β Python test framework
- pytest-cov β Coverage reporting plugin for pytest
- black β Opinionated Python code formatter
- isort β Python import sorter
- pylint β Static code analysis for Python
- djlint β Linter and formatter for Jinja2 templates
- Lighthouse β Performance, accessibility, and SEO auditing for web apps
- OWASP ZAP β Automated security scanning and penetration testing
Special thanks to these libraries and their maintainers for powering Grylli.
- webhook (adnanh/webhook) β Simple webhook server