Skip to content
Draft
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
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ jobs:
- name: Test mobile operation leases
working-directory: mobile
run: npm run test:operation-lease
- name: Test mobile graph downsampling
working-directory: mobile
run: npm run test:graph-downsampling
- name: Test mobile live evidence
working-directory: mobile
run: npm run test:live-evidence
- name: Test deterministic mobile session report
working-directory: mobile
run: npm run test:session-report
- name: Test mobile session archive and comparison
working-directory: mobile
run: npm run test:session-archive
- name: Test mobile validation helpers
working-directory: mobile
run: npm run test:validation
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ That mission does not change the public boundary. Ozealis is not approved equipm
## Current capabilities

- ESP32 firmware with pressure sensing, guarded motor-driver abstraction, fault capture, BLE telemetry, settings persistence, logs, and OTA command plumbing.
- Bluetooth companion app with scan/connect, live telemetry, settings read/write, log/fault export, and OTA command entry.
- Bluetooth companion app with scan/connect, live telemetry, settings read/write, log/fault export, deterministic Advanced session-report export, manual evidence archiving and neutral same-device comparisons, and OTA command entry.
- Open hardware files, BoM, and 3D-printable enclosure parts.
- Explicit safety and contribution boundaries for open-source collaboration.

Expand All @@ -61,6 +61,10 @@ Use Node.js `22.x` for mobile commands; the mobile package declares this engine
cd mobile
npm ci --ignore-scripts
npm run check:expo
npm run test:live-evidence
npm run test:session-report
npm run test:graph-downsampling
npm run test:session-archive
npm run typecheck
npm run test:web-export
npm run web
Expand All @@ -70,6 +74,12 @@ There is no `npm run build` script. `npm run test:web-export` is a bundler smoke

Create and install the native development client with `npm run android:dev-build`, or with `npm run ios:dev-build` from macOS for iOS. After that client is installed, `npm run android` / `npm run ios` starts Metro and launches the existing development build.

Advanced Data can export a deterministic `SessionReportV1` JSON artifact from one strict, full-resolution live or retained evidence snapshot. The report binds the exact raw evidence and the derived analysis input separately with SHA-256, preserves FNV-1a transfer metadata, and summarizes cadence, gaps, optional-channel coverage, neutral heuristic output, and explicit limitations. It omits the BLE device ID, app session ID, BLE name, settings, and credentials by default. Device `ms` values are uptime, not wall-clock timestamps, and the hashes provide local artifact integrity rather than device, operator, or measurement authenticity. This experimental report is non-diagnostic and contains no clinical AHI, diagnosis, screening, treatment, efficacy, probability, or confidence output.

Advanced Data also provides a manual, read-only archive for a bounded evidence capture. Each saved entry binds its exact raw CSV, exact selected airflow CSV, retained fault CSV when present, and byte-identical report JSON. A content `archiveId` deduplicates the same owner/source/evidence/report independently of save time, while a separate committed-envelope SHA-256 binds the archive timestamp and all stored metadata. The owner is represented by a domain-separated pseudonymous fingerprint rather than the raw BLE identity, but low-entropy or MAC-shaped identifiers can be dictionary-enumerated by someone with an archive or backup; this remains linkable pseudonymization, not identity protection. The app can compare factual values only between validated captures from the same pseudonymous owner, and suppresses deltas when source windows or heuristic results are not comparable.

Archive restore and save are bounded to 40,000,000 bytes per entry, 256 MiB across recognized committed and pending files, and at most 250 published captures. Valid interrupted `.pending` saves are reverified and recovered; invalid or duplicate pending files are surfaced for explicit removal. A directory-level restore failure blocks new saves instead of presenting an empty archive as ready, and valid entries beyond the published limit remain on disk rather than being silently deleted. These files remain private experimental evidence in app document storage, may be included in platform backups, and do not extend the firmware's 30-minute retained window or the app's 60-minute connected live window into a full-night recorder.

See [docs/mobile-app.md](docs/mobile-app.md).

