14 KiB
Who Need Help — implementation verification
Observed on 2026-07-18 in the local workspace. This report separates observed results from product limits and unknown production properties.
Verified MVP capabilities
| Requirement | Status | Observed evidence | Limit |
|---|---|---|---|
| Urgent medicine-help flow | Implemented and tested | Request creation, discovery, matching, start, handover, two-party completion, and review rules are covered by the Phoenix test suite and exercised in the local UI. | The product coordinates pickup of an already purchased or reserved legal item; it is not a pharmacy, medical, or emergency service. |
| Urgent roadside help | Implemented and tested | Fuel, car wheel, bicycle, motorcycle, vehicle-breakdown, and secured-incident categories are seeded as a translated hierarchy. Server and LiveView tests exercise category paths, required fields, allowed values, boolean normalization, unknown-field rejection, and request creation. | Roadside requests require no immediate danger; this is not emergency response or professional recovery. |
| Extensible categories | Implemented and tested | Categories and validated text/select/boolean fields are stored in PostgreSQL. Proposal, vote, approve, reject, and merge paths have automated tests. | Coffee, cinema, hiking, and other social activities remain separate from urgent-help safety and ranking rules. |
| Separate Activity mode | Implemented and tested | Coffee, cinema, walk, and hiking categories use a separate activity lifecycle. Domain and two-client LiveView tests cover creation, join request, organizer approval, capacity enforcement, public/pending/chat privacy, exact-location disclosure to approved users, group chat, blocking, completion, and zero impact on helper reputation. Activity and message reports expose only the linked group conversation to an audited moderator; moderators can hide and restore reported activities. | This does not guarantee participant identity or physical safety. |
| Map and discovery | Implemented and browser-verified | A headed Chrome session rendered the MapLibre request map, marker, controls, attribution, and configured OpenStreetMap raster tiles. Tile requests returned HTTP 200 during the check. | A production operator must configure a tile provider appropriate for its policy and traffic. |
| Private matched chat | Implemented and cross-client verified | A message sent from the helper browser appeared in the requester's browser without reload. An earlier Android emulator run also sent a message that appeared in the requester browser in real time. | There is no unsolicited general-purpose inbox. |
| Consent-driven live tracking | Implemented and cross-client verified | On API 37, Android started TrackingService as a location foreground service with a persistent Stop notification. After Home minimized the Activity, an emulator coordinate change reached PostGIS. Notification Stop removed the service, notification, active session, and raw position. |
Browsers stop with the page. Android has no ACCESS_BACKGROUND_LOCATION, unattended start, or route history. |
| Privacy settings | Implemented and browser-verified | The profile exposed hidden, approximate public, exact for active match, and explicit exact-public options. Blocking and current-position cleanup have automated tests. | Exact public location remains a user opt-in; legal privacy and retention text still requires jurisdiction-specific review before launch. |
| Reputation and anti-abuse | Implemented at MVP level | Handover codes, two-party completion, double-blind reviews, unique-counterpart ranking, optional movement/proximity evidence, reports, blocks, abuse signals, and moderator audit paths have automated tests. | The system is not bot-proof and does not claim identity verification. No punitive numeric policy is enabled without measured and approved thresholds. |
| Social profiles | Manual links implemented; GitHub verification implemented and automated-tested | Manual links cannot set verification fields. The optional GitHub flow uses state, PKCE, a user-bound one-time session, unique provider ownership, and an audit record; 146 tests pass, including callback replay/state checks. No access-token field exists and the controller receives only normalized identity attributes. | The staging operator has not supplied GitHub OAuth credentials, so the real external provider redirect/callback remains disabled and has not been browser-verified. Other providers remain manual/unverified. |
| Voluntary thanks | Implemented as an external optional link | A helper can expose an optional link after completion; the UI states that the platform does not process the payment. | The platform does not provide payments, escrow, refunds, tax reporting, or payment guarantees. |
| Android client | Debug client implemented and emulator-verified | The native package org.whoneedhelp.mobile.debug launches the same authenticated LiveView app. Login, map, chat, permission prompts, minimized foreground-service location updates, notification Stop, and server cleanup were exercised on API 37. |
Production signing, a public production origin, Play Store publication, unattended/background-permission tracking, and iOS are not implemented. |
| Multiple web/worker instances | Implemented and locally verified | Docker Compose and kind each ran 2 web and 2 worker replicas. The project probes cross-node Phoenix PubSub using different BEAM nodes. Kubernetes web/worker pods were Ready with zero restarts at the final observation. | Local PostGIS is a single instance. Production database HA, backups, and recovery are operator work and are not claimed complete. |
Reproducible checks
./scripts/test.sh: 146 tests, 0 failures after verified GitHub linking on Elixir 1.20.2 and Erlang/OTP 29.0.3.mix compile --force --warnings-as-errorsandmix format --check-formatted: passed after the OAuth change.- The generated Activity migration was rolled back by exactly one step and
migrated forward again against
who_need_help_test; both directions passed. - The Activity-report migration was also rolled back and migrated forward. The observed database constraint changed from exactly one of 3 urgent-help targets to exactly one of 5 urgent-help/Activity targets.
mix format --check-formatted: passed in the final run.- Android Docker build target:
testDebugUnitTest,lintDebug, andassembleDebugpassed; the final lint report contains no errors or warnings. - Release Android guard: a release build without
WNH_BASE_URLfailed as intended; release configuration accepts only an explicit HTTPS origin. - Helm lint, template rendering, server-side dry-run, rollout waits, readiness checks, and cross-node PubSub verification passed in the local kind cluster.
- Browser verification used headed Chrome. The authenticated matched-request page rendered its chat, MapLibre marker, and live-location controls with no console errors or warnings after the foreground-service rollout.
Local generated evidence (ignored by Git):
output/playwright/final-request-map-chat.pngoutput/playwright/final-privacy-profile.pngoutput/android/final-image-smoke.pngoutput/android/foreground-notification.png
Android artifact:
android/dist/who-need-help-debug.apk- Final post-upgrade SHA-256:
063f3d8d877009ee229a403692e4b16517244dd9e7dddab4c2b3c202c5def4b8 - Observed manifest values: version
0.1.0-debug, minimum SDK 24, target and compile SDK 37.
Configuration finding
WNH_DEBUG_BASE_URL is a required Android build input, because the trusted
same-origin value is compiled into the debug APK. Its concrete local value is
read from the repository's ignored .env by scripts/android-build.sh and is
passed to Docker with --build-arg. android/Dockerfile only declares
ARG WNH_DEBUG_BASE_URL; it contains no URL default.
The example http://10.0.2.2:4010 remains only as unit-test data for origin
matching. It is not a runtime or build default. The ignored .env is also
excluded from the root Docker build context.
Database observations
The post-roadside local Compose observation found 2 users, 1 help request, 7 messages, and 1 assignment before and after deployment. Category seeding changed only the category count from 1 to 9: 7 selectable categories and 2 inactive grouping nodes. These are local scenario data; the database is not assumed empty.
The Activity migration then preserved those same user/help/message/assignment
counts, added empty activities, activity_participants, and
activity_messages tables, and expanded the category tree to 14 rows: 9 help
rows and 5 Activity rows. A validated custom-format dump was created immediately
before that migration at
output/backups/compose-before-activity-20260718-203930.dump; this ignored local
artifact is not a substitute for the still-required isolated restore drill.
The final kind observation found 0 users, 0 help requests, and 0 messages. That
state was queried before changing the database workload. The migration from the
old emptyDir Deployment created a custom-format dump, validated it with
pg_restore --list, changed PostGIS to a persistent StatefulSet, and restored
the dump before the Helm upgrade. The final kind database reported PostgreSQL
18.4 and PostGIS 3.6.4.
The local kind Secret contains independently generated values. Only their decoded lengths were inspected: the PostgreSQL password is 64 characters and each application secret is 128 characters. Secret values were not printed or written to tracked files.
Dependency-upgrade observations
- The running Compose and kind releases reported Elixir 1.20.2 and Erlang/OTP 29.
- Compose reported Traefik 3.7.8 and Mailpit 1.30.4; Compose and kind both reported PostgreSQL 18.4 and PostGIS 3.6.4.
- The production asset build reported Tailwind CSS 4.3.3 and daisyUI 5.6.18; esbuild is configured at 0.28.1.
- The isolated Node build stage reported Node.js 24.18.0, npm 12.0.1,
npm outdated --jsonreturned{}, andnpm cireported zero known vulnerabilities. - The Android build used Android Gradle Plugin 9.3.0, Gradle 9.6.1, Command-line Tools 22.0, and its embedded Android CLI 1.0.15857036. Unit tests, lint, and debug assembly passed; the lint report says “No errors or warnings.” The rebuilt emulator image contains the API 37 ps16k AVD and APK, and its emulator 36.6.11.0 binary returned its version successfully at runtime.
- The final complete
scripts/kind-up.shrun was idempotent and finished with both 2-replica Deployments Ready and a successful cross-replica PubSub probe.
The migration 20260718114233 and reverse block lookup index
blocks_blocked_id_blocker_id_index were present. An actual EXPLAIN ANALYZE
for that reverse lookup selected the index. The table contained no block rows,
so this confirms query shape, not production performance.
Exact production capacity, minimum CPU/RAM, and scaling thresholds are unknown: there is no representative load dataset or target-environment measurement. The Helm chart therefore does not invent resource limits or an HPA policy.
Public staging observation
On 2026-07-18, whoneedhelp.imalto.site was published through the existing
Ubuntu Nginx gateway and its OpenVPN path to the local Compose proxy. HTTP
redirected to HTTPS; the homepage, fingerprinted assets, and both health
endpoints returned HTTP 200. A headed Chrome session rendered the public page
with zero console errors or warnings. A complete WebSocket Upgrade request to
/live/websocket returned 101 Switching Protocols.
The observed Let's Encrypt certificate had the correct
DNS:whoneedhelp.imalto.site SAN and an expiry of 2026-10-16. Nginx and the
Certbot renewal timer were active. This verifies the current staging path; it
does not make the workstation or gateway a production availability
environment.
After the Activity rollout, headed Chrome rendered the updated public navigation, followed the Activity link to the authenticated route, and received the expected login redirect and flash with zero console errors or warnings.
After commit 307794c, staging was rebuilt with the optional GitHub OAuth
boundary. Both web and both worker replicas started, the cross-node PubSub probe
passed, public home/readiness returned HTTP 200, and the before/after database
counts remained 2 users / 1 request / 7 messages / 14 categories / 1 assignment / 0 activities / 0 reports / 0 social identities.
A headed Chrome E2E run then registered a uniquely named example.invalid
account, consumed its confirmation link from local Mailpit, authenticated,
opened /profile, and observed:
- the GitHub network option and the explicit provider-disabled explanation;
- no Verify button while both OAuth environment variables were absent;
- a manually added Telegram link labelled
unverified; - removal of that owner-controlled manual link;
- a direct
/auth/social/githubrequest returning to the profile with the controlledprovider is not configuredflash; - zero browser console errors or warnings.
The exact E2E user was checked for every foreign-key relationship to users.
It had only its one authentication token and no domain records after the manual
link was removed. That exact user was then deleted through Ecto, its browser
session became invalid, the generated Mailpit message was deleted individually,
and the original database counts were observed again. The browser windows were
left open.
Local ignored browser evidence:
.playwright-cli/page-2026-07-18T18-09-44-303Z.png
Known work before a public production launch
- Replace the temporary staging origin with the production-owned domain and production-sign the Android app.
- Operate PostgreSQL/PostGIS with backups, recovery testing, and the required availability model.
- Load-test representative data and traffic, then set measured pool, resource, autoscaling, and action-limit policies.
- Add pagination or bounded loading where real measurements show that request, chat, moderation, or leaderboard result sets require it.
- Publish jurisdiction-specific emergency contacts, privacy, retention, prohibited-items, and voluntary-payment guidance after legal review.
- Create and configure a GitHub OAuth App, then exercise the real external provider redirect/callback in a headed browser. Until then staging keeps the provider disabled and manual links remain unverified.