- JavaScript 46.8%
- Go 27.2%
- CSS 13%
- HTML 12.6%
- Dockerfile 0.4%
|
|
||
|---|---|---|
| static | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| init.sql | ||
| LICENSE | ||
| main.go | ||
| README.md | ||
DayLine 📱
Zero‑knowledge medicine & mood tracker – Your health data stays encrypted in your browser. The server never sees plaintext.
DayLine is a privacy-first health tracking application that puts you in complete control of your data through end-to-end encryption. Track your medicine intake, mood patterns, and health insights without compromising privacy.
✨ Features
- 🔐 End‑to‑end encryption – All entries are encrypted with AES‑GCM before leaving your browser. Only you hold the master key. The server is completely blind to your data.
- 💊 Medicine logging – Track doses, timing, side effects, notes, and bookmark frequently used medicines for quick logging.
- 😊 Mood tracking – Quick 5‑point mood logging (😡😢😐😴😊) with visual indicators and time-series data.
- 📊 Visual insights – Medicine intake frequency charts and average mood charts with flexible time ranges (24h, 7d, 28d, 12m).
- 📅 Year in colours – GitHub-style heatmap view of your mood throughout the year to see patterns at a glance.
- 🔑 Recovery passphrase – Optional secondary encryption key to restore access if you forget your password. You control the recovery data.
- 🔐 Password change – Securely change your password while re-encrypting your master key without losing any data.
- 👥 Admin controls – Enable/disable public sign‑ups and manage registered users.
- 🐳 Docker‑ready – Easy deployment with Docker Compose for home servers or cloud platforms.
- 📱 Responsive design – Works seamlessly on desktop, tablet, and mobile devices.
🏗️ Tech Stack
| Component | Technology |
|---|---|
| Frontend | HTML5, CSS3 (Material Design 3), Vanilla JS |
| Backend | Go 1.22 |
| Database | MariaDB 11 |
| Encryption | Web Crypto API (AES‑GCM, PBKDF2) |
| Deployment | Docker + Docker Compose |
🚀 Getting Started
Quick Start with Docker (Recommended)
1. Clone the repository
git clone https://github.com/your-username/dayline.git
cd dayline
2. Configure environment (optional)
Copy the example environment file and customize as needed:
cp .env.example .env
Then edit .env to customize settings (all variables are optional with sensible defaults):
DAYLINE_PORT=8080 # Port to access the app
DB_HOST=db # Database hostname
DB_USER=dayline # Database username
DB_PASS=your_strong_password # Database password (change in production!)
DB_ROOT_PASS=root_password # MariaDB root password (change in production!)
FORCE_SECURE=false # Set to true when using HTTPS
See .env.example for all available configuration options and detailed explanations.
3. Run with Docker Compose
docker compose up -d
The app will be available at http://localhost:8080 (or the port you configured).
4. First run
- The first user to register automatically becomes the admin.
- After that, public sign‑ups are disabled by default. The admin can enable them in Settings → Admin.
Manual Setup (Without Docker)
Requirements
- Go 1.22+
- MariaDB 10.5+ (or MySQL 8.0+)
Steps
-
Create a database and user:
CREATE DATABASE dayline CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'dayline'@'%' IDENTIFIED BY 'your_strong_password'; GRANT ALL PRIVILEGES ON dayline.* TO 'dayline'@'%'; FLUSH PRIVILEGES; -
Import the schema:
mysql -u root -p dayline < init.sql -
Set environment variables:
export DB_USER=dayline export DB_PASS=your_strong_password export DB_HOST=localhost export DB_PORT=3306 export DB_NAME=dayline -
Build and run:
go mod tidy go build -o dayline . ./daylineThe server will listen on
:8080by default.
🔐 How Encryption Works
DayLine implements a zero-knowledge architecture where the server never has access to your plaintext data:
┌─────────────────────────────────────────────────────────────┐
│ Your Browser (Client-Side) │
│ • Generate random 256-bit master key │
│ • Encrypt master key with password (PBKDF2) │
│ • Encrypt all entries with master key (AES-GCM) │
│ • Decrypt locally before displaying │
└─────────────────────────────────────────────────────────────┘
│
[HTTPS only]
↓
┌─────────────────────────────────────────────────────────────┐
│ Server (Blind Storage) │
│ • Store encrypted master key (wrapped with password) │
│ • Store encrypted entries (ciphertext + IV) │
│ • NO plaintext data EVER visible │
│ • Cannot decrypt even if compromised │
└─────────────────────────────────────────────────────────────┘
Detailed Flow
-
Registration:
- Generate random 256-bit master key in browser
- Derive encryption key from password using PBKDF2 (600k iterations, SHA-256)
- Encrypt master key with password-derived key
- Send encrypted master key to server
- Server stores encrypted master key, never sees plaintext
-
Login:
- Server returns encrypted master key
- Browser decrypts with password-derived key
- Master key loaded into memory (never persisted)
- All user data can now be decrypted locally
-
Adding Entry:
- Entry (medicine/mood) encrypted locally with master key
- Ciphertext + IV sent to server
- Server stores as-is, cannot decrypt
-
Recovery Passphrase:
- Raw master key encrypted with recovery passphrase
- Stored on server separately from password-derived encryption
- If you forget password: use recovery passphrase to recover raw master key
- Set new password, re-encrypt master key with new password
- All data remains accessible (no data loss)
Security Properties
- Forward secrecy: Even if server is compromised, encrypted data cannot be decrypted without your password
- No password vulnerability: Password only used locally; never transmitted
- PBKDF2 strengthening: 600,000 iterations slow down brute-force attacks (≈100ms per attempt)
- Authenticated encryption: AES-GCM includes authentication (detects tampering)
- Unique IVs: Every encryption uses a random IV, preventing pattern analysis
📋 API Overview
Most endpoints require a valid session cookie. All request/response bodies are JSON.
Health Check
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/health |
GET | No | Health check endpoint (for orchestration) |
Authentication
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/register |
POST | No | Create account with encrypted master key |
/api/login |
POST | No | Authenticate, receive encrypted master key |
/api/logout |
POST | Yes | Destroy session |
/api/me |
GET | Yes | Get current user info + recovery hint |
Password Management
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/users/me/password |
POST | Yes | Change password (verify old, update hash) |
/api/users/me/masterkey |
PUT | Yes | Update encrypted master key (password recovery) |
Health Data
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/entries |
GET | Yes | List encrypted entries (filter by type, date range) |
/api/entries |
POST | Yes | Add new encrypted entry |
/api/entries/{id} |
DELETE | Yes | Delete an entry |
/api/medicines |
GET | Yes | List saved medicine templates |
/api/medicines |
POST | Yes | Save a medicine template |
/api/medicines/{id} |
DELETE | Yes | Remove a saved medicine |
Recovery
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/recovery/setup |
POST | Yes | Store encrypted recovery blob + hint |
/api/recovery/recover |
POST | No | Retrieve recovery blob for username |
Admin (Admin-only)
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/admin/settings |
GET | Yes | Get public signup setting |
/api/admin/settings |
PUT | Yes | Update signup setting |
/api/admin/users |
GET | Yes | List all registered users |
🔐 Security Considerations
HTTPS Requirement ⚠️
Always use HTTPS in production. DayLine sets the Secure flag on session cookies only over HTTPS. Without HTTPS:
- Session cookies can be intercepted
- Passwords transmitted in plaintext
- Encryption keys vulnerable to man-in-the-middle attacks
To enable HTTPS:
- Use a reverse proxy (Nginx, Caddy, Traefik)
- Obtain SSL/TLS certificates (Let's Encrypt is free)
- Configure proxy to terminate TLS and forward traffic to DayLine
Example with Caddy:
example.com {
reverse_proxy localhost:8080
}
Cookies Behind Reverse Proxy
If DayLine is behind an HTTPS-terminating reverse proxy (e.g., Nginx, Caddy), the application may not detect TLS and cookies won't have the Secure flag. Set FORCE_SECURE=true in environment variables to force the Secure flag:
export FORCE_SECURE=true
docker compose up -d # or ./dayline if running manually
Session Invalidation
Password Change: When you change your password, all existing sessions for your account are invalidated and rotated to a new one. This ensures old sessions (even on other devices) are invalidated.
Account Recovery: When recovering your account using the recovery passphrase, all existing sessions are deleted. You must log in again with your new password.
Manual Session Cleanup (if needed): To manually invalidate all sessions for a user, run:
DELETE FROM sessions WHERE user_id = <user_id>;
Or to clear all sessions for all users:
DELETE FROM sessions;
Rate Limiting
Sensitive endpoints are rate-limited to 10 attempts per 5 minutes per IP:
/api/login/api/register/api/recovery/recover
This prevents brute-force attacks on passwords and recovery passphrases.
Password Requirements
- Minimum 8 characters (enforced by frontend)
- Recommended: 12+ characters with mixed case, numbers, symbols
- Avoid common passwords
Recovery Passphrase
- Use a unique, strong passphrase (different from your login password)
- Write it down and store securely (not digitally)
- Store the hint somewhere you'll remember (but not the passphrase itself)
- Recovery only works if you set up the recovery feature before forgetting password
Session Security
- Sessions expire after 30 days
- Sessions stored server-side with random tokens
- HttpOnly flag prevents JavaScript access
- SameSite=Strict prevents CSRF attacks
- Secure flag prevents transmission over HTTP
What the Server Cannot Do
Even if the server is fully compromised by an attacker:
- Cannot read your medicine or mood entries
- Cannot recover your master key
- Cannot decrypt recovery passphrase data
- Cannot change your password without your permission
- Cannot see plaintext health data
- Cannot use your data to train AI models
🔄 Password Recovery Flow
If you forget your password:
- Click "Recover account" on login screen
- Enter your username
- See the recovery hint you set (if any)
- Enter your recovery passphrase
- Set a new password
- DayLine will:
- Decrypt your master key using recovery passphrase
- Re-encrypt master key with new password
- Update encrypted master key on server
- Log you in automatically with new password
- All your data remains encrypted and accessible!
Important: Recovery only works if you set up recovery passphrase before forgetting your password.
🔑 Changing Your Password
You can change your password anytime without recovery:
- Go to Settings → Change password
- Enter your current password
- Enter your new password (min 8 chars)
- DayLine will:
- Verify your current password
- Re-encrypt master key with new password
- Update server
- All data remains intact and accessible
🔨 Environment Variables
All configuration is managed through environment variables. For Docker, copy .env.example to .env and customize as needed.
| Variable | Default | Description |
|---|---|---|
DAYLINE_PORT |
8080 |
Port where the app is accessible |
DB_HOST |
db |
Database hostname (or localhost) |
DB_PORT |
3306 |
Database port |
DB_NAME |
dayline |
Database name |
DB_USER |
dayline |
Database username |
DB_PASS |
changeme |
Database password (change in production!) |
DB_ROOT_PASS |
rootchangeme |
MariaDB root password (change in production!) |
FORCE_SECURE |
false |
Force Secure flag on cookies. Set to true when behind HTTPS reverse proxy |
MARIADB_CHARSET |
utf8mb4 |
Database character set |
MARIADB_COLLATION |
utf8mb4_unicode_ci |
Database collation |
Note: All variables have sensible defaults and are optional. Only customize if you need non-default values.
Important: For HTTPS behind reverse proxy (Nginx, Caddy, etc.), set FORCE_SECURE=true to enable the Secure flag on cookies.
For Docker setup, copy .env.example to .env and modify as needed.
For manual setup, set these as environment variables before running the application.
🐛 Troubleshooting
"user not found" on login
- Check username spelling
- Make sure you registered first
- Admin may have disabled public signups (reset via database)
"recovery not available"
- Recovery feature not set up for this account
- Set up recovery from Settings before forgetting password
"too many requests"
- Rate limiting active (10 requests per 5 minutes)
- Wait a few minutes and try again
Cookies not working behind HTTPS proxy
- Symptoms: Session doesn't persist, logged out after page refresh
- Solution: Set
FORCE_SECURE=truein environment variables - This forces the Secure flag on cookies when behind HTTPS reverse proxy
- Example:
FORCE_SECURE=true docker compose up -d
Database connection fails
- Check DB_HOST, DB_PORT, DB_USER, DB_PASS
- Ensure MariaDB/MySQL is running
- Check firewall rules
HTTPS not working
- Verify proxy is configured correctly
- Check SSL/TLS certificate validity
- Ensure proxy forwards to correct DayLine port
📊 Data Privacy
- No tracking: No analytics, no telemetry, no cookies for tracking
- No third-party services: All data stays on your server
- Transparent: Open source code, audit-friendly
- Your control: You own your data and can export/delete anytime
- No backups on us: We never backup your encrypted data
🚧 Future Improvements
Potential enhancements (not yet implemented):
- Data export: Export encrypted data for archival or migration
- Sync multiple devices: Encrypted device-to-device sync
- Medication interactions: Check drug interaction warnings
- Time-based reminders: Push notifications for medicine timing
- Symptom tracking: Connect mood to symptoms for pattern analysis
- Doctor sharing: Securely share summaries with healthcare providers
- 2FA: TOTP-based second factor authentication
- Offline mode: Use service worker for offline data access
- Advanced charts: ML-based mood prediction, trend analysis
📝 Contributing
Pull requests welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes with clear messages
- Push to branch (
git push origin feature/amazing-feature) - Open a pull request
For major changes, please open an issue first to discuss.
📄 License
Creative Commons CC0 1.0: No legal advice; no warranties; use at own risk.
Owners can waive rights to freely share, remix, and reuse works worldwide without claiming infringement.
Applying CC0 means surrendering all copyright and related rights permanently, globally, for any purpose.
If invalid, a fallback licence grants the public a free, unlimited right to use the work.
No trademark, patent rights waived; work is provided as-is; no liability or rights clearance.
Questions or issues? Open an issue on GitHub or review the code – it's open source for transparency!
Privacy first, always. 🔒