Skip to content

Latest commit

History

332 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

DailyNotes: Daily tasks and notes in Markdown


In Loving Memory of Joe Ipson

This project is dedicated to Joe Ipson, the original creator of DailyNotes, who passed away in the summer of 2025 after a courageous battle with cancer.

Joe was a kindred spirit who believed in the simple power of writing things down. He built DailyNotes to bring the mindful experience of a physical planner into the digital world. His vision was to create something personal, self-hosted, and beautifully simple.

This project continues in his memory. Every commit, every feature, every bug fix is a small tribute to a dear friend whose spirit lives on in the code he wrote and the ideas he shared.

Rest easy, Joe. We'll take it from here.


About

The idea for this app came from using my Hobonichi Techo planner every morning to write down what I needed to accomplish that day & using it for scratching down random thoughts and notes as the day went on. The closest thing I've seen to an app for replacing this system is Noteplan, but I don't use a Mac or an iOS device, and it's not self-hostable, so I decided to write my own.

To check your current version, open Settings in the app and look in the About section.

Since I had the need for keeping track of to-dos throughout the day, regular Markdown didn't work for me since it doesn't natively support tasks. So as an alternative I'm using Github Flavored Markdown (GFM). I really wanted it to feel like an actual text editor and not just a textbox, so I decided to use CodeMirror to handle all the input. Fira Code is used to provide font ligatures. Some other nice features include code highlighting, text/code folding, and a task list where you can toggle the status of any task from any date or note.

Features

Joe had a vision for what DailyNotes could become before calling it a 1.0 release. I've done my best to interpret and implement those features, along with feature requests from GitHub issues that Joe was considering. Here's what makes DailyNotes a powerful daily planning tool:

Core Experience

  • GitHub Flavored Markdown — Full GFM support with task lists (- [ ] / - [x]), tables, code blocks, and more
  • CodeMirror Editor — A real text editor experience with syntax highlighting, code folding, and keyboard shortcuts
  • Fira Code Font — Beautiful font ligatures for a polished writing experience
  • Auto-save — Never lose your work with optional automatic saving
  • Data Encryption — All notes encrypted at rest with AES encryption

Organization & Search

  • Powerful Search — Syntax-based search with tag:, project:, and full-text queries
  • Nested Tags — Hierarchical tag organization (e.g., work/meetings, home/family)
  • Kanban Board — Visual task management with drag-and-drop columns
  • Task List — View and toggle tasks within each note with one-click status updates

Preview & Visualization

  • HTML Preview — Live markdown preview with Cmd+K V (side-by-side) or Shift+Cmd+V (full screen)
  • Mermaid Diagrams — Create flowcharts, sequence diagrams, ERDs, and more directly in your notes
  • Themes — Light, Dark, and System themes to match your environment

Calendar Integration

  • Calendar Feed (ICS) — Subscribe to your notes in Google Calendar, Apple Calendar, or any ICS-compatible app
  • External Calendar Support — Display events from external ICS feeds alongside your daily notes

Self-Hosted & Private

  • Self-hosted — Your data stays on your server, under your control
  • Docker Ready — Easy deployment with Docker and Docker Compose
  • Multi-user Support — Multiple users with separate, encrypted data
  • No Vendor Lock-in — Export all your notes as markdown files anytime

Account Security

  • Password Recovery — Reset your password via email if forgotten
  • Magic Link Sign-in — Sign in with a secure email link instead of a password
  • Email Management — Add or update your email in Settings to enable these features

Password Recovery & Magic Link

DailyNotes supports password recovery and passwordless sign-in via email. These features require SMTP configuration (see Environment Variables).

Setting Up Your Email

  1. Click the menu icon (⋮) in the header
  2. Select Settings
  3. In the Account section, enter your email address
  4. Click Add Email

Once configured, you can use password recovery and magic link sign-in.

Forgot Password

If you forget your password:

  1. Go to the login page
  2. Click Forgot password?
  3. Enter your email address
  4. Check your email for a reset link (valid for 1 hour)
  5. Click the link and enter your new password

Magic Link Sign-in

