Skip to content
Β 
Β 

Latest commit

Β 

History

2,658 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Latest release Last update Active development

Easy install Docker Android Android TV PWA

The GlassKeep website: the feature list, real screenshots, the six workspace themes, and the install instructions

victor-root.github.io/glasskeep-enhanced has the full feature list with real screenshots, the six workspace themes in light and dark, and the install instructions.

✨ GlassKeep

A self-hosted, local-first notes app focused on offline support, safer note management, mobile usability, straightforward self-hosting, and a native Android companion app.

Originally based on Glass Keep.


🎯 Highlights

GlassKeep brings together:

  • πŸ”„ local-first usage and offline support
  • 🌐 cross-server collaboration: pair two GlassKeep servers and share notes across them in real time
  • πŸ—‘οΈ safer note deletion with Trash / restore
  • πŸ“± first-class mobile usability
  • ✏️ a WYSIWYG / live-formatting editor for text notes
  • πŸͺŸ side-by-side note reading and comparison
  • πŸ€– a native Android companion app
  • πŸ“Ί an Android TV layout designed for couch use and remote control
  • 🌍 a clean, extensible i18n foundation
  • πŸ› οΈ straightforward self-hosting
  • ✨ a polished, stable experience
  • 🎨 a full-featured drawing mode
  • πŸ’¬ configurable AI assistant with local or remote endpoints
  • πŸ” server-side encryption & passkeys
  • πŸ”” in-app update notifications
  • πŸš€ one-click in-app updates from the admin panel: live progress, cancel/rollback, AI-translatable changelog after install
  • πŸŽ™οΈ audio notes
  • πŸ–ΌοΈ cohesive branding and icons across the web app, PWA, favicon, Android launcher, and TV banner

For a more complete and structured overview, see:

πŸ‘‰ IMPROVEMENTS.md


🧩 Core features

GlassKeep also includes:

  • πŸ” authentication and multi-user support
  • πŸ‘‘ admin panel
  • πŸ—οΈ secret recovery key login
  • πŸ“ Markdown notes, checklists, drawings, and images
  • πŸ‘₯ real-time collaboration on notes
  • πŸ“¦ import / export with cross-device duplicate detection
  • πŸ“₯ Google Keep import (Takeout .zip, full colour, images, line breaks)
  • πŸ€– optional AI assistant via any OpenAI-compatible endpoint (Ollama, Open WebUI, LiteLLM, OpenAI, …)
  • πŸ“² PWA support
  • ⏰ note reminders with notifications everywhere: in-app, browser/PWA push, and on the Android app even when fully closed (no Google, no third-party service)

πŸ“± Android app + πŸ“Ί Android TV

A native Android companion app is available for GlassKeep, making self-hosted mobile usage more convenient.

The Android app is a WebView wrapper for GlassKeep and does not necessarily change with every project release.

The same APK also runs on Android TV: the app detects leanback hardware (or the ?tv=1 URL override) and switches the React frontend to a dedicated TV layout designed for the couch and the D-pad. No separate build, no separate install: phone, tablet and TV all share one codebase. See section 4 of IMPROVEMENTS.md for the full TV layout details.

The launcher icon, the Android TV banner, the PWA install icon, and the favicon have all been redrawn from a single master so the app looks coherent across every surface.

Download latest Android APK

Get it on Omnify Get it on GitHub

Current APK version: 1.4.7

The Android source code is available in the android/ directory.

πŸ”‘ Passkeys in the Android app

