Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/validacao-e2e/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ description: Como validar a conexão real dos apps Flutter com o backend do Sina
./scripts/qa/e2e.sh --emulator --full # + a bateria integration_test inteira dos três apps
```

The script brings up Docker Compose, waits for the healthcheck, applies the seed, runs `scripts/dev/sync_dev_ca.sh` (copies the **two** development CAs — the broker's and the RPC's — into `apps/acs/assets/certs/` and `apps/patient/assets/certs/`, which are gitignored and regenerated), runs `scripts/qa/tls_invariants.sh` (the four groups that measure RNF04/L-08 on the live stack: HTTPS with the dev CA, TLS 1.3 only, no cleartext path to the RPC, and the script-side https host rule), and then runs each app's `tool/live_check.dart`. Those scripts run on the plain Dart VM — no emulator — using the apps' own network code: the patient one covers health/login/triage/alert/idempotency, and the ACS one covers the full cycle including the MQTT/TLS subscription and `visits.sync`. Both tools read the RPC CA from `infra/docker/traefik/runtime/certs/ca.crt` themselves, and neither takes an https host for granted — a missing CA stops the leg with the path in the message instead of reporting "the backend did not answer".
The script brings up Docker Compose, waits for the healthcheck, applies the seed, runs `scripts/dev/sync_dev_ca.sh` (copies the **two** development CAs — the broker's and the RPC's — into `apps/acs/assets/certs/` and `apps/patient/assets/certs/`, which are gitignored and regenerated), runs `scripts/qa/tls_invariants.sh` (the six groups that measure RNF04/L-08 on the live stack: HTTPS with the dev CA, TLS 1.3 only, no cleartext path to the RPC, the script-side https host rule, the Traefik → backend hop with mutual TLS (Group E, #90: probes from another container on the Compose network — no client certificate and a foreign CA are refused at the handshake, the RPC CA's certificate gets 200, and a burst of refused handshakes does not take the listener down) and the broker on TLS 1.3 only), and then runs each app's `tool/live_check.dart`. Those scripts run on the plain Dart VM — no emulator — using the apps' own network code: the patient one covers health/login/triage/alert/idempotency, and the ACS one covers the full cycle including the MQTT/TLS subscription and `visits.sync`. Both tools read the RPC CA from `infra/docker/traefik/runtime/certs/ca.crt` themselves, and neither takes an https host for granted — a missing CA stops the leg with the path in the message instead of reporting "the backend did not answer".

`integration_test/` in each app holds the on-device version, excluded from `flutter test` by construction. Run it against an already-running emulator with `--dart-define=SINALACS_HOST=https://10.0.2.2/` (and, for the ACS, `SINALACS_MQTT_HOST=10.0.2.2` plus `SINALACS_MQTT_PASSWORD="$MQTT_ACS_PASSWORD"` — without the password the suite fails in `setUpAll` with the command you need). `scripts/qa/e2e.sh --emulator` already passes all three.

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project overview

