A stupid simple, no auth (unless you want it!), modern notepad application with auto-save functionality and dark mode support.
- Features
- Quick Start
- Important: Docker Permissions
- Configuration
- Security
- Technical Details
- Links
- Contributing
- Future Features
- Simple, clean interface
- Auto-saving
- Dark mode support
- Responsive design
- Docker support
- Optional PIN protection (4-10 digits)
- File-based storage
- Data persistence across updates
- Markdown Formatting with enhanced support
- GitHub-style alert blocks (Note, Tip, Important, Warning, Caution)
- Extended table formatting
- Auto-expand collapsible details in print (configurable)
- Code syntax highlighting in
fenced codeblocks
- Direct notepad linking with URL parameters
- Copy shareable notepad links
- Browser navigation support (back/forward buttons)
- Fuzzy Search (by filename and file contents)
- PWA Support with automatic cache updates
- Docker (recommended)
- Node.js >=20.0.0 (for local development)
# Pull and run with one command
docker run -p 3000:3000 \
-v ./data:/app/data \
dumbwareio/dumbpad:latest- Go to http://localhost:3000
- Start typing - Your notes auto-save
- Marvel at how dumb easy this was
β οΈ Note: If the container crashes with permission errors, see Docker Permissions section below.
Create a docker-compose.yml file:
services:
dumbpad:
image: dumbwareio/dumbpad:latest
container_name: dumbpad
restart: unless-stopped
ports:
- ${DUMBPAD_PORT:-3000}:3000
volumes:
- ${DUMBPAD_DATA_PATH:-./data}:/app/data
environment:
# The title shown in the web interface
SITE_TITLE: ${DUMBPAD_SITE_TITLE:-DumbPad}
# Optional PIN protection (leave empty to disable)
DUMBPAD_PIN: ${DUMBPAD_PIN:-}
# The base URL for the application
BASE_URL: ${DUMBPAD_BASE_URL:-http://localhost:3000} # Use ALLOWED_ORIGINS below to restrict cors to specific origins
# (OPTIONAL)
# Usage: Comma-separated list of urls: http://localhost:port,http://internalip:port,https://base.proxy.tld,https://authprovider.domain.tld
# ALLOWED_ORIGINS: ${DUMBPAD_ALLOWED_ORIGINS:-http://localhost:3000} # Comment out to allow all origins (*)
# LOCKOUT_TIME: ${DUMBPAD_LOCK_TIME:-15} # Customize pin lockout time (if empty, defaults to 15 in minutes)
# MAX_ATTEMPTS: ${DUMBPAD_MAX_ATTEMPTS:-5} # Customize pin max attempts (if empty, defaults to 5)
# COOKIE_MAX_AGE: ${DUMBPAD_COOKIE_MAX_AGE:-24} # Customize maximum age of cookies primarily used for pin verification (default 24) in hours
# PAGE_HISTORY_COOKIE_AGE: ${DUMBPAD_PAGE_HISTORY_COOKIE_AGE:-365} # Customize age of cookie to show the last notepad opened (default 365 | max 400) in days - shows default notepad on load if expired
# MARKDOWN CODE SYNTAX HIGHLIGHTING (only use below if you want to restrict to specific languages):
# By default, DumbPad includes support for all ~180 languages supported by highlight.js.
# view entire list and usage in /docs/MARKDOWN_SYNTAX_HIGHLIGHTING_USAGE.md
# HIGHLIGHT_LANGUAGES=c,csharp,css,dockerfile,go,html,java,javascript,json,kotlin,markdown,perl,php,python,ruby,sql,swift,typescript,xml,yamlThen run:
docker compose up -d- Go to http://localhost:3000
- Start typing - Your notes auto-save
- Rejoice in the glory of your dumb notes
β οΈ Note: If the container crashes with permission errors, see Docker Permissions section below.
- Install dependencies:
npm install- Set environment variables in
.envorcp .env.example .env:
PORT=3000 # Port to run the server on
DUMBPAD_PIN=1234 # Optional PIN protection
SITE_TITLE=DumbPad # Custom site title
BASE_URL=http://localhost:3000 # Base URL for the application- Start the server:
npm startIf you're using Windows PowerShell with Docker, use this format for paths:
docker run -p 3000:3000 -v "${PWD}\data:/app/data" dumbwareio/dumbpad:latestAs of PR #76, DumbPad now runs as a non-root user (UID 1000) inside the Docker container for improved security. This can cause permission issues in two scenarios:
- Upgrading from a previous version - Existing data directory may have incorrect permissions, causing notepads to appear blank (Issue #74)
- Fresh installation - Docker may create the data directory with host user permissions that don't match UID 1000, causing container restart loops with
EACCES: permission deniederrors (Issue #79)
- Fresh installations: Container crashes on startup with
Error: EACCES: permission denied, open '/app/data/notepads.json' - Upgrades: Previously saved notepads appear blank or empty
Set the ownership of your data directory to match the container's non-root user (UID 1000):
Linux/macOS:
# Stop the container first
docker stop dumbpad
# Fix permissions (replace /path/to/your/data with your actual path)
sudo chown -R 1000:1000 /path/to/your/dataExample for common setups:
# If using the default ./data directory
sudo chown -R 1000:1000 ./data
# If using a custom path like /opt/docker/dumbpad
sudo chown -R 1000:1000 /opt/docker/dumbpad
# For Unraid users
sudo chown -R 1000:1000 /mnt/user/appdata/dumbpadWindows (Docker Desktop):
# Windows users typically don't need to change permissions
# Docker Desktop handles volume permissions automaticallyFor new installations, create the data directory with correct permissions before starting the container:
Linux/macOS:
# Create data directory
mkdir -p ./data
# Set correct ownership
sudo chown -R 1000:1000 ./data
# Now start the container
docker compose up -dWindows (Docker Desktop):
# No special setup needed - Docker Desktop handles permissions automatically
docker compose up -dAfter updating permissions, verify everything is working:
-
Start/Restart the container:
docker restart dumbpad # or if starting fresh docker compose up -d -
Check container logs (should start without errors):
docker logs dumbpad
-
Verify file ownership inside container:
docker exec dumbpad ls -la /app/dataYou should see files owned by
nodeor UID1000 -
Test the application by accessing http://localhost:3000 and creating a test notepad
Running containers as non-root users is a security best practice that:
- Limits potential damage from container escapes
- Reduces attack surface
- Aligns with security compliance standards
- π Auto-saving notes
- π Dark/Light mode support
- π Optional PIN protection
- π± Mobile-friendly interface / PWA Support
- ποΈ Multiple notepads
- π Enhanced Markdown Formatting with GitHub-style alerts and extended tables
- π Direct notepad linking with shareable URLs
- π§ Browser navigation support (back/forward buttons)
- β¬οΈ Download notes as text or markdown files
- π¨οΈ Print functionality with auto-expanded collapsible sections
- π Fuzzy Search by name or contents
- π Real-time saving
- π½ Add .txt files into data folder to import (requires page refresh)
- β‘ Zero dependencies on client-side
- π‘οΈ Built-in security features
- π¨ Clean, modern interface
- π¦ Docker support with easy configuration
- π Optional CORS support
- βοΈ Customizable settings
- π Automatic cache updates and version management
| Variable | Description | Default | Required |
|---|---|---|---|
| PORT | Server port | 3000 | No |
| BASE_URL | Base URL for the application | http://localhost:PORT | Yes |
| DUMBPAD_PIN | PIN protection (4-10 digits) | None | No |
| SITE_TITLE | Site title displayed in header | DumbPad | No |
| NODE_ENV | Node environment mode (development or production) | production | No |
| ALLOWED_ORIGINS | Allowed CORS origins (* for all or comma-separated list) |
* | No |
| LOCKOUT_TIME | Lockout time after max PIN attempts (in minutes) | 15 | No |
| MAX_ATTEMPTS | Maximum PIN entry attempts before lockout | 5 | No |
| COOKIE_MAX_AGE | Maximum age of authentication cookies (in hours) | 24 | No |
| PAGE_HISTORY_COOKIE_AGE | Age of cookie storing last opened notepad (in days, max 400) | 365 | No |
| TRUST_PROXY | Enable proxy trust for X-Forwarded-For headers | false | No |
| TRUSTED_PROXY_IPS | Comma-separated list of trusted proxy IPs | None | No |
| HIGHLIGHT_LANGUAGES | Comma-separated list of code syntax languages to restrict to | all if not supplied | No |
When deploying DumbPad behind a reverse proxy (nginx, Apache, Cloudflare, etc.), you may need to configure proxy trust to correctly identify client IP addresses for rate-limiting and authentication.
By default, TRUST_PROXY is false (most secure). Only enable proxy trust if you're deploying behind a trusted reverse proxy. Incorrect configuration can allow attackers to bypass rate-limiting and authentication by spoofing the X-Forwarded-For header.
Enable TRUST_PROXY=true only if:
- You're deploying behind a reverse proxy (nginx, Cloudflare, etc.)
- The proxy adds
X-Forwarded-Forheaders - You can explicitly list all trusted proxy IPs in
TRUSTED_PROXY_IPS
Example 1: Behind nginx on the same host
TRUST_PROXY=true
TRUSTED_PROXY_IPS=127.0.0.1Example 2: Docker with nginx reverse proxy
TRUST_PROXY=true
TRUSTED_PROXY_IPS=172.17.0.1 # Docker default gateway IPExample 3: Multiple proxies (load balancer + nginx)
TRUST_PROXY=true
TRUSTED_PROXY_IPS=10.0.0.1,10.0.0.2 # IPs of both proxiesExample 4: Cloudflare (using Cloudflare IPs)
TRUST_PROXY=true
# List all Cloudflare IP ranges that connect to your origin
# See: https://www.cloudflare.com/ips/
TRUSTED_PROXY_IPS=173.245.48.0,103.21.244.0,103.22.200.0,...To find your proxy's IP address:
Docker:
docker network inspect bridge | grep GatewayCheck incoming connections:
# While DumbPad is running, check who's connecting
netstat -tn | grep :3000- When
TRUST_PROXY=false(default): Always uses the direct socket IP address, ignoringX-Forwarded-Forheaders - When
TRUST_PROXY=truewithTRUSTED_PROXY_IPS: Validates the immediate connecting IP against the trusted list before trustingX-Forwarded-For - When
TRUST_PROXY=truewithoutTRUSTED_PROXY_IPS: TrustsX-Forwarded-Forfrom any source (β οΈ not recommended for production)
- Variable-length PIN support (4-10 digits)
- Constant-time PIN comparison
- Brute force protection:
- 5 attempts maximum
- 15-minute lockout after failed attempts
- IP-based tracking with spoofing protection
- Validated client IP extraction (ignores untrusted X-Forwarded-For by default)
- Secure cookie handling
- No client-side PIN storage
- Rate limiting
- Collaborative editing
- CORS support for origin restrictions (optional)
- Configurable proxy trust with IP validation
Access settings via the gear icon (βοΈ) in the header or use keyboard shortcut:
- Windows/Linux:
Ctrl+Alt+, - macOS:
Cmd+Ctrl+,
| Setting | Description | Default | Options |
|---|---|---|---|
| Auto-save Status Interval | Time interval for auto-save notifications (0 = disabled) | 1000ms | Any number (milliseconds) |
| Remote Connection Messages | Show notifications when users connect/disconnect | Enabled | Enabled/Disabled |
| Disable Print Expansion | Prevent auto-expanding collapsed sections when printing | Disabled | Enabled/Disabled |
| Default Markdown Preview | Default view when loading DumbPad (Client-based) | Editor | Editor, Split, Full |
- Unique Names: Notepad names are automatically made unique by the server. If you try to create or rename a notepad with an existing name, the server will append a suffix (e.g., "Note-1", "Note-2")
- Name Validation: The server handles all name validation and uniqueness checks. The frontend will display a notification if your requested name was modified
- Auto-save: Changes are automatically saved every 300ms after you stop typing, with periodic saves every 2 seconds
- Persistence: All settings are stored in your browser's local storage and persist across sessions
- Backend: Node.js (>=20.0.0) with Express
- Frontend: Vanilla JavaScript (ES6+) with enhanced markdown support
- Container: Docker with multi-stage builds
- Security: Express security middleware
- Storage: File-based with auto-save
- Theme: Dynamic dark/light mode with system preference support
- PWA: Service Worker with automatic cache updates and version management
- Markdown: Enhanced with alert blocks, extended tables, and collapsible content
- Real-time: WebSocket-based collaboration and live updates
- Navigation: SPA-style routing with shareable URLs and browser history support
- Print: Advanced print preview with auto-expanding collapsible content and theme preservation
- express: Web framework
- cors: Cross-origin resource sharing
- dotenv: Environment configuration
- cookie-parser: Cookie handling
- express-rate-limit: Rate limiting
- marked: Markdown formatting
- marked-alert: GitHub-style alert blocks for markdown
- marked-extended-tables: Enhanced table support for markdown
- marked-highlight: Syntax highlighting for code blocks in markdown
- @highlightjs/cdn-assets: Highlight.js assets for code syntax highlighting
- fuse.js: Fuzzy searching
- ws: WebSocket support for real-time collaboration
The data directory contains:
notepads.json: List of all notepads- Individual
.txtfiles for each notepad's content - Drop in .txt files to import notes (requires page refresh)
data directory when updating! This is where all your notes are stored.
- Start typing: Notes auto-save as you type (every 300ms after stopping, with periodic saves every 2 seconds)
- Theme toggle: Switch between light/dark mode with the toggle button
- Force save:
Ctrl+S(orCmd+Son Mac) - Search:
Ctrl+K(orCmd+K) to open fuzzy search across all notepads - Copy link: Click the link button (π) to copy the current notepad's shareable URL
- Settings: Click the gear icon (βοΈ) or use
Ctrl+Alt+,(orCmd+Ctrl+,)
- Create: Click the + button or
Ctrl+Alt+N(orCmd+Ctrl+N) - Rename: Click rename button or
Ctrl+Alt+R(orCmd+Ctrl+R) - Delete: Click delete button or
Ctrl+Alt+X(orCmd+Ctrl+X) - Navigate: Use dropdown, arrow keys (
Ctrl+Alt+β/β), or browser back/forward buttons - Download: Click download button or
Ctrl+Alt+A(orCmd+Ctrl+A) for .txt/.md export - Print:
Ctrl+P(orCmd+P) with enhanced formatting and auto-expanded collapsible sections
Swap between different modes using the 3-way markdown toggle button: (Editor, Split, Full)
| Editor | Split Preview | Full Preview |
|---|---|---|
![]() |
![]() |
![]() |
You can also set your default preview mode in settings.
DumbPad now supports enhanced markdown features:
> [!NOTE]
> This is a note alert block
> [!TIP]
> This is a tip alert block
> [!IMPORTANT]
> This is an important alert block
> [!WARNING]
> This is a warning alert block
> [!CAUTION]
> This is a caution alert block- Advanced table formatting with alignment
- Enhanced styling for better readability
<details>
<summary>Click to expand</summary>
Content that will be automatically expanded when printing
</details>- Uses highlight.js for syntax highlighting using fenced code blocks
- Defaults to all supported languages (configured via
HIGHLIGHT_LANGUAGESenvironment variable if you would like to restrict to specific languages) - Read more and view examples in /docs/MARKDOWN_SYNTAX_HIGHLIGHTING_USAGE.md
```javascript
console.log("Hello, world!");
```- Direct notepad linking:
?id=notepadname- Opens a specific notepad by name (case-insensitive) - Browser navigation: Use back/forward buttons to navigate between notepads
- Shareable URLs: Copy links to share specific notepads with others
- Backend: Node.js with Express
- Frontend: Vanilla JavaScript
- Storage: File-based storage in
datadirectory - Styling: Modern CSS with CSS variables for theming
- Security: Constant-time PIN comparison, brute force protection
- GitHub: github.com/dumbwareio/dumbpad
- Docker Hub: hub.docker.com/r/dumbwareio/dumbpad
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes using conventional commits
- Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See Development Guide for local setup and guidelines.
Made with β€οΈ by DumbWare.io
- Website: dumbware.io
- Join the Chaos: Discord π¬
- File attachments
- Markdown code syntax highlighting
Got an idea? Open an issue or submit a PR