Sign in without entering your password:

  1. Go to the login page
  2. Click Sign in with email
  3. Enter your email address
  4. Check your email for a sign-in link (valid for 15 minutes)
  5. Click the link to automatically sign in

Security Features

FeatureDescription
Secure tokensCryptographically random, SHA-256 hashed
Rate limiting3 requests per email per hour
Short expirationReset: 1 hour, Magic link: 15 minutes
Single-use tokensEach token can only be used once
Email enumeration preventionSame response whether email exists or not
Encrypted email storageEmail addresses encrypted at rest (AES)

SMTP Configuration Examples

Gmail (with App Password):

SMTP_HOST: smtp.gmail.comSMTP_PORT: 587SMTP_USER: your-email@gmail.comSMTP_PASSWORD: your-app-password # Generate at myaccount.google.com/apppasswordsSMTP_FROM_NAME: DailyNotesAPP_URL: https://your-dailynotes-instance.com

Mailgun:

SMTP_HOST: smtp.mailgun.orgSMTP_PORT: 587SMTP_USER: postmaster@your-domain.mailgun.orgSMTP_PASSWORD: your-mailgun-passwordSMTP_FROM_EMAIL: noreply@your-domain.comAPP_URL: https://your-dailynotes-instance.com

Amazon SES:

SMTP_HOST: email-smtp.us-east-1.amazonaws.comSMTP_PORT: 587SMTP_USER: your-ses-smtp-usernameSMTP_PASSWORD: your-ses-smtp-passwordSMTP_FROM_EMAIL: noreply@your-verified-domain.comAPP_URL: https://your-dailynotes-instance.com

Important Notes:

  • Gmail requires an App Password, not your regular password
  • The APP_URL must match the URL users access DailyNotes from (for email links to work)
  • If SMTP is not configured, password recovery and magic link features are automatically disabled
  • Users can still sign in with username/password even without email configured

Themes

DailyNotes supports Light, Dark, and System themes to match your preferred working environment.

Theme Options

ThemeDescription
🌙 DarkDefault dark interface, optimized for low-light environments
☀️ LightClean, bright interface with light backgrounds
💻 SystemAutomatically follows your operating system's color scheme setting

How to Change Themes

  1. Click the menu icon (⋮) in the header
  2. Select Settings
  3. In the Appearance section, click your preferred theme
  4. The theme changes instantly and is saved for future sessions

The System option automatically switches between light and dark themes based on your OS settings (e.g., macOS Dark Mode, Windows Dark Theme). This is perfect if you prefer dark mode at night and light mode during the day.

Mermaid Diagrams

DailyNotes supports Mermaid diagrams in the markdown preview, allowing you to create flowcharts, sequence diagrams, class diagrams, and more directly in your notes.

Creating Diagrams

Use a fenced code block with mermaid as the language:

```mermaidgraph TD A[Start] --> B{Decision} B -->|Yes| C[Do something] B -->|No| D[Do something else] C --> E[End] D --> E```

Viewing Diagrams

Diagrams are rendered in the HTML preview:

  • Side-by-side: Press Cmd+K V (Mac) or Ctrl+K V (Windows/Linux)
  • Preview only: Press Shift+Cmd+V (Mac) or Shift+Ctrl+V (Windows/Linux)

Supported Diagram Types

Mermaid supports many diagram types. Here are some examples:

Diagram TypeUse CaseExample Syntax
FlowchartProcess flows, decisionsgraph TD or graph LR
SequenceAPI calls, interactionssequenceDiagram
ClassObject relationshipsclassDiagram
StateState machinesstateDiagram-v2
Entity RelationshipDatabase schemaserDiagram
GanttProject timelinesgantt
PieData distributionpie
Git GraphBranch visualizationgitGraph

Theme Support

Diagrams automatically adapt to your app theme:

  • Dark theme: Diagrams render with dark-friendly colors
  • Light theme: Diagrams render with light-friendly colors
  • Switching themes re-renders diagrams with the appropriate color scheme

Error Handling

If a diagram has syntax errors, DailyNotes displays a helpful error message instead of breaking the preview. This makes it easy to debug and fix diagram issues.

