rename: the server is Inkwell — package, env vars, image, compose, export marker
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
CI & Build / TypeScript typecheck (push) Successful in 7s
CI & Build / Python tests (push) Successful in 15s
CI & Build / integration (push) Successful in 45s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 4m17s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 7m33s
Desktop (Tauri) / Update manifest (push) Successful in 7s
Android / Kotlin + Rust (APK) (push) Successful in 11m25s

Step 2 of milestone 481. The operator chose a full rename (Scribe note 5071), so
this goes past the display strings into the identities:

- src/thoughtsync → src/inkwell; every import, the Dockerfile and both compose
  commands, alembic env, pyproject
- THOUGHTSYNC_* → INKWELL_* (database URL, secret key, log level, tag/port/bind)
- container data dir /var/thoughtsync → /var/inkwell
- image git.fabledsword.com/bvandeusen/inkwell; Postgres user/db default inkwell;
  CI's integration service follows
- the files the image serves are inkwell.*. fetch-clients.sh still fetches the
  thoughtsync-named release assets, because the lanes that publish them are
  renamed in steps 3 and 4
- exports are written with app "inkwell"

Two deliberate exceptions, both because data rides on them:

- compose volumes are now named explicitly and overridable (INKWELL_DB_VOLUME,
  INKWELL_DATA_VOLUME), so a deployment installed as ThoughtSync points at the
  volumes and DB identity it already has. .env.example says exactly what to set
- import still accepts app "thoughtsync", because exports written before the
  rename are backups. Tested both ways

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 14:18:49 -04:00
co-authored by Claude Opus 5.5
parent f806e35d41
commit a706644455
78 changed files with 231 additions and 168 deletions
+27 -21
View File
@@ -1,4 +1,4 @@
# ThoughtSync — PRODUCTION stack (app + Postgres).
# Inkwell — PRODUCTION stack (app + Postgres).
#
# This is the default compose file: `docker compose up -d` runs a real deployment
# from the published image. Development lives in docker-compose.dev.yml (hot-reload,
@@ -10,15 +10,15 @@
# Per family rule 12 the agent does NOT start this — run it yourself.
#
# Upgrades: docker compose pull && docker compose up -d
# Rollback: set THOUGHTSYNC_TAG to a commit sha in .env, then the same two commands.
# Rollback: set INKWELL_TAG to a commit sha in .env, then the same two commands.
# Every push publishes an immutable :<sha> image for exactly this.
#
# Schema migrations run automatically at container start (see the Dockerfile CMD),
# so an upgrade is just a pull and a restart. Take a backup first anyway:
#
# docker compose exec -T db pg_dump -U thoughtsync thoughtsync > backup.sql
# docker compose exec -T db pg_dump -U inkwell inkwell > backup.sql
#
# Attachments are files, not rows — they live in the `thoughtsync-data` volume and
# Attachments are files, not rows — they live in the `inkwell-data` volume and
# a pg_dump does NOT contain them. Back up both or you'll restore notes whose images
# are gone.
@@ -27,22 +27,21 @@ services:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-thoughtsync}
POSTGRES_USER: ${POSTGRES_USER:-inkwell}
# No default on purpose. A production compose that ships a known password is
# how self-hosted databases end up in search engines; compose fails fast here
# instead, with the message below.
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env — see .env.example}
POSTGRES_DB: ${POSTGRES_DB:-thoughtsync}
POSTGRES_DB: ${POSTGRES_DB:-inkwell}
volumes:
# Volume names kept from the previous compose file so an existing deployment
# upgrades in place. Renaming them would silently start against an empty
# database while the old one sat there, orphaned and looking like data loss.
- thoughtsync-db:/var/lib/postgresql/data
# Which volume this resolves to is set at the bottom of this file, and an
# existing deployment overrides it from .env rather than renaming anything.
- inkwell-db:/var/lib/postgresql/data
# Deliberately NOT published to the host. The app reaches Postgres over the
# compose network; exposing 5432 only widens the attack surface. If you need
# psql, `docker compose exec db psql -U thoughtsync` gets you there without it.
# psql, `docker compose exec db psql -U inkwell` gets you there without it.
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-thoughtsync} -d ${POSTGRES_DB:-thoughtsync}"]
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-inkwell} -d ${POSTGRES_DB:-inkwell}"]
interval: 10s
timeout: 5s
retries: 10
@@ -56,9 +55,9 @@ services:
max-file: "3"
app:
# :latest tracks `main`. Set THOUGHTSYNC_TAG=dev in .env to follow the
# :latest tracks `main`. Set INKWELL_TAG=dev in .env to follow the
# development line instead, or a commit sha to pin exactly.
image: git.fabledsword.com/bvandeusen/thoughtsync:${THOUGHTSYNC_TAG:-latest}
image: git.fabledsword.com/bvandeusen/inkwell:${INKWELL_TAG:-latest}
restart: unless-stopped
depends_on:
db:
@@ -66,11 +65,11 @@ services:
environment:
# The only required application setting. Everything else a person might want
# to tune lives in the admin Settings UI, backed by the database (rule 25).
THOUGHTSYNC_DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-thoughtsync}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-thoughtsync}
INKWELL_DATABASE_URL: postgresql+asyncpg://${POSTGRES_USER:-inkwell}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-inkwell}
volumes:
# Uploaded attachments. /var/thoughtsync is fixed in the app (Config.DATA_DIR),
# Uploaded attachments. /var/inkwell is fixed in the app (Config.DATA_DIR),
# not configurable — mount it or lose every image on container recreation.
- thoughtsync-data:/var/thoughtsync
- inkwell-data:/var/inkwell
# WHERE THE APP IS REACHABLE FROM. Three shapes, and the right answer is
# different for each — the default serves the first.
#
@@ -86,14 +85,14 @@ services:
# and publishing one is a second, unauthenticated way in that bypasses the
# proxy — including whatever the proxy is doing about TLS and auth.
#
# 3. Reverse proxy on the HOST (not in Docker). Set THOUGHTSYNC_BIND=127.0.0.1 in
# 3. Reverse proxy on the HOST (not in Docker). Set INKWELL_BIND=127.0.0.1 in
# .env, so the port exists but only the host itself can reach it.
#
# If you are exposing this to the internet, you want 2 or 3. Leaving it at 1
# means the app is reachable directly on port 5000, past everything your proxy
# does.
ports:
- "${THOUGHTSYNC_BIND:-0.0.0.0}:${THOUGHTSYNC_PORT:-5000}:5000"
- "${INKWELL_BIND:-0.0.0.0}:${INKWELL_PORT:-5000}:5000"
healthcheck:
# python rather than curl: the runtime image is python:3.12-slim and carries no
# HTTP client binary. Hits the app's own /api/health.
@@ -113,5 +112,12 @@ services:
logging: *logging
volumes:
thoughtsync-db:
thoughtsync-data:
# Named explicitly so the name is one a person can type, and overridable so a
# deployment that predates the rename to Inkwell keeps the volumes it already has
# (see "Upgrading from ThoughtSync" in .env.example). Pointing these at the wrong
# name does not fail: it starts against an EMPTY database and the old one sits
# there untouched, which looks exactly like data loss.
inkwell-db:
name: ${INKWELL_DB_VOLUME:-inkwell-db}
inkwell-data:
name: ${INKWELL_DATA_VOLUME:-inkwell-data}