# Who Need Help Who Need Help is a working mutual-aid MVP for urgent, local, voluntary help. The first priority is pickup and delivery of a legal medicine that has already been purchased or reserved. The same urgent-help flow also supports safe, non-emergency fuel delivery, wheel help, bicycle and motorcycle problems, vehicle breakdowns, and practical support after a secured road incident. 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, seven selectable urgent/roadside scenarios, community proposals/votes, and human approve/reject/merge tools. - Separate social Activity mode for coffee, cinema, walks, and hikes, with organizer-approved membership, capacity-safe joins, private group chat, and exact coordinates visible only to approved participants. Activities never affect urgent-helper reputation. - 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, 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: - local email inbox: 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: - Mailpit: The script refuses to modify a pre-existing cluster named `who-need-help` unless the project ownership marker exists. On first install it creates independent random application and PostgreSQL credentials in the `who-need-help-local` Kubernetes Secret without writing them to Git. The local PostGIS data directory persists inside the kind node. When migrating an older project-owned cluster from the former `emptyDir` deployment, the script creates and validates a local dump before replacing the database workload, then restores that dump. After a successful rollout it also removes the obsolete chart Secret and only the local Helm history revisions that stored the former inline credential fields. For an external cluster, provide a real PostgreSQL/PostGIS service and a pre-created Secret through required `existingSecret`; the chart never renders credentials from tracked values. The Secret must contain `DATABASE_URL`, `SECRET_KEY_BASE`, `HANDOVER_SECRET`, and `RELEASE_COOKIE`. 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) - [Verified dependency baseline](docs/dependency-baseline.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.