Files
inkwell/README.md
T
bvandeusenandClaude Opus 5.5 5556bb2136
CI & Build / Build now, or wait for Android? (push) Successful in 2s
Android / Build, or is the channel already serving this? (push) Successful in 2s
Android / Core and FFI clippy and tests (push) Skipped
Android / Kotlin + Rust (APK) (push) Skipped
Android / Build the server image (push) Skipped
CI & Build / Python lint (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 10s
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 / Python tests (push) Successful in 13s
CI & Build / integration (push) Successful in 1m34s
CI & Build / Build & push image (push) Successful in 29s
Compose files run on the orchestrator's defaults (#6220)
Shipped stacks drop every healthcheck and the depends_on
service_healthy wait, per rule 198 "Shipped compose files run on the
orchestrator's defaults: no healthcheck, no restart policy". A
pg_isready healthcheck turns slow storage into a killed database. The
app needs neither: inkwell.dbwait waits up to 60s for Postgres at
start, and the engine's pool_pre_ping retries per request.

- docker-compose.yml: db and app healthchecks removed; depends_on is
  a plain start order. Comments cut to one or two lines per setting
  under rule 32 "Comments describe the product to a stranger": the
  family/rule references are gone, and the three-way port explanation
  points at docs/public-hosting.md, which already holds it.
- docker-compose.dev.yml: db healthcheck and service_healthy removed;
  header shortened.
- README: the copy-paste stack loses the same two blocks.

restart: unless-stopped stays: plain Compose defaults to no restart,
and Swarm ignores the key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 19:45:46 -04:00

123 lines
4.5 KiB
Markdown

# Inkwell
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).
- **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.
## Layout
```
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
```
## 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`:
```yaml
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: inkwell
POSTGRES_PASSWORD: CHANGE_ME # change this
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
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
ports:
- "5000:5000"
volumes:
inkwell-db:
inkwell-data:
```
Then 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.
## Milestones
- **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.
Work happens on `dev`; `main` is protected (PR-only).