Skip to content

Latest commit

 

History

154 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cleep

A self-hosted, open-source Google Keep alternative. Fast, colorful notes with checklists, photo/video/audio attachments, and labels — running entirely on your own hardware, backed by your own Postgres database.

License: Apache-2.0 Docker Image Node

Try the live demo → — a static build with a mocked, browser-only backend (see Demo mode) so you can click around without installing anything.

No subscriptions, no ads, no third party reading your notes — just your data, on your server.

Cleep notes grid, light mode Cleep notes grid, dark mode

Editing a checklist note

Features

  • 📝 Notes & checklists — pin, color, archive, trash (with undo everywhere it matters)
  • 🖼️ Attachments — photos, videos, and audio recorded straight from the browser
  • 🔗 Link previews — every URL in a note gets a card in both the grid and the open note: the OpenGraph image, or the most prominent image on the page when there's no OG tag, falling back to the site's favicon and then a plain domain tile — so there's always something to show. The image and favicon are fetched and re-served by Cleep, so cards render even behind a strict img-src 'self' CSP and off-site hosts never see the reader's IP
  • 🏷️ Labels for organizing notes into collections, plus multi-select bulk actions
  • 🔍 Search across your whole library, plus filter by one or several collections at once (Any/All)
  • ⌨️ Keyboard shortcuts/ to search, c new note, l new checklist, g n/a/t to jump around, ? for the full list
  • 👥 Multi-user accounts with session-based auth — everyone gets their own private notes; admins can reset a locked-out user's password
  • 📦 Import from Google Keep and export everything as a zip — your data is never trapped
  • 📱 Installable as a PWA — add it to your home screen and it works offline (needs HTTPS, see below)
  • 🐳 One docker compose up — Postgres and the app, nothing else to configure

Quick start (Docker Compose)

  1. Clone the repo and set up your environment file:

    git clone https://github.com/blindpassasjer/cleep.git
    cd cleep
    cp .env.example .env

    Open .env and fill in:

    • SESSION_SECRET — generate one with openssl rand -hex 32
    • POSTGRES_PASSWORD and DATABASE_URL — keep the password in sync between the two
    • ADMIN_EMAIL / ADMIN_PASSWORD (optional) — bootstraps your first account on startup, so you can sign in immediately instead of building a registration flow

    Every variable is documented inline in .env.example, including COOKIE_SECURE and TRUST_PROXY for deployments behind a reverse proxy, and the optional AUTH_RATE_LIMIT_MAX / UPLOAD_RATE_LIMIT_MAX knobs for tuning the rate limiter.

  2. Start it:

    docker compose up -d
  3. Open http://<host>:6169 and sign in with the admin account you configured above.

That's it — Postgres migrations run automatically before the app starts.

Upgrading

git pull            # if you track the repo (for docker-compose.yml + scripts)
docker compose pull
docker compose up -d

Schema migrations run automatically on container start. No .env changes are required — every setting added since your last pull is optional and defaults to the previous behavior. Two things worth knowing:

  • If you already run behind TLS with COOKIE_SECURE=true, Cleep now also sends an HSTS header (6-month max-age). That's correct for an HTTPS deployment but means browsers will refuse plain http:// to that hostname for a while — as before, only set COOKIE_SECURE=true when TLS is actually terminated in front of Cleep.
  • If you deploy with this repo's docker-compose.yml, git pull it so the new AUTH_RATE_LIMIT_* / UPLOAD_RATE_LIMIT_* passthrough lines are present (they default to empty and are harmless if missing).

Where your data lives

Everything is stored in plain, host-visible folders instead of opaque Docker volumes, so you can browse, back up, or move it like any other files:

What Where
Notes, users, labels (Postgres) ./data/postgres
Photos, videos, audio recordings ./data/attachments/<user-id>/<note-id>/<file>

Backups

scripts/backup.sh writes a timestamped folder (a pg_dump of the database + a tar of ./data/attachments) under ./backups. Run it from the directory with docker-compose.yml:

./scripts/backup.sh                       # -> ./backups/<timestamp>/
./scripts/backup.sh --keep 7              # prune to the 7 newest afterwards