Learn More

For full syntax documentation and examples, visit the Mermaid documentation.

Calendar feed (ICS)

  • Generate or rotate a private read-only ICS URL with GET/POST /api/calendar_token (requires auth).
  • Subscribe in Google Calendar via Settings -> Add calendar -> From URL using /api/calendar.ics?token=<your_token>.
  • Each daily note becomes an all-day event; the feed updates when notes change (Google polls periodically).
  • Rotate the token to immediately revoke previous subscriptions or disable sharing with DELETE /api/calendar_token.
  • You can now subscribe to external ICS feeds (e.g., Google private links) in Settings; DailyNotes shows those events on the matching day.

Search

DailyNotes features a powerful syntax-based search that lets you quickly find notes using text queries.

Search Syntax

SyntaxDescriptionExample
tag:valueFilter by tagtag:meeting
project:valueFilter by projectproject:work
t:valueShorthand for tagt:1on1
p:valueShorthand for projectp:DN
tag:"multi word"Quoted values for spacestag:"code review"
Plain textSearch note contentbudget report

Example Searches

QueryWhat it finds
budgetAll notes containing "budget"
tag:meetingAll notes tagged "meeting"
project:work tag:Q4Notes in "work" project with "Q4" tag
tag:1on1 tag:feedbackNotes with both tags (AND)
project:DN project:personalNotes in either project (OR)
tag:meeting notes agendaTagged "meeting" containing "notes" AND "agenda"

Search Logic

  • Multiple tags = AND (note must have all specified tags)
  • Multiple projects = OR (note can be in any specified project)
  • Multiple text terms = AND (note must contain all words)

Features

  • Autocomplete: Type tag: or project: to see suggestions from your existing tags/projects
  • Keyboard navigation: Use arrow keys to select, Tab/Enter to confirm
  • Result highlighting: Matching text is highlighted in search results with context snippets
  • Syntax help: Click the ? button for a quick reference

Nested Tags

DailyNotes supports hierarchical tag organization using / as a delimiter. This lets you create tag hierarchies like work/meetings, home/family, or projects/dailynotes/frontend.

Creating Nested Tags

Use the / character in your frontmatter to create nested tags:

---title: Weekly Team Synctags: work/meetings, work/1on1, home/family---

Sidebar Display

Nested tags appear as a collapsible tree in the sidebar:

DisplayDescription
▶ workCollapsed parent tag (click arrow to expand)
▼ workExpanded parent showing children below
meetingsChild tag indented under parent
1on1Flat tag (no /) displayed inline at top
  • Flat tags (without /) are displayed inline at the top, wrapping as needed
  • Parent tags show a collapsible chevron (▶/▼)
  • Child tags are displayed inline under their parent when expanded
  • Expand/collapse state is saved and persists across page refreshes

Search Behavior

Nested tags support hierarchical search - searching for a parent tag matches all its children:

Search QueryMatches
tag:workNotes with work, work/meetings, work/1on1
tag:work/meetingsOnly notes with exactly work/meetings
tag:homeNotes with home, home/family, home/tech

This makes it easy to search broadly (tag:work for all work-related notes) or specifically (tag:work/meetings for just meeting notes).

Examples

Tags in FrontmatterSidebar Display
tags: meeting, 1on1, reviewmeeting1on1review (inline)
tags: work/meetings, work/reviews▼ workmeetingsreviews
tags: home/tech, home/family, work▼ homefamilytech, work

Tips

  • Existing tags are unchanged: Tags without / continue to work exactly as before
  • Autocomplete works: Type tag:work/ to see suggestions for nested tags
  • Mix and match: You can use both flat and nested tags in the same note
  • Deep nesting: Multiple levels work too: projects/dailynotes/frontend/components

Kanban Board

DailyNotes includes an optional Kanban board view for organizing tasks with drag-and-drop support.

Enabling Kanban

  1. Click the menu icon (⋮) in the header
  2. Select Settings
  3. Find the Kanban section
  4. Toggle Enable Kanban board

When enabled, the Tasks icon in the header changes to a columns icon. Click it to open the Kanban modal.

Task Syntax

