Files
kin/docs/SCOPE.md
Marcus Rehbock ac0b82e6a0
Some checks failed
Build & Release APK / build (push) Failing after 13s
Kin: personal relationships app — server (cadence/due/migrations) + Expo Android app
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 00:50:06 -07:00

4.6 KiB

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.