Restore one with ./scripts/restore.sh ./backups/<timestamp> (it prompts before overwriting; pass --yes to skip). A daily backup is one cron line: 0 3 * * * cd /srv/cleep && ./scripts/backup.sh --keep 14.

Import / export

Settings → Your data has an Export all data button (a .zip of every note, label and attachment) and an Import from Google Keep picker — export your notes from Google Takeout (select Keep), then upload the resulting .zip. Titles, checklists, colors, labels, pin/archive state and media all come across; trashed Keep notes are skipped.

Publishing your own image

The docker-publish.yml GitHub Actions workflow builds and pushes ghcr.io/blindpassasjer/cleep on every push to main. GHCR packages default to private on their first publish, even in a public repo — after the first workflow run, open the package settings on GitHub and set its visibility to public so docker compose pull works without authentication.

Demo mode

The live demo is a static build deployed to GitHub Pages by .github/workflows/deploy-demo.yml on every push to main. GitHub Pages can only serve static files, so the demo build swaps the real Express/Postgres API (src/api/client.ts) for a mock (src/api/mockClient.ts) that runs entirely in the browser: notes and labels are saved to localStorage on your own device, attachments live only in memory for the session, and there's no real login, multi-user, or admin behavior. Nothing is sent to a server. Use "Reset demo data" in Settings to start over.

Build it yourself with:

VITE_DEMO=true npm run build

To run the demo as a live dev server instead — e.g. behind a reverse proxy on a test host — use npm run dev:demo. It starts Vite with the mock backend, serves from the domain root (not the /cleep/ subpath the Pages build uses), trusts test.manriquez.no, and points HMR back through that host's TLS. It also binds Vite's port with strictPort, so if another project already has dev:demo running there this one exits with "Port 5173 is already in use" — stop the other one first, and whichever is running is what test.manriquez.no shows. Change the host by editing VITE_DEV_ORIGIN in package.json, or the port with PORT=… npm run dev:demo.

PWA and HTTPS

Cleep is installable as a Progressive Web App — an "Install"/"Add to Home Screen" prompt, its own window, and an offline app shell. This, like microphone access for audio recordings, only works over a secure context (HTTPS, or localhost). Browsers won't register a service worker at all on a plain http://<nas-ip>:6169 origin, so with the default setup above neither installability nor offline support will be available — the app itself still works fine either way.

To unlock both, put a reverse proxy with a TLS certificate in front of Cleep — Caddy, Traefik, Nginx Proxy Manager, or your NAS's built-in one all work well. Once you do, also set TRUST_PROXY=true in .env — otherwise every request looks to the app like it's coming from the proxy's own IP, which shares the login rate limiter across all visitors and can trip a "Too many requests" error after perfectly normal use.

Local development

npm install
npm run server:dev   # API server on :6169
npm run dev           # Vite dev server on :5173, proxies /api to :6169

Point DATABASE_URL at a local Postgres instance, set SESSION_SECRET and ATTACHMENTS_DIR (any local folder for uploaded files), then run npm run db:migrate before starting the server.

To develop the frontend against a remote backend instead of running npm run server:dev locally, set VITE_API_PROXY_TARGET in .env (e.g. VITE_API_PROXY_TARGET=https://test.manriquez.no) and just run npm run dev.

Tests

DATABASE_URL_TEST=postgres://cleep:cleep@localhost:5432/cleep_test npm test

npm test runs the Vitest server-integration suite against a real Postgres (it creates and truncates tables itself — point DATABASE_URL_TEST at a throwaway database). CI (.github/workflows/ci.yml) runs lint, both builds, and the tests against a Postgres service container on every push and PR.

Tech stack

React 18 · Express · PostgreSQL · Drizzle ORM · TypeScript · Vite — no framework lock-in, no managed cloud service required, just a small, readable codebase you can actually audit.

Contributing

Issues and pull requests are welcome — this is a small enough codebase that most changes are straightforward to review.

License

Apache-2.0

About

Yet another Google Keep clone

Topics

Resources

Stars

22 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages