Root, CI, docs and packaging describe the project to a stranger (#6225)
Android / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 12s
CI & Build / Python tests (push) Successful in 23s
Android / Core and FFI clippy and tests (push) Successful in 51s
CI & Build / integration (push) Successful in 1m49s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 2m20s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m6s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m46s
Desktop (Tauri) / Update manifest (push) Successful in 3s
Android / Kotlin + Rust (APK) (push) Successful in 8m31s
Android / Build the server image (push) Successful in 1s

#6225 "Comment standard sweep: root, config, CI workflows, docs and
packaging", milestone 546 "Bring the repo to the comment standard (rule 32)
and compose defaults (rule 198)".

- README restructured in newcomer order: what it is, quick start,
  configuration, setup pitfalls, updating and backups, features,
  troubleshooting, development, licence. Its quick-start compose carries
  no restart policy.
- Dockerfile and .env.example trimmed to one or two lines per setting.
- Workflows, packaging scripts and docs lose issue, run, note, rule and
  milestone references and their history narration. Comments that still
  described path filters and "step 6" as future work now state what the
  workflows do.
- ci-requirements.md rewritten against the current workflows. It said the
  build gated on typecheck and lint only, the integration lane did not
  gate, a v* tag published :latest, and ci-tauri lacked bsdtar and zstd;
  none of those is true now (the last checked against run 9630's log).
- Echo strings in guard-forward.sh and version.sh drop their note
  citations; every string a test asserts on is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 20:44:02 -04:00
co-authored by Claude Opus 5.5
parent 01b53118b9
commit f16224b07c
18 changed files with 666 additions and 843 deletions
+126 -88
View File
@@ -1,84 +1,41 @@
# Inkwell
<p align="center">
<img src="frontend/public/icon.svg" width="96" alt="Inkwell logo">
</p>
Self-hosted personal thought-capture web app in the **FabledSword** family — a
Google-Keep-style **masonry post-it board** for capturing disparate thoughts in
under a second, designed to grow into a lightweight second brain (labels, search,
`[[wiki-links]]`, graph).
<h1 align="center">Inkwell</h1>
- **Stack:** Quart (async Python) + Vue 3 + PostgreSQL, Docker, Fabled-Git CI.
- **Auth:** native email + password (signed-cookie sessions). Multi-user with the
family owner + direct-share + group-share ACL.
- **Web-first.** An Android companion is a later, conditional milestone.
<p align="center">Capture a thought in under a second, and find it again later.</p>
## Layout
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.
```
src/inkwell/ Quart app (app factory, auth, models, ACL, config, db)
alembic/ async migrations (schema built via `alembic upgrade head`)
tests/ DB-free unit tests (pytest)
frontend/ Vue 3 + Vite + TypeScript + Tailwind SPA
Dockerfile 2-stage build (Vue -> python:3.12-slim runtime)
docker-compose.yml local app + Postgres stack
```
## Quick start
## Development
Backend (needs a Postgres reachable at `INKWELL_DATABASE_URL`):
```sh
pip install -e ".[dev]"
alembic upgrade head
hypercorn 'inkwell.app:create_app()' --bind 0.0.0.0:5000
```
Frontend (proxies `/api` to `:5000`):
```sh
cd frontend
npm install
npm run dev # http://localhost:5173
```
Or run it in Docker. **Hot-reload dev stack** (edit code, changes reload live) —
Postgres + backend + Vite dev server, app at http://localhost:5173:
```sh
docker compose -f docker-compose.dev.yml up
```
Or the **full production image** (SPA baked in) at http://localhost:5000:
```sh
docker compose up --build
```
## Deploy (self-host)
A complete two-container stack (app + Postgres) you can copy and run. Save it as
`docker-compose.yml`, change the two `CHANGE_ME` passwords (they must match), then
`docker compose up -d`:
Save this as `docker-compose.yml`, replace both `CHANGE_ME`s with the same password,
and run `docker compose up -d`:
```yaml
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: inkwell
POSTGRES_PASSWORD: CHANGE_ME # change this
POSTGRES_PASSWORD: CHANGE_ME
POSTGRES_DB: inkwell
volumes:
- inkwell-db:/var/lib/postgresql/data
app:
image: git.fabledsword.com/bvandeusen/inkwell:latest # :dev for the current dev build
restart: unless-stopped
image: git.fabledsword.com/bvandeusen/inkwell:latest
depends_on:
- db
environment:
INKWELL_DATABASE_URL: postgresql+asyncpg://inkwell:CHANGE_ME@db:5432/inkwell
volumes:
- inkwell-data:/var/inkwell # uploaded images; omit if you don't use attachments
- inkwell-data:/var/inkwell
ports:
- "5000:5000"
@@ -87,36 +44,117 @@ volumes:
inkwell-data:
```
Then open `http://<host>:5000` and register — **the first account becomes the admin**.
Open `http://<host>:5000` and register. **The first account becomes the admin.**
- **Only `INKWELL_DATABASE_URL` is required.** `INKWELL_SECRET_KEY` is optional; if
unset, a signing key is generated and persisted in the database (sessions survive restarts).
- Uploaded images live under the `inkwell-data` volume at `/var/inkwell`.
- The app waits for the database and runs migrations (`alembic upgrade head`) automatically on start.
- **Image tags:** `:latest` (stable, built from `main`) · `:dev` (latest `dev`
build) · `:<git-sha>` on `main` only (immutable, the rollback unit). There are
no version-shaped tags: nothing pins one, and the build reports its own version
at `/api/config` and `/health`.
- **Putting it on the public internet:** there are four things to do first — close
registration, terminate TLS and forward `X-Forwarded-Proto`, stop publishing the app
port, and back up the attachment volume as well as the database. See
[docs/public-hosting.md](docs/public-hosting.md), which also lists what the app
hardens on its own and what it deliberately doesn't.
- **Install as an app (PWA):** Inkwell is installable ("Add to Home Screen" / the
browser's install button) for an app-like window. Browsers only offer install over a
**secure context**, so put the app behind a reverse proxy terminating **HTTPS** (or reach
it via `localhost`) — plain `http://<host>:5000` won't show the install prompt.
The repository's own [docker-compose.yml](docker-compose.yml) is the same stack read
from a `.env` file; copy [.env.example](.env.example) to `.env` to use it.
## Milestones
## Configuration
- **M0 — Foundation & Identity** ✅: Quart+Vue+Postgres skeleton, native auth,
sharing-ACL spine, Fabled-Git CI.
- **M1 — Capture Core** ✅: note model + masonry board (quick-add, colors, pin,
edit-in-place, archive, trash+restore).
- **M1.5 — Roles & DB-backed Settings** ✅: first user is admin, admin Settings UI,
`DATABASE_URL` is the only required env.
- M2 — Organize: labels/tags, search, checklists, attachments.
- M3 — Second-brain seeds: `[[wiki-links]]`, backlinks, graph view, reminders.
- M4 — Launch hardening: responsive/PWA, settings UI, polish.
| 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. |
Work happens on `dev`; `main` is protected (PR-only).
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](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](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_ME`s 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](.env.example) |
## Development
Backend (needs Postgres at `INKWELL_DATABASE_URL`):
```sh
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](docs/sync.md); how the Android app reaches a
server is in [docs/android-distribution.md](docs/android-distribution.md). Work
happens on `dev`; `main` is the stable line.
## Licence
No licence has been chosen yet, so all rights are reserved.