## Local verification
Expand All @@ -88,7 +98,7 @@ The local source-release gate prefers `.venv/bin/python3.12` on POSIX systems or
node scripts/verify-local.mjs
```

This runs the BLE protocol contract, release text guard, documentation link check, mobile Simple-mode copy check, clean mobile lockfile install, Expo dependency compatibility check, mobile production audit baseline check, mobile validation tests, mobile typecheck, mobile web export smoke, firmware builds, firmware flash-budget check, and repository hygiene check.
This runs the BLE protocol contract, release text guard, documentation link check, mobile Simple-mode copy check, clean mobile lockfile install, Expo dependency compatibility check, mobile production audit baseline check, mobile validation, evidence-preserving graph downsampling, deterministic session-report, and archive/comparison tests, mobile typecheck, mobile web export smoke, firmware builds, firmware flash-budget check, and repository hygiene check.

## BLE protocol

Expand Down
7 changes: 7 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ Please report issues involving:
- Current ESP32 boards are expected to pair with the operating system "Just Works" flow, which does not provide passkey verification.
- There is no app-layer authorization, rotating key, or documented physical commissioning flow yet.
- Mobile foreground-operation and connection-transition tokens prevent stale or overlapping app callbacks from selecting a replacement device session; paged transfers also stop issuing later page requests after token invalidation. This is local race control only. It does not authenticate an operator, retract a write already handed to BLE, replace firmware interlocks, or make the app a safety boundary.
- Retained and live evidence manifests provide local source binding and integrity checks only. The retained manifest binds transferred generations and hashes to a BLE device ID; the live manifest binds one app connection generation and CSV hash to a device ID. Neither proves device, operator, or measurement authenticity under the current "Just Works" pairing and no-app-authorization design.
- Advanced `SessionReportV1` exports add SHA-256 binding for the exact raw evidence and its derived analysis input while preserving source FNV-1a transfer metadata. These digests detect local artifact changes; they are not signatures and do not establish device, operator, measurement, or report authenticity. The report omits raw BLE device IDs, app session IDs, BLE/device names, settings, OTA URLs, SSIDs/passwords, and other credentials by default, but its timing and optional oxygen, pulse, temperature, or humidity summaries can still be sensitive user data. Device `ms` values are uptime rather than wall-clock timestamps.
- The manual Advanced evidence archive retains the exact raw and selected airflow CSV, a retained fault CSV when present, and the byte-identical report JSON in app document storage. The content `archiveId` is a same-owner/source/evidence/report deduplication identity and intentionally remains the same when only the archive timestamp changes. A separate domain-separated committed-envelope SHA-256 binds that timestamp and the rest of the canonical stored metadata. Both are unkeyed integrity digests rather than signatures, access controls, or proof of origin.
- The archive replaces the raw BLE identity with a domain-separated SHA-256 owner fingerprint and normalizes the live app-session identifier, but this is linkable pseudonymization rather than anonymity or identity protection. Someone who can read an archive or backup can dictionary-enumerate low-entropy or MAC-shaped candidate BLE identifiers against the unkeyed fingerprint. Domain separation prevents the same raw hash from being reused across purposes; it does not add secret entropy.
- Archive mutations are serialized and a save is staged under a random UUID `.pending` filename, read back and verified, moved to the matching `.json` filename, and verified again before publication. Restore accepts only the strict versioned schema, revalidates and recovers complete pending saves, and surfaces invalid or duplicate pending saves for explicit destructive removal. Corrupt, duplicate, and overflow committed entries are not admitted or automatically deleted. An archive-level restore failure leaves storage not ready and blocks new saves instead of publishing a ready-empty archive.
- Archive work is bounded to 40,000,000 bytes per serialized entry, 256 MiB across recognized committed and pending files, and at most 250 published validated captures. The app refuses a new save at the entry or aggregate limit; restore publishes at most 250 and leaves excess valid files on disk. Random filenames, bounds, and fail-closed parsing reduce accidental disclosure, corruption, and resource-exhaustion risk; they do not protect file contents from an actor who can read or rewrite app storage.
- Archive files may be included in operating-system, device-transfer, or account backups; the app does not claim backup exclusion or app-layer archive encryption. On platforms with a share sheet, CSV/JSON exports are staged in cache and the app attempts to delete the temporary file after sharing returns. When sharing is unavailable, the export remains in app document storage by design. Copies handed to another app, backup provider, or recipient are outside Ozealis lifecycle control.
- OTA payload authenticity depends on the firmware update source and transport path. Firmware currently accepts `http://` and `https://` OTA URLs and does not perform project-owned signed-image verification or certificate pinning.
- OTA credentials are still handled as sensitive RAM data: the app clears the in-app password field after each write attempt and after canceling the final OTA confirmation, clears all in-app OTA command fields on device-session reset, OTA write exceptions use fixed failure copy instead of displaying raw exception text near credentials, and firmware clears stale pending OTA buffers, pending credential buffers, task-local SSID/password, and task-local URL after ordinary handoff, Wi-Fi failure, or OTA completion. This reduces credential lifetime in ordinary RAM, but does not make OTA a trusted or hardened update channel.
- The mobile app is a control surface, not a safety boundary.
Expand Down
Loading
Loading