bvandeusenandClaude Opus 5.5 66fd5707b7
CI & Build / Python lint (push) Successful in 2s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Android / Build, or is the channel already serving this? (push) Successful in 3s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Skipped
Desktop (Tauri) / Tauri desktop (Linux) (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Skipped
Desktop (Tauri) / Update manifest (push) Skipped
CI & Build / Web typecheck and unit tests (push) Successful in 9s
CI & Build / Python tests (push) Successful in 12s
Android / Core and FFI clippy and tests (push) Successful in 25s
CI & Build / integration (push) Successful in 1m29s
CI & Build / Build & push image (push) Skipped
Android / Kotlin + Rust (APK) (push) Successful in 6m56s
Android / Build the server image (push) Successful in 1s
tagQueryAt decides in one check, within detekt's return limit
Task #6316.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:38:36 -04:00

Inkwell logo

Inkwell

Capture a thought in under a second, and find it again later.

Inkwell is a self-hosted notes app built around a Keep-style board of cards. Type and it's saved; tag with #words, tick checklists, set reminders, attach files, and search or browse by date when you need something back. It runs in the browser, as a desktop app for Linux and Windows that works offline, and as an Android app, all syncing with your own server.

Quick start

Save this as docker-compose.yml, replace both CHANGE_MEs with the same password, and run docker compose up -d:

services:
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: inkwell
      POSTGRES_PASSWORD: CHANGE_ME
      POSTGRES_DB: inkwell
    volumes:
      - inkwell-db:/var/lib/postgresql/data

  app:
    image: git.fabledsword.com/bvandeusen/inkwell:latest
    restart: unless-stopped
    depends_on:
      - db
    environment:
      INKWELL_DATABASE_URL: postgresql+asyncpg://inkwell:CHANGE_ME@db:5432/inkwell
    volumes:
      - inkwell-data:/var/inkwell
    ports:
      - "5000:5000"

volumes:
  inkwell-db:
  inkwell-data:

Open http://<host>:5000 and register. The first account becomes the admin.

The repository's own docker-compose.yml is the same stack read from a .env file; copy .env.example to .env to use it.

Configuration

Variable Required What it is
INKWELL_DATABASE_URL yes The Postgres connection URL.
INKWELL_SECRET_KEY no Signs sessions. Unset, one is generated and kept in the database.
INKWELL_LOG_LEVEL no INFO by default. Sign-ins and other credential events log at INFO.

Everything else — site name, sign-ups, email, attachment limits, trash retention, link previews, backups, proxy hops — is set by the admin in Settings.

Before you put it on the internet

Close registration, terminate TLS and forward X-Forwarded-Proto, stop publishing the app port directly, and back up the attachment volume as well as the database. docs/public-hosting.md walks through each one, and lists what the app already hardens on its own.

The browser only offers to install Inkwell as an app (PWA) over HTTPS or localhost.

Updating and backups

  • Update: docker compose pull && docker compose up -d. Migrations run on start.
  • Roll back: pin image: …/inkwell:<commit-sha>. Every build on main publishes one.
  • Back up: turn on scheduled backups in Settings → Backups (a folder, an S3-compatible bucket, or Fabled Archives). See docs/backups.md. Otherwise back up both volumes: inkwell-db and inkwell-data.

Features

Capturing

  • A masonry board of cards. Start typing anywhere on it to write a new note.
  • Notes save as you type; there is no save button.
  • Checklists inside a note, ticked straight from the card.
  • Images and files attached to a note, shown on its card; click an image to see it full size or copy it back out.
  • Paste a link and it gets a preview card.
  • On the desktop, a global hotkey opens a small capture window over whatever you're doing.
  • On Android, share text or images into Inkwell from any app.

Organising

  • #tags written in the text become tags; a line of tags on its own is tidied away.
  • Each tag gets a colour, and tags can be renamed, recoloured, merged and deleted.
  • Pin, archive, drag to reorder.
  • Trash with undo, emptied after a retention period the admin sets.

Finding

  • Full-text search, combined with filters for tags, attachments and notes shared with you.
  • A timeline that groups notes by when you wrote them.
  • A command palette that jumps to any note by name.
  • Version history: every editing session keeps the note as it was before.

Remembering

  • Reminders, one-off or repeating, with Done and snooze from the card or the notification.
  • The desktop and Android apps notify you when the app isn't in front.

Sharing and accounts

  • Share a note with a person or a group, to view or to edit.
  • Invites, groups, per-account storage limits and an activity log for the admin.
  • Password reset by email, or by a link the admin makes.
  • Export everything as a zip, and import an Inkwell export or a Google Keep Takeout.

Every device

  • Web app, installable as a PWA.
  • Desktop app for Linux (.deb, Arch package, AppImage) and Windows. It keeps its own local store, works with no server at all, and syncs once you link one. Install it from your server's Account page, or with curl -fsSL https://<your-server>/install.sh | sh.
  • Android app, downloaded from your server and updated by it.

Troubleshooting

What you see Why Fix
The app container restarts with a database error at startup Postgres isn't up yet, or the two passwords differ Wait a minute; check both CHANGE_MEs match
No "Install app" option in the browser The page isn't on HTTPS Serve it over HTTPS, or open it on localhost
The desktop or Android app can't reach a plain-HTTP server A bare hostname is read as https:// Type the address with http://
A deployment upgraded from ThoughtSync shows no notes It's pointed at new, empty volumes Set INKWELL_DB_VOLUME and INKWELL_DATA_VOLUME; see .env.example

Development

Backend (needs Postgres at INKWELL_DATABASE_URL):

pip install -e ".[dev]"
alembic upgrade head
hypercorn 'inkwell.app:create_app()' --bind 0.0.0.0:5000

Frontend, proxying /api to :5000: cd frontend && npm install && npm run dev (http://localhost:5173). Or run everything with hot reload: docker compose -f docker-compose.dev.yml up.

Path What's there
src/inkwell/ The Quart server
alembic/ Database migrations
frontend/ The Vue web app, also the desktop app's UI
core/ The Rust store and sync engine the desktop and Android apps share
desktop/ The Tauri desktop app and its packaging
android/ The Kotlin/Compose Android app

How devices sync is in docs/sync.md; how the Android app reaches a server is in docs/android-distribution.md. Work happens on dev; main is the stable line.

Licence

Inkwell is free software under the GNU Affero General Public License v3.0 only (AGPL-3.0-only). If you run a modified version for other people over a network, the licence requires you to offer them its source.

S
Description
Self-hosted personal thought-capture web app (FabledSword family) — a Google-Keep-style masonry post-it board for fast idea capture, growing into a lightweight second brain. Quart + Vue 3 + PostgreSQL.
Readme AGPL-3.0
4.2 MiB
2026-08-23 16:50:53 -04:00
Languages
Python 29.8%
Rust 26%
Kotlin 20.3%
Vue 12%
TypeScript 6.5%
Other 5.3%