8000 chore(release): append the app-live and scan-close lanes to the 0.4.0 aggregate by shroominic · Pull Request #163 · shroominic/OpenGlucose · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
17db5b4
feat(cbio): stream GS1 history and live readings into the app
shroominic Sep 17, 2026
1920c45
fix(ble): close the scan stream before plugin cleanup can block it
shroominic Sep 17, 2026
1aab143
fix(cbio): keep masked auth vectors out of key-shaped string form
shroominic Sep 18, 2026
de8a360
chore(cbio): format the lane integration harness
shroominic Sep 18, 2026
bc52c12
Merge remote-tracking branch 'origin/feature/cbio-gs1-app-live' into …
shroominic Sep 18, 2026
a1a36c3
Merge remote-tracking branch 'origin/fix/ble-scan-stream-close' into …
shroominic Sep 18, 2026
5f41ba9
fix(workspace): isolate host and device integration test roots
shroominic Sep 17, 2026
174ceeb
chore(ble): format the scan-close transport change
shroominic Sep 18, 2026
762c845
chore(cbio): format the app-live session tests
shroominic Sep 18, 2026
033b727
chore(release): append the app-live and scan-close lanes and carry th…
shroominic Sep 18, 2026
b1ac236
docs(deps): record the cgm_cbio vendor-derived material review
shroominic Sep 18, 2026
b13f240
docs(cbio): publish plaintext vectors only, never masked bytes
shroominic Sep 18, 2026
e81e335
fix(cbio): drop the unverified mg/dL claim from the raw record
shroominic Sep 18, 2026
a53b00f
fix(cbio): render the closed GS1 phase, not the driver's status text
shroominic Sep 18, 2026
4d9ee95
chore(release): align the 0.4.0 identity with main and the shipped cg…
shroominic Sep 18, 2026
a580afa
chore(release): carry the cgm_cbio version into the app lockfile
shroominic Sep 18, 2026
0dafd7a
docs(cbio): record that the fixture encoding is not a security control
shroominic Sep 18, 2026
f457b9c
Merge remote-tracking branch 'origin/fix/cbio-integration-test-format…
shroominic Sep 18, 2026
d03e791
Merge remote-tracking branch 'origin/fix/cbio-ble-transport-formattin…
shroominic Sep 18, 2026
3fb9da1
Merge remote-tracking branch 'origin/fix/cbio-app-test-formatting' in…
shroominic Sep 18, 2026
cdb13c2
Merge remote-tracking branch 'origin/fix/cbio-masked-vector-literals'…
shroominic Sep 18, 2026
2f65839
Merge remote-tracking branch 'origin/docs/cbio-vendor-material-review…
shroominic Sep 18, 2026
4812c87
Merge remote-tracking branch 'origin/docs/cbio-auth-material-redactio…
shroominic Sep 18, 2026
8000
b31f724
Merge remote-tracking branch 'origin/fix/release-identity-metadata' i…
shroominic Sep 18, 2026
59b0a6d
Merge remote-tracking branch 'origin/fix/cbio-progress-copy' into rel…
shroominic Sep 18, 2026
e9d3eeb
style(cbio): format the app surface test after the progress-copy fix
shroominic Sep 18, 2026
8e3dcba
fix(ios): record the integration_test pod the sensor lane added
shroominic Sep 18, 2026
c397761
fix(session): bound automatic reconnect and stop retrying a refused c…
shroominic Sep 18, 2026
a719aab
fix(cbio): read private-bench evidence on the Linux runner
shroominic Sep 18, 2026
d86bba7
fix(cbio): give every closed failure its own code and its own sentence
shroominic Sep 18, 2026
f483880
fix(cbio): disclose the positions the stored history never received
shroominic Sep 18, 2026
6ec7ff2
fix(cbio): give the device harnesses a real verdict (#155)
shroominic Sep 18, 2026
e856f16
feat(cbio): record a durable redacted GS1 evidence artifact (#156)
shroominic Sep 18, 2026
0d873f6
Merge remote-tracking branch 'origin/fix/ios-podfile-lock-integration…
shroominic Sep 18, 2026
018dd90
Merge remote-tracking branch 'origin/fix/cbio-private-bench-linux' in…
shroominic Sep 18, 2026
a7c21bc
fix(cbio): require injected vendor material instead of compiled const…
shroominic Sep 18, 2026
05785b0
fix(cbio): resolve the live lane's vendor material from an injected s…
shroominic Sep 18, 2026
0dc7a57
test(cbio): pin the GS1 surface to the timestamp-free reading it publ…
shroominic Sep 18, 2026
7549756
chore(release): advance the app-live lane to its injected-credential tip
shroominic Sep 18, 2026
d255bc6
Merge remote-tracking branch 'origin/test/cbio-timestamp-guard' into …
shroominic Sep 18, 2026
a6a30ca
chore(release): absorb the bounded-reconnect fix onto the redacted cr…
shroominic Sep 18, 2026
2461f15
chore(release): absorb the GS1 failure taxonomy onto the aggregate
shroominic Sep 18, 2026
50fa6c7
chore(release): absorb the stored-history hole disclosure
shroominic Sep 18, 2026
ad1ec4e
chore(release): absorb the device-harness verdicts onto the redacted …
shroominic Sep 18, 2026
5297e28
Merge remote-tracking branch 'origin/feature/cbio-gs1-evidence-artifa…
shroominic Sep 18, 2026
94cb0be
Merge remote-tracking branch 'origin/fix/cbio-archive-unit-claim' int…
shroominic Sep 18, 2026
0490128
fix(security): prove and scope the cgm_cbio vendor-material guard
shroominic Sep 18, 2026
859ee1b
chore(release): format the absorbed lane and close two union resolutions
shroominic Sep 18, 2026
38c1d5f
chore(release): exclude the raw-record unit-claim removal from this c…
shroominic Sep 18, 2026
a084d8f
test(cbio): give the new failure-taxonomy cases injected material
shroominic Sep 18, 2026
27ffff6
test(capture): wait for the status condition, not a fixed event-loop …
shroominic Sep 18, 2026
92a1de2
Merge remote-tracking branch 'origin/fix/protocol-capture-status-flak…
shroominic Sep 18, 2026
4a865cd
docs(release): record the absorbed sensor-lane packaging in both chan…
shroominic Sep 18, 2026
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
14 changes: 14 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

vendor-material-guard:
name: Vendor Material Guard
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Install checksum-pinned quality tools
run: make tooling-bootstrap

- name: Reject vendor material and prove the rule with a canary
run: ./scripts/check-cbio-vendor-material.sh

dependency-review:
name: Dependency Review
if: github.event_name == 'pull_request'
Expand Down
20 changes: 15 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ expectations are defined in [docs/compatibility.md](docs/compatibility.md).

### Added

- Add a `cgm_cbio` GS1 scaffold, pure `FF30` discovery mapping, synthetic
contract/frame tests, offline plaintext inspection, and SiSensing UUID/protocol
evidence. The package has no
transport, app registration, authentication, or glucose support. Record a
separately authorized Mac bench probe in the evidence document.
- Add the `cgm_cbio` GS1 live driver and app surface: `FF30` discovery mapping,
read-only `FF31` reply inspection, an authenticated, fail-closed read session
with bounded history sync, and live glucose rows. Vendor link material is
injected at build time, never compiled into the package. SiSensing UUID and
protocol evidence stay recorded. Record a separately authorized Mac bench
probe in the evidence document.
- Add a durable, redacted GS1 evidence artifact for device-backed runs:
`make cbio-gs1-evidence` records one `gs1-session-<utc>-<harness>.json` per run
with the session outcome, record counts and index ranges, closed error
taxonomy, and app/harness identity, and indexes it. Both device harnesses now
assert the record's invariants instead of printing lines for a human to read.
- Add a self-proving vendor-material guard (`make vendor-material-guard`, a
dedicated security workflow job): the rule rejects a synthetic canary and must
also clear the checked-out tree, so a rule that stops matching fails the check
rather than reporting a clean repository.
- Add a private Android Gen1 Libre 2 receiver path with an explicitly initiated
NFC streaming exchange, encrypted receiver state, durable login counters,
exact-target Bluetooth connection, and CRC-validated packet diagnostics.
Expand Down
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ SHELL := /bin/sh

.PHONY: help bootstrap tooling-bootstrap tooling-check hooks format \
format-check lint typecheck test-unit test-integration test-e2e test \
cbio-gs1-evidence \
build build-android build-web build-ios build-macos test-ios-native \
test-macos-native \
verify-android-release-signing check
verify-android-release-signing vendor-material-guard check

platform_checks :=
ifeq ($(shell uname -s),Darwin)
Expand All @@ -26,6 +27,9 @@ tooling-bootstrap: ## Install checksum-pinned repository quality tools.
tooling-check: tooling-bootstrap ## Run ShellCheck and actionlint with pinned versions.
@./scripts/check-tooling.sh

vendor-material-guard: tooling-bootstrap ## Reject vendor material and prove the guard with a canary.
@./scripts/check-cbio-vendor-material.sh

hooks: ## Install the pinned Lefthook Git hooks.
@./scripts/install-lefthook.sh

Expand All @@ -50,6 +54,9 @@ test-integration: ## Run every tagged or directory-based integration test.
test-e2e: ## Report the explicitly deferred device end-to-end lane.
@./scripts/flutter-workspace.sh test-e2e

cbio-gs1-evidence: ## Run one GS1 session on a device and record a redacted artifact.
@./scripts/cbio-gs1-evidence.sh

test: test-unit test-integration ## Run all locally configured automated tests.

build: build-android build-web ## Build the portable Android and web targets.
Expand All @@ -75,4 +82,4 @@ test-macos-native: ## Run the macOS RunnerTests on this Mac.
verify-android-release-signing: ## Prove Android release signing fails closed without credentials.
@./scripts/flutter-workspace.sh verify-android-release-signing

check: tooling-check format-check lint test build verify-android-release-signing $(platform_checks) ## Run every required CI gate supported by this host.
check: tooling-check vendor-material-guard format-check lint test build verify-android-release-signing $(platform_checks) ## Run every required CI gate supported by this host.
10 changes: 10 additions & 0 deletions NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ copyrights, licenses, and notice requirements. The MIT License does not
relicense those components. A distributor is responsible for preserving every
applicable third-party notice in source and binary distributions.

Interoperability material: `packages/cgm_cbio` carries link material and a
stream masking constant derived from a shipped third-party sensor application.
The derivation record, including the artifact hash and the cross-check against
an independent open-source client, is in
[docs/testing/cbio-gs1-auth-material.md](docs/testing/cbio-gs1-auth-material.md);
the record references the artifact rather than restating the bytes. Because the
material is third-party derived, the same pre-publication inventory and review
steps below apply to it, and its redistribution terms are part of the pending
review logged in [docs/dependencies.md](docs/dependencies.md).

Before publishing an artifact:

1. resolve dependencies from the committed manifests and application
Expand Down
28 changes: 22 additions & 6 deletions docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,32 @@ See [NOTICE.md](../NOTICE.md) for distribution guidance and
[docs/compatibility.md](compatibility.md) for dependency-related support-floor
changes.

## Offline Cbio GS1 scaffold
## Cbio GS1 interoperability package

`cgm_cbio` uses only the existing in-repository `cgm_core` and `cgm_ble`
contracts, plus the existing `lints` and `test` development tools. The package
is MIT, unpublished, and has no native components, permissions, network
destinations, telemetry, build scripts, credentials, or persistent data.
It imports no vendor library or algorithm. Offline resolution uses the local
Pub cache; no new external package is introduced. The app manifest and lockfile
are unchanged. Library lockfiles stay uncommitted. Remove the scaffold and its
workspace enumeration to roll back; no sensor or storage migration is needed.
destinations, telemetry, build scripts, or persistent data. Offline resolution
uses the local Pub cache; no new external package is introduced. The app
manifest and lockfile are unchanged. Library lockfiles stay uncommitted.

The package does carry interoperability material that is **not** authored by
OpenGlucose contributors: a per-frame stream masking constant and the link
material for the GS1 protocol, both derived from a shipped third-party sensor
application and cross-checked against an independent open-source client. The
derivation record is kept in
[testing/cbio-gs1-auth-material.md](testing/cbio-gs1-auth-material.md) and
references the artifact rather than restating the bytes.

Two reviews stay open and gate any distribution of this package:

1. redistribution and notice terms for the derived material, together with the
inventory step described in [NOTICE.md](../NOTICE.md); and
2. how the material reaches a build at all, since today it is a committed
source constant present in every artifact built from this branch.

Remove the package and its workspace enumeration to roll back; no sensor or
storage migration is needed.
Exact-model protocol and licensing review are still required before adding
any vendor-derived implementation.

Expand Down
67 changes: 67 additions & 0 deletions docs/testing/cbio-gs1-app-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Cbio GS1 app-live evidence

What the installed debug app does with a real GS1 sensor, on a Pixel 6, with the
driver in this branch. Every number below was read off the device screen after
the capture; nothing is inferred from unit tests.

## Screen evidence

Captured from the running app (`/tmp/og-cbio-live-raw-800.png` after
`sips -Z 800`; full-resolution source `/tmp/og-cbio-live-raw.png`):

| Surface | What it rendered |
| --- | --- |
| Hero card | `5.3` with no unit suffix, stage pill `Live` |
| Hero subtitle | `Sensor raw value · index 10067` |
| Stored-history line | `10067 readings stored · sensor minutes 1–10067` |
| Provisional marker | `Provisional reading. Sensor raw / 10, scale unverified.` |
| History card | `10067 readings`, the same provisional marker, `Sensor history complete. 1 records.` |
| History chart | full ~7 day curve, ranges `3h / 12h / 1d / 3d / ALL` |

An earlier capture of the same session (`/tmp/og-cbio-live-800.png`) is the
before-fix screen: it rendered `101 mg/dL`, which claimed a glucose unit the
protocol does not establish.

## Discovery, link, and stream evidence

- The app's own nearby-sensor scan lists the sensor (`1 sensor found`,
`Supported sensor · Bluetooth · good signal`); before the transport fix in the
stacked scan-stream change the same scan hung on "Looking for supported
sensors nearby…" forever.
- `adb logcat` shows the live characteristic delivering continuously while the
screen above is up: `[FBP] onCharacteristicChanged: chr: ff31`, roughly every
500 ms.
- The stored count grows while the screen is open (5680 → 6128 → 10067), and the
history card ends on `Sensor history complete.`, so the backfill reached the
sensor's live edge rather than stopping at the first batch.

## Scale and time provenance

- The value shown is the `0x08` record's raw counter divided by ten. The capture
harness read raw `47..53` from the same field at index `9940..9992`; those are
`4.7..5.3` on this scale, which is the `5.3` the app renders. App and harness
therefore agree on the field and the scale; only the unit label differed.
- The unit stays unverified: no reference measurement exists for this sensor, so
no surface may present the number as mg/dL or mmol/L.
- Reading positions are the protocol's own `index` counter, which advances one
per stored minute. It carries no epoch, so the app does not invent a
wall-clock time from it.

## How to reproduce

```bash
export PATH=/Users/fungus/dev/openhealth/.toolchains/flutter/bin:$PATH
cd openhealth
flutter build apk --debug
adb -s <device> install -r -t build/app/outputs/flutter-apk/app-debug.apk
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.BLUETOOTH_SCAN
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.BLUETOOTH_CONNECT
adb -s <device> shell pm grant com.openglucose.app.debug android.permission.ACCESS_FINE_LOCATION
adb -s <device> shell am start -n com.openglucose.app.debug/com.aidex.aidex_flutter.MainActivity
# dashboard -> Connect a sensor -> Connect, then wait for the history window
adb -s <device> shell screencap -p /sdcard/screen.png && adb -s <device> pull /sdcard/screen.png /tmp/screen.png
sips -Z 800 /tmp/screen.png --out /tmp/screen-800.png
```

The device address is never committed; it is passed at run time when a harness
needs discovery bypassed.
37 changes: 25 additions & 12 deletions docs/testing/cbio-gs1-auth-material.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,35 +143,48 @@ Not in scope of the recovered initial sequence: activation (`0A 07 LE32(epoch)
LE32(1234) C`) and the clock update are state-changing sensor writes and were
not performed by this lane.

## Synthetic test vectors
## Test vectors

Every frame is summed to zero modulo 256 by its trailing `C` byte and then
RC4-masked with the `0x11164` key at stream offset zero. Inputs below are
synthetic: the address is `66:55:44:33:22:11` (reversed to `66 55 44 33 22 11`
in the frame) and the timestamp is `1700000000`.

| Purpose | Plaintext frame | Expected masked bytes |
| --- | --- | --- |
| Authentication (synthetic address) | `1901006655443322115448453534345530545949544534363154` | `3ef66fbf5d376bfeacd5ce463a8332d9b2e7bd0576c155804f5e` |
| Device information `F0`/`02` | `03f0020b` | `24076dd2` |
| Glucose query, index 1 | `060a01000000ef` | `21fd6ed90873b7` |
| Raw read, index 1 | `060801000000f1` | `21ff6ed90873a9` |
| Clock update | `060300f153654e` | `21f46f285b1616` |
| Activation (state-changing) | `0a0700f15365d204000070` | `2df06f285b168ad8bd81f6` |
This record publishes **plaintext layouts only**. Masked bytes are a function of
the key, so any masked byte string published here would be derived from the real
key and would belong to the material this document refuses to restate. Generate
them at run time instead:

```dart
// key is injected, never compiled; see packages/cgm_cbio/lib/src/cbio_credentials.dart
maskCbioFrame(plaintext, key: credentials.streamKey);
```

| Purpose | Plaintext frame |
| --- | --- |
| Authentication (synthetic address, material field omitted) | `19 01 00 <6 reversed address octets> <16-byte material> C` |
| Device information `F0`/`02` | `03f0020b` |
| Glucose query, index 1 | `060a01000000ef` |
| Raw read, index 1 | `060801000000f1` |
| Clock update | `060300f153654e` |
| Activation (state-changing) | `0a0700f15365d204000070` |

The raw-read row independently reproduces the `06 08 01 00 00 00 F1` plaintext
derived in `cbio-gs1-offline.md` from the native builders, which is a useful
self-check that the plaintext construction and the masking are consistent.

The authentication row is deliberately incomplete. Its bytes `9..24` are the
link credential; written out, the row would contain the credential itself.

## Limits

- The protected DEX was not re-unpacked in this pass, so the literal hex token
string behind the registered block is still not reproduced. Its decoded
output (the material) is established by static analysis of `register_key`, by
two independent implementations, and by the live authentication success.
- The `16`-byte material and the `.rodata` RC4 key are recorded only in the
private scratch directory and in the issue that tracks this work; they are
deliberately not reproduced as bare constants in this document beyond the
test vectors above.
private scratch directory. They are not reproduced in this document, in any
other repository file, or in any test fixture, and the package reads them from
an injected `CbioCredentialSource` instead.
- Glucose scaling, validity flags, and the exact epoch convention remain open
and are unchanged by this pass.
73 changes: 73 additions & 0 deletions docs/testing/cbio-gs1-evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# CBIO GS1 device evidence

A GS1 session used to leave nothing behind: the harnesses printed lines, the
app kept the last 250 log entries in memory, and the only durable record of a
hardware run was whatever a human had saved by hand. This lane turns one device
run into one reviewable artifact.

## Command

```sh
make cbio-gs1-evidence DEVICE_ID=<adb serial>
```

Optional:

- `HARNESS=<openhealth/integration_test/..._test.dart>` selects the harness
(default: the authenticated session).
- `EVIDENCE_DIR=<dir>` selects the output directory
(default: `../evidence` next to this repository).
- `CBIO_STREAM_SECONDS=<n>` bounds the post-read streaming window.
- `CBIO_EVIDENCE_LOG=<file>` re-records an artifact from an already captured
device log instead of running a device. This is how an artifact is reproduced
without hardware.

The command runs the assertion-bearing harness on the device, keeps the device
log, and writes one artifact into the evidence directory. It exits non-zero when
the harness fails, after writing the artifact, so a failed session still leaves
evidence and still fails loudly.

## Artifact

One file per run: `gs1-session-<utc>-<harness>.json`, plus an `INDEX.md` row.
The file is the harness's `CBIO-EVIDENCE` record, validated before it is
written:

| Key | Content |
|---|---|
| `schema` | `cbio.session-evidence/1` |
| `identity` | harness path, harness revision, app package, app revision, platform string |
| `outcome` | `completed`, `aborted_no_target`, `aborted_missing_characteristics`, `aborted_authentication_failed`, `failed` |
| `writes` | command frames actually sent, per classified kind |
| `records` | glucose and raw record counts, first/last index, raw value range |
| `notifications` | FF31 notifications observed |
| `startedAtUtc`, `endedAtUtc`, `durationMilliseconds` | bounded session timing |
| `errors` | counts per closed reason from a fixed vocabulary |
| `unitStatus` | always `unverified` |

The raw glucose field has no verified scale, so no artifact, log or document may
present it as a physical glucose value.

## Redaction

`cbioSessionEvidenceArtifactViolations` rejects an artifact that:

- is missing schema, identity, outcome, records, writes or errors;
- carries an outcome, write kind, or error reason outside the closed set;
- carries a record range or raw value outside the observed envelope;
- carries a non-monotonic clock or a duration that disagrees with it;
- claims a verified unit;
- contains a Bluetooth address in colon or dash form;
- contains a long hex run that could carry credential or vendor material.

The model itself has no field for a sensor address or credential bytes, and the
harness never logs the authentication plaintext, so a rejected artifact means a
new leak path was introduced, not a formatting accident.

## Safety envelope

The allowed writes for this lane are device information, authentication, one
clock set per session, glucose reads and raw history reads. The harness
classifies every frame before transmission and fails before sending anything
outside its allow-list, so activation, reset, threshold, calibration,
key-registration and firmware frames are unreachable from this lane.
Loading
Loading
0