SinalACS is a platform that prioritizes Primary Health Care visits in Brazil by turning structured clinical signals into a risk-ranked work queue for the Community Health Agent (ACS). Project documentation, code comments, and UI copy are in Portuguese — match that language for anything user-facing or spec-related. Current state: functional prototype, validated locally via Docker Compose; not production-ready. Already delivered: ACS institutional login (RF07, matrícula + senha/Argon2id, credentials kept locally — no integration with a real identity provider), patient passwordless login (RF01, CPF + birth date + OTP), RBAC per micro-area (RNF06), TLS 1.3 on the RPC via Traefik (RNF04), on the Traefik → backend hop (one-way TLS 1.3 with the chain verified; no mTLS, Serverpod cannot request a client certificate — #78) and on the broker (TLS 1.3 only, password + ACL + client certificate on the local stack, `scripts/qa/mtls_invariants.sh` and `scripts/qa/tls_invariants.sh`), TOTP MFA for the ACS login, RF08 micro-area persisted in the encrypted local database (72 h, per owner), the ACS refresh token (rotating, idle window 2 h, absolute cap 8 h from the password+TOTP login, family revoked on reuse/other device/inactive account; the app keeps no password) bound to the Keystore: an AES-256-GCM + RSA-OAEP envelope whose RSA key requires user authentication on every use, so the token is decrypted only through `BiometricPrompt` + `CryptoObject` on cold start (API 30+ also accepts the device PIN; on API 30+ a newly enrolled fingerprint does **not** invalidate the key; proved on the API 36 emulator only, `scripts/qa/acs_keystore_bound_e2e.sh`), plus the 30 s background lock, which stays an interface gate over the token held in RAM and "Sair e encerrar o turno", `FLAG_SECURE` on the ACS window in every build (only a debug build made with `./scripts/dev/run_acs.sh --build --captura` opens it, by a compile-time constant that release refuses), release builds that refuse the debug key unless explicitly allowed (a release keystore is still to be provisioned), the UBS call button, the per-owner offline visit queue (schema v7, legacy rows quarantined and uploaded with unknown authorship, deferred upload token, wipe only when everything is sent) the closing of the `login()` race (`BackendClient._exclusive`), the ACS's resolution of a visit sync conflict (#79: persisted conflict with the server copy, side-by-side screen, keep mine / edit and send / keep the server's, `visits.resolveConflict`), the remote device wipe ordered by the backoffice (#81: the coordinator of the UBS or the admin orders it from the ACS's "Aparelhos" dialog, the app wipes at startup, resume, network return or refresh, the SQLCipher key is rotated by event and never by calendar; the device's per-device MQTT client certificate is still missing, tracked by the continuation issue to be opened), the telemetry of the PRD §1.3 events (#83: `alert_triggered`, `alert_received_acs`, `sync_attempt`, `sync_result`, `visit_registered` in `telemetry_events`, patient only as an HMAC pseudonym, offline-queued on the ACS device and sent by `telemetry.ingest`, report by `bin/telemetry_report.dart`; DPO review pending), plus the backoffice account management (#43: listing, ACS create/link/activate/deactivate and password/MFA reset, staff MFA reset with the activation code emitted by the screen, and the coordinator's audit scoped to his UBS — every operation audited, refusals included). Still missing: invalidation of the token key by a newly enrolled fingerprint on API 30+ and a proof of the Keystore binding on API 24–29 (the SQLCipher key and the deferred upload token stay unbound by design), a refresh token for the patient (OTP stays 1 h), iOS, a physical-device proof on Android ≤ 8.1 (API 24–27) (the emulator on API 24 and 27 showed no theme breakage; see `PROGRESS.md`, section Tema em API 24–27), background (OS-level) upload of visits left on the device (WorkManager is outside `spec/stack.md`), unblocking of a queue whose 7-day upload token expired or was refused (it needs the owner to log in again and blocks the wipe) and LGPD purge of an owner who never returns (see `PROGRESS.md`, section Fila de visitas por dono e corrida do login), per-device client-certificate provisioning in production (mTLS is delivered only on the local stack), creating coordinator accounts and assigning their `ubsId` through the UI (a coordinator still exists only in the seeds and by SQL; #43 covers the ACS lifecycle and the MFA resets), the ACS's own password change (a lost password is reset by the coordination), the audit trail's append-only enforcement against the database superuser (since #82 a per-statement trigger refuses `UPDATE`/`DELETE`/`TRUNCATE` on `audit_logs`, `audit_exports` and `audit_chain_checkpoints` and `PUBLIC` lacks those privileges, but the backend still connects as the superuser/owner, which bypasses both — role separation is #96), a real file download of the CSV export (it goes through the clipboard), the owner's acceptance of the green "Cadeia íntegra" exception to `spec/ui_design.md` and the data-protection officer's review of the export (`PROGRESS.md`, section #45), and a production deploy (`backend/DEPLOY.md` is only a free-tier demo path). The data-subject request handling (#42) is delivered; the validation of its anonymization rules with the data-protection officer is still open (`PROGRESS.md`).
SinalACS is a platform that prioritizes Primary Health Care visits in Brazil by turning structured clinical signals into a risk-ranked work queue for the Community Health Agent (ACS). Project documentation, code comments, and UI copy are in Portuguese — match that language for anything user-facing or spec-related. Current state: functional prototype, validated locally via Docker Compose; not production-ready. Already delivered: ACS institutional login (RF07, matrícula + senha/Argon2id, credentials kept locally — no integration with a real identity provider), patient passwordless login (RF01, CPF + birth date + OTP), RBAC per micro-area (RNF06), TLS 1.3 on the RPC via Traefik (RNF04), on the Traefik → backend hop (mutual TLS 1.3 since #90: the Traefik verifies the backend's leaf and presents its own client certificate through `serversTransport.certificates`, and the backend requires a client certificate signed by the RPC dev CA, refusing anyone without one at the handshake — Serverpod is consumed through a pinned fork, `hbgit/serverpod`, because 3.4.13 and 4.x bind the API server with no way to require a client certificate; `backend/patches/README.md`) and on the broker (TLS 1.3 only, password + ACL + client certificate on the local stack, `scripts/qa/mtls_invariants.sh` and `scripts/qa/tls_invariants.sh`), TOTP MFA for the ACS login, RF08 micro-area persisted in the encrypted local database (72 h, per owner), the ACS refresh token (rotating, idle window 2 h, absolute cap 8 h from the password+TOTP login, family revoked on reuse/other device/inactive account; the app keeps no password) bound to the Keystore: an AES-256-GCM + RSA-OAEP envelope whose RSA key requires user authentication on every use, so the token is decrypted only through `BiometricPrompt` + `CryptoObject` on cold start (API 30+ also accepts the device PIN; on API 30+ a newly enrolled fingerprint does **not** invalidate the key; proved on the API 36 emulator only, `scripts/qa/acs_keystore_bound_e2e.sh`), plus the 30 s background lock, which stays an interface gate over the token held in RAM and "Sair e encerrar o turno", `FLAG_SECURE` on the ACS window in every build (only a debug build made with `./scripts/dev/run_acs.sh --build --captura` opens it, by a compile-time constant that release refuses), release builds that refuse the debug key unless explicitly allowed (a release keystore is still to be provisioned), the UBS call button, the per-owner offline visit queue (schema v7, legacy rows quarantined and uploaded with unknown authorship, deferred upload token, wipe only when everything is sent) the closing of the `login()` race (`BackendClient._exclusive`), the ACS's resolution of a visit sync conflict (#79: persisted conflict with the server copy, side-by-side screen, keep mine / edit and send / keep the server's, `visits.resolveConflict`), the remote device wipe ordered by the backoffice (#81: the coordinator of the UBS or the admin orders it from the ACS's "Aparelhos" dialog, the app wipes at startup, resume, network return or refresh, the SQLCipher key is rotated by event and never by calendar; the device's per-device MQTT client certificate is still missing, tracked by the continuation issue to be opened), the telemetry of the PRD §1.3 events (#83: `alert_triggered`, `alert_received_acs`, `sync_attempt`, `sync_result`, `visit_registered` in `telemetry_events`, patient only as an HMAC pseudonym, offline-queued on the ACS device and sent by `telemetry.ingest`, report by `bin/telemetry_report.dart`; DPO review pending), plus the backoffice account management (#43: listing, ACS create/link/activate/deactivate and password/MFA reset, staff MFA reset with the activation code emitted by the screen, and the coordinator's audit scoped to his UBS — every operation audited, refusals included). Still missing: invalidation of the token key by a newly enrolled fingerprint on API 30+ and a proof of the Keystore binding on API 24–29 (the SQLCipher key and the deferred upload token stay unbound by design), a refresh token for the patient (OTP stays 1 h), iOS, a physical-device proof on Android ≤ 8.1 (API 24–27) (the emulator on API 24 and 27 showed no theme breakage; see `PROGRESS.md`, section Tema em API 24–27), background (OS-level) upload of visits left on the device (WorkManager is outside `spec/stack.md`), unblocking of a queue whose 7-day upload token expired or was refused (it needs the owner to log in again and blocks the wipe) and LGPD purge of an owner who never returns (see `PROGRESS.md`, section Fila de visitas por dono e corrida do login), per-device client-certificate provisioning in production (mTLS, on the broker and on the Traefik → backend hop, is delivered only on the local stack; the backend does not log the client certificate's CN), creating coordinator accounts and assigning their `ubsId` through the UI (a coordinator still exists only in the seeds and by SQL; #43 covers the ACS lifecycle and the MFA resets), the ACS's own password change (a lost password is reset by the coordination), the audit trail's append-only enforcement against the database superuser (since #82 a per-statement trigger refuses `UPDATE`/`DELETE`/`TRUNCATE` on `audit_logs`, `audit_exports` and `audit_chain_checkpoints` and `PUBLIC` lacks those privileges, but the backend still connects as the superuser/owner, which bypasses both — role separation is #96), a real file download of the CSV export (it goes through the clipboard), the owner's acceptance of the green "Cadeia íntegra" exception to `spec/ui_design.md` and the data-protection officer's review of the export (`PROGRESS.md`, section #45), and a production deploy (`backend/DEPLOY.md` is only a free-tier demo path). The data-subject request handling (#42) is delivered; the validation of its anonymization rules with the data-protection officer is still open (`PROGRESS.md`).

Two core flows:
- **Paciente (patient)**: passwordless auth (OTP), urgency alert, structured triage, request status tracking, "Meus Dados" (LGPD).
Expand Down
Loading