Use the >>column syntax at the end of any task to assign it to a specific column:

Task SyntaxColumn Assignment
- [ ] Plain taskDefaults to "todo"
- [x] Completed taskDefaults to "done"
- [ ] In progress >>doingExplicit "doing" column
- [x] Done but in review >>reviewStays in "review" (explicit wins)

Column names support letters, numbers, and hyphens: >>in-progress, >>stage-2, >>Q4

Default Columns

SettingDefault Value
Default columnstodo, done
Configurable inSettings → Kanban

Add, remove, or reorder columns in the Settings panel.

Per-Note Column Override

Override columns for a specific note using YAML frontmatter:

---title: Sprint Planningkanban:
- backlog
- in-progress
- review
- done----[ ] Design mockups >>backlog
-[ ] Implement API >>in-progress
-[ ] Write tests >>review

Auto-Column Creation

If you use a column that doesn't exist in your configuration:

ScenarioResult
Columns: [todo, done]Default setup
Task uses >>stagingColumns become [todo, staging, done]
Task uses >>reviewColumns become [todo, staging, review, done]

New columns are automatically inserted before "done". To reorder, update the columns in Settings or note frontmatter.

Drag and Drop

  • Drag any task card to move it between columns
  • The task's >>column syntax is automatically updated in the markdown
  • Changes are saved immediately to the note

Column Assignment Rules

ConditionResulting Column
No >>column + unchecked [ ]todo
No >>column + checked [x]done
Explicit >>column (any checkbox state)The specified column

Key point: Explicit >>column syntax always takes precedence over the checkbox state. This lets you have completed tasks in a "review" column.

In Action

Here are some screenshots of what it looks like:

Main editor:

Search page:

Task list:

Running

The recommended way of running is to pull the image from Docker Hub.

Docker Setup

Environment Variables

Environment VariableDescriptionDefault
API_SECRET_KEYUsed to sign API tokens.Will be generated automatically if not passed in.
DATABASE_URIConnection string for DB.Will create and use a SQLite DB if not passed in.
DB_ENCRYPTION_KEYSecret key for encrypting data. Length must be a multiple of 16.

Warning: If changed data will not be able to be decrypted!
Will be generated automatically if not passed in.
PREVENT_SIGNUPSDisable signup form? Anything in this variable will prevent signups.False
BASE_URLUsed when using a subfolder on a reverse proxyNone
PUIDUser ID (for folder permissions)None
PGIDGroup ID (for folder permissions)None
DEFAULT_TIMEZONEOptional TZ name (e.g., America/Denver) for external ICS events; falls back to server local timeNone
SMTP_HOSTSMTP server hostname for sending emails (password reset, magic links)None (email features disabled)
SMTP_PORTSMTP server port587
SMTP_USERSMTP username/email for authenticationNone
SMTP_PASSWORDSMTP password or app-specific passwordNone
SMTP_USE_TLSUse TLS for SMTP connection (true or false)true
SMTP_FROM_EMAILFrom address for sent emailsSMTP_USER value
SMTP_FROM_NAMEDisplay name for sent emailsDailyNotes
APP_URLPublic URL of your DailyNotes instance (used in email links)http://localhost:8000

Volumes

Volume NameDescription
/app/configUsed to store DB and environment variables. This is not needed if you pass in all of the above environment variables.

Docker Run

By default, the easiest way to get running is:

docker run -p 8000:8000 -v /config_dir:/app/config xhenxhe/dailynotes

Docker Compose

Here is a complete docker-compose example with all configuration options:

