who_need_help/docs/verification.md

1902 lines
128 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Who Need Help — implementation verification
Observed through 2026-07-28 in the local workspace. This report separates observed
results from product limits and unknown production properties.
## Physical Android and Play preflight on 2026-07-28
The application source below is exact local commit
`c863b47e5ff9a0e90240a7cf286e627bd87e04ec`. No push, hackathon-test
deployment, production deployment, Devpost edit, branch, or tag was made.
- `./scripts/quality.sh` passed ShellCheck, Hadolint at the configured
threshold, actionlint, release/rollback/migration drills, every Compose
render, Prometheus and Alertmanager validation, Helm lint, tracked-source
and runtime-image scans, formatting, warnings-as-errors compilation, xref,
Credo, Sobelow, Dialyzer, Hex/npm audits, and all 394 ExUnit tests. The
configured source and image scans reported zero vulnerability or secret
findings, and the quality run exited successfully after exact scoped
cleanup.
- The existing signed development application and its instrumentation package
were installed atomically on the connected physical Android device. The
development cross-client replay passed magic-link login, private Android to
browser and browser to Android messages, real FCM registration and
notification delivery, foreground live-location sharing, browser marker
visibility, stop-sharing cleanup, zero retained raw position, and zero
browser TLS or fatal errors. Exact cleanup retained no run-scoped users or
new push device and restored every other tracked application-table count.
Evidence is
`output/android-browser-development-e2e/20260728010201-2368495`.
- The physical-device path now reuses the explicitly selected authorized
device and already installed development packages. It refuses test/staging
variants, does not create an emulator image, and retains run diagnostics
while enforcing exact fixture cleanup after success, failure, or interrupt.
- A read-only inspection of the authenticated Play Console showed no existing
Play application. Google was still verifying the uploaded identity
documents; contact-phone verification was unavailable until that review
completes, and **Create app** was disabled. Firebase development and
production Android clients already exist, but they are not Play listings.
No duplicate Firebase, Google Cloud, or Play application was created.
## Live development deployment verification on 2026-07-25
The local development Compose project was rebuilt and restarted from exact
local commit `7a54477a308181768469bb1012b1ec561400b9fe`. This was a
development-domain deployment only. The hackathon test deployment, production
deployment, Git remote, Devpost entry, branches, and tags were not changed.
- The ordinary development Compose project runs two healthy web replicas, two
healthy worker replicas, a healthy PostgreSQL/PostGIS container, and the
development proxy. The four BEAM nodes passed the repository's realtime
cluster check.
- Both direct proxy readiness on `127.0.0.1:4010` with the configured host
header and public readiness at
`https://whoneedhelp.imalto.site/healthz/ready` returned HTTP 200 with
`{"status":"ready"}`. The public home page returned HTTP 200 and the title
`Who Need Help`.
- `./scripts/check-environment-readiness.sh .env --require-release` reported
zero blocking items and zero local-only warnings. The check found the
development origin, independent application secrets, external SMTP,
support inbox, web and Android Google sign-in, VAPID, Firebase client,
FCM service account, Android App Links, and development signing inputs
present and internally consistent; it did not print their secret values.
- A headed Chrome inspection of the public development domain confirmed the
real MapLibre demo map, localized English and Russian navigation and page
content, the searchable compact language menu, the authentication redirect
for protected request discovery, and the 390 by 844 mobile navigation
layout. The inspected page reported zero browser console errors or warnings.
A mobile viewport capture is retained at
`output/playwright/dev-live-mobile-menu-20260725.png`.
- `./scripts/staging-e2e-run.sh` passed the real public HTTPS mutual-aid flow
against `https://whoneedhelp.imalto.site`: two users, medicine request,
helper replacement, realtime private chat, consent-based tracking,
handover, both-party completion, reporting signals, and blind reviews.
Evidence is `output/staging-e2e/20260725212537-291190`.
- Exact fixture cleanup removed 3 synthetic users, 2 requests, 3 assignments,
2 messages, 1 tracking session, 2 reviews, 2 abuse signals, and 13 audit
events. The before/after application-table snapshots have the same SHA-256
(`e0effacce81c9662c448d4d073578d54c5f779bd1d9f6f9188c7911afbd55659`)
and the cleanup diff is empty. The two unreferenced temporary E2E image tags
were removed explicitly after the run.
- The full public auth/email staging drill was not run against this
development topology because its required local Mailpit endpoint at
`127.0.0.1:8027` was not running. Auth and email behavior remains covered by
the isolated 45-scenario browser matrix and 390-test local quality gate
below; this observation is not represented as a live external-email check.
## Cross-browser and release rehearsal follow-up on 2026-07-25
The observations below apply to local commit
`9875fd7d052367596d3745d130dc3c6342667419`. No push, hackathon-test
deployment, production deployment, Devpost edit, branch, or tag was made.
- `./scripts/e2e-run.sh` passed all 45 scenarios in Chromium, Firefox, and
WebKit. The run covered registration and account settings, localization,
responsive accessibility, requests and activities, viewport discovery and
clustering, exact/approximate/hidden locations, private chat, tracking,
handover, blind reviews, notifications, moderation, and serving-node
restart recovery. Evidence is
`output/e2e/20260725205052-3435033`. The run-scoped containers, networks,
volumes, and images were absent after cleanup.
- The isolated 30-second load run used 40 public HTTP, 40 Phoenix WebSocket,
and 8 authenticated mutual-aid VUs against 3 web and 2 worker replicas.
All 37,854 HTTP requests and 34,578 public checks passed. It completed
1,630 authenticated LiveView join/message/tracking-start/tracking-update/
tracking-stop cycles and 240/240 WebSocket heartbeats. Overall HTTP p95 was
8.27 ms. These are workstation measurements, not production limits.
- PostgreSQL recorded 138,772 commits with zero rollback, deadlock, conflict,
temporary file, or lock waiter. It peaked at 21 client backends and 5 active
backends with at least 76 observed connection slots of headroom. The three
web replicas recorded 177,453 Ecto queries with 0.299 ms average total
duration and 0.027 ms average queue duration. The largest observed
`pg_stat_statements` maximum was 9.808 ms. Exact fixture cleanup restored
every tracked table count. Evidence is
`output/performance/candidate-28940f9-20260725-load`.
- Under that concurrent synthetic profile, individual observed container
maxima were 355.99 MB for a web replica, 228.69 MB for a worker,
146.38 MB for PostgreSQL, and 364.17 MB for the proxy. These are load-run
samples and are not idle-memory measurements or minimum-server claims.
- The associated resilience run recorded 1,164 readiness samples with zero
failures while replacing web and worker processes. Evidence is
`output/resilience/candidate-28940f9-20260725-resilience`. Its exact
Compose project, database volume, networks, and image tags were removed.
- A mode-`0600` custom-format backup and checksum were created at
`output/backups/preprod-20260725T210230Z.dump`. A temporary restore read
33 public application tables and 1,813 rows, confirmed all 19 migrations
and PostGIS 3.6.4, and removed the temporary database.
- The same backup passed the isolated upgrade rehearsal after the rehearsal
was made to wait for the actual Traefik application route rather than only
container state. All 19 migration versions and 11 required cursor indexes
matched, application table counts were unchanged, two web and two worker
replicas formed a four-node cluster, and PubSub plus HTTP checks passed.
Evidence is `output/upgrade-rehearsal/20260725210859-3996878`; all
run-scoped Docker resources were removed. Failed rehearsal runs now retain
proxy and Docker socket-proxy logs before exact cleanup.
- `./scripts/quality.sh` passed after these changes. It included ShellCheck,
release/rollback/migration drills, Compose and Helm validation, source and
image scans, formatting, warnings-as-errors compilation, xref, Credo,
Sobelow, Dialyzer, Hex/npm audits, and 390 passing ExUnit tests. The
configured scans reported zero vulnerability findings and exact
quality-run cleanup left no scoped Docker resources.
- The signed development Android APK was rebuilt from the current source.
Unit tests, Android Lint, package/signing checks, and online App Links
validation passed. On the connected physical Android API 36 device,
`whoneedhelp.imalto.site` was reported as `verified`, `/safety` cold-started
`org.whoneedhelp.mobile.development`, and the real WebView DOM smoke passed
1/1 for `/` and `/safety`. The device sleep setting was restored after the
run. The earlier complete physical chat/FCM/tracking replay remains recorded
separately below.
## Final local candidate verification on 2026-07-25
The web/backend candidate below is exact commit
`4f2ab7d205ad1e137b07a8365e30b7cf007e2a19`. A subsequent test-only Android
commit, `c89dc78`, updates the instrumentation path for the notification
settings panel; it does not change the deployed application. No push,
hackathon-test deployment, production deployment, Devpost edit, branch, or tag
was made.
- `./scripts/e2e-run.sh` passed all 45 scenarios in Chromium, Firefox, and
WebKit. The matrix covers public and authenticated mobile layouts,
localization, accessibility, registration and Google flows, request and
Activity lifecycles, map viewport discovery and clustering, private chat,
location privacy and tracking, notifications, moderation, and restart
recovery. Evidence is `output/e2e/20260725141107-815241`; its isolated
Compose resources were removed.
- `./scripts/test.sh` and the final `./scripts/quality.sh` both reported
387 passing ExUnit tests. The quality gate also passed ShellCheck, Hadolint
at the configured threshold, actionlint, Compose profile rendering,
Prometheus and Alertmanager validation, Helm lint, formatting,
warnings-as-errors compilation, xref, strict Credo, Sobelow, Dialyzer,
Hex/npm audits, release/rollback/migration drills, and tracked-source and
runtime-image scans. The configured vulnerability scans reported zero
findings, and the run-scoped quality resources were removed.
- The isolated 30-second load profile ran 40 public HTTP, 40 Phoenix
WebSocket, and 8 authenticated mutual-aid VUs against 3 web and 2 worker
replicas. All 38,240 HTTP requests and 34,866 checks passed, 240/240
heartbeat replies arrived, and 1,679 authenticated chat/tracking iterations
completed. Overall HTTP p95 was 7.28 ms, authenticated HTTP p95 was
10.81 ms, and WebSocket-connect p95 was 4.76 ms. These are workstation
observations, not production limits or minimum resource requirements.
- PostgreSQL recorded 141,740 committed and zero rolled-back transactions,
with no deadlock, conflict, temporary file, or lock waiter. It peaked at
21 client backends and 5 active backends with at least 76 observed
connection slots of headroom. The three web replicas recorded 181,796 Ecto
queries with 0.275 ms average total duration and 0.022 ms average pool queue
duration. Exact fixture cleanup restored every tracked table count.
- The peak sum of the seven isolated application/proxy/database container
samples was 1,836.4 MiB. Individual maxima were 331.6 MiB for a web replica,
204.6 MiB for a worker, 140.7 MiB for PostgreSQL, and 323.1 MiB for the
proxy. This measured concurrent profile is not a minimum-server claim.
Complete evidence is `output/performance/final-4f2ab7d`.
- The follow-up resilience drill recorded 1,084 readiness samples with zero
failures while restarting and replacing web and worker processes. The final
three-web/two-worker cluster passed cross-node PubSub, and an Oban probe
completed on its second configured attempt after one recorded failure.
Evidence is `output/resilience/final-4f2ab7d`; its isolated stack was
removed.
- A mode-`0600` custom-format development backup was created at
`output/backups/final/dev-20260725-142457-4f2ab7d.dump`; its SHA-256 is
`94747a728f06e9d2c4901ede1236ea0c02319c4b2174e1f52fbd326c9514bd9e`.
The restore drill observed 33 tables, 1,830 rows, all 19 migrations, and
PostGIS 3.6.4 in a temporary database, then removed that database without
changing source counts.
- The same backup passed a full release upgrade rehearsal: all 19 migration
versions and 11 required cursor indexes matched, the application-table diff
was empty, two web and two worker replicas formed a four-node cluster, and
PubSub plus HTTP checks passed. Evidence is
`output/upgrade-rehearsal/20260725142518-1260191`; every run-scoped
container, network, volume, and image was removed.
- A connected physical Android device (`23122PCD1G`) passed the final
development cross-client flow. Instrumentation reported `OK (1 test)` in
16.386 seconds; Android and Chromium exchanged one private message in each
direction, the browser observed the live-location marker, FCM delivery
completed, the foreground service stopped, and the database retained zero
active tracking sessions and zero current positions. Exact fixture cleanup
removed two synthetic users, their request, assignment, two messages, push
jobs, and tracking session with a zero-byte database-count diff. Evidence is
`output/android-physical-final/physical-final-20260725145850-1557566`.
## Goal-completion replay on 2026-07-24
These observations apply to exact local commit
`4f64eab7968182faa16a6ccbc21555de4287c9ec`. The two unrelated, pre-existing
working-tree changes in the staging E2E task and wrapper were excluded from
both isolated worktrees.
- `./scripts/quality.sh` passed every configured source, Compose, Helm,
release, rollback, migration, security, dependency, static-analysis, and
production-image gate. ExUnit reported 367 passing tests; Trivy, Hex, and
npm reported no advisory or vulnerability finding at their configured
severities. The exact temporary quality worktree, containers, networks,
volumes, and image tags were absent afterward.
- A fresh detached-HEAD browser replay passed all 42 scenarios in the
Chromium, Firefox, and WebKit matrix. It exercised registration and account
changes, requests and activities, exact/approximate/hidden map discovery,
clustering, chat, consent-based tracking, handover, blind reviews,
moderation, notifications, restart recovery, accessibility, and responsive
overflow. The exact E2E project
`who_need_help_e2e_20260724185814-755941` and its isolated database were
absent afterward.
- A fresh isolated 30-second load replay used two web and two worker replicas
with 40 public HTTP, 40 WebSocket, and 8 authenticated mutual-aid virtual
users. All 34,755 checks and 38,153 HTTP requests passed, including 1,691
authenticated chat/tracking iterations and 240 WebSocket heartbeat
sessions. Overall HTTP duration averaged 3.095 ms with p95 7.198 ms; the
authenticated scenario averaged 7.48 ms with p95 11.21 ms. These are
workstation observations, not production limits.
- PostgreSQL observed no rollback, deadlock, conflict, temporary file, or lock
waiter. It peaked at 16 client backends with at least 81 observed connection
slots of headroom. Per-web Ecto telemetry recorded 183,553 queries with a
272.59 microsecond average total duration, including 246.44 microseconds of
execution and 25.20 microseconds of pool queue time. Exact fixture cleanup
restored every tracked application-table count.
- Ten sequential resource samples observed maxima of 345.4 and 359.2 MiB for
the web replicas, 225.6 and 222.1 MiB for workers, 120.0 MiB for PostgreSQL,
and 310.7 MiB for the isolated proxy. Evidence is retained under
`output/performance/goal-final-20260724`; the exact load project
`who_need_help_load_20260724190445947286` and its containers, network,
database volume, and image tags were absent afterward.
## Final local development audit on 2026-07-24
These observations apply to the local checkout, its isolated test projects, and
`https://whoneedhelp.imalto.site`. No push, test/production deployment, Devpost
edit, branch, or tag was made.
- Development Google OAuth, Firebase Android configuration, Analytics, and FCM
are consolidated in the Spark-plan project `who-need-help-development`. It
contains the Android app `org.whoneedhelp.mobile.development`, both measured
development signing-certificate fingerprints, and the dedicated
`wnh-dev-fcm-sender` service account. Public Android identifiers and the FCM
service-account credential are present only in the ignored mode-`0600`
development `.env`. `check-environment-readiness.sh .env --require-release`
reports zero blocking items and zero local-only warnings.
- The superseded project `who-need-help-dev-firebase` was audited before
shutdown on 2026-07-28. Google Analytics and BigQuery integration were not
enabled, and Firestore, Cloud Storage, Cloud SQL, BigQuery datasets, and
Firebase Authentication contained no configured data resources. Its only
non-default resource was the replaced development FCM service account and
key. Google accepted the exact project deletion request and reported the
project as shut down and scheduled for deletion with the documented 30-day
owner recovery window. The development, production, and frozen staging
projects were not selected by that operation.
- The current development workers were recreated with that FCM configuration
only after a mode-`0600` custom-format database backup was written to
`output/backups/dev-before-fcm-workers-20260724-114450.dump` and its checksum
and archive catalog passed. Web replicas, proxy, database, test, production,
public Git, and Devpost were not changed.
- The complete Android/browser development replay passed on the stable Android
37.1 Google APIs image with Google Play Services `262031038`. It completed a
magic-link login, Android-to-browser chat, browser-to-Android chat, FCM device
registration, a real FCM system notification, foreground live-location
sharing, and stop-sharing cleanup. The exact fixture retained two messages
and six tracking samples during verification, then cleanup restored the
original database counts with zero current tracking positions. Evidence is
`output/android-browser-development-e2e/20260724121700-2530398`; the
run-scoped container, AVD volume, image, and database fixture were absent
afterward.
- The same cross-client development flow then passed on a connected physical
Android device. The signed development app completed magic-link login,
registered its FCM token, exchanged one message in each direction with the
browser, displayed a real private FCM notification, started the foreground
location service, stored one physical location sample, and stopped sharing
with zero active sessions and zero retained current positions. The replay
exposed a Firebase Messaging main-thread call; auto-init and unregister now
run serially on the application Firebase executor. Instrumentation reported
`OK (1 test)` in 8.73 seconds. Exact fixture cleanup restored both synthetic
users, their request, assignment, messages, push jobs, and tracking session
to zero. Evidence is
`output/android-physical-development-e2e/physical-20260724-191948-1352510`.
- A separate signed development smoke passed on Android 37.1. Android verified
the exact App Link host, the implicit same-origin deep link opened the app,
the home and `/safety` DOM assertions passed, an unrelated HTTPS origin was
not claimed, and no WebView load or TLS error was recorded. Evidence is
`output/android-development-smoke/20260724124143-3282044`; its exact
container, volume, and image were absent afterward.
- `./scripts/test.sh` passed all 356 ExUnit tests. The final isolated browser
suite passed all 42 scenarios in Chromium, Firefox, and WebKit after updating
one stale assertion to the product's already-covered double-blind review
reveal behavior. Evidence is `output/e2e/20260724122808-2848321`; all
run-scoped containers, networks, database volume, and images were absent
afterward.
- `./scripts/quality.sh` passed ShellCheck, Hadolint at the configured threshold,
actionlint, every Compose render, Prometheus and Alertmanager validation,
Helm lint, tracked-source and image scans, formatting, warnings-as-errors
compilation, xref, strict Credo, Sobelow, Dialyzer, Hex/npm audits, all 356
ExUnit tests, and the isolated release/rollback/migration drills. The quality
project's exact temporary resources were absent afterward.
- The final isolated 30-second load run used 3 web and 2 worker replicas with 40
public HTTP, 40 WebSocket, and 8 authenticated mutual-aid virtual users. It
completed 38,364 HTTP requests and 34,890 checks with zero failures, including
1,729 authenticated chat/tracking iterations. HTTP p95 was 6.776 ms and the
authenticated HTTP p95 was 9.88 ms. These are workstation measurements, not
production limits or minimum resource requirements.
- During that load, PostgreSQL peaked at 21 client backends with at least 76
connection slots of observed headroom. It recorded zero rollbacks, deadlocks,
conflicts, temporary files, or lock-waiting backends. Ecto telemetry across
the three web replicas observed 186,908 queries with a 0.250 ms average total
duration; the largest individual statement maximum in `pg_stat_statements`
was 8.300 ms. The exact application-table cleanup diff was empty. Evidence is
`output/performance/final-local-audit-load`.
- Observed load peaks were 335.7 MiB for one web replica, 239.5 MiB for one
worker, 141.6 MiB for PostgreSQL, and 307.2 MiB for the isolated proxy.
CPU peaks occurred under the deliberately concurrent workstation replay and
are retained in `resource-summary.json`; no arbitrary production threshold is
inferred from them.
- The follow-up resilience drill restarted and sequentially replaced all 3 web
and 2 worker replicas. It recorded 1,244 readiness samples with zero failures,
observed all replacement web nodes, and passed the cross-node PubSub probe.
Application logs contained no error, fatal, panic, timeout, deadlock, or
out-of-memory marker. Evidence is
`output/resilience/final-local-audit-resilience`; the exact isolated load
project and resources were absent afterward.
- A visible Chrome session on the development origin rendered the real MapLibre
landing-page demo with its three synthetic markers and zoom controls. The
unauthenticated Requests navigation correctly redirected to login and showed
the access guard. The isolated browser suite covers the authenticated request
map, viewport discovery, clustering, and request lifecycle.
## Local dev hardening audit on 2026-07-23
These observations apply only to the local checkout, its Compose project, and
`https://whoneedhelp.imalto.site`. No push, test/production deployment, Devpost
edit, branch, or tag was made during this audit.
- The final `./scripts/quality.sh` run passed ShellCheck, Hadolint at the
configured threshold, actionlint, every Compose render, Prometheus and
Alertmanager validation, Helm lint, Trivy source and image scans, formatting,
compilation, xref, strict Credo, Sobelow, Dialyzer, Hex audit, both npm audits,
and all 353 ExUnit tests. The configured image scans reported zero
vulnerabilities, and the run left no project-scoped quality containers,
networks, volumes, or one-run image tags.
- A dedicated Brevo SMTP key and verified sender
`Who Need Help Development <dev@whoneedhelp.com>` were configured only in the
ignored local development `.env`. Brevo reported the sender domain as
authenticated with DKIM and DMARC. A release-container delivery probe sent one
non-authentication verification message to the operator inbox without
creating application or database data; Brevo's transactional log recorded
`Sent`, `Delivered`, and `First opening` for that exact subject and sender.
The disposable probe container was removed, and no test or production sender,
key, environment, container, or deployment was changed. On 2026-07-24 the
development application replicas were rebuilt and restarted with the external
SMTP and Google configuration. Port 587 produced no SMTP banner from either
the host or application container, while port 2525 completed a verified
STARTTLS handshake from both. The ignored development `.env` therefore uses
port 2525. A real application-generated Google ownership-verification message
was accepted in 853 ms and observed in the Gmail inbox from
`dev@whoneedhelp.com`.
- At this point in the audit, `./scripts/check-environment-readiness.sh .env`
still reported the four Firebase Android values and the FCM service-account
credential as blockers. They were subsequently configured and externally
exercised as recorded in the final 2026-07-24 audit above.
- The user subsequently accepted Firebase's separate terms in the authenticated
dev-port Chrome session. A separate Spark-plan development Firebase project
was then created and configured as recorded in the final audit above.
- Real browser Web Push was exercised on the development origin through the
user's existing dev-port Chrome profile. The exact origin permission was
changed from `Ask (default)` to `Allow`; the application registered a second,
user-owned Web Push device with `POST /mobile/push-devices` returning `201`.
A one-time development notification then created one dispatch job and one
device-delivery job. Both completed on their first attempt without recorded
errors, while the LiveView inbox updated to one unread notification and the
device remained active. Subscription endpoints and key material were not
printed or copied. The operating-system notification surface itself was not
programmatically observable, so no claim is made about its visual appearance.
- The same Chrome session did not expose a WebGL context on the notifications
page. Both the initial map mount and the explicit retry reported
`webgl-context-unavailable` with no server or console error. The location form
remained usable through its documented manual-coordinate fallback. This
records a browser capability observation rather than a map-server failure.
- The SSH production release `plan` action was repeated read-only. It observed
production commit `921e04b3608007675e22e7e26e0beb3975dbba58`, compact
topology, external PostgreSQL 18.4, healthy application containers, and
passing public readiness. The plan correctly refused release because the
production checkout still lacks browser VAPID, the Firebase Android client,
server FCM delivery, and Android App Links. It reported 57 pending local
commits and made no remote change.
- A separate read-only isolation check observed the public Git `main` reference
still at production commit `921e04b3608007675e22e7e26e0beb3975dbba58`.
The test checkout remained clean at
`cf7bacdf61ffb171ebac85e32dfcf12bc6972d59`, with
`DEPLOYMENT_ENV=test`, Compose project `who_need_help_test`, its own
container database, and healthy application/database containers. Both
`https://test.whoneedhelp.com/healthz/ready` and
`https://whoneedhelp.com/healthz/ready` returned `ready`; no test,
production, public-Git, or Devpost mutation was performed. The server-side
`validate-deployment-isolation.sh` check also passed across the two checkouts,
confirming independent Git metadata and one mode-`0600` `.env` per
deployment, with separate Compose projects, images, databases, OAuth
clients, email paths, and application secrets.
- A separate manual application rollback command now consumes only a successful
release's mode-`0600` manifest. Its plan verifies current/previous commits,
old application/edge images, backup checksum/catalog, runtime identity, and
public health. Apply requires an exact target/previous-commit confirmation,
atomically restores four image selectors, and recreates only the active
application topology and edge with `--no-deps --no-build`; Git, migrations,
the database, test, public Git, and Devpost are excluded. An isolated offline
fixture passed read-only plan, successful apply, and injected-edge-failure
recovery, including restoration of the original image selection.
- The clean local commit
`89851097fd5cbe58ce4dc41c2322810894cad50a` was packaged as a Git bundle under
`output/releases/89851097fd5cbe58ce4dc41c2322810894cad50a/`. Its SHA-256
checksum, bundle object graph, and `HEAD` identity all passed verification.
The isolated production rollback drill was repeated after packaging and again
passed plan, apply, and injected edge-failure recovery without contacting or
changing the production runtime.
- A current development database backup was created at
`output/backups/compose-20260723-194923.dump` with SHA-256
`4202a152751d588c19069eb46a25753901238729b47e6197945afdca20b65c2e`.
The checksum and archive catalog passed, and an isolated restore read 31
public tables and 1,717 rows, observed PostGIS 3.6.4 and all 18 migrations,
then removed the exact temporary database. The live counts remained 7 users,
12 requests, 15 messages, and 18 migrations; local and public DEV readiness
both continued to pass.
- The same backup then passed a full isolated upgrade rehearsal at source
commit `1793258f97e45c3de24d4d855465e1572890ecf2`. The rebuilt release restored
all data, retained all 18 migration versions and all 11 required cursor
indexes, produced a zero-byte application-table diff, formed a four-node
cluster with cross-node PubSub, and passed trusted-proxy/public-origin HTTP
checks on two web and two worker replicas. Evidence is
`output/upgrade-rehearsal/20260723195143-1759181`; the exact rehearsal
containers, networks, volume, database, and image were absent afterward.
- A clean deployment from the tracked archive at commit
`71510a2ab4fb5e2fb2aa65bdf05a358e5045923b` passed on Docker Engine
29.6.2 and Compose 5.3.1. It independently built the application, Docker
socket proxy, PostGIS, and Traefik images, applied all 18 migrations twice
without changing the 14 seeded categories, started two healthy web and two
healthy worker replicas, formed a four-node BEAM cluster, passed a
cross-node PubSub probe, and returned ready HTTP plus working home,
registration, and Mailpit responses. Evidence is
`output/portability/20260723195646-1902488-616f83`. After the run, exact
checks found zero project containers, networks, volumes, and one-run images;
the temporary tracked-archive workspace was absent, while both local and
public DEV readiness remained HTTP 200.
- Two web and two worker replicas, PostGIS, Mailpit, Traefik, and the scoped
Docker socket proxy were running after the audit. Both web replicas and both
workers were healthy; public liveness and readiness returned `ok` and
`ready`. Read-only database checks observed 7 users, 12 help requests, 8 help
assignments, 15 messages, 2 activities, 4 activity participants, 9 tracking
sessions, 0 current positions, 6 reviews, 4 support requests, and 18 applied
migrations.
- A same-helper withdraw/rejoin defect was reproduced against existing dev data:
messages belonged to the earlier assignment and disappeared from the rejoined
assignment. Pair chat now follows `(request, helper)` across that helper's
assignments, while a replacement helper receives a separate empty thread.
Domain tests cover both continuity and replacement isolation. The supporting
`help_assignments(request_id, helper_id)` index was created through the Ecto
migration workflow, applied to dev, and observed in PostgreSQL.
- Headed Chrome verified email magic-link login for an existing Activity
participant, completed-Activity exact-location access, participant lists, and
read-only group-chat history with no console errors or warnings. It also
verified the request pair-chat replay, mobile and desktop navigation, Escape
dismissal with focus restoration, request creation layout, viewport discovery,
and the mobile landing-page map after its lazy-load boundary. Audit screenshots
are retained under `output/playwright/`.
- A final fresh isolated browser run on commit
`0b9589f4fa327bf756ffb0b101fa1a042c3d2be9` passed its one-time administrator bootstrap
1/1 and all 42/42 scenarios in Chromium, Firefox, and WebKit. It re-exercised
authentication/settings, request discovery and clustering, location disclosure,
the full two-user medicine flow, active-node failover, Activity moderation,
notifications/export, localization, accessibility, responsive layouts, and
reconnect behavior. Evidence is retained at
`output/e2e/20260723232436-3328529`. A preceding run exposed stale E2E marker
text after the map legend improvement; the expectation was corrected to the
observed `Helper's live location` accessible name. A fresh focused auth replay
also passed 3/3 across the same browser engines. All run-scoped containers,
networks, Postgres volumes, and image tags were absent after both successful
runs.
- The isolated lifecycle load wrapper completed a scoped smoke run with 5,634 of
5,634 successful checks and 6,136 requests with zero failed requests, then
removed its exact containers, networks, named volume, temporary environment,
and image tags. The full retained measurement at
`output/performance/goal-dev-20260723-005415` recorded 35,004 successful checks,
38,270 requests with zero failures, 1,625 authenticated iterations, 240 WebSocket
sessions, a 7.63 ms HTTP p95, and a peak of 21 database connections. These are
workstation observations, not minimum server requirements.
- The retained resilience drill at
`output/resilience/goal-dev-drain-retry-20260723` observed explicit readiness
drain before sequential web replacement, replaced all 3 web and 2 worker
replicas, verified cluster/PubSub recovery and an Oban retry, and recorded zero
failed readiness samples. The ordinary deployment keeps Traefik's insecure API
disabled; the drill enables it only inside its isolated Compose networks to
inspect backend health state.
- A custom-format backup at
`output/backups/dev-goal-20260723-005318.dump` was restored into a fresh
database and verified with 31 tables, 1,722 rows, 17 then-current migrations,
and PostGIS 3.6.4. The later chat index migration explains the current dev
count of 18 migrations; the earlier backup was not rewritten.
## Local completion audit on 2026-07-22
The following results describe the uncommitted local workspace only. No test or
production deployment, repository push, or Devpost edit was performed as part of
this audit.
- `mix precommit` passed compilation with warnings treated as errors, formatting,
strict Credo and Sobelow checks, and all 337 ExUnit tests.
- The isolated Playwright suite passed all 42 scenarios in Chromium, Firefox, and
WebKit. It covers the requester/helper lifecycle, matched and Activity chat,
consent-driven location sharing, helper withdrawal and replacement, activity
leave/rejoin, moderation, notification preferences, nearby alerts, data export,
accessibility, and serving-node failure/reconnection. Evidence is retained at
`output/e2e/20260722212235-234952`.
- A fresh focused Chromium replay of nearby alerts, private notification inbox,
preferences, and data export passed in
`output/e2e/20260722213500-500926`. A separate headed Chrome session then
completed email-only registration through isolated Mailpit and rendered the
connected notifications/nearby-alert UI with zero console errors or warnings.
- The Android Docker build passed JVM unit tests, lint, debug APK assembly, debug
instrumentation APK assembly, and the configured Android test target.
- A 30-second isolated load run used 88 concurrent virtual users, completed 13,672
iterations and 38,586 HTTP requests, and recorded 35,118/35,118 successful
checks with zero failed HTTP requests. Observed HTTP latency was 2.19 ms average
and 6.42 ms p95; authenticated paths were 7.07 ms average and 9.68 ms p95.
Minimum observed database connection headroom was 76. These are workstation
measurements, not minimum server requirements. Evidence is retained at
`output/performance/goal-local-20260722`.
- The 50,000-row-per-table PostGIS benchmark measured the viewport query at about
10.985 ms, clustering at about 21.164 ms, concentrated leaderboard aggregation
at 48.566 ms, and reputation aggregation at 34.601 ms. Evidence is retained at
`output/db-scale/20260722204033-3485619`.
- Direct Web Push and FCM adapters, private payload shape, durable retries,
invalid-device cleanup, browser/device registration lifecycle, and Android deep
links are implemented and locally tested. Delivery through an external Web Push
endpoint or a physical Android device remains unverified because this local
audit had no VAPID/FCM credentials or registered external device.
- Account export is implemented as an authenticated allowlisted JSON download.
Account-deletion requests now have a moderator-only, read-only relationship
preflight. Destructive erasure/anonymisation is intentionally not enabled until
a jurisdiction-specific retention policy and operator approval workflow are
defined.
## 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 | The committed isolated Chromium suite rendered request and Activity maps, waited for MapLibre `idle`, and completed with no console, page, or request failures against a local PNG raster fixture. An earlier headed session rendered the configured OpenStreetMap tiles. | 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. |
| Account registration and sign-in | Implemented and browser-verified | Email registration is a single passwordless flow: it records the display name and acceptance once, sends a confirmation link, and does not duplicate a user on subsequent sign-in. Confirmed users can keep using magic links or add a password in settings. Google OpenID Connect registration, sign-in, link, unlink, replay prevention, verified-email enforcement, and account-ownership rules are covered by the 353-test suite. Real headed Chrome on the development origin exercised the Google callback, existing-account ownership email, one-time identity connection, and subsequent one-click Google login without creating a duplicate user. The application-generated authentication email was observed in Gmail from the development sender. | Production SMTP delivery and the production Google callback remain unverified. |
| Notifications and nearby alerts | Implemented and browser/physical-device verified in development | Users can configure push/email preferences, quiet hours, category/urgency/day/time filters, a private matching center, and 1/3/5/10/25 km radii. Durable inbox notifications and Oban delivery jobs are tested; public notification payloads omit chat text, exact coordinates, and the private saved-area label. The focused Chromium replay completed subscription, matching request, inbox delivery, navigation, and export. Real development Web Push delivery completed without a recorded error, and a real private FCM notification reached the physical Android development app. | Production Web Push and production Android FCM delivery remain unverified and require isolated production credentials. |
| Social profiles | Manual links implemented; optional 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. The local protocol drill also performs real HTTP token/user exchanges without returning an access token to the application. | GitHub OAuth credentials are intentionally absent and are not required for registration or the help flow. The real external provider redirect/callback remains disabled and unverified; other providers remain manual/unverified. |
| Support and content removal | Implemented and browser-verified | Public support, account deletion, general removal, and TAKE IT DOWN forms create separate audited queues; private email status links verify public contacts; authenticated submissions use the account email; moderator-only operations can update status and notify verified contacts. TAKE IT DOWN accepts URLs/text only and records a 48-hour review due time. Authenticated users can download an allowlisted JSON export, and moderators can run a read-only deletion relationship preflight. | The current product hosts no user media and does not claim TAKE IT DOWN coverage. Staffing, jurisdiction-specific legal classification, final retention rules, destructive account erasure/anonymisation, and identical-media-copy handling remain operational/legal work. |
| 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 | Local, development, test/staging, and production build identities implemented | The native packages `org.whoneedhelp.mobile.debug`, `org.whoneedhelp.mobile.development`, `org.whoneedhelp.mobile.staging`, and `org.whoneedhelp.mobile` are separated by build type and signing identity. Lifecycle, permission, deep-link, foreground tracking, recoverable main-page failure, notification-Stop, and Activity-destruction tests remain in place. Ephemeral signed development and production pipelines verify package IDs, certificates, unit tests, lint, APKs and instrumentation artifacts; production also verifies the signed AAB with Bundletool. The API 37 smoke verified the development App Link and WebView boundaries. A physical development device then passed magic-link login, bidirectional browser chat, real FCM delivery, foreground location sampling, Stop cleanup, and exact fixture cleanup. Evidence is `output/android-physical-development-e2e/physical-20260724-191948-1352510`. | Play registration/App Signing, production-device FCM delivery, unattended/background-permission tracking, and iOS are not complete. |
| Multiple web/worker instances | Implemented and locally failure/rollout-verified | The final isolated Compose drill passed BEAM crashes and sequential replacement with 3 web/2 worker replicas: all five nodes joined, PubSub passed, and 744/744 readiness requests succeeded. The project-owned kind cluster replaced all 2 web/2 worker pod UIDs under `maxUnavailable=0`; all four replacement pods joined, PubSub passed, and 363/363 samples ultimately succeeded. | Local PostGIS is a single instance. Production database HA, backups, and recovery are operator work and are not claimed complete. |
| Local observability | Implemented and protocol-verified | Pinned Prometheus scraped the exact 3 web and 2 worker targets with a file Bearer credential; Grafana provisioned a healthy datasource and ten-panel web/worker/BEAM/Ecto/Oban dashboard; Alertmanager delivered firing and resolved webhooks for an induced scoped replica stop. | Local delivery does not establish production retention, notification-provider reliability, on-call policy, or measured alert thresholds. |
| Encrypted local backup | Implemented and failure-verified | Pinned Restic streamed PostgreSQL custom format into pinned local MinIO with no host plaintext dump, passed full-data checking and a fresh-database restore, rejected a corrupted repository, and published no snapshot for an interrupted upload. The one-run MinIO project and volume were removed after retaining the non-secret evidence. | The drill proves the local mechanism, not off-site durability, database HA, or a production RPO/RTO/retention policy. |
| External protocol boundaries | Implemented and locally failure-verified | Assent/Req and Swoosh/gen_smtp paths have internal success/rejection/retry/replay/timeout coverage. Google OIDC discovery/authorization/token/JWKS with nonce and PKCE and the optional GitHub boundary are locally exercised through mocks. The development origin additionally exercised the real Google provider, an authenticated Brevo SMTP delivery observed in Gmail, real browser Web Push delivery, and real physical-device Android FCM delivery. The current push code includes provider-neutral HTTP delivery plus direct standards-based Web Push and FCM adapters with durable Oban retries and invalid-device cleanup. UniSender observations below are retained only as historical evidence and do not describe current wiring. | The real GitHub provider, production Google callback, production authentication-email delivery, production Web Push/FCM delivery, and APNs remain unverified. SMTP exactly-once delivery is not claimed. |
## Reproducible checks
- On 2026-07-21, commit `437650d5e2c376b7e4254265c022fff67bbafb6f`
was deployed only to `https://test.whoneedhelp.com`. Two independent headed
Chrome contexts registered dedicated requester and helper accounts and
completed the medicine-request lifecycle: validated creation, discovery,
acceptance, realtime chat, start, two consented location samples, realtime
marker movement, tracking stop and raw-position cleanup, handover code,
both-party completion, double-blind reviews, and leaderboard update. The same
run verified privacy-setting persistence, category proposal/vote, Activity
creation/join approval/private chat/exact-location disclosure/completion,
message-scoped reporting, and block/unblock behavior. Read-only database
checks confirmed two tracking samples, 175.73 metres of aggregate movement,
no active tracking session or stored current position after stop, and review
reveal only after both reviews existed. A blocked conversation still rendered
its form even though the server rejected the message; that defect was fixed,
covered by a regression test, redeployed only to test, and browser-rechecked.
The Dockerized Phoenix suite passed 286 tests, format checking passed, and
both clean password logins were repeated in fresh headed browser contexts
with zero console errors or warnings. Production remained on commit
`4f2a9aaacc8eca606f3287df5c8663be49f9595b` throughout this verification.
- On 2026-07-21, commit `811ddf4c5c3847fbb0c2861e6e72fcd6a9bd91fe`
was deployed only to `https://test.whoneedhelp.com`. Headed Chrome attached to
the user's existing dev-port profile completed real Google authorization,
one-time account creation, logout, and returning-user Google login. Browser
console inspection reported zero errors and zero warnings for the app flow.
Read-only PostgreSQL checks observed exactly `1 user / 1 Google identity / 0
duplicate provider UIDs`; the user was confirmed, had accepted terms, and
had no password. A subsequent email magic-link was delivered to the isolated
test Mailpit, required an explicit confirmation POST, signed the same user
in, and left `0` reusable `login` tokens and `1` active `session` token.
Application logs recorded only the expected 200/302 responses for those
paths. Production remained on commit
`4f2a9aaacc8eca606f3287df5c8663be49f9595b` during this verification.
- A separate non-authentication production delivery-format check was accepted
by UniSender Go and observed in Gmail from
`Who Need Help <contact@whoneedhelp.com>`. The intended HTTPS test-domain
link remained the link's actual destination because link tracking was
disabled. UniSender appended its sender attribution and an unsubscribe link
on `email.whoneedhelp.com`; this provider-added footer was observed rather
than inferred. No account or application database row was created by that
delivery check.
- On 2026-07-21, `./scripts/test.sh` and the Dockerized `mix precommit` each
passed 273 tests after the support/content-removal implementation. The full
isolated `./scripts/quality.sh` gate passed compiler, xref, Credo, Sobelow,
Dialyzer, Hex/npm audits, Compose/Helm validation, and configured source/image
scans. Headed Chrome over `https://whoneedhelp.imalto.site` verified public
support acknowledgement and contact verification, general removal intake,
the separate urgent TAKE IT DOWN form and 48-hour due time, passwordless
account confirmation, admin-only queues, audited operator response email,
account-deletion navigation, Russian localization, selected-status
preservation, and zero browser-console errors or warnings.
- On 2026-07-20, after adding Google OpenID Connect authentication,
`./scripts/test.sh` and `mix precommit` each passed 260 tests with zero
failures. The full isolated `./scripts/quality.sh` gate also passed format,
compilation with warnings as errors, xref, Credo, Sobelow, Dialyzer, Hex and
npm audits, Compose/Helm rendering, environment validation, and configured
HIGH/CRITICAL source/image scans.
- The Google boundary drill used the production Assent adapter against a local
OIDC server with discovery, authorization, token, signed ID token, JWKS,
state, nonce, PKCE, unverified-email rejection, and one-time callback replay
checks. It passed together with GitHub OAuth, SMTP, push, and two-worker Oban
paths. Evidence is retained at
`output/external-boundaries/google-auth-fixed-20260720`.
- In an earlier isolated local run, headed Chrome verified the temporary HTTPS
origin through the
email-only registration form, Mailpit confirmation link, one-time login,
password creation, logout, password login, Russian locale selection, and
Google connection settings. That run's Google credential pair was empty,
so the UI correctly left Google actions disabled. Browser console inspection
reported zero errors and zero warnings. The run-owned account and its one
cascading login token were removed after read-only relationship checks; no
help, activity, message, tracking, review, or OAuth rows belonged to it. The
browser was left open.
- The isolated Phoenix suite completed on 2026-07-19 with 172
tests and 0 failures after cursor pagination, database aggregation, and the
full localization changes
on Elixir 1.20.2 and Erlang/OTP 29.0.3.
- `mix compile --force --warnings-as-errors` and
`mix format --check-formatted`: passed against the same final source.
- `./scripts/quality.sh` passed ShellCheck 0.11.0, Hadolint 2.14.0 at warning
threshold, actionlint 1.7.12, every configured Compose profile render, Helm
lint, Trivy source/rendered-manifest scanning, xref, Credo high-priority
checks, Sobelow strict/private checks, Hex audit, 186 Phoenix tests, both npm
audits, and the
backup/MinIO/mc/external-mock/release-image vulnerability scans. The rendered
Helm manifest reported zero HIGH/CRITICAL misconfigurations; the Alpine
backup, MinIO, mc, and external-mock images and the Debian 13.6 release image
each reported zero HIGH/CRITICAL vulnerabilities under the configured gates.
The backup binary reported
Restic 0.19.1 compiled with Go 1.26.5; MinIO and mc reported their pinned
commits and Go 1.26.5.
- Dialyzer passed with three path- and warning-specific documented filters and
zero unused filters. Two findings are opaque-type warnings at Ecto
`Multi`/`MapSet` call sites; the third is the generated Gettext backend's
three-form plural-rule opaque-value warning;
the filters are visible in `.dialyzer_ignore.exs` rather than hidden by a
broad pattern. This is not a claim that third-party dependency compilation is
warning-free.
- 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 local Docker build targets `testDebugUnitTest`, `lintDebug`,
`assembleDebug`, and `assembleDebugAndroidTest` passed. The isolated runners
then passed 7/7 instrumentation tests on API 24, API 30, API 34, and API 37.
Each API also passed the separate external process-death probe. The
sixth test verifies that the foreground service survives Home plus Activity
destruction and remains user-stoppable. The seventh forces the first
main-frame request to disconnect, checks the native recovery dialog, presses
Retry, and asserts the subsequently rendered WebView DOM. The
public-staging target passed unit tests, `lintStaging`, `assembleStaging`,
and `assembleStagingAndroidTest`; the API 37 staging smoke asserted the
rendered `/` and `/safety` DOM from the temporary HTTPS origin with no
observed load/TLS errors. Evidence is retained at
`output/android-instrumentation/api24/20260720134554-2360359`,
`output/android-instrumentation/api30/20260720135239-2511867`,
`output/android-instrumentation/api34/20260720133058-2058554`,
`output/android-instrumentation/api37-0/20260720133350-2117132`, and
`output/android-staging-smoke/20260720052802-802174`.
The AVD configuration requests a 1 GiB userdata partition. Emulator 36.6.11
nevertheless checked for approximately 7.37 GiB while initializing API 34;
two attempts exited before ADB because only 6.1-6.3 GiB remained in the
container writable layer. The runner now mounts the AVD directory on an
isolated run-scoped Docker volume, verifies that the emulator remains
running before waiting for ADB, and removes both the container and volume on
exit. The complete four-API matrix passed with that layout, and a post-run
check found no retained `who-need-help-android-avd-*` volumes.
- Release Android guard: a staging/release build with a non-HTTPS
`WNH_BASE_URL` failed at its dedicated preflight; the successful staging
build used the explicit temporary 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.
- The hardened kind rollout then ran PostGIS as UID/GID 70 and Mailpit as
UID/GID 10001. Both used read-only root filesystems, dropped all Linux
capabilities, disabled privilege escalation, and used RuntimeDefault seccomp.
Both web and both worker replicas were Ready; their migration-wait init
containers also used read-only root filesystems and dropped capabilities.
The cross-node probe observed all four BEAM nodes, and live/readiness returned
`ok`/`ready`. Database counts remained `0 users / 0 help requests`.
- The final isolated Compose resilience drill observed a restart count increase
for one crashed web and worker BEAM process, replaced every replica
sequentially, observed all 5 cluster nodes, passed PubSub, and completed a
real Oban retry on attempt 2 after one recorded error. Its exact job row and
all fixture domain rows were absent afterward; all 744 readiness samples
succeeded.
- The final reproducible kind rolling drill replaced all four pod UIDs,
observed all 4 BEAM nodes, passed PubSub, and left the database-count diff
empty. All 363 readiness samples ultimately returned 200; two samples needed
one reconnect attempt each during local single-node NodePort endpoint
replacement.
- The final local observability drill matched all 3 web and 2 worker
Prometheus instance targets, checked the provisioned Grafana datasource and
ten-panel dashboard, received firing and resolved Alertmanager webhooks for
the exact stopped/recovered web replica, restored every target to `up`, and
left the application-table count diff empty. Its retained evidence contains
neither the metrics token nor the random Grafana password. Final evidence is
retained at
`output/observability/queue-worker-metrics-fixed-20260720`.
- The observability stop command changed only the four scoped monitoring
container states. All app/database/worker container IDs stayed unchanged and
running, and the checked user/request/message counts were identical before
and after. A subsequent full drill returned all monitoring services to
healthy.
- The final encrypted S3 drill restored 23 public application tables, all
10 current migrations, 14 categories, and PostGIS 3.6.4 into a fresh
temporary database. `restic check --read-data` passed before cleanup.
Flipping the first byte of an isolated cloned repository made
both `check` and `dump` exit nonzero. Stopping the exact backup container
after uploaded encrypted objects produced exit 130 and zero snapshots;
Restic identified and pruned 32.809 MiB of unreferenced packs, then passed a
second full-data check. The source database count diff was empty and no
configured secret appeared in retained evidence. The scoped MinIO project,
repository volume, and runtime directories were subsequently removed; the
evidence remains at
`output/backups-s3/final-backup-current-20260719`.
- The latest external-boundary drill passed real client-side OAuth token and
user HTTP exchanges, PKCE/state checks, provider denial, one-time-code replay,
a fresh flow after a temporary token error, and timeout failure. It also
passed SMTP acceptance, permanent rejection without retry, one retry after a
temporary greeting, timeout, and repeated submission. The push checks passed
the disabled adapter, rejection, HTTP retry, replay and ambiguous-timeout
deduplication, then created real acceptance/chat events against an ephemeral
PostGIS database. Two worker replicas processed them; acceptance completed on
attempt 1, chat completed on Oban attempt 2 after a temporary provider
failure, replay never reached HTTP, and the chat text was absent from the
payload. Every retained file is mode `0600`, no generated credential was
retained, no host port was published, and the exact project, volume, network,
and one-run images were absent after cleanup. Evidence is retained at
`output/external-boundaries/final-boundaries-20260719`.
- The committed browser suite passed its 1/1 bootstrap and all 30/30 scenarios:
10 each in Chromium, Firefox, and WebKit, against a fresh PostGIS volume with
two web and two worker replicas on 2026-07-20. The retained successful-run
artifact directory is `output/e2e/20260720042117-3279852`.
It covered axe checks across four pages and two themes, keyboard navigation,
three responsive widths, Activity/moderation/privacy, the two-user medicine
handover and blind reviews, public boundaries, local raster maps, and an
offline LiveSocket transport failure/reconnect. Each browser also recovered
active chat and tracking after its serving BEAM node stopped. It verified
persistent Russian/Ukrainian public language selection and the selected
Ukrainian locale inside an authenticated LiveView. The uniquely named
Compose volume and networks were removed.
- The default localization domain contains 508 current messages and the errors
domain contains 40. Russian and Ukrainian catalogs have no empty or fuzzy
current entries. Category names, descriptions, structured-field labels and
select/boolean values use locale maps with an English fallback; the
compatibility `description` column remains as a fallback for pre-existing
categories.
- The isolated database-scale harness migrated an empty PostgreSQL 18.4
database to the exact pre-index version, seeded the configured canonical
sample, captured JSON `EXPLAIN (ANALYZE, BUFFERS)` plans before and after the
generated cursor-index migration, and removed its project and volume. The
final 50,000-row-per-large-table run selected every asserted cursor index;
two consecutive request pages matched the first 50 ordered rows with no gap
or duplicate. Exact observations and their non-SLO limitations are recorded
in `docs/performance.md`.
- Browser verification used the required headed Chrome wrapper. The final
public homepage rendered its account/language controls, urgent-medicine hero,
help steps, Safety link, and three language choices. The browser reported
zero console errors or warnings; the document, fingerprinted CSS/JS, and logo
requests all returned HTTP 200. The browser was left open.
Local generated evidence (ignored by Git):
- `output/playwright/final-request-map-chat.png`
- `output/playwright/final-privacy-profile.png`
- `output/android/final-image-smoke.png`
- `output/android/foreground-notification.png`
- `output/android/staging-deep-link-safety.png`
- `output/android-instrumentation/20260719053610-2122819/results.txt`
- `output/e2e/20260719053436-2083768/`
Android artifact:
- `android/dist/who-need-help-debug.apk`
- SHA-256:
`4520aa0b50eaf53bb7052e7f4c096a73d456f45781380187586c79d7267c21c5`
- `android/dist-staging/who-need-help-staging.apk`
- SHA-256:
`08d990a9268a382052be5f2d3ba5afba2aa65a9ad5f9ae1ad4bff720f8999a6e`
- Observed staging manifest values: package
`org.whoneedhelp.mobile.staging`, version `0.1.0-staging`, minimum SDK 24,
target and compile SDK 37, cleartext traffic disabled, `singleTop`
`MainActivity`, and an exact HTTPS host of `whoneedhelp.imalto.site`.
- `apksigner verify` accepted the staging artifact's v2 generic debug
signature. That signing identity is only for installable staging verification
and is not a production release identity.
## 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.
Before the local `auth_identities` migration, a PostgreSQL custom-format dump
was created at `output/backups/compose-20260720-203913.dump`; its SHA-256 is
`ed8d663b114f3823479198005857af5501a90dacbec3f0855296327ee9cd6d20`.
An isolated restore drill read 23 public tables, 1,488 rows, 12 applied
migrations, and PostGIS 3.6.4 from that dump, then removed the temporary
database. The source Compose database subsequently reported 13 applied
migrations and an empty `auth_identities` table before browser verification.
## 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 --json` returned `{}`, and `npm ci` reported 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.sh` run 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 production dataset, traffic model, or
target-environment measurement. The isolated measurements in
`docs/performance.md` include the earlier paced public/readiness/heartbeat
comparison and a later 3-web/2-worker run with real authentication, LiveView,
chat writes, tracking writes, and explicit position deletion. The latter
completed 1,888 authenticated chains and 240 heartbeat sockets without a
functional failure and restored every tracked application table count after
fixture cleanup. Neither short run found a saturation point or represents
production traffic, so the Helm chart does not invent resource limits or an HPA
policy.
The 2026-07-20 memory investigation did identify and remove a development
runtime artifact: kind's nested container runtime exposed a huge `nofile`
limit, causing each OTP VM to allocate a roughly 1.5 GiB port table. Explicit
`+Q 65536` configuration reduced the entire kind container from 9.748 GiB to
1.796 GiB. A later rolling replacement retained that value in all four pods,
left the application-table count diff empty, and passed readiness and
cross-node PubSub. This fixes that specific over-allocation; it is not a
production minimum-RAM measurement. Detailed evidence and per-container values
are recorded in `docs/performance.md`.
The optional kind and load verification environments were stopped after their
final checks without deleting their containers or volumes. Immediately before
the pause, both isolated databases contained zero users, requests, and
messages; every kind pod was Ready. The stopped kind control plane had been
using 1.706 GiB, while the running load application/database/monitoring
containers used roughly another 1.6 GiB in the same observation. A complete
resume test measured the Kubernetes API Ready after 6.83 seconds, then
`kind-up.sh` reconciled the chart, rolled both 2-replica Deployments, passed
four-node PubSub and the BEAM port-limit checks, and `kind-stop.sh` paused it
again. The ordinary public Compose route remained HTTP 200 throughout.
## 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.
Earlier on 2026-07-19 a read-only check resolved the origin to `77.110.101.144`;
HTTP redirected to HTTPS, and the homepage, Safety page, and readiness endpoint
returned HTTP 200 with successful TLS verification. The certificate still had
the exact SAN and the same expiry. The backing ordinary Compose project was
healthy and its four-node PubSub probe passed. It contained 2 users, 1 help
request, 7 messages, and 8 applied migrations. At that checkpoint no mutation
was performed before a validated backup/restore and explicit authorization.
The later authorized rollout is recorded below; this paragraph is historical,
not the current deployment state.
A mode-`0600` custom-format backup of that exact database was then created at
`output/backups/compose-20260719-060810.dump` with SHA-256
`726cca11f69b1aab5e7141ba5f13ebe92c7f27c6b14172891cd2e462acc069f0`.
Its checksum and `pg_restore` catalog were validated. The restore drill read 23
public tables and 1,484 rows from a uniquely named temporary database, observed
PostGIS 3.6.4, applied all 10 current migrations there, passed migration
readiness, and removed that database. A final read-only source check still
reported 8 migrations and the same user/request/message counts; the four
running application container IDs were unchanged.
The same archive then passed
`./scripts/upgrade-rehearsal-compose.sh` against application commit `af9018f`.
The isolated restored copy advanced from 8 to all 10 current migrations and
reported all 11 cursor indexes valid. Two web and two worker replicas formed a
four-node BEAM cluster, the cross-node PubSub probe passed, public requests
produced the expected production HTTPS redirects, trusted-proxy pages and both
health endpoints returned HTTP 200, and the application-table count diff was
empty. The exact project, database volume, networks, and one-run image were
absent after cleanup. Evidence is retained at
`output/upgrade-rehearsal/20260719062507-3274364`. The ordinary Compose
container IDs, 8-migration state, and user/request/message counts remained
unchanged.
After explicit approval to update the development Compose project, application
commit `143e159` was deployed on 2026-07-19. Immediately before the rollout, a
new mode-`0600` custom-format backup was created at
`output/backups/compose-20260719-093528.dump`; its SHA-256 is
`fd5f293c63972b19af9bc967ea229cc2617c17ad32d5fcd695f2dafafef4d790`.
The archive passed the isolated restore drill before any source-database
migration.
The ordinary Compose migration job then advanced the source database from 8 to
all 10 current migrations and started two web and two worker replicas from
image
`sha256:6685247b8e872ade5e36f2b9e88bfca09b1606823ffb3e4f1469fbcfdc15d48c`.
The complete before/after application-table snapshot had an empty diff,
including the existing 2 users, 1 request, 7 messages, 1 assignment, and 4
ended tracking sessions. Post-rollout catalog checks found the localized
category-description column and all 11 valid cursor indexes. Local live and
readiness checks, the four-node cluster/PubSub probe, public home, Safety,
live/readiness, TLS verification, and a WebSocket Upgrade all passed. The
checked web/worker log window contained no application error, warning,
exception, deadlock, or HTTP 5xx match.
Headed Chrome then rendered the updated Safety page in English and Russian,
redirected the protected Requests route to the Russian login form with the
expected authorization notice, and reported zero console errors or warnings.
The visible browser was deliberately left open. Mode-`0600` rollout evidence
is retained under
`output/staging-rollout/20260719093528-3223196/`; the browser screenshot is
retained under ignored `.playwright-cli/`.
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/github` request returning to the profile with the
controlled `provider is not configured` flash;
- 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`
## Public Android and cross-client observation
The public-staging APK was built with the explicit ignored `.env`
`WNH_BASE_URL`, installed on the API 37 emulator, and connected to
`https://whoneedhelp.imalto.site`. Android package inspection found the exact
configured HTTPS authority and no filter for `attacker.example`. Because the
manifest intentionally does not claim App Link verification, a normal same-host
implicit `ACTION_VIEW` may show Android's resolver. Selecting the application
opened its native `MainActivity`; both `/` and `/safety` loaded from the public
origin with no observed WebView load or TLS errors. The external origin
resolved to the browser rather than the app.
The manifest intentionally sets `android:autoVerify="false"`: this is a
same-origin HTTPS deep link, not a claimed verified Android App Link. Enabling
verification requires the final production application ID and signing
certificate fingerprint to be published in the deployment's
`/.well-known/assetlinks.json`.
A cross-client scenario then used headed Chrome as the requester and the
public-staging Android app as the helper. The observed behavior was:
- the Android app authenticated against the public HTTPS origin and rendered
the MapLibre request;
- chat messages travelled in both directions, including an Android-bound
browser message appearing without reload;
- Android granted foreground location permissions, started
`TrackingService`, and showed its persistent Stop notification;
- after Home minimized the Activity, an emulator location change from
`50.46009833, 30.5334` to `50.4611, 30.5344` reached PostGIS and produced
`121.97` metres of observed movement;
- the headed browser received the helper marker in real time and removed it
when tracking stopped;
- notification Stop removed the Android service and notification, ended the
exact tracking session, left zero active sessions, and deleted its raw current
position.
The final reproducible cross-client runner performed the same boundary through
the staging instrumentation APK and containerized Chromium. Android consumed a
run-scoped one-time login token, chat messages crossed in both directions
without reload, Android foreground tracking produced a live marker in the
browser, and stopping it removed the marker. The fixture verifier found exactly
the two expected messages, one ended tracking session, and no raw current
position. Cleanup removed only the UUIDs recorded in the run manifest; the
before/after count diff across 19 application tables was empty. Browser,
instrumentation, fixture verification, and cleanup all passed. Evidence is
`output/android-browser-staging-e2e/20260720053109-872921`.
The browser scenario exposed one UI defect: movement evidence was persisted and
the marker updated, but the evidence badge remained stale until reload. The
tracking PubSub event now carries the already-derived movement/proximity
timestamps, and the LiveView updates its in-memory assignment from that same
event without an extra query. Domain and two-client LiveView regression tests
assert the event and badge change.
That fix was deployed with image digest
`sha256:1b991c1e07d98babb5151c010250cd0b9458195cf3de5a91972456ee72d75673`.
Both web and both worker containers used the digest, the cross-node PubSub probe
passed, local and public readiness returned HTTP 200, and no application
error/warning was found in the checked post-rollout logs. The before/after
database counts remained `2 users / 1 matched request / 7 messages / 14
categories / 1 assignment / 4 ended tracking sessions / 0 active tracking
sessions / 0 activities / 0 reports / 0 social identities`.
The exact temporary E2E users, tokens, request, assignment, messages, tracking
session, and audit records were removed in one scoped transaction. Queries by
their exact identifiers found no remaining rows and the original database
counts were restored. The headed browser windows and emulator were left open.
## Operations and metrics verification
On 2026-07-18, the Compose database was archived with PostgreSQL 18
custom-format `pg_dump`. The published archive and SHA-256 sidecar were
validated before an isolated restore. The restore drill created a database from
`template0`, restored with `pg_restore --exit-on-error`, read 23 public
application tables and 1106 restored rows, reported PostGIS 3.6.4, observed all
8 current migrations, and removed the exact temporary database. A follow-up
catalog query returned zero remaining restore-drill databases. Source counts
before and after remained `2 users / 1 help request / 7 messages / 14 categories
/ 1 assignment / 0 activities / 0 reports / 0 social identities`.
The same rollout started 2 healthy web replicas and 2 worker replicas. Local
live/readiness, public HTTPS readiness, and the public root returned HTTP 200.
The cross-node PubSub probe passed across all four connected BEAM nodes. The
protected metrics route returned HTTP 401 both locally and through public HTTPS
without credentials, returned valid Prometheus text with the generated ignored
local token, and was scraped directly from each web container. Helm 4.2.3 lint
and template rendering passed.
Before the runtime image change, every BEAM container logged that
`libsctp.so.1` was unavailable. The rebuilt Debian trixie release contains the
`libsctp1` package and the exact shared library; no SCTP, application error, or
application warning appeared in the post-rollout web/worker logs checked during
this verification window.
## Final full-stack regression
The broad pre-production regression below tested application commit `c6aa3d1`.
The later responsive-header follow-up at `768d63a` was separately subjected to
the complete quality and browser suites, deployed, and verified as recorded
after this section.
- `./scripts/quality.sh` passed the complete static, formatting, compiler,
xref, Credo, Sobelow, Dialyzer, dependency-audit, manifest, secret, and image
gates. ExUnit reported 172 passed. The final log is
`output/regression/final-20260719/quality-current.log`.
- The isolated browser project passed its bootstrap 1/1 and 27/27 scenarios:
9 in each of Chromium, Firefox, and WebKit, with no skipped, flaky, or
unexpected result. The suite includes accessibility, responsive navigation,
medicine handover, blind reviews, Activity privacy/moderation, localization,
reconnect, and active chat/tracking recovery after the serving BEAM node
stops. Evidence is `output/e2e/20260719162432-2790071`.
- Android debug and staging unit/lint/build gates passed. Seven instrumentation
tests passed independently on API 30, 34, and 37, including foreground
tracking surviving Home plus Activity destruction and native Retry recovery
after a forced main-frame disconnect. The separate API 37 public-staging
smoke asserted the home and Safety DOM over the exact temporary HTTPS origin,
matched no external-origin app filter, and observed zero load/TLS errors.
- A real public Chromium two-user scenario registered isolated requester/helper
fixtures, completed medicine discovery, acceptance, realtime chat, consent
tracking, handover, both confirmations, and blind reviews. It passed 1/1 in
12.8 seconds. Exact fixture cleanup removed its users and domain rows; the
before/after public database diff was empty. Evidence is
`output/staging-e2e/20260719170530-3787963`.
- The 600.5-second authenticated soak completed 271,072 iterations and 767,225
HTTP requests with zero failed HTTP requests. All 703,995 public checks
passed; expected-response latency was 2.337 ms average, 5.705 ms p95, and
175.955 ms maximum in this local run. Eight authenticated sessions completed
31,607 request-page/LiveView/chat/tracking start-update-stop chains and 4,800
heartbeat WebSockets without an authenticated or WebSocket error. The
database observed zero rollbacks, deadlocks, conflicts, or temporary files
during the measured interval. Exact fixture cleanup restored every tracked
table count. These are observations, not production SLOs or capacity limits;
evidence is `output/performance/authenticated-soak-10m-20260719`.
- The final isolated Compose failure/replacement drill used 3 web and 2 worker
replicas. All five nodes rejoined, PubSub passed, 744/744 readiness samples
succeeded, and the injected Oban job completed on attempt 2 after exactly one
recorded error. Evidence is
`output/resilience/final-resilience-current-20260719`.
- The final project-owned kind rolling drill replaced all four web/worker pod
UIDs, left all four replacements Ready with zero restarts, passed PubSub and
an empty database diff, and completed 363/363 readiness samples; two needed
one retry during the local single-node endpoint change. The current retained
kind release is Helm revision 13 with 2/2 web, 2/2 worker, and PostGIS 1/1
Ready. Evidence is `output/resilience/final-kind-current-20260719`.
- Prometheus matched all three direct web targets before and after the scoped
failure; Grafana provisioning passed; Alertmanager delivered both firing and
resolved webhooks for `who_need_help_load-web-19`; the database diff was
empty. Evidence is
`output/observability/final-observability-current-20260719`.
- Restic/MinIO passed encrypted streaming backup, full-data repository check,
fresh-database restore, corrupted-repository fail-closed checks, and
interruption after uploaded objects with zero published snapshots. The
source database diff was empty. Evidence is
`output/backups-s3/final-backup-current-20260719`; the one-run MinIO project
and volume were removed after the drill.
- The database-scale harness used 50,000 configured rows per large table,
captured 16 before/after plans, selected every asserted cursor index, and
verified consecutive request pages without a gap or duplicate. The exact
observations and limits are in
`output/db-scale/20260719164620-3314314` and `docs/performance.md`.
- OAuth, SMTP, and HTTP-push boundaries passed success, rejection, timeout,
retry, replay, and deduplication checks against local internal-only
counterparts. Product acceptance and chat events produced durable jobs
consumed by two worker replicas; message content was excluded from push.
GitHub OAuth remains optional and disabled without credentials. Evidence is
`output/external-boundaries/final-boundaries-20260719`.
- A clean tracked-archive deployment at `c6aa3d1` applied all 10 migrations,
repeated migration idempotently, seeded 14 categories without duplication,
started 2 web and 2 worker replicas, passed HTTP/Mailpit/cluster/PubSub, and
left its project, image, volume, and extracted workspace absent after
cleanup. Evidence is
`output/portability/20260719171321-3973031-61769f`.
- The ordinary public Compose database was backed up to mode-`0600`
`output/backups/compose-20260719-165722.dump` with SHA-256
`5b4d8a78665c5d47863af8e1e885684b110d94c278474e37c8465e0814f7000f`.
The archive passed catalog validation and an isolated restore before rollout.
The current public image ID is
`sha256:009f6754fb57a410f16252205bbfa1467a08b82a733de59c3c80e431afa8fc42`
on 2 healthy web and 2 running worker replicas. The deployment diff was
empty; the final read-only observation remains 2 users, 1 request, 7
messages, 4 ended tracking sessions, and 10 migrations.
- After the workstation restart, the development route initially timed out
because its existing OpenVPN connection was down. Read-only inspection
confirmed the gateway Nginx site still targeted the assigned client address
`10.8.0.14:4010`, while the workstation had no tunnel interface. The exact
NetworkManager profile was `openvpn__toha_nobara_pc`; its observed historical
lease was the same `10.8.0.14`.
- The profile emitted OpenVPN's warning that no server-certificate verification
method was enabled. After checking the installed NetworkManager,
NetworkManager-openvpn, and OpenVPN versions and the OpenVPN 2.7 manual, the
local profile was changed to require `remote-cert-tls=server`. Reactivation
restored `tun0` at `10.8.0.14`; the subsequent journal showed the expected
server peer and completed initialization without the certificate-verification
warning. The remaining `persist-key` and legacy cipher notices were recorded
but not changed without a separate compatibility review.
- The gateway could then reach `http://10.8.0.14:4010/healthz/ready`, and both
the public development readiness endpoint and homepage returned HTTPS 200.
The online Android App Links verifier also passed for the staging package,
staging signing certificate, and `https://whoneedhelp.imalto.site` statement.
No Nginx, test, production, Devpost, remote Git, or deployed application
configuration was changed by this recovery.
- Scoped cleanup left zero containers, volumes, and networks for the
`who_need_help_load` project and removed its ignored observability/backup-S3
runtime directories. The reusable ordinary public Compose project and the
project-owned kind cluster intentionally remain running.
## Responsive-header follow-up
Commit `768d63a` replaced the duplicated public navigation bars with one
responsive application header. At widths below 1280 px it exposes the primary
links, language selection, theme controls, and authentication actions through a
single native `details` menu; wider layouts show the same actions inline.
- `./scripts/quality.sh` passed after the change, including 172 ExUnit tests,
formatting, compiler, xref, Credo, Sobelow, Dialyzer, dependency audits,
Compose/Helm validation, and gated image scans. Evidence is
`output/regression/header-unification-quality-20260719.log`.
- The isolated browser project passed bootstrap 1/1 and 27/27 scenarios across
Chromium, Firefox, and WebKit. Its responsive accessibility case exercised
widths 360, 768, 1030, and 1440 px, asserted a single header and primary
navigation, and detected no horizontal overflow. Evidence is
`output/e2e/20260719174824-537439`.
- Before the public Compose rollout, the database was backed up to mode-`0600`
`output/backups/compose-before-header-20260719-175353.dump` with SHA-256
`fea893340d2428a61ebed8abb83816a6273b1afeb5d794763b00c81a37d15e2d`.
The archive passed an isolated restore with 23 readable public tables, 1,488
rows, all 10 migrations, and PostGIS 3.6.4 before the temporary restore
database was removed.
- The deployed image ID is
`sha256:8c3790e6b5fdd3f1feb08165d33d5b2ddaa9089b6606da44a804c5facd4765f6`
on 2 healthy web and 2 running worker replicas. The before/after database
snapshots were byte-identical: 2 users, 1 request, 7 messages, 4 tracking
sessions, and 10 migrations.
- Public HTTPS and `/healthz/ready` returned HTTP 200. The four BEAM replicas
were connected and the deployment's cross-node PubSub check passed. Headed
Chrome then verified the public page at 1030 and 360 px: one visible header,
accessible menu contents, no horizontal overflow, and zero console errors or
warnings. The visible browser was left open at 1030 px with the menu closed.
## Brand icon follow-up
Commit `c28c9a3` replaced the placeholder Phoenix mark with the selected
Who Need Help symbol. The symbol remains image-only while the header name stays
as accessible HTML text.
- One source SVG reproducibly generates 48 px browser fallback, PWA 192/512,
maskable 512/1024, Apple touch 180, Apple-ready 1024, Google Play 512, and
legacy Android density assets through `scripts/generate-brand-assets.sh`.
The Google Play file is 512×512, 8-bit RGBA PNG, and 27,107 bytes.
- The PWA manifest separates `any`, `maskable`, and `monochrome` purposes.
Android API 26+ uses foreground/background adaptive layers, API 33+ adds a
monochrome layer, and API 24/25 retain generated density resources. The
notification small icon is now a dedicated monochrome vector rather than the
launcher artwork.
- The pinned Android SDK 37 Docker build passed JVM unit tests, `lintDebug`,
`assembleDebug`, and `assembleDebugAndroidTest`. The exported debug APK and
lint report are under `android/dist/`.
- `./scripts/quality.sh` passed with 173 ExUnit tests and every static,
dependency, security, manifest, and image gate. Evidence is
`output/regression/brand-assets-quality-20260719.log`.
- The isolated responsive/accessibility browser suite passed 6/6 across
Chromium, Firefox, and WebKit. Evidence is
`output/e2e/20260719193958-2786366`.
- Before rollout, mode-`0600`
`output/backups/compose-before-brand-20260719-194155.dump` was created with
SHA-256
`9be1197a02783b959e2eee783c6d74b4bf2a65a581c6a4566f0e815c7465ea61`.
Its isolated restore retained 24 public tables, the observed domain counts,
all 10 migrations, and PostGIS 3.6.4; the temporary database was removed.
- Public image ID
`sha256:8db2281f2cf51b787fc22d99f2911ee870c9ee66a0ae6e9b801796ab1aff605f`
runs on 2 healthy web and 2 running worker replicas. The before/after
database snapshots are byte-identical and cross-node PubSub passed.
- Every public brand URL, the manifest, service worker, homepage, and readiness
endpoint returned HTTP 200. Public and repository SVG SHA-256 values matched.
Headed Chrome showed the new 36×36 header mark at 1030 px with no overflow or
console messages. Service worker `v2` activated and removed the old `v1`
cache. Evidence is `output/brand/public-header-1030-20260719.png`.
## Current public functional replay
The committed two-user medicine scenario now includes browser geolocation in
the primary path rather than relying only on the separate failure-recovery
scenario.
- Against `https://whoneedhelp.imalto.site`, the current public release passed
request creation, helper acceptance, two-way realtime chat, explicit
geolocation consent, appearance of the helper's live marker in the
requester's browser, explicit tracking stop and marker deletion, the
one-time handover code, both completion confirmations, and double-blind
review reveal. The Chromium scenario passed 1/1 in 14.7 seconds. Exact
cleanup removed its two users, request, assignment, messages, reviews,
tracking session, abuse signals, and audit events; the before/after
application-table count diff was empty. Evidence is
`output/staging-e2e/20260719202129-3641894`.
- The complete browser suite with that expanded primary path passed 27/27:
9 scenarios each in Chromium, Firefox, and WebKit against a fresh isolated
2-web/2-worker Compose cluster. The independent active-node-failure path also
verified tracking and chat recovery after the serving BEAM node restarted.
Evidence is `output/e2e/20260719202632-3770163`; its containers, networks,
and database volume were removed by the run-scoped cleanup.
The broader public-staging replay was then expanded without reusing or deleting
unrelated application records:
- One run against `https://whoneedhelp.imalto.site` passed 3/3 Chromium
scenarios covering registration, email confirmation, password setup, email
change and re-login; medicine request, helper acceptance, private realtime
chat, browser geolocation sharing and deletion, handover confirmation and
double-blind reviews; motorcycle broken-chain roadside help with helper
withdrawal and requester cancellation; Activity creation, join approval,
approved group chat, reporting, evidence, blocking/unblocking, privacy
defaults, social-link add/remove, category proposal and moderator actions.
- The fixture tool first verified the exact database name and an unused
run-specific email prefix. Cleanup validated ownership of every related
request, assignment, activity, participant, message, report, proposal,
review, tracking, audit and push-job record before deleting it. It removed
only the six observed run users and their validated relationships. Exact
Mailpit message IDs for the run were deleted separately and their absence was
rechecked. The before/after counts across 19 application tables produced an
empty diff. Evidence is
`output/staging-full-e2e/20260719210733-461538`.
- The current isolated browser matrix passed bootstrap 1/1 and application
scenarios 30/30: 10 each in Chromium, Firefox and WebKit. It includes active
BEAM-node failure while chat and browser tracking are in use, followed by
reconnect on an available replica and continued chat/tracking operation.
Evidence is `output/e2e/20260719211815-708067`; the isolated containers,
networks and PostgreSQL volume were removed automatically.
- `./scripts/quality.sh` passed on the same source state: formatting, strict
compilation, xref, Credo, Sobelow, Dialyzer, 173 ExUnit tests, dependency
audits, Compose/Helm/observability checks and the configured image scans.
Evidence is
`output/regression/full-public-web-quality-20260719.log`.
## Final Android and operational replay
Application commit `b96d443` and its exact source state were subjected to the
following additional local and temporary-origin checks:
- `./scripts/quality.sh` passed all configured source, Compose, Helm,
observability, dependency, release-image, and security gates with 174/174
ExUnit tests. Evidence is
`output/regression/android-cross-quality-20260720.log`.
- Android debug and staging unit/lint/APK builds passed. The isolated API
30/34/37 matrix passed 7/7 device tests per API. Public API 37 DOM smoke
passed 1/1. The Android/browser staging run passed Android magic-link login,
private chat in both directions, foreground tracking, browser marker
appearance/removal, fixture verification, and an empty 19-table cleanup
diff. Evidence is
`output/android-browser-staging-e2e/20260720053109-872921`.
- The external-boundary drill passed OAuth, SMTP, provider-neutral HTTP push,
product job integration, retry, rejection, timeout, replay, and
deduplication paths with two isolated workers. Its one-run project, volume,
images, network, and generated secrets were removed. Evidence is
`output/external-boundaries/final-boundaries-20260720`.
- The isolated 3-web/2-worker Compose failure drill crashed one web and one
worker BEAM process, sequentially replaced every replica, rejoined all five
nodes, passed PubSub, and completed the injected Oban job on attempt 2 after
one recorded failure. All 725 readiness samples succeeded and the exact job
was deleted. Evidence is
`output/resilience/final-resilience-20260720`.
- The 30-second, 88-VU load replay completed 13,765 iterations and 38,826 HTTP
requests. All 35,280 checks passed; HTTP failures were 0. It completed 1,765
authenticated LiveView page/chat/tracking start-update-stop chains and 240
heartbeat sockets without a functional error. PostgreSQL observed zero
rollbacks, deadlocks, conflicts, or temporary files. Exact fixture cleanup
produced an empty application-table diff. Evidence is
`output/performance/final-after-queue-20260720`.
- Prometheus exactly matched and scraped all three web targets before and after
the induced replica stop. Grafana's provisioned Prometheus datasource
returned `OK`; Alertmanager delivered firing and resolved webhooks for the
scoped instance. The database count diff was empty. Evidence is
`output/observability/final-observability-20260720`.
- Restic created and checked an encrypted local S3 snapshot, restored it into a
fresh database, rejected a corrupted repository, and published no snapshot
for an interrupted upload. The interrupted repository passed checking after
pruning unreferenced data; the source database diff was empty. Evidence is
`output/backups-s3/final-backup-20260720`.
- The 50,000-row database replay selected every asserted cursor index and
retained the same generated table counts before and after the index
migration. Its isolated project, volume, and image were removed. Evidence is
`output/db-scale/20260719233526-97476`.
- A clean deployment from `git archive b96d443` generated independent secrets,
applied all 10 migrations, repeated migration without changing its 10
migration or 14 category counts, and passed HTTP, Mailpit, four-node cluster,
and PubSub checks on 2 web/2 worker replicas. Its workspace, image, project,
volumes, and networks were absent after cleanup. Evidence is
`output/portability/20260719233712-142771-ac1883`.
- The project-owned kind cluster was rebuilt with the current image. The final
rolling drill replaced all four application pod UIDs with four Ready,
zero-restart pods, rejoined all four BEAM nodes, passed PubSub, and left an
empty database diff. All 395 readiness samples ultimately succeeded; two
used one transport retry during the local single-node NodePort endpoint
replacement. Evidence is
`output/resilience/final-kind-rollout-fixed-20260720`.
The first 2026-07-20 kind attempt exposed a race in the verification harness:
one label-based `kubectl wait` invocation retained a terminating pod in its
initial resource set. The harness now waits for old UIDs to disappear, captures
the exact four replacement pod names, and waits for those resources. The fixed
full replay above passed.
## Final public and browser replay after one-time-link hardening
The final public replay exposed a transport edge case specific to one-time
confirmation links: the server had committed the email change and consumed the
token before Chromium reported `ERR_NETWORK_CHANGED`, while the generic
navigation helper then replayed the already-consumed URL. The browser harness
now opens one-time links at most once. If that single navigation loses its
response to a transient transport reset, it verifies the committed account
state through the idempotent settings page instead of replaying the token.
- The complete public Chromium replay passed 3/3 against
`https://whoneedhelp.imalto.site`: registration and email/password changes;
medicine request, helper matching, two-way chat, browser location tracking,
marker deletion, handover and blind reviews; Activities, privacy, social
links, blocks, reports, category voting and moderation. Its cleanup restored
all 19 recorded application-table counts byte-for-byte and removed the
run-specific Mailpit messages. Evidence is
`output/staging-full-e2e/20260720042807-3462613`.
- The fresh isolated browser matrix passed bootstrap 1/1 and 30/30 application
scenarios, 10 each in Chromium, Firefox and WebKit, including realtime
recovery after the serving BEAM node was restarted. The run-scoped
containers, networks and PostgreSQL volume were removed. Evidence is
`output/e2e/20260720042117-3279852`.
- The public HTTPS PWA replay passed 1/1, covering manifest and install assets,
service-worker cache update, offline public fallback, and exclusion of
private application pages from the offline cache. Evidence is
`output/staging-pwa-e2e/20260719235520-665802`.
- `./scripts/quality.sh` passed again on the same source state with 174/174
ExUnit tests and all configured source, dependency, Compose, Helm,
observability and container-image security gates. Evidence is
`output/regression/final-single-use-quality-20260720.log`.
## Queue and ingress-policy hardening replay
The 2026-07-20 hardening pass removed the unused Oban `default` consumer and
made the two real queue limits explicit in Compose, generated local
environments, and Helm. After rebuilding the isolated load release, both
worker nodes reported exactly `maintenance: [limit: 2]` and
`push: [limit: 1]`; the five-node cluster and cross-node PubSub probe passed.
The explicit 1,000-job burst completed 1,000/1,000 jobs in the observed 4,516
ms, produced no matched application/database error, removed exactly its 1,000
run-scoped rows, left zero probe jobs, and retained identical domain-table
counts. Evidence is
`output/performance/oban-burst-1000-final-20260720`.
The Helm chart now renders one ingress NetworkPolicy for the chart instance.
Helm lint and the rendered-manifest Trivy scan pass. The manifest permits
chart-instance pod-to-pod Erlang distribution and the configured HTTP
listener while isolating other inbound pod ports. Actual packet enforcement
has not been claimed because it depends on the target cluster's CNI; that must
be verified on the eventual deployment environment.
The same source state passed `scripts/quality.sh`: 186/186 ExUnit tests,
format and warnings-as-errors compilation, xref, Credo, Sobelow, Dialyzer,
Hex/npm audits, ShellCheck, Hadolint, actionlint, every Compose render, Helm
lint, observability configuration, rendered-manifest scanning, and all
configured runtime image scans. Every reported HIGH/CRITICAL vulnerability
count was zero.
## Final pre-production audit replay on 2026-07-20
The final manual code pass fixed LiveView form metadata leaking into dynamic
category data, stale review state across replicas, forms retaining submitted
report/proposal/chat content, an invalid leave action on completed activities,
and blocked-organizer metadata remaining visible in a participant's activity
history. Regression coverage now includes all of those cases.
- `./scripts/quality.sh` passed on the final source state with 243/243 ExUnit
tests, format and warnings-as-errors compilation, xref, Credo, Sobelow,
Dialyzer, Hex/npm audits, Compose and Helm rendering, observability
validation, source secret/misconfiguration scanning, and every configured
production and infrastructure image scan. The reported configured-threshold
vulnerability count was zero.
- The final isolated 50,000-row database replay measured public help discovery
at 0.144 ms and the blocked participant-activity page at 22.837 ms for 25
returned rows on this workstation. The aggregate leaderboard and helper
reputation queries measured 45.006 ms and 38.443 ms respectively. These are
observations, not production resource requirements. Evidence is
`output/db-scale/20260720154617-630777`.
- The 30-second isolated load run used 3 web and 2 worker replicas. It completed
38,936 HTTP requests and 2,060 WebSocket sessions with zero HTTP failures;
overall HTTP p95 was 5.321 ms. It also completed 1,820 authenticated
LiveView/chat/tracking cycles. Peak sampled container memory was 296-324 MB
per web replica, 205-219 MB per worker, 121 MB for PostgreSQL, and about
1.54 GiB when summing the seven measured runtime-container maxima. Database
sampling observed no deadlocks, lock waits, temporary files, or rollbacks
during the run. Evidence is
`output/performance/final-current-20260720`.
- The subsequent replacement drill recorded 650/650 successful readiness
samples, a five-node BEAM cluster, cross-node PubSub, healthy web/worker
recovery, and an Oban probe completing on attempt 2 after its intentional
first failure. Evidence is
`output/resilience/final-current-resilience-20260720`.
- A visible-browser replay on `https://whoneedhelp.imalto.site` covered
registration and magic login, account/profile/privacy controls, social-link
semantics, medicine request creation and matching, private realtime chat,
optional live tracking UI and cleanup, handover, double confirmation,
double-blind reviews, activity creation/join approval/exact-location privacy,
group chat and completion, reports/moderation/audit evidence, category
proposal/voting/moderation, and blocking in both directions. The final fresh
post-deployment tab reported zero console errors and zero warnings.
- The first public automation replay exposed two verification defects after the
UI fixes: duplicate valid review status text made a broad locator ambiguous,
and a committed password change lost only its redirect GET to
`ERR_NETWORK_CHANGED`. Commit `9463009` scopes the review assertion to the
persistent status and submits the password mutation only once before
recovering through an idempotent settings GET. The clean replay passed 3/3.
Evidence is `output/staging-full-e2e/20260720160445-1004343`.
- The final HTTPS PWA replay passed 1/1, Android/browser chat and tracking
passed twice, and the packaged native staging APK passed its Android 37
public-origin smoke. Evidence is
`output/staging-pwa-e2e/20260720160533-1019120`,
`output/android-browser-staging-e2e/20260720160544-1022997`, and
`output/android-staging-smoke/20260720160743-1061722`.
The manually created browser-audit user, request, assignment, activity,
messages, tracking session, reviews, reports, category proposals, vote, abuse
signals, audit events, and Mailpit messages were removed by exact identifiers.
The existing helper account was restored to role `user`, no password, and no
blocks. Post-cleanup counts retained two users plus one unrelated request and
assignment; those unrelated rows were not changed.
## Production-handoff replay on 2026-07-20
Commit `14987bb` adds a first-deployment Compose path without inventing the
unknown server topology. `scripts/init-production-env.sh` generated independent
database, Phoenix, handover, BEAM-cookie and metrics values without printing
them, wrote mode `0600`, and refused to overwrite its destination.
`scripts/validate-production-env.sh` accepted the fully supplied fixture and
rejected the unresolved proxy/SMTP fixture. The configurable
`HTTP_BIND_ADDRESS` rendered as both `0.0.0.0` and `127.0.0.1`.
`compose.production.yaml` kept Mailpit inactive unless its explicit local
profile was enabled.
- The complete isolated quality/security gate passed after these changes:
244/244 ExUnit tests, format and warnings-as-errors compilation, xref, Credo,
Sobelow, Dialyzer, Hex/npm audits, ShellCheck, actionlint, all Compose/Helm
renders, observability validation, tracked-source secret/misconfiguration
scanning, and all configured production/infrastructure image scans. The
configured-threshold vulnerability count was zero.
- The deployed Compose stack applied all 12 migrations and reported two
healthy web plus two healthy worker replicas. A repeated cluster probe
observed all four BEAM nodes and passed cross-node PubSub. Fresh cgroup
observations were approximately 210-221 MB per application replica; this is
not a future server minimum or capacity promise.
- Public HTTPS returned ready/live success, HSTS, secure HttpOnly SameSite
cookies and the expected security headers. The scheme-specific CSP contained
`wss://whoneedhelp.imalto.site` and no plaintext WebSocket origin.
- A headed Chrome registration used a unique run-scoped address, received its
confirmation message in Mailpit, required explicit confirmation, and created
an authenticated session. The profile exposed location-visibility choices,
manually added unverified social-link semantics, reputation, blocks and an
optional external thank-you link. Sensitive settings correctly required
email reauthentication after the sudo window. The password form's browser
accessibility warning was fixed by associating its username; the fresh
deployed settings tab reported zero errors and zero warnings.
- A full public staging replay passed 3/3 registration/settings, mutual-aid and
activity/moderation scenarios. Its database cleanup diff was empty. Evidence
is `output/staging-full-e2e/20260720184356-3858682`.
- The public PWA install/cache-update/offline-fallback replay passed 1/1.
Evidence is `output/staging-pwa-e2e/20260720184450-3875584`.
- The current staging APK/test APK were rebuilt for the configured HTTPS
origin. The Android 37/browser replay passed magic login, bidirectional
realtime chat, foreground location sharing, a PostGIS-backed position and
notification Stop cleanup; it retained zero raw positions and its database
cleanup diff was empty. Evidence is
`output/android-browser-staging-e2e/20260720184520-3885007`.
- Backup `output/backups/compose-20260720-182338.dump` has SHA-256
`ac19d5dbb3c1b797269003d6b302a2ae3d8d9634be6ad31ea2468d7f5a837a3b`.
Its fresh-database restore drill found 23 public tables, 1,489 rows, all 12
migrations and PostGIS 3.6.4. The upgrade rehearsal passed with an empty
application-table diff, four cluster nodes, PubSub and HTTP; evidence is
`output/upgrade-rehearsal/20260720182353-3474703`.
The final manual registration account had no request, assignment, message,
activity, review, report, block, social-identity or tracking references. It and
its two session tokens were removed in one exact transaction; its two exact
Mailpit message IDs were also removed. Final domain counts returned to two
users, one unrelated request, one unrelated assignment and seven unrelated
messages. One PostgreSQL error in the audit window came from an incorrect
column name in a read-only operator query; PostgreSQL aborted that transaction.
The subsequent log window contained no matched error/warning entry.
## Private-Git and final boundary replay on 2026-07-20
The newly configured private `origin` and its `main` branch both resolved to
commit `378768b` before this replay. The observed server API identified Gitea
1.22.0. Because that release reads repository workflows from
`.gitea/workflows/`, a separate Gitea workflow now runs the same four local
gates sequentially on the dedicated `who-need-help-ci` label. Both the GitHub
and Gitea workflows passed pinned actionlint locally. Remote Actions enablement,
runner registration and an actual Gitea run remain unverified until a trusted
runner is provisioned.
This replay found and corrected two integration regressions that the ordinary
Phoenix suite did not cover: the external-boundary workers were missing the
new required metrics credential, and the drill's product request was missing
the new required safety acknowledgement. The repaired drill then passed OAuth,
SMTP, provider-neutral push, two Oban worker replicas, retry, replay
deduplication and product-event delivery. Its non-secret mode-`0600` evidence
is `output/external-boundaries/preprod-gitea-pass`.
- The final isolated quality/security gate passed with 244/244 ExUnit tests and
zero configured-threshold source/image findings. The Gitea runner state,
binary and local data directory are now excluded from Git and Docker build
contexts and covered by the quality script.
- The isolated Playwright suite passed all 30 scenarios across Chromium,
Firefox and WebKit, including medicine matching, chat, tracking, handover,
blind reviews, privacy, moderation, account changes and replica restart
recovery. Evidence is `output/e2e/20260720190130-1182`.
- The debug Android build passed unit tests, lint, debug APK and test APK
assembly using `.env.example`; no generated APK is tracked.
- The temporary HTTPS origin returned HTTP 200 from both `/healthz/live` and
`/healthz/ready`. The observed local Compose project had two healthy web and
two healthy worker replicas with zero restarts.
- One idle `docker stats --no-stream` snapshot measured approximately
1,040 MiB combined across the database, Docker API proxy, Mailpit, edge
proxy, two web replicas and two worker replicas. This workstation snapshot is
not a production minimum, limit or capacity claim.
## Compose topology and database-mode verification on 2026-07-21
- The current ExUnit suite passed 260/260 after adding the combined runtime
role and deployment-mode selection.
- The complete isolated quality/security gate passed: ShellCheck, Hadolint,
actionlint, all Compose mode renders and production-environment rejection
cases, Helm lint/render, tracked-source scanning, format, warnings-as-errors
compilation, xref, Credo, Sobelow, Dialyzer, Hex/npm audits, 260/260 ExUnit
tests, and every configured runtime/infrastructure image scan. The
configured HIGH/CRITICAL finding count was zero.
- A fresh isolated `compact + container` production render contained only
`db`, one-shot `migrate`, and `app`. Readiness and a local PubSub probe passed;
the running combined container reported the expected 65,536 Erlang port
limit. Its exact containers, networks, and database volume were removed.
- A separate PostgreSQL 18.4/PostGIS 3.6 fixture was published only on the
Docker bridge gateway. The `split + external` render contained migrate,
Docker API proxy, Traefik, two web replicas, and two worker replicas, and did
not contain a `db` service or database volume. The preflight reported
PostgreSQL/PostGIS versions and `TLS=false` for that local fixture, all 13
migrations applied, readiness passed, all four BEAM nodes joined, and a
cross-node PubSub broadcast passed. Its logs contained no error marker and
all run-scoped containers, networks, fixture container, and fixture volume
were removed.
- The first compact drill exposed an isolation defect: the generated
production environment lacked `COMPOSE_PROJECT_NAME`, so Compose selected
the ordinary project name and recreated its database container with the
drill's environment. Authentication failed before any migration ran and the
compact app remained in `Created`. Read-only checks still found 13
migrations, 2 users, and 1 request. Backup
`output/backups/compose-20260720-223237.dump` was created, the ordinary split
stack was recreated with its original ignored `.env`, the same counts and
HTTP readiness were rechecked, and the never-started compact container was
removed. The initializer now writes and validates an explicit project name,
and both subsequent isolated drills used distinct verified project names.
- The final ordinary development stack again reported two healthy web and two
healthy worker replicas, cross-node PubSub, local and temporary-public-origin
readiness, unchanged counts (13 migrations, 2 users, 1 request), and no
error marker in the post-rollout application/proxy log window.
These checks establish the mode wiring on this workstation. They do not prove
the future provider's TLS/CA policy, network reachability, backup service, high
availability, or target-server capacity.
## Retired UniSender Go delivery boundary observed on 2026-07-21
This section is retained only as historical evidence. The adapter, runtime
mode, environment variables, Compose wiring, Helm wiring, and deployment
instructions were removed when both public environments migrated to SMTP.
None of the observations below describe the current delivery path.
- The production server returned HTTP 200 and a successful authenticated result
from UniSender Go's `system/ping` Web API method. The API key was loaded from
the server's mode-`0600` credential fragment and was not printed.
- Direct TCP connection attempts from the same server to SMTP ports 25, 465,
and 587 timed out for UniSender Go; control attempts to other public SMTP
providers also timed out. This observation does not establish where the
filtering occurs.
- At that time, `WhoNeedHelp.Email.UnisenderGoAdapter` mapped the application's
existing Swoosh messages to the provider's HTTPS `email/send.json` contract. Six
focused tests passed for the exact request shape (including explicit
`track_read=0` and `track_links=0`) and API-key header, success, redacted
recipient rejection, structured API errors, invalid responses, and rejection
of unsupported or provider-invalid messages before network I/O.
The then-current runtime configuration and production environment validation
could select either `smtp` or `unisender_go` without requiring SMTP settings
in API mode. That selectable API path has since been removed.
- Authoritative DNS and the provider UI both showed the sending domain as
verified with DKIM active, while the delegated link domain showed configured.
A second real Web API message was accepted for one recipient with no rejected
recipients and reached Gmail's Inbox after one second. Gmail's original
message view reported SPF PASS, DKIM PASS with `d=whoneedhelp.com` and
selector `gokey`, and DMARC PASS.
- The delivered MIME still contained the provider's one-pixel
`go2_read_tracker` even though the request explicitly supplied
`track_read=0` and `track_links=0`. UniSender Go documents that honoring those
zero values, and using `skip_unsubscribe=1`, requires provider approval. A
support request covering all three permissions was sent to the official
support address from the account mailbox. Privacy-disable behavior and
unsubscribe removal remain unverified until the provider enables them and a
new delivered MIME is inspected.
## Development Android and provider isolation on 2026-07-23 and 2026-07-24
- The development checkout now owns
`org.whoneedhelp.mobile.development`, an independent mode-`0600` signing
identity outside the repository, and matching App Links values in its one
ignored mode-`0600` `.env`. The test/staging package remains
`org.whoneedhelp.mobile.staging`; production remains
`org.whoneedhelp.mobile`.
- The signed ephemeral development pipeline passed development unit tests,
Android lint, APK and instrumentation-APK assembly, package/certificate
verification, and offline App Links identity validation. The signed
production pipeline separately passed release tests, lint, R8/resource
shrinking, APK/AAB signing checks, Bundletool validation, and production App
Links identity validation.
- The API 37 development emulator smoke now passes the signed
`org.whoneedhelp.mobile.development` APK against
`https://whoneedhelp.imalto.site`. It observed the home and `/safety`
main-frame loads, asserted both DOMs, rejected an unrelated HTTPS origin,
found no load/TLS error, and captured the rendered phone screenshots.
Evidence is retained at
`output/android-development-smoke/20260723230559-2828624`. The Android 17
verifier reported the development host as `verified`; a real implicit
`/safety` intent cold-started `org.whoneedhelp.mobile.development`, while an
unrelated HTTPS origin remained assigned to Chrome. Its run-scoped
container, AVD volume, and image were all absent after cleanup.
- This replay exposed and fixed two stale checks rather than treating a build as
runtime proof. Development/staging APKs deliberately have Android debugging
disabled, so their previous smoke harness waited for a debug-only page-load
message even after WebView rendered successfully. Redacted path-only load
diagnostics are now enabled only for the signed non-production public build
types, while WebView debugging and the release build remain disabled. The
public DOM assertion was also updated from the retired hero copy to the
current `Need help nearby? Ask the community.` heading.
- The complete isolated quality/security gate passed with 353 ExUnit tests,
ShellCheck, Hadolint, actionlint, Compose/Helm validation, format/compiler,
xref, Credo, Sobelow, Dialyzer, Hex/npm audits, source scanning, and all
configured runtime image scans. Its unique Compose project, volume, and
temporary image tags were absent after cleanup.
- The development Google Web OAuth client is configured for the exact
`https://whoneedhelp.imalto.site` origin and callback, and its ID/secret are
present only in the ignored development `.env`. The current development
replicas were rebuilt with that configuration and remained healthy.
- On 2026-07-24 the same Google Cloud development project was checked through
the user's already-authorized dev-port Chrome profile. It contained only the
expected Web client before a separate Android client was created for
`org.whoneedhelp.mobile.development` and the SHA-1 of the stable development
signing certificate. A read-only return to the Clients page then showed
exactly the development Web and development Android clients. No test or
production client was changed.
- Real headed Chrome verification used only the user's existing dev-port
profile. The Google Web flow reached the exact development callback, required
one email-ownership confirmation before attaching a matching existing local
account, then completed a subsequent Google sign-in without another email.
Read-only PostgreSQL checks observed seven users and exactly one Google auth
identity, and the settings page reported the Google account as connected.
The application-generated confirmation email was observed in Gmail from the
development sender. No test or production OAuth client, sender, deployment,
database, public Git reference, or Devpost entry was changed.
- The isolated external-boundary drill passed OAuth and Google OIDC
success/rejection/replay/timeout cases, SMTP rejection/retry/timeout cases,
and provider-neutral push delivery with two healthy worker replicas. Its
product replay returned the retained notification and retained Oban delivery
job, the chat body stayed out of the remote payload, and the run-scoped
Compose project, containers, PostgreSQL volume, and images were absent after
cleanup. Evidence is retained at
`output/external-boundaries/local-boundaries-fix-20260723`.
- Firebase Android and server FCM credentials were subsequently configured in a
separate development-only Spark project and exercised as recorded in the
final 2026-07-24 audit above. No test or production provider configuration was
changed.
## Known work before a public production launch
The operator-facing sequence is maintained in
[`docs/public-launch-checklist.md`](public-launch-checklist.md). That checklist
separates repository-verifiable evidence from external provider, staffing, and
jurisdiction-specific decisions; an unchecked or unknown item is not claimed
complete.
- Promote the tested release from `test.whoneedhelp.com` to the independent
production project only after explicit approval. Recheck the production
health endpoints, migrations, Google callback, and authentication-email flow
after that promotion; the current test origin still depends on its configured
workstation/VPN/gateway path.
- The final Android application ID is `org.whoneedhelp.mobile`. The application
publishes environment-specific `/.well-known/assetlinks.json`. The online
development response now agrees with
`org.whoneedhelp.mobile.development` and its signed certificate, and the
verified implicit same-origin App Link rendered in the API 37 smoke. Before
a Play release, register the application, add the Play App Signing
certificate fingerprint alongside any sideload/upload fingerprint, repeat
domain verification with that Play certificate, and complete store
policy/release work. A dedicated upload key and signed APK/AAB exist, but no
Play application has been registered.
- Operate PostgreSQL/PostGIS with off-site backups, recovery testing, monitoring,
and the availability model selected for real usage.
- The development Brevo SMTP transport and sender have completed both an
external release-container probe and an application-generated authentication
delivery observed in Gmail. Repeat the same post-deploy application flow for
production only after the final release scope is reviewed and explicitly
approved.
- Exercise registration, sign-in, and settings linking against the production
Google OAuth client on its exact HTTPS callback origin after the tested
release is explicitly promoted. The test client and callback have already
completed real registration and returning-user login.
- Development VAPID and isolated Firebase/FCM are configured. Real development
browser Web Push, emulator FCM, and physical-device FCM delivery all
completed successfully. The physical development replay also covered
foreground tracking while the native service was active and verified Stop
cleanup. Before a public mobile release, repeat browser/Android delivery
against each explicitly promoted production origin. Unattended background
location was not requested or verified. APNs and iOS are outside the current
scope.
- Google Analytics for Firebase is intentionally deferred rather than silently
enabled by the Firebase project wizard. Before enabling it, implement the
consent, privacy-notice, event allow-list, sensitive-field exclusions, and
opt-in initialization requirements recorded in
[`docs/public-launch-checklist.md`](public-launch-checklist.md). This is
separate from the existing identifier-free daily aggregate
`ProductAnalytics` counters.
- Load-test representative data and traffic, then set measured pool, resource,
autoscaling, and action-limit policies.
- Publish jurisdiction-specific emergency contacts, privacy, retention,
prohibited-items, and voluntary-payment guidance after legal review.
- The UI now has authenticated and external account-deletion/data-request
intake, contact verification, case status, an audited operator queue, an
authenticated allow-listed JSON export, and a read-only deletion preflight.
Actual erasure/anonymization remains non-executable until a legally reviewed
retention policy defines the treatment of linked safety and dispute records.
- Staff and monitor the implemented moderation/support queues and establish an
incident-response/on-call process for real users.
- If verified GitHub identity is desired, create and configure its OAuth App and
exercise the external redirect/callback. This is an optional enhancement, not
a registration or launch blocker.
- Production capacity, minimum CPU/RAM, database HA topology, and autoscaling
thresholds remain unknown until representative target-environment
measurements exist.