No description
  • TypeScript 95.4%
  • CSS 1.7%
  • JavaScript 1.6%
  • HTML 0.8%
  • Dockerfile 0.5%
Find a file
2026-01-03 12:17:26 +02:00
public/icons initial 2026-01-03 12:17:26 +02:00
src initial 2026-01-03 12:17:26 +02:00
.gitignore initial 2026-01-03 12:17:26 +02:00
Dockerfile initial 2026-01-03 12:17:26 +02:00
index.html initial 2026-01-03 12:17:26 +02:00
nginx.conf initial 2026-01-03 12:17:26 +02:00
package.json initial 2026-01-03 12:17:26 +02:00
pnpm-lock.yaml initial 2026-01-03 12:17:26 +02:00
pnpm-workspace.yaml initial 2026-01-03 12:17:26 +02:00
postcss.config.js initial 2026-01-03 12:17:26 +02:00
README.md initial 2026-01-03 12:17:26 +02:00
tailwind.config.js initial 2026-01-03 12:17:26 +02:00
tsconfig.json initial 2026-01-03 12:17:26 +02:00
tsconfig.node.json initial 2026-01-03 12:17:26 +02:00
vite.config.ts initial 2026-01-03 12:17:26 +02:00
vitest.config.ts initial 2026-01-03 12:17:26 +02:00

Keep Notes

A self-hosted, open-source Google Keep clone. Full-featured note-taking app with offline support, sync, and Google Keep import.

Keep Notes Go React PostgreSQL Docker

Features

  • 📝 Notes with rich features: titles, content, colors, labels, checklists
  • 📌 Pin important notes to keep them at the top
  • 🗂️ Archive and Trash for organization
  • 🏷️ Labels for categorization
  • 🔍 Full-text search across all notes
  • 📱 PWA - installable on Android and iOS
  • 🔄 Offline support with automatic sync
  • Conflict resolution for simultaneous edits
  • 📤 Google Keep import - migrate your existing notes
  • 🔐 Secure authentication with 1-year sessions
  • 📧 Password reset via email (Resend integration)
  • 🐳 Docker-ready for easy deployment

Quick Start

Prerequisites

  • Docker and Docker Compose
  • (Optional) Resend API key for email functionality

Using Docker Compose

  1. Clone the repository

    git clone https://github.com/yourusername/keep-notes.git
    cd keep-notes
    
  2. Configure environment

    cp .env.example .env
    # Edit .env with your settings
    
  3. Start the services

    docker-compose up -d
    
  4. Access the app

Environment Variables

Variable Description Default
POSTGRES_USER PostgreSQL username keep
POSTGRES_PASSWORD PostgreSQL password keeppassword
POSTGRES_DB Database name keepnotes
JWT_SECRET Secret for JWT tokens Required
RESEND_API_KEY Resend API key for emails Optional
FROM_EMAIL Email sender address noreply@example.com
APP_URL Frontend URL http://localhost:3000

Development Setup

Backend (Go)

cd backend

# Install dependencies
go mod download

# Set environment variables
export DATABASE_URL="postgres://keep:keeppassword@localhost:5432/keepnotes?sslmode=disable"
export JWT_SECRET="your-dev-secret"

# Run the server
go run cmd/server/main.go

Frontend (React)

cd frontend

# Install dependencies
npm install

# Start development server
npm run dev

Database

# Start PostgreSQL with Docker
docker run -d \
  --name keep-postgres \
  -e POSTGRES_USER=keep \
  -e POSTGRES_PASSWORD=keeppassword \
  -e POSTGRES_DB=keepnotes \
  -p 5432:5432 \
  postgres:16-alpine

Project Structure

keep-notes/
├── backend/
│   ├── cmd/server/          # Application entry point
│   ├── internal/
│   │   ├── api/             # HTTP handlers and routes
│   │   ├── config/          # Configuration
│   │   ├── database/        # Database connection and migrations
│   │   ├── logger/          # Structured logging
│   │   ├── models/          # Data models
│   │   └── services/        # Business logic
│   ├── Dockerfile
│   └── go.mod
├── frontend/
│   ├── public/              # Static assets
│   ├── src/
│   │   ├── components/      # React components
│   │   ├── lib/             # API client, database, sync
│   │   ├── pages/           # Page components
│   │   ├── store/           # Zustand stores
│   │   └── types/           # TypeScript types
│   ├── Dockerfile
│   └── package.json
├── docker-compose.yml
└── README.md

API Endpoints

Authentication

  • POST /api/auth/register - Register new user
  • POST /api/auth/login - Login
  • POST /api/auth/logout - Logout
  • GET /api/auth/me - Get current user
  • POST /api/auth/forgot-password - Request password reset
  • POST /api/auth/reset-password - Reset password

Notes

  • GET /api/notes - List notes (query params: archived, trashed, label_id)
  • POST /api/notes - Create note
  • GET /api/notes/:id - Get note
  • PATCH /api/notes/:id - Update note
  • DELETE /api/notes/:id - Delete note
  • GET /api/notes/search?q=query - Search notes
  • DELETE /api/notes/trash - Empty trash

Labels

  • GET /api/labels - List labels
  • POST /api/labels - Create label
  • PATCH /api/labels/:id - Update label
  • DELETE /api/labels/:id - Delete label

Sync

  • POST /api/sync - Sync notes (offline support)

Import

  • POST /api/import/google-keep - Import from Google Keep (multipart/form-data)

Offline Support & Sync

The app uses IndexedDB to store notes locally, enabling:

  1. Offline access - View and edit notes without internet
  2. Background sync - Changes sync automatically when online
  3. Conflict resolution - UI for resolving conflicting edits

Sync Flow

  1. Client sends pending changes with last_sync timestamp
  2. Server applies changes and returns any server-side updates
  3. Conflicts are detected via version numbers
  4. User resolves conflicts through the UI

Importing from Google Keep

  1. Go to Google Takeout
  2. Click "Deselect all"
  3. Select only "Keep"
  4. Create and download the export
  5. In the app, click Settings → Import from Google Keep
  6. Upload the .zip file

Running Tests

Backend

cd backend
go test ./...

Frontend

cd frontend
npm test

Production Deployment

With Docker Compose

# Build and start in production mode
docker-compose -f docker-compose.yml up -d --build

Security Checklist

  • Change JWT_SECRET to a strong random string
  • Change POSTGRES_PASSWORD to a strong password
  • Set up HTTPS with a reverse proxy (nginx, Traefik, Caddy)
  • Configure APP_URL with your domain
  • Set up proper backup for PostgreSQL data

Reverse Proxy Example (nginx)

server {
    listen 80;
    server_name keep.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name keep.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/keep.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/keep.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    location /api {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Tech Stack

Backend

  • Go 1.22 - Fast, efficient backend
  • Chi - Lightweight HTTP router
  • pgx - PostgreSQL driver
  • JWT - Authentication tokens
  • bcrypt - Password hashing
  • Resend - Transactional emails

Frontend

  • React 18 - UI framework
  • TypeScript - Type safety
  • Vite - Build tool with PWA plugin
  • Tailwind CSS - Styling
  • Zustand - State management
  • React Query - Server state
  • idb - IndexedDB wrapper

Infrastructure

  • PostgreSQL 16 - Database
  • Docker - Containerization
  • nginx - Frontend serving

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is open source and available under the MIT License.

Acknowledgments

  • Inspired by Google Keep
  • Icons from Material Design
  • Color palette from Google Keep