docs: adopt Coolify-managed Postgres over SQLite
This commit is contained in:
10
DESIGN.md
10
DESIGN.md
@@ -35,7 +35,7 @@ Strength weights used in matrices: **9** strong, **3** medium, **1** weak, blank
|
||||
| F7 | Deliver a push promptly after a defect is filed | ↓ | ≤ ~10s to subscribed devices |
|
||||
| F8 | Enroll & maintain push subscriptions | → | Multiple devices; survives re-subscribe |
|
||||
| F9 | Gate every request behind Google SSO + server-side domain check | → | 100% of routes; `hd` never trusted alone |
|
||||
| F10 | Persist data durably across redeploys | → | 0 data loss (volume-backed SQLite) |
|
||||
| F10 | Persist data durably across redeploys | → | 0 data loss (Coolify-managed Postgres, auto-backups) |
|
||||
|
||||
## 3. Cascade — Goals → Functions → How → Components
|
||||
|
||||
@@ -71,7 +71,7 @@ Strength weights used in matrices: **9** strong, **3** medium, **1** weak, blank
|
||||
- **How**: Google OAuth/OIDC; server-side verified-email-domain check (`hd` not trusted alone); default-deny middleware
|
||||
- **Component**: C9 Auth middleware
|
||||
- **F10** Persist durably across redeploys _→ 0 loss_
|
||||
- **How**: SQLite file on a Coolify persistent volume (rejected: Postgres container — see T5)
|
||||
- **How**: Coolify-managed PostgreSQL with automatic backups (rejected: SQLite on a volume — see T5, ADR 0003)
|
||||
- **Component**: C8 schema, C11 deploy
|
||||
|
||||
## 4. House — Functions × Goals (transposed for width)
|
||||
@@ -123,7 +123,7 @@ Cells: link strength (9/3/1/blank). Importance = `Σ(goal weight × strength)`.
|
||||
| C5 | DotMap (dots + diagonal dates + "+N more") | — |
|
||||
| C6 | VerbatimModal + reverse-chronological Feed | ADR 0002 |
|
||||
| C7 | Nuxt/Nitro server API (defects, projects, subs) | ADR 0001 |
|
||||
| C8 | SQLite + Drizzle schema (defects, projects, push_subscriptions) | ADR 0001 |
|
||||
| C8 | PostgreSQL + Drizzle schema (defects, projects, push_subscriptions) | ADR 0001, ADR 0003 |
|
||||
| C9 | Google OAuth + session + domain-check middleware | — |
|
||||
| C10 | Web Push sender (VAPID) + service worker + sub store | — |
|
||||
| C11 | Docker image + compose + Coolify volume mount | — |
|
||||
@@ -151,7 +151,7 @@ Cells: link strength (9/3/1/blank). Importance = `Σ(goal weight × strength)`.
|
||||
| 4 | F4 dot legibility | Readable to the cap | Seed N defects on one section, eyeball | Lower the visible-dot cap / tighten "+N more" threshold |
|
||||
| 5 | F7 push latency | ≤10s to device | File a defect, time the phone | Accept the delay; show an in-app unread badge as fallback (no retry queue) |
|
||||
| 6 | F6 dashboard render | ≤1s @ ~2k defects | Seed 2k rows, measure render | Add index on (section_id, created_at); precompute section counts |
|
||||
| 7 | F10 durability | 0 data loss on redeploy | Redeploy on Coolify, verify data present | Confirm volume mount; block launch until verified |
|
||||
| 7 | F10 durability | 0 data loss on redeploy | Redeploy on Coolify, verify data present | Restore from Coolify's automatic Postgres backup; block launch until verified |
|
||||
|
||||
## 8. Tradeoffs — Got / Paid / ADR
|
||||
|
||||
@@ -161,7 +161,7 @@ Cells: link strength (9/3/1/blank). Importance = `Σ(goal weight × strength)`.
|
||||
| T2 | Append-only log over lifecycle | Trivial model, fast v1 | No countermeasure/close tracking (deferred) | — |
|
||||
| T3 | Transparent attribution over blameless | Accountability + follow-up path | Cultural risk of chilled reporting | ADR 0002 |
|
||||
| T4 | Defect = board artifact over project-work classification | Board-as-product weak-point analysis | Can't capture project-level problems (different concept) | ADR 0001 |
|
||||
| T5 | SQLite over Postgres | Zero-ops, single container | Must mount a persistent volume; single-writer; manual backups | — |
|
||||
| T5 | Coolify-managed Postgres over SQLite | Automatic backups; no deploy-strategy constraint; concurrent connections | One more service in dev and prod than a single file | ADR 0003 |
|
||||
| T6 | Web Push over Slack/email | No third-party dependency; native phone push | Subscription management; breaks if browser storage cleared | — |
|
||||
| T7 | Section-level pin over exact-spot | Simpler model | Dot positions cosmetic; precise location lost | — |
|
||||
| T8 | Fire-and-forget push, no queue | Filing stays fast; minimal infra | Push is best-effort, not retried on failure | — |
|
||||
|
||||
11
README.md
11
README.md
@@ -28,14 +28,19 @@ House of Quality: **F1 board definition → F2/F3 filing → F4/F5 weak-point ma
|
||||
| [docs/house-of-quality.md](./docs/house-of-quality.md) | TikZ "House of Quality" — a visual rendering of the DESIGN.md matrices for review/slides. |
|
||||
| [docs/adr/0001](./docs/adr/0001-defects-are-about-the-board-not-project-work.md) | Why a Defect is feedback about the **Board artifact**, not the reporter's project work. |
|
||||
| [docs/adr/0002](./docs/adr/0002-attribution-is-transparent-not-blameless.md) | Why attribution is **transparent** rather than blameless. |
|
||||
| [docs/adr/0003](./docs/adr/0003-use-coolify-managed-postgres-over-sqlite.md) | Why the data store is **Coolify-managed Postgres**, not SQLite. |
|
||||
| [tasks/plan.md](./tasks/plan.md) | Implementation plan — phased, dependency-ordered tasks with acceptance criteria. |
|
||||
|
||||
## Stack
|
||||
|
||||
Nuxt 3 (full-stack Vue) · SQLite + Drizzle on a persistent volume · Google
|
||||
OAuth (`hd=theodo.com` + server-side domain recheck) · Web Push (PWA) ·
|
||||
Nuxt 3 (full-stack Vue) · PostgreSQL + Drizzle (Coolify-managed, auto-backups) ·
|
||||
Google OAuth (`hd=theodo.com` + server-side domain recheck) · Web Push (PWA) ·
|
||||
Docker Compose on Coolify.
|
||||
|
||||
## Deployment notes
|
||||
|
||||
- **SQLite needs a persistent Coolify volume** — without it, redeploys wipe all data.
|
||||
- **Use Coolify's managed Postgres** (with scheduled backups); the app connects
|
||||
via `DATABASE_URL`. See [ADR 0003](./docs/adr/0003-use-coolify-managed-postgres-over-sqlite.md).
|
||||
- **Don't auto-deploy from `main`** — every merge would deploy. Use a tagged
|
||||
release or manual trigger.
|
||||
- **Don't trust the OAuth `hd` claim alone** — verify the email domain server-side.
|
||||
|
||||
22
docs/adr/0003-use-coolify-managed-postgres-over-sqlite.md
Normal file
22
docs/adr/0003-use-coolify-managed-postgres-over-sqlite.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Use Coolify-managed PostgreSQL over SQLite
|
||||
|
||||
The `/deep-design` session chose SQLite on a persistent volume for its zero-ops
|
||||
simplicity at this scale. Deploying on **Coolify** changes the calculus: Coolify
|
||||
provides **managed PostgreSQL with automatic scheduled backups**, whereas a
|
||||
SQLite file inside an app volume has *no* managed backup and forces a
|
||||
single-instance, stop-then-start deploy strategy to avoid file-lock contention
|
||||
when Coolify briefly runs old and new containers during a rolling restart. We
|
||||
switch to **Coolify-managed PostgreSQL** (via Drizzle's `pg` driver).
|
||||
|
||||
## Consequences
|
||||
|
||||
- Backups are handled by Coolify's database backup feature — no bespoke tooling
|
||||
(e.g. Litestream) needed.
|
||||
- Deploys no longer need a recreate/single-instance constraint; the app can
|
||||
restart freely while Postgres persists as a separate service.
|
||||
- Adds a Postgres service to local dev (`docker-compose`) and a managed DB in
|
||||
prod — one more moving part than a single file.
|
||||
- **Supersedes** the SQLite choice in DESIGN.md (T5, F10) and the original
|
||||
`/deep-design` decision.
|
||||
- Unchanged: deploys must **not** auto-run from `main` (every merge would deploy);
|
||||
use a tagged release or manual trigger.
|
||||
Reference in New Issue
Block a user