193 lines
7.1 KiB
Markdown
193 lines
7.1 KiB
Markdown
# Who Need Help
|
|
|
|
Who Need Help is a working mutual-aid MVP for urgent, local, voluntary help.
|
|
The first supported scenario is pickup and delivery of a legal medicine that
|
|
has already been purchased or reserved.
|
|
|
|
It is not an emergency or medical service, does not prescribe or sell medicine,
|
|
and does not process payments. A helper may publish an optional external
|
|
thank-you link; money goes directly between users outside the platform.
|
|
|
|
## What is implemented
|
|
|
|
- Phoenix 1.8 LiveView application with email magic-link/password auth and 18+
|
|
self-attestation.
|
|
- Data-driven, translated category tree with validated per-category fields,
|
|
community proposals/votes, and human approve/reject/merge tools.
|
|
- Request lifecycle: `open → matched → in_progress → completed`, plus cancel
|
|
and expiry paths.
|
|
- PostgreSQL/PostGIS locations, MapLibre map, private matched chat, Phoenix
|
|
PubSub/Presence, and optional consent-driven live location sharing.
|
|
- Handover code plus both-party confirmation before verified completion.
|
|
- Double-blind reviews, public trust summaries, and a helper leaderboard that
|
|
prioritizes unique location-supported and handover-verified counterparts
|
|
before raw totals.
|
|
- Bidirectional discovery blocks, scoped reports, account/request/category
|
|
moderation, abuse-signal review, and audited moderator access to only the
|
|
conversation linked by a report.
|
|
- Optional GPS evidence derived from browser accuracy envelopes. Raw current
|
|
positions are deleted on stop, terminal match state, or participant block.
|
|
- PostgreSQL-backed cross-replica action-limit policies configured by the
|
|
operator. No unapproved numeric thresholds are enabled by default.
|
|
- Public manual social links, always marked unverified in this MVP.
|
|
- EN/UK/RU UI foundation and installable PWA metadata/service worker.
|
|
- Native Android WebView client with the same authenticated LiveView, map,
|
|
private chat, and a user-started location foreground service. Its persistent
|
|
notification exposes Stop, it continues while the Activity is minimized, and
|
|
it retains only the current point. The reproducible Docker target currently
|
|
exports a debug APK; production signing and store publication are not
|
|
configured.
|
|
- Local, advisory Codex category review through the user's ChatGPT-authenticated
|
|
Codex CLI. It receives a PII-free export and never writes to the database.
|
|
- One immutable release image with `web`, `worker`, and one-shot `migrate`
|
|
roles.
|
|
- Docker Compose and Helm/kind deployment paths with 2 web and 2 worker
|
|
replicas by default.
|
|
|
|
OAuth social verification, public coffee/cinema/hiking activities, background
|
|
PWA or unattended location tracking, platform payments, production Android
|
|
signing/store publication, iOS, automatic punitive fraud decisions, and
|
|
jurisdiction-specific public-launch policies are deliberately not claimed as
|
|
complete.
|
|
|
|
## Fast start with Docker Compose
|
|
|
|
Prerequisite: Docker with the Compose plugin.
|
|
|
|
```bash
|
|
./scripts/compose-up.sh
|
|
```
|
|
|
|
Open:
|
|
|
|
- app: <http://localhost:4010>
|
|
- local email inbox: <http://localhost:8027>
|
|
|
|
Compose starts Traefik, PostGIS, Mailpit, a migration runner, 2 web replicas,
|
|
and 2 Oban worker replicas. It waits for readiness and verifies a PubSub message
|
|
broadcast from a different BEAM node. Registration emails appear in Mailpit.
|
|
|
|
Inspect the exact state:
|
|
|
|
```bash
|
|
docker compose -p who_need_help ps -a
|
|
docker compose -p who_need_help logs -f web worker
|
|
./scripts/verify-realtime-cluster.sh compose
|
|
```
|
|
|
|
Local defaults are intentionally limited to local development. Copy
|
|
`.env.example` to `.env` and replace every secret before any public deployment.
|
|
Generate independent values with `mix phx.gen.secret`; do not reuse the session
|
|
secret as the BEAM cookie or handover secret. External Helm deployments must
|
|
set `app.host`, `app.scheme`, and `app.urlPort` to the public URL used in email
|
|
links, and must set `app.mapTileUrl` to a tile service whose policy and capacity
|
|
fit the deployment.
|
|
|
|
Rotate all local application secrets and the existing local PostgreSQL role
|
|
without printing the generated values:
|
|
|
|
```bash
|
|
./scripts/rotate-local-secrets.sh
|
|
docker compose up -d --wait
|
|
```
|
|
|
|
This preserves the named PostgreSQL volume, but invalidates existing browser
|
|
sessions and changes handover codes for active local requests.
|
|
|
|
## Tests
|
|
|
|
The reproducible test command builds a dedicated test target and uses the
|
|
project's PostGIS service:
|
|
|
|
```bash
|
|
./scripts/test.sh
|
|
```
|
|
|
|
It creates/updates only the project-scoped `who_need_help_test` database.
|
|
|
|
## First administrator
|
|
|
|
Register and confirm the first account, then explicitly bootstrap it:
|
|
|
|
```bash
|
|
./scripts/bootstrap-admin.sh you@example.com --confirm
|
|
```
|
|
|
|
This succeeds only while no administrator exists and writes an audit event.
|
|
After bootstrap, an administrator can manage roles in `/moderation`; the last
|
|
administrator cannot demote themselves. For kind, append `kind`:
|
|
|
|
```bash
|
|
./scripts/bootstrap-admin.sh you@example.com --confirm kind
|
|
```
|
|
|
|
## Optional shared action limits
|
|
|
|
`RATE_LIMIT_POLICIES_JSON` configures atomic PostgreSQL counters shared by every
|
|
web replica. Its shape is:
|
|
|
|
```json
|
|
{"action_name":{"limit":"POSITIVE_INTEGER","window_seconds":"POSITIVE_INTEGER"}}
|
|
```
|
|
|
|
The strings above describe the required types and are not a runnable policy.
|
|
Keep `{}` until numeric limits have been approved from policy and observed
|
|
traffic. Supported actions are listed in `docs/trust-safety.md`.
|
|
|
|
## Local Kubernetes verification
|
|
|
|
The kind script downloads checksum-verified kubectl, Helm, and kind binaries
|
|
into `.tools/`, creates a project-owned cluster, loads the local image, and
|
|
installs the Helm chart, waits for both Deployments, and verifies cross-node
|
|
PubSub:
|
|
|
|
```bash
|
|
./scripts/kind-up.sh
|
|
```
|
|
|
|
Open:
|
|
|
|
- app: <http://localhost:4011>
|
|
- Mailpit: <http://localhost:8028>
|
|
|
|
The script refuses to modify a pre-existing cluster named `who-need-help`
|
|
unless the project ownership marker exists.
|
|
|
|
For an external cluster, provide a real PostgreSQL/PostGIS service and a
|
|
pre-created Secret through `existingSecret`. The chart intentionally has no
|
|
invented CPU/RAM limits or HPA thresholds; measure this application in the
|
|
target environment before setting them.
|
|
|
|
## Local Codex category review
|
|
|
|
Export the redacted proposals from a running release:
|
|
|
|
```bash
|
|
docker compose -p who_need_help exec -T web \
|
|
/app/bin/who_need_help eval \
|
|
'WhoNeedHelp.CatalogModeration.export_open_proposals() |> IO.puts()' \
|
|
> proposals.json
|
|
```
|
|
|
|
Then run the advisory review:
|
|
|
|
```bash
|
|
./scripts/codex-review-categories.sh proposals.json recommendations.json
|
|
```
|
|
|
|
The script exits unless `codex login status` is exactly
|
|
`Logged in using ChatGPT`. It uses no OpenAI API key, no usage-based API billing,
|
|
and no fallback provider. Recommendations require a human moderator action.
|
|
|
|
## Design documentation
|
|
|
|
- [Product specification](docs/product-spec.md)
|
|
- [Architecture](docs/architecture.md)
|
|
- [Trust and safety](docs/trust-safety.md)
|
|
- [Implementation verification and known limits](docs/verification.md)
|
|
- [PostgreSQL/PostGIS ADR](docs/decisions/0001-postgresql-postgis-over-spacetimedb.md)
|
|
|
|
Exact dependency versions are locked in `mix.lock`,
|
|
`assets/package-lock.json`, the Dockerfile, Compose file, and tool bootstrap
|
|
script.
|