services:
dailynotes:
image: xhenxhe/dailynotes:latestcontainer_name: DailyNotesports:
- '8000:8000'volumes:
# Persistent storage for database and config
- ./dailynotes-data:/app/configenvironment:
# Required: Secret key for signing JWT tokens# Generate with: openssl rand -hex 32API_SECRET_KEY: 'your-secure-api-secret-key-here'# Required: Encryption key for data at rest (must be multiple of 16)# Generate with: openssl rand -hex 32# WARNING: Changing this will make existing data unreadable!DB_ENCRYPTION_KEY: 'your-secure-db-encryption-key-here'# Optional: Database connection string# Default: SQLite database in /app/config/app.db# Examples:# PostgreSQL: postgresql://user:password@host:5432/dailynotes# MySQL: mysql+pymysql://user:password@host:3306/dailynotes?charset=utf8mb4# DATABASE_URI: "sqlite:////app/config/app.db"# Optional: Prevent new user signups (set to any value to disable)# PREVENT_SIGNUPS: "true"# Optional: Base URL when using a reverse proxy subfolder# Example: If accessing via https://example.com/notes, set to "/notes"# BASE_URL: ""# Optional: Set user/group ID for file permissions# Useful for matching host user permissions# PUID: "1000"# PGID: "1000"restart: unless-stopped# Optional: Health checkhealthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:8000/health']interval: 30stimeout: 10sretries: 3start_period: 40s# Optional: Use with PostgreSQL# postgres:# image: postgres:15-alpine# container_name: dailynotes-db# environment:# POSTGRES_DB: dailynotes# POSTGRES_USER: dailynotes# POSTGRES_PASSWORD: your-secure-db-password# volumes:# - ./postgres-data:/var/lib/postgresql/data# restart: unless-stopped

Quick Start with Docker Compose:

  1. Save the above as docker-compose.yml

  2. Generate secure keys:

    # Generate API secret key
    openssl rand -hex 32
    # Generate DB encryption key
    openssl rand -hex 32
  3. Update the API_SECRET_KEY and DB_ENCRYPTION_KEY values

  4. Start the application:

    docker-compose up -d
  5. Access at http://localhost:8000

Important Notes:

  • The DB_ENCRYPTION_KEY encrypts all your notes. Never change it after initial setup or your data will be unreadable!
  • The ./dailynotes-data directory will store your database and configuration
  • The default port is now 8000 for better compatibility

Reverse Proxy

If you're running DailyNotes behind a reverse proxy, here are example configurations for popular web servers.

Caddy

Caddy automatically handles HTTPS certificates. Add to your Caddyfile:

dailynotes.example.com {
 reverse_proxylocalhost:8000
}

For a subfolder setup:

