Update README changelog, remove Firebug IT branding, retire dead mobile middleware

README.md: backfill the Changelog with the 2026.9.5, 2026.8.31.1, 2026.8.31,
and v1.2.0 releases that were missing (it jumped straight from 1.0.0 to
1.1.0). Also correct the stale "Mobile App Support"/"Mobile App
Integration" sections - the native Android app is retired, and the
sync protocol they describe now powers the offline PWA instead.

Remove Firebug IT branding/contact info across README.md, API.md, and
the site footer, and drop the stale tasks.firebugit.com fallback from
development.py's ALLOWED_HOSTS/CSRF_TRUSTED_ORIGINS.

AllowMobileAppFramingMiddleware detected the native app via a
'com.firebugit.keepitgoing' User-Agent check to allow WebView iframe
embedding. With that app retired, replaced it with
SecurityHeadersMiddleware, which applies the same X-Frame-Options/CSP
headers unconditionally instead of only for non-mobile requests -
same protection for real users, dead branch and dead branding gone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Keith Smith
2026-09-05 14:12:39 -06:00
co-authored by Claude Sonnet 5
parent 6791f008e9
commit f41939983a
8 changed files with 113 additions and 97 deletions
+2 -3
View File
@@ -1182,10 +1182,9 @@ Current version: 1.0 (no version prefix in URLs)
For API support or questions: For API support or questions:
- GitHub Issues: Create an issue in the repository - GitHub Issues: Create an issue in the repository
- Email: keith@firebugit.com - Email: keithsmith@darksingularity.org
--- ---
**Built by Firebug IT**
**Documentation Version: 1.0** **Documentation Version: 1.0**
**Last Updated: 2025-01-10** **Last Updated: 2026-09-05**
+70 -27
View File
@@ -39,7 +39,7 @@ A powerful Django-based task management system with time tracking, tag organizat
### API & Sync ### API & Sync
- **RESTful API**: Full REST API for programmatic access - **RESTful API**: Full REST API for programmatic access
- **Mobile App Support**: Sync protocol for Android app - **Offline-First Sync Protocol**: Bidirectional sync with conflict resolution, powering the offline PWA
- **Real-time Updates**: Task changes sync across devices - **Real-time Updates**: Task changes sync across devices
- **Conflict Resolution**: Handles offline changes and syncing - **Conflict Resolution**: Handles offline changes and syncing
@@ -72,7 +72,7 @@ A powerful Django-based task management system with time tracking, tag organizat
1. **Clone the repository** 1. **Clone the repository**
```bash ```bash
git clone https://git.firebugit.com/Firebug_IT/KeepItGoingServer.git git clone https://git.darksingularity.org/DarkSingularity/KeepItGoingServer.git
cd KeepItGoingServer cd KeepItGoingServer
``` ```
@@ -423,9 +423,9 @@ Key environment variables (see `.env.example`):
#### Core Settings #### Core Settings
- `SECRET_KEY` - Django secret key (required in production, generate with `python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"`) - `SECRET_KEY` - Django secret key (required in production, generate with `python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"`)
- `DEBUG` - Enable debug mode (True/False, must be False in production) - `DEBUG` - Enable debug mode (True/False, must be False in production)
- `ALLOWED_HOSTS` - Comma-separated list of allowed hosts (e.g., `tasks.firebugit.com,localhost`) - `ALLOWED_HOSTS` - Comma-separated list of allowed hosts (e.g., `tasks.darksingularity.org,localhost`)
- `CSRF_TRUSTED_ORIGINS` - HTTPS origins for CSRF (e.g., `https://tasks.firebugit.com`) - `CSRF_TRUSTED_ORIGINS` - HTTPS origins for CSRF (e.g., `https://tasks.darksingularity.org`)
- `SITE_DOMAIN` - Domain name for email links (e.g., `tasks.firebugit.com`) - `SITE_DOMAIN` - Domain name for email links (e.g., `tasks.darksingularity.org`)
#### Database #### Database
- `DATABASE_URL` - Database connection string (optional, overrides settings) - `DATABASE_URL` - Database connection string (optional, overrides settings)
@@ -441,7 +441,7 @@ Key environment variables (see `.env.example`):
- `EMAIL_USE_SSL` - Use SSL (True/False, use True for port 465) - `EMAIL_USE_SSL` - Use SSL (True/False, use True for port 465)
- `EMAIL_HOST_USER` - SMTP username/email - `EMAIL_HOST_USER` - SMTP username/email
- `EMAIL_HOST_PASSWORD` - SMTP password or API key - `EMAIL_HOST_PASSWORD` - SMTP password or API key
- `DEFAULT_FROM_EMAIL` - From email address (e.g., `KeepItGoing <noreply@firebugit.com>`) - `DEFAULT_FROM_EMAIL` - From email address (e.g., `KeepItGoing <noreply@darksingularity.org>`)
- `SERVER_EMAIL` - Server error email address - `SERVER_EMAIL` - Server error email address
#### Email Verification Settings #### Email Verification Settings
@@ -642,7 +642,7 @@ sudo useradd -m -s /bin/bash keepitgoing
sudo su - keepitgoing sudo su - keepitgoing
# Clone repository # Clone repository
git clone https://git.firebugit.com/Firebug_IT/KeepItGoingServer.git git clone https://git.darksingularity.org/DarkSingularity/KeepItGoingServer.git
cd KeepItGoingServer cd KeepItGoingServer
# Create virtual environment # Create virtual environment
@@ -665,9 +665,9 @@ cat > .env << 'EOF'
# Django Settings # Django Settings
SECRET_KEY=GENERATE_WITH_get_random_secret_key SECRET_KEY=GENERATE_WITH_get_random_secret_key
DEBUG=False DEBUG=False
ALLOWED_HOSTS=tasks.firebugit.com,localhost,127.0.0.1 ALLOWED_HOSTS=tasks.darksingularity.org,localhost,127.0.0.1
CSRF_TRUSTED_ORIGINS=https://tasks.firebugit.com CSRF_TRUSTED_ORIGINS=https://tasks.darksingularity.org
SITE_DOMAIN=tasks.firebugit.com SITE_DOMAIN=tasks.darksingularity.org
# Database Configuration # Database Configuration
# Choose ONE of the following based on your database from Step 2: # Choose ONE of the following based on your database from Step 2:
@@ -688,8 +688,8 @@ EMAIL_PORT=587
EMAIL_USE_TLS=True EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password EMAIL_HOST_PASSWORD=your-app-password
DEFAULT_FROM_EMAIL=KeepItGoing <noreply@firebugit.com> DEFAULT_FROM_EMAIL=KeepItGoing <noreply@darksingularity.org>
SERVER_EMAIL=server@firebugit.com SERVER_EMAIL=server@darksingularity.org
# Email Verification # Email Verification
EMAIL_VERIFICATION_TOKEN_EXPIRY_HOURS=24 EMAIL_VERIFICATION_TOKEN_EXPIRY_HOURS=24
@@ -919,7 +919,7 @@ upstream keepitgoing {
server { server {
listen 80; listen 80;
server_name tasks.firebugit.com; server_name tasks.darksingularity.org;
# Redirect HTTP to HTTPS # Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri; return 301 https://$server_name$request_uri;
@@ -927,11 +927,11 @@ server {
server { server {
listen 443 ssl http2; listen 443 ssl http2;
server_name tasks.firebugit.com; server_name tasks.darksingularity.org;
# SSL Configuration # SSL Configuration
ssl_certificate /etc/letsencrypt/live/tasks.firebugit.com/fullchain.pem; ssl_certificate /etc/letsencrypt/live/tasks.darksingularity.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tasks.firebugit.com/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/tasks.darksingularity.org/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3; ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5; ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on; ssl_prefer_server_ciphers on;
@@ -985,7 +985,7 @@ sudo systemctl restart nginx
```bash ```bash
# Obtain SSL certificate # Obtain SSL certificate
sudo certbot --nginx -d tasks.firebugit.com sudo certbot --nginx -d tasks.darksingularity.org
# Certbot will automatically configure nginx # Certbot will automatically configure nginx
# Certificate auto-renews via cron # Certificate auto-renews via cron
@@ -1004,7 +1004,7 @@ sudo ufw status
#### Step 10: Initial Admin User and Approval #### Step 10: Initial Admin User and Approval
```bash ```bash
# Log in to the web interface at https://tasks.firebugit.com/admin # Log in to the web interface at https://tasks.darksingularity.org/admin
# Use the superuser credentials created in Step 3 # Use the superuser credentials created in Step 3
# If ALLOW_SELF_REGISTRATION=True and new users register: # If ALLOW_SELF_REGISTRATION=True and new users register:
@@ -1115,7 +1115,7 @@ from django.core.mail import send_mail
send_mail( send_mail(
'Test Subject', 'Test Subject',
'Test message.', 'Test message.',
'noreply@firebugit.com', 'noreply@darksingularity.org',
['your-email@example.com'], ['your-email@example.com'],
) )
# Check for any errors # Check for any errors
@@ -1198,9 +1198,9 @@ sudo tail -f /var/log/nginx/access.log
sudo journalctl -u keepitgoing -f sudo journalctl -u keepitgoing -f
``` ```
## Mobile App Integration ## Offline-First Sync
This server works with the KeepItGoing Android app via the sync endpoint. The sync endpoint (`/api/sync/`) powers the offline PWA's background sync. It was originally built for a native Android app, which has since been retired in favor of the PWA, but the protocol itself is unchanged.
**Sync Protocol:** **Sync Protocol:**
- Client sends current state and last sync timestamp - Client sends current state and last sync timestamp
@@ -1219,16 +1219,63 @@ This server works with the KeepItGoing Android app via the sync endpoint.
## License ## License
Proprietary - All Rights Reserved Proprietary - All Rights Reserved
Copyright (c) 2025 Firebug IT Copyright (c) 2026 Keith Smith
## Support ## Support
For issues and questions: For issues and questions:
- Create an issue in the repository - Create an issue in the repository
- Email: keith@firebugit.com - Email: keithsmith@darksingularity.org
## Changelog ## Changelog
### 2026.9.5
**New Features:**
- Installable Progressive Web App (PWA): app manifest, service worker, and offline fallback pages
- Web Push notifications
- Full offline task management: create, edit, complete tasks, manage subtasks and tags, and track time while offline, with automatic sync once back online
- Per-task reminder notifications - "before due", "due now", and "overdue" - delivered by email and/or push, timed per task and configurable via the profile's Default Reminder setting
**Bug Fixes:**
- Overdue status now accounts for due time, not just due date
- Background sync now runs on every page load instead of being throttled, so the offline cache can no longer serve stale data (e.g. a tag deleted online reappearing offline)
- Offline mode now matches the online dashboard: correct subtask nesting, tag sidebar, overdue styling, and full task editing/tags/time tracking
- Fixed a crash when editing a task's due date/time
- Service worker precache now reliably picks up updated files after a deploy instead of a stale cached copy
- "Overdue" and "due now" notifications no longer arrive at the same instant - overdue now waits an hour
- Changing the reminder-timing setting now retroactively updates reminders already scheduled on existing tasks
### 2026.8.31.1
- Custom recurrence patterns: recurring tasks can repeat on specific days of the week, an interval of weeks (e.g. every other Wednesday), a specific day of the month, or the Nth weekday of the month (e.g. every second Tuesday)
- New "Custom" recurrence builder in the task create/edit UI generates the underlying RRULE pattern automatically
### 2026.8.31
**New Features:**
- Notification system simplified to a single daily task-due email, sent within each user's local morning window
- Self-registration can now be enabled/disabled via configuration (disabled by default)
**Bug Fixes:**
- Task sort now defaults to due date instead of raw/unsorted ordering
- Priority sort now orders correctly (high to low / low to high)
- Recurring tasks no longer create duplicate instances on completion
- Daily email timing now correctly accounts for all user timezones
- Overdue calculation now uses the user's local timezone
- Fixed Celery worker/beat health check issues
**Infrastructure:**
- Docker Compose now builds from the git repository
- Replaced pytz with zoneinfo for timezone handling
### v1.2.0 - Recurring Tasks
- Fully functional recurring tasks (daily, weekly, biweekly, monthly, yearly)
- Automatic creation of next task instance on completion
- Recurrence end date support
- Hourly background job to ensure no missed recurrences
### Version 1.1.0 (2025-01-22) ### Version 1.1.0 (2025-01-22)
- **Email Verification System**: Users must verify email before logging in - **Email Verification System**: Users must verify email before logging in
- **Admin Approval Workflow**: Admins approve new users via Django admin - **Admin Approval Workflow**: Admins approve new users via Django admin
@@ -1249,7 +1296,3 @@ For issues and questions:
- Web interface with dark mode - Web interface with dark mode
- Responsive design - Responsive design
- RESTful API - RESTful API
---
Built with ❤️ by Firebug IT
+1 -1
View File
@@ -44,7 +44,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'tasks.middleware.AllowMobileAppFramingMiddleware', # Allow mobile app iframe embedding 'tasks.middleware.SecurityHeadersMiddleware',
] ]
ROOT_URLCONF = 'config.urls' ROOT_URLCONF = 'config.urls'
+2 -2
View File
@@ -20,10 +20,10 @@ if not SECRET_KEY:
DEBUG = True DEBUG = True
# Development-only hosts # Development-only hosts
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]', '10.0.2.2', '192.168.1.241', 'tasks.firebugit.com'] ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]', '10.0.2.2', '192.168.1.241']
# CSRF - for development testing only # CSRF - for development testing only
CSRF_TRUSTED_ORIGINS = os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',') if os.environ.get('CSRF_TRUSTED_ORIGINS') else ['https://tasks.firebugit.com'] CSRF_TRUSTED_ORIGINS = os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',') if os.environ.get('CSRF_TRUSTED_ORIGINS') else []
# Database - SQLite for development # Database - SQLite for development
DATABASES = { DATABASES = {
+2 -2
View File
@@ -1,3 +1,3 @@
from .mobile_app import AllowMobileAppFramingMiddleware from .security_headers import SecurityHeadersMiddleware
__all__ = ['AllowMobileAppFramingMiddleware'] __all__ = ['SecurityHeadersMiddleware']
-61
View File
@@ -1,61 +0,0 @@
"""
Middleware to allow iframe embedding for the KeepItGoing mobile app.
The mobile app uses Capacitor WebView which embeds the website in an iframe.
This middleware detects requests from the mobile app and removes both
X-Frame-Options and Content-Security-Policy frame-ancestors headers to allow
iframe embedding, while keeping clickjacking protection for regular web browsers.
"""
class AllowMobileAppFramingMiddleware:
"""
Remove frame-blocking headers for requests from KeepItGoing mobile app.
The mobile app uses a Capacitor WebView. We detect these requests via
User-Agent and remove X-Frame-Options and CSP frame-ancestors headers.
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
# Check if request is from KeepItGoing mobile app
user_agent = request.META.get('HTTP_USER_AGENT', '')
# Detect Capacitor/Android WebView patterns
is_mobile_app = (
'wv' in user_agent.lower() or # Android WebView
'CapacitorHttp' in user_agent or
'com.firebugit.keepitgoing' in user_agent or
('KeepItGoing' in user_agent and 'Mobile' in user_agent)
)
if is_mobile_app:
# Mobile app: Allow iframe embedding - don't add frame-blocking headers
# Remove any existing frame headers
if 'X-Frame-Options' in response:
del response['X-Frame-Options']
if 'Content-Security-Policy' in response:
del response['Content-Security-Policy']
else:
# Regular browsers: Add security headers for clickjacking protection
if 'X-Frame-Options' not in response:
response['X-Frame-Options'] = 'DENY'
if 'Content-Security-Policy' not in response:
response['Content-Security-Policy'] = (
"default-src 'self'; "
"script-src 'self' 'unsafe-inline'; "
"style-src 'self' 'unsafe-inline'; "
"img-src 'self' data: https:; "
"font-src 'self' data:; "
"connect-src 'self'; "
"frame-ancestors 'none'; "
"base-uri 'self'; "
"form-action 'self';"
)
return response
+35
View File
@@ -0,0 +1,35 @@
"""
Middleware that applies clickjacking protection headers to every response.
Previously also allowed iframe embedding for a native mobile app's WebView
(detected via User-Agent); that app has been retired in favor of the PWA,
so the headers are now applied unconditionally.
"""
class SecurityHeadersMiddleware:
"""Add X-Frame-Options and a CSP frame-ancestors policy to every response."""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if 'X-Frame-Options' not in response:
response['X-Frame-Options'] = 'DENY'
if 'Content-Security-Policy' not in response:
response['Content-Security-Policy'] = (
"default-src 'self'; "
"script-src 'self' 'unsafe-inline'; "
"style-src 'self' 'unsafe-inline'; "
"img-src 'self' data: https:; "
"font-src 'self' data:; "
"connect-src 'self'; "
"frame-ancestors 'none'; "
"base-uri 'self'; "
"form-action 'self';"
)
return response
+1 -1
View File
@@ -142,7 +142,7 @@
{% block auth_content %}{% endblock %} {% block auth_content %}{% endblock %}
</div> </div>
<footer class="footer"> <footer class="footer">
<p>&copy; 2025 Firebug IT. All rights reserved.</p> <p>&copy; 2026 Keith Smith. All rights reserved.</p>
</footer> </footer>
{% endif %} {% endif %}
{% endblock %} {% endblock %}