From APK 1.3.0 onward, the Android app supports passkeys natively (fingerprint, face unlock, hardware security keys, password managers: the same authenticators you'd use from your browser). It works out-of-the-box with the official APK on a regular HTTPS install.

⚠️ Both the APK and the server must be up to date. Android passkeys and cross-device QR sign-in rely on endpoints introduced in server v2.3.7. Pair APK 1.3.0+ with server v2.3.7+; mixing an old server with a new APK (or vice-versa) silently falls back to password-only login.

πŸ“– Full setup guide, prerequisites, custom-build instructions and troubleshooting: see PASSKEYS.md. Worth a read even for the smooth path: the non-standard-port and reverse-proxy edge cases trip a lot of self-hosted setups.


πŸ–₯ Installation

🐧 Recommended native installation (Debian / Ubuntu / Proxmox LXC)

Run as root on a clean Debian-based system:

curl -fsSL https://raw.githubusercontent.com/Victor-root/glasskeep-enhanced/main/install.sh | sudo bash

The script is designed to make installation as simple as possible:

  • it directly offers install / update / uninstall

  • it asks for the important information up front

  • it creates the admin account

  • it generates the configuration automatically

  • it sets up the systemd service

  • it can handle HTTPS depending on your setup:

    • reverse proxy
    • self-signed certificate
    • custom SSL certificate
  • it optionally sets up at-rest encryption to protect notes in the database (you can enable it later from the admin panel if you prefer)

This is the main installation method recommended for this project.

⚠️ Two settings to check on your reverse proxy

Raise the request size limit. Notes carry their images inline, so a note with a couple of photos is several MB. GlassKeep itself accepts up to 160 MB, but nginx refuses anything over 1 MB by default and answers 413 before the request ever reaches it. That breaks image uploads, imports, and cross-server sharing of notes with pictures. In your server { … } block:

client_max_body_size 160m;

Caddy has no such limit by default; Apache uses LimitRequestBody 0; on Traefik it is unlimited unless you set buffering.maxRequestBodyBytes.

Keep custom error pages away from /api/. If your proxy replaces upstream error responses with its own pages, GlassKeep's answers stop reaching the browser. The app says why a request failed in the response body, so a wrong passphrase, a rejected recovery key or a taken email all arrive as an HTML page the app cannot read, and the screen can only fall back to a vague message. In nginx this is proxy_intercept_errors on; combined with error_page; turn it off where the API lives:

location /api/ {
    proxy_intercept_errors off;
    proxy_pass http://127.0.0.1:8080;
    # … your existing proxy_set_header lines
}

Your pretty pages keep working everywhere else. On Traefik the equivalent is scoping the errors middleware so it does not cover /api/; Caddy's handle_errors should likewise skip it.


🐳 Docker installation

Docker is also available, especially for NAS and similar environments. The same docker-compose.yml works whether you deploy from a terminal or from a graphical interface. Pick the one that matches your setup.

πŸ“‹ The compose file

services:
  glasskeep:
    image: ghcr.io/victor-root/glasskeep-enhanced:latest
    container_name: glasskeep
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      ADMIN_EMAIL: "your-admin-username"
      ADMIN_PASSWORD: "choose-a-strong-password"
      # Optional, for passkeys on a public domain (see the passkey note).
      # WEBAUTHN_RP_ID: "notes.example.com"
    volumes:
      - ./data:/data
      # Lets the admin panel update the container in one click.
      - /var/run/docker.sock:/var/run/docker.sock

Once the container is up, open http://<your-host>:8080 and sign in with the admin username and password you chose.

πŸ”‘ Passkeys behind a reverse proxy

A passkey is tied to a domain name, and the server will not read that name off the requests it receives, since whoever sends a request writes it. Confirm your domain once from the admin panel, under Login page settings, or uncomment WEBAUTHN_RP_ID in the compose file to pin it there instead. Both work; on a local address there is nothing to do at all.

πŸš€ Deploy

πŸ–₯️ Command line (Linux / macOS / WSL)

Edit the ADMIN_EMAIL and ADMIN_PASSWORD values below, then paste the whole block into your terminal. It creates the folder, writes the compose file and starts the container in one go.

mkdir -p ~/glasskeep && cd ~/glasskeep && cat > docker-compose.yml <<'EOF'
services:
  glasskeep:
    image: ghcr.io/victor-root/glasskeep-enhanced:latest
    container_name: glasskeep
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      ADMIN_EMAIL: "your-admin-username"
      ADMIN_PASSWORD: "choose-a-strong-password"
      # Optional, for passkeys on a public domain (see the passkey note).
      # WEBAUTHN_RP_ID: "notes.example.com"
    volumes:
      - ./data:/data
      # Lets the admin panel update the container in one click.
      - /var/run/docker.sock:/var/run/docker.sock
EOF
docker compose up -d

🐳 Don't have Docker yet?

  • Linux: one-liner that works on Debian / Ubuntu / Fedora / Arch / …:

    curl -fsSL https://get.docker.com | sudo sh
  • macOS / Windows: install Docker Desktop and make sure it's running before continuing.

πŸ™ Portainer
  1. Open Portainer β†’ select your environment β†’ Stacks β†’ Add stack
  2. Give it a name (e.g. glasskeep)
  3. Choose Web editor and paste the compose file above
  4. Edit the ADMIN_EMAIL / ADMIN_PASSWORD values
  5. Click Deploy the stack

The container appears in the Containers tab once it's pulled and started.

πŸ“¦ Synology (Container Manager / DSM 7.2+)
  1. Open Container Manager β†’ Project β†’ Create
  2. Set Project name to glasskeep and Path to a folder of your choice (e.g. /docker/glasskeep)
  3. Source β†’ Create docker-compose.yml and paste the compose file above
  4. Edit the ADMIN_EMAIL / ADMIN_PASSWORD values
  5. Click Next β†’ Done

On DSM the Docker socket is owned by root:root (there is no docker group). GlassKeep's entrypoint detects this and grants the in-container node user access to it automatically, so the in-app "Update now" button works after the container's first start, no extra setup. If you added the socket mount to an existing project, recreate the container once (stop then start the project in Container Manager, or docker compose up -d --force-recreate) so the entrypoint can apply the permission.

🟧 Unraid

The simplest route is the Compose Manager plugin (Community Apps):

  1. Install Compose Manager from Community Apps if you don't have it
  2. Go to the Docker tab β†’ Add New Stack β†’ name it glasskeep
  3. Click the gear β†’ Edit Stack β†’ Compose and paste the compose file above
  4. Adjust the ADMIN_* values, click Save
  5. Click Compose Up

You can also use the native Add Container form, but the compose route preserves the Docker-socket mount for one-click updates.

🏠 CasaOS
  1. Click the βž• icon on the dashboard β†’ Install a customized app β†’ Import
  2. Paste the compose file above
  3. Edit the ADMIN_EMAIL / ADMIN_PASSWORD values
  4. Click Install

The app shows up on the dashboard with a launcher tile pointing to port 8080.

πŸ›Ÿ TrueNAS SCALE

TrueNAS SCALE doesn't ship a generic compose UI, so the simplest route is the built-in shell:

  1. Open System Settings β†’ Shell
  2. Create a folder for the stack: mkdir -p /mnt/<your-pool>/glasskeep && cd $_
  3. Paste the compose file above into docker-compose.yml
  4. Run docker compose up -d

πŸ”„ Update

The easiest way is to open the admin panel and click "Update now", the new image is pulled and the container is replaced automatically. Your data is preserved.

If you prefer the command line:

cd ~/glasskeep && docker compose pull && docker compose up -d

πŸ’‘ Existing install without one-click updates? You have two options:

  • Patch the existing compose, add - /var/run/docker.sock:/var/run/docker.sock under the volumes: block of your current docker-compose.yml, then re-deploy the stack once.
  • Redeploy from the current example, replace your docker-compose.yml with the one at the top of this section (keep your ADMIN_* values and your ./data volume), then re-deploy. The current example already ships the right configuration, so you'll never have to touch the file again.

Either way, the in-app Update now button takes over from there.


πŸ€– AI assistant

GlassKeep no longer ships an embedded local model, it was too small to be genuinely useful. Instead, it connects to any OpenAI-compatible chat endpoint, so each instance picks what fits its hardware, privacy needs and budget, fully local with Ollama / Open WebUI, or remote via OpenAI, OpenRouter, …

Two AI features are available once configured:

  • πŸ”Ž Global AI search, ask questions across your notes from the search bar. The backend pre-selects relevant notes before calling the model, and only cites notes it actually received (no fabricated sources).
  • πŸ—’οΈ Per-note assistant, discuss the currently opened note with the AI. Conversations are temporary by default; a save button can keep them per note.

Admins control AI at the instance level: disable it entirely, configure a server-side provider (optionally shared with users so the API key stays hidden), or let each user bring their own endpoint in their settings.

⚠️ Notes sent to a remote provider leave your GlassKeep instance. For sensitive data, prefer a local setup such as Ollama + Open WebUI on your LAN/LXC.

Recommended starter model (light enough to run even on CPU-only setups):

ollama pull qwen3:4b-instruct-2507-q4_K_M

πŸ“˜ Full setup guide, base-URL gotchas, model recommendations, privacy notes, admin/user config flows, AI_CHANGES.md


🌍 Adding a new language

  1. Copy src/i18n/locales/en.js to a new file, e.g. src/i18n/locales/it.js, and translate all values

  2. In src/i18n/index.js:

    • import the new locale: import { it } from "./locales/it";
    • add the language code to SUPPORTED_LANGUAGES: export const SUPPORTED_LANGUAGES = ["fr", "en", "it"];
    • add the native display name to LANGUAGE_NATIVE_LABELS: it: "Italiano"
    • extend the dict selector: const dict = locale === "fr" ? fr : locale === "it" ? it : en;
  3. In server/index.js, add the new code to the validation allowlist in PATCH /api/user/profile (the line that checks lang !== "fr" && lang !== "en")

  4. Rebuild the app: npm run build

The language selector in the settings panel will automatically show the new option. Missing keys fall back to English automatically.


πŸ—ΊοΈ Roadmap

πŸ—“οΈ Planned

  • More translations with better RTL language support
  • Make the Android app available on F-Droid

πŸ’­ Under consideration

  • (open, suggestions welcome)

πŸ” Security

  • JWT_SECRET is automatically generated by the native install script
  • if you run the server outside the script, you must provide your own valid secret
  • serving the app behind HTTPS is still recommended
  • the recovery secret key should be treated like a password

πŸ™ About this project

GlassKeep originally started from the foundation of Glass Keep, a project I genuinely liked from the start.

What began very modestly as a French translation gradually evolved into a much broader project focused on local-first usage, offline support, mobile usability, simpler self-hosting, and a more complete day-to-day experience.

GlassKeep now follows its own direction, but it still keeps clear attribution to the original project and its author.

Thanks to nikunjsingh93 for the original foundation that made this possible.


πŸ™Œ Special thanks

Thanks to @Rikhtar for active testing, bug reports, UX feedback.


πŸ“ License

MIT

Originally based on Glass Keep by nikunjsingh93. Original attribution remains preserved in the project.

About

A modern, private, self-hosted alternative to Google Keep, with offline access, collaboration, and powerful extended features.

Topics

Resources

Contributing

Security policy

Stars

15 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages