Kin: personal relationships app — server (cadence/due/migrations) + Expo Android app
Some checks failed
Build & Release APK / build (push) Failing after 13s
Some checks failed
Build & Release APK / build (push) Failing after 13s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
88
docs/SCOPE.md
Normal file
88
docs/SCOPE.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Kin — scope & architecture
|
||||
|
||||
*Written 2026-08-09, at project start.*
|
||||
|
||||
## What this is
|
||||
|
||||
A personal "relational wealth" app: the single place that answers **who
|
||||
haven't I talked to in too long?** and makes fixing that a two-tap action.
|
||||
Inspired by Monica CRM, but deliberately thin — one user, one server, no
|
||||
accounts, no social features.
|
||||
|
||||
## Product principles
|
||||
|
||||
1. **The due list is the product.** Contacts, notes, and history exist to
|
||||
feed one screen: who to reach out to today. Everything else is secondary.
|
||||
2. **Logging must be cheaper than not logging.** Two taps ("text, today,
|
||||
done") or the log stays empty and the due list lies. The old web CRM had
|
||||
44 people and 0 interactions — that's the failure mode to design against.
|
||||
3. **Cadence over guilt.** Each person gets a cadence (weekly → 6-monthly)
|
||||
or none at all. No global "you're behind on 30 people" — only people you
|
||||
*chose* to track surface in reminders. Snooze is honest: it defers,
|
||||
it doesn't fake contact.
|
||||
4. **Thin client, durable data.** All state lives in the `personal` Postgres
|
||||
database on the VPS. The phone caches for instant open and offline
|
||||
reading; writes go straight to the API.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Pixel (Kin app, Expo RN) ──Bearer token──▶ Caddy ──▶ crm container (Bun+Hono) ──▶ personal-db (Postgres)
|
||||
Browser (web UI) ──basic_auth───▶ ↑ same API, same data
|
||||
```
|
||||
|
||||
- **Server**: the existing `crm` container, extended. Hono + porsager/postgres
|
||||
on Bun. Now has boot-time SQL migrations (`schema_migrations`), a
|
||||
`GET /api/due` endpoint, cadence/snooze/archive columns, relationship
|
||||
create/delete, and Bearer-token auth for non-browser clients.
|
||||
- **Due math** (server): `urgency = days_since_last_contact / cadence_days`,
|
||||
anchored on `created_at` for never-contacted people so new adds surface
|
||||
immediately. `overdue` ≥ 1.0, `due_soon` ≥ 0.75. Sorted by urgency, so a
|
||||
weekly friend 3 days late outranks a yearly contact 3 days late.
|
||||
- **App**: Expo SDK 57 + expo-router, structured like the GTD app (read-cache
|
||||
store on `useSyncExternalStore` + AsyncStorage, refresh on
|
||||
foreground/focus). Dark warm theme, Pixel 9 XL first.
|
||||
- **Reminders**: local notifications only, no server push. On every sync the
|
||||
app reschedules a daily digest for the next 7 days ("N people are due for a
|
||||
catch-up") at the configured hour. If the app isn't opened for a week, the
|
||||
notifications run out — which is itself the right nudge. Server-side
|
||||
push via the existing ntfy container is the v2 escape hatch if this proves
|
||||
too passive.
|
||||
|
||||
## Screens
|
||||
|
||||
| Screen | Job |
|
||||
|---|---|
|
||||
| **Today** (home) | Due list in sections: Reach out / Coming up / On track / Snoozed. Quick-log button on every row. Empty state doubles as onboarding. |
|
||||
| **People** | Whole network, client-side search + tag filter (44 people — no server round-trips). |
|
||||
| **Person** | Contact actions (Message/Call/WhatsApp/Email deep links), cadence picker, log + snooze, notes, relationships, history (long-press to delete). |
|
||||
| **Log** (modal) | Type chips + today/yesterday + optional note. Two taps minimum. |
|
||||
| **Add/Edit** (modal) | Name, phone, email, location, tags, notes, cadence. |
|
||||
| **Settings** (modal) | Server URL, token, reminder hour. Probes the API before saving. |
|
||||
|
||||
## Deliberately out of v1
|
||||
|
||||
- **Offline write queue** — GTD's op-queue store is proven but heavy; a
|
||||
relationships app tolerates "log it when you're back online". Revisit if it
|
||||
annoys in practice.
|
||||
- **Birthday reminders** — column exists (`people.birthday`), UI doesn't.
|
||||
- **Relationship editing in-app** — API supports create/delete now; app only
|
||||
displays. Add when graph curation actually matters.
|
||||
- **Web UI parity** — the vanilla-JS web UI still works for bulk edits at
|
||||
crm.rehbock.xyz; it doesn't know about cadence yet.
|
||||
- **Contact import from the phone** — seed data came from the newsletter
|
||||
subscriber list; `expo-contacts` import is a v2 candidate.
|
||||
- **Multi-user / open-source hardening** — single-user by design. The repo is
|
||||
structured to be open-sourceable (no secrets in git, migrations from
|
||||
scratch, auth documented), but no login system until someone else needs it.
|
||||
|
||||
## Infrastructure facts
|
||||
|
||||
- VPS dir: `~/personal/crm` (container `crm`, network `caddy_net` +
|
||||
`personal-db_default`). DB: `personal` in the shared `personal-db` Postgres.
|
||||
- Caddy: `crm.rehbock.xyz`; Bearer requests bypass basic_auth (app validates),
|
||||
everything else challenges.
|
||||
- CI: Gitea Actions on the desktop runner (label `desktop`), signed APK per
|
||||
push to main, released as `kin-v1.<run>.apk` for Obtainium.
|
||||
- Keystore: `~/.android-keys/kin-release.jks`, alias `kin` — distinct from
|
||||
GTD's so Android treats the apps independently.
|
||||
Reference in New Issue
Block a user