example.com {
 handle_path/notes/* {
 reverse_proxylocalhost:8000
}
}

Set BASE_URL=/notes in your DailyNotes environment when using a subfolder.

Nginx
server{listen80;server_name dailynotes.example.com;location / {proxy_passhttp://localhost:8000;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}}

For a subfolder setup:

location /notes/ {proxy_passhttp://localhost:8000/;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}

Set BASE_URL=/notes in your DailyNotes environment when using a subfolder.

For HTTPS, add SSL configuration or use Certbot: sudo certbot --nginx -d dailynotes.example.com

Apache

Enable required modules:

sudo a2enmod proxy proxy_http headers

Virtual host configuration:

<VirtualHost*:80>
ServerName dailynotes.example.com
ProxyPreserveHostOnProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
RequestHeaderset X-Forwarded-Proto "http"
</VirtualHost>

For a subfolder setup:

<Location/notes>
ProxyPass http://localhost:8000ProxyPassReverse http://localhost:8000RequestHeaderset X-Forwarded-Proto "http"
</Location>

Set BASE_URL=/notes in your DailyNotes environment when using a subfolder.

For HTTPS, use Certbot: sudo certbot --apache -d dailynotes.example.com

Development setup

Option 1: Docker Development Environment (Recommended)

The easiest way to get started with development is using Docker. This approach provides:

  • ✅ Consistent environment across all platforms (macOS, Linux, Windows)
  • ✅ No need to install Python, Node.js, or manage versions
  • ✅ Hot-reloading for both frontend and backend code
  • ✅ Automatic database setup and migrations
  • ✅ Isolated environment that won't affect your system

Quick Start:

# Start the development environment (builds, starts, and opens browser)
./dev

That's it! The script will:

  1. Build the development Docker image
  2. Start both Quart backend (port 8000) and Vue.js (port 8080) servers
  3. Set up the database and run migrations
  4. Open your browser to http://localhost:8080

Development Workflow:

All your code changes will be automatically detected:

  • Python/Quart changes: Uvicorn server auto-reloads
  • Vue.js changes: Hot module replacement (HMR) updates the browser instantly
  • Database: Persisted in ./config/app.db on your host machine

Useful Commands:

# View live logs from both servers
docker compose -f docker-compose.dev.yml logs -f
# Stop the development environment
docker compose -f docker-compose.dev.yml down
# Restart services
docker compose -f docker-compose.dev.yml restart
# Access the container shell (for debugging)
docker exec -it dailynotes-dev bash
# Rebuild after dependency changes
docker compose -f docker-compose.dev.yml build

Testing with PostgreSQL or MySQL:

DailyNotes supports PostgreSQL and MySQL in addition to SQLite. To test with these databases:

# Start with PostgreSQL
./dev --postgres
# Start with MySQL
./dev --mysql
# Stop any database environment
./dev --down

These use production-like Docker Compose configurations (docker-compose-postgres.yml and docker-compose-mysql.yml) that include the database servers. The database data is persisted in Docker volumes.

File Structure:

  • Dockerfile.dev - Development Docker image with both Python and Node.js
  • docker-compose.dev.yml - Development compose config with volume mounts
  • docker-compose-postgres.yml - PostgreSQL testing configuration
  • docker-compose-mysql.yml - MySQL testing configuration
  • docker-entrypoint-dev.sh - Startup script that runs both servers

What's Mounted:

The following directories are mounted from your host to enable hot-reloading:

  • ./app/ - Python backend code
  • ./client/src/ - Vue.js source code
  • ./client/public/ - Static assets
  • ./config/ - Database and environment variables (persisted)
  • ./migrations/ - Database migrations

Environment Variables:

By default, the development environment will auto-generate secure keys in ./config/.env. To customize:

# Edit docker-compose.dev.ymlenvironment:
API_SECRET_KEY: 'your-dev-key'DB_ENCRYPTION_KEY: 'your-16-char-multiple-key'PREVENT_SIGNUPS: 'true'# Optional: disable signups

Option 2: Local Development Setup

If you prefer to run services directly on your host machine:

Automated Setup (macOS/Linux)

The easiest way to set up your development environment is to use the automated setup script:

./dev-setup.sh

This script will:

  • Check for Python 3 and Node.js
  • Automatically use Node.js 16 via nvm (if available)
  • Create a Python virtual environment
  • Install all Python dependencies
  • Install all Node.js dependencies
  • Generate environment configuration

After setup completes, run the development servers:

# Backend (in one terminal)source venv/bin/activate
./run.sh
# Frontend (in another terminal)cd client
npm run dev # Uses Node 16 via nvm automatically

Note: The project includes a .nvmrc file that specifies Node.js 16. If you have nvm installed, it will automatically use the correct version.

Manual Setup (Windows or alternative)

If you're on Windows or prefer to set up manually:

Installing dependencies

You need Python 3.8+ and Node.js 16 installed.

Recommended: Use nvm (macOS/Linux) or nvm-windows to manage Node.js versions. The project includes a .nvmrc file that specifies Node.js 16.

# If using nvm (recommended)
nvm install 16
nvm use 16
# Create and activate virtual environment (recommended)
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate# Install dependencies
pip install -r requirements.txt
cd client
npm ci

Creating the environment

You can use the environment variables from above, or you can generate new ones by running the following:

./verify_env.py

Keep in mind that since the data is encrypted, if you modify the DB_ENCRYPTION_KEY variable, your data will not be accessible anymore.

Running

During development you need to run the client and server simultaneously

# Activate virtual environment firstsource venv/bin/activate # On Windows: venv\Scripts\activate# Run backend
./run.sh
# In a separate terminalcd client
# If using nvm, switch to Node 16 first (or run npm run dev)
nvm use # Reads from .nvmrc file# Start frontend dev server
npm run serve
# Or use the dev script which automatically uses Node 16:
npm run dev

About

App for taking notes and tracking tasks on a daily basis

Topics

Resources

Stars

713 stars

Watchers

12 watching

Forks

Releases

Packages

Used by

Contributors

Languages