added media fixes, search, push updates, pwa, metrics. preparing release 1.0
CI / Go (build + vet + test) (push) Canceled after 0s
CI / Web (build + lint) (push) Canceled after 0s

This commit is contained in:
vantablackTea
2026-09-27 23:26:15 +03:00
parent 855a62755c
commit 6fc6d977bf
59 changed files with 6823 additions and 113 deletions
+22
View File
@@ -0,0 +1,22 @@
# ADR 0001: Monolith over microservices
- Status: accepted
- Date: 2026-09
## Context
The messenger serves a single family (tens of users) on a home-lab mini PC
with ~8 GB RAM. A microservice split would add operational overhead
(networking, service discovery, per-service containers) with zero benefit at
this scale.
## Decision
Build a single Go process exposing REST, WebSocket, media serving and static
files, with PostgreSQL as the only external state. Internal packages
(`store`, `auth`, `realtime`, `api`, `media`) are cleanly separated with
explicit interfaces, so extraction later is mechanical.
## Consequences
- One container, trivial `docker compose up`.
- In-process WebSocket hub gives O(1) presence checks for push decisions.
- If the service ever needs horizontal scaling, `realtime` and `store`
already hide their implementations behind interfaces.
+21
View File
@@ -0,0 +1,21 @@
# ADR 0002: In-memory WebSocket hub
- Status: accepted
- Date: 2026-09
## Context
Realtime delivery (messages, typing, presence, read receipts) must reach
connected clients immediately. Options: external broker (Redis pub/sub,
NATS), Postgres LISTEN/NOTIFY, or an in-process hub.
## Decision
Use an in-process hub: `map[userID] -> connections` plus
`map[conversationID] -> subscribers`, guarded by a mutex. Membership and
read-state are resolved through a `MembershipProvider` interface implemented
by the store.
## Consequences
- Sub-millisecond fan-out, no extra moving parts.
- Presence checks (`IsOnline`) are lock-free reads used by the push notifier.
- Multi-node scaling would require a broker; the hub's publish methods are
the seam where that would plug in.
+20
View File
@@ -0,0 +1,20 @@
# ADR 0003: Search via ILIKE instead of pg_trgm
- Status: accepted
- Date: 2026-09
## Context
Message search must find substring matches across a user's conversations.
pg_trgm + GIN gives indexed trigram search, but the extension is not
installed on the target system and cannot be created by a non-superuser role
(and adding it to the Docker image is an OS-level dependency).
## Decision
Search with `body ILIKE '%term%'` restricted to the user's conversations,
ordered by message id descending with a limit.
## Consequences
- No extension or superuser requirement; works on any PostgreSQL install.
- Sequential scan is instant for thousands of messages (family scale).
- Scaling path documented: `CREATE EXTENSION pg_trgm` + GIN index with
`gin_trgm_ops`, keeping the same SQL semantics.
+22
View File
@@ -0,0 +1,22 @@
# ADR 0004: Media behind an S3-like interface
- Status: accepted
- Date: 2026-09
## Context
Attachments (images, files, videos) need storage, thumbnails for images, and
bounded upload size. Storage must survive container restarts and, ideally, be
swappable without touching API code.
## Decision
Define a `Storage` interface (`Save`/`Open`/`Delete` keyed by opaque keys) in
`internal/media`. Phase 2 ships `Local` — files on disk with atomic
temp-file+rename writes and path-traversal protection. API handlers depend
only on the interface.
## Consequences
- Metadata lives in Postgres (`media` table); bytes live in storage.
- Replacing disk with MinIO/S3 means a new `Storage` implementation, no
handler changes.
- Image thumbnails are generated with stdlib decoding + `x/image/draw`,
avoiding CGO and external binaries.
+21
View File
@@ -0,0 +1,21 @@
# ADR 0005: E2EE deferred, libsignal when implemented
- Status: accepted
- Date: 2026-09
## Context
A family messenger should eventually protect message content at rest and in
transit, ideally with end-to-end encryption. Rolling a custom crypto protocol
is unsafe and a red flag in review.
## Decision
- In transit: TLS everywhere (Caddy on the VPS / reverse proxy; homelab
ingress).
- At rest: Postgres and media disk live on trusted hardware.
- E2EE: deferred to a future major version, implemented with an established
library (libsignal) — never hand-written primitives.
## Consequences
- The wire protocol and data model are ready for E2EE (opaque message bodies,
device-agnostic session model).
- No false-security claims in the README.