-
Notifications
You must be signed in to change notification settings - Fork 6
chore: CI, contributor docs, and a Dart-only language bar #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # The TypeScript under ts/ is the frozen reference oracle: it generated | ||
| # decode_parity_cases.json (2934 cases) and dart_header.json (550 cases), which | ||
| # the Dart suite asserts against on every run. It is kept for provenance and | ||
| # regeneration, not shipped or imported by anything. | ||
| # | ||
| # Marking it vendored keeps GitHub's language bar reporting what this package | ||
| # actually is — pure Dart — without deleting the oracle. | ||
| ts/** linguist-vendored | ||
| *.json linguist-generated | ||
| decode_parity_cases.json linguist-generated | ||
| dart_header.json linguist-generated |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # GitHub funding config only accepts URLs, not raw wallet addresses. | ||
| custom: | ||
| - https://github.com/OpenStrap/edge/blob/main/DONATE.md |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| name: test | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| workflow_dispatch: | ||
|
|
||
| # Read-only: this job only builds and tests. Nothing here needs write access, | ||
| # and it executes code from pull requests. | ||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| # pubspec declares `sdk: ^3.5.0`, so 3.5.0 is a claim this package makes | ||
| # to anyone depending on it. Test it, rather than only testing whatever | ||
| # `stable` happens to be — otherwise the lower bound is a guess. | ||
| sdk: ['3.5.0', 'stable'] | ||
| name: test (Dart ${{ matrix.sdk }}) | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| with: | ||
| # Don't leave GITHUB_TOKEN in .git/config where PR-authored test code | ||
| # could read it. Nothing in this job pushes back to the repo. | ||
| persist-credentials: false | ||
| - uses: dart-lang/setup-dart@v1 | ||
| with: | ||
| sdk: ${{ matrix.sdk }} | ||
|
|
||
| - name: Install dependencies | ||
| run: dart pub get | ||
|
|
||
| - name: Analyze | ||
| run: dart analyze --fatal-infos | ||
|
|
||
| # The golden capture (whoop_hist.jsonl) is a real band recording kept | ||
| # beside the repo rather than committed to it, so the tests that need it | ||
| # SKIP here and run locally. Everything else — including the 2934-case | ||
| # decode_parity_cases.json oracle and the 550 dart_header.json cases — | ||
| # is tracked in-repo and runs on every PR. | ||
| - name: Test | ||
| run: dart test --reporter=expanded | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # Contributing | ||
|
|
||
| This package is the byte layer: bytes off the band in, named records out. Keep | ||
| it that way — it has **zero runtime dependencies** and no I/O, and that's a | ||
| feature, not an accident. It runs on-device inside the app. | ||
|
|
||
| ## What belongs here (and what doesn't) | ||
|
|
||
| | Your change | Repo | | ||
| |---|---| | ||
| | A record type, opcode, event, CRC/framing, GATT constant | **here** | | ||
| | A metric, or how a number is computed from records | [analytics](https://github.com/OpenStrap/analytics) | | ||
| | Bluetooth reliability, storage, sync, UI | [edge](https://github.com/OpenStrap/edge) | | ||
|
|
||
| Nothing in `lib/` may import `dart:io`, do network calls, or pull in a package. | ||
| If you need those, your change belongs in edge. | ||
|
|
||
| ## Protocol findings are the most valuable contribution | ||
|
|
||
| A lot of the current event table is empirical guesswork by one person. If you've | ||
| worked out a field, an opcode, or an event we don't decode — or found one we | ||
| decode *wrongly* — that helps everyone with one of these bands. | ||
|
|
||
| Open an issue with: | ||
|
|
||
| - The raw frame bytes (hex), as many samples as you have. | ||
| - What you think the field is, and at what offset. | ||
| - **How you convinced yourself.** This is the important part. "HR at offset 17 | ||
| tracked my actual pulse across 40 minutes of wear, and offset 14 didn't" is a | ||
| real answer. Correlating against a known ground truth beats pattern-matching. | ||
|
|
||
| Firmware versions differ. If a field only holds on one `histVersion`, say which | ||
| one — `parseR24` already routes per-version and that's usually where a new | ||
| finding lands. | ||
|
|
||
| ## Ground rules | ||
|
|
||
| **Don't decode something you can't justify.** A wrong field is worse than a | ||
| missing one, because it silently poisons every metric downstream. If a value is | ||
| only *probably* right, gate it behind a plausibility check the way | ||
| `_physiologicallyPlausible` does (HR 25–230, |g|² 0.25–3.24) rather than | ||
| trusting it outright. | ||
|
|
||
| **Never guess a value to fill a gap.** If a record doesn't decode, return | ||
| `null`. The app archives undecodable records rather than discarding them, so a | ||
| `null` here is recoverable later; a fabricated value is not. | ||
|
|
||
| **Some opcodes are dangerous.** `dangerousCmds` exists for a reason — force-trim, | ||
| reboot, power-cycle, firmware load. Don't wire any of them into an automatic | ||
| path, and don't add new ones without a very clear justification in the PR. | ||
|
|
||
| ## Tests | ||
|
|
||
| Run from the repo root: | ||
|
|
||
| ```bash | ||
| dart pub get | ||
| dart analyze | ||
| dart test | ||
| ``` | ||
|
|
||
| Two things guard every change: | ||
|
|
||
| - **`decode_parity_cases.json`** — 2934 cases checked against the frozen | ||
| TypeScript oracle in `ts/`. If you change a decoder and parity breaks, either | ||
| your change is wrong or the oracle needs regenerating — work out which, and | ||
| say so in the PR. | ||
| - **`dart_header.json`** — 550 hand-checked R24 header cases. | ||
|
|
||
| Both are tracked in-repo and run in CI. A third set replays | ||
| `whoop_hist.jsonl`, a real band capture kept *beside* the repo rather than in | ||
| it; those tests skip automatically when it's absent, which is what CI does. | ||
|
|
||
| New record types or opcodes should come with test cases from real frames. | ||
|
|
||
| ## Pull requests | ||
|
|
||
| - Branch off `main`, one logical change per PR. | ||
| - Say how you verified it against real hardware. | ||
| - No `Co-Authored-By` trailers. | ||
|
|
||
| ## Scope | ||
|
|
||
| Facts about a wire protocol, worked out by observing your own device, are fine. | ||
| Vendor source code, firmware, decompiled binaries, and material from other | ||
| reverse-engineering projects whose licences don't permit reuse are not — don't | ||
| paste them into code, comments, commits, or PR descriptions. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| # Security Policy | ||
|
|
||
| ## Reporting a vulnerability | ||
|
|
||
| Please **don't** open a public issue for a security problem. | ||
|
|
||
| Use GitHub's private reporting instead: | ||
| [**Report a vulnerability →**](https://github.com/OpenStrap/protocol/security/advisories/new) | ||
|
|
||
| That goes straight to the maintainer and stays private until there's a fix. | ||
|
|
||
| Rough expectations, set honestly — this is a one-maintainer project, not a | ||
| company with an on-call rota: | ||
|
|
||
| - Acknowledgement within about a week. | ||
| - An assessment, and a fix or a clear "won't fix and here's why", within 30 days | ||
| for anything that puts user data at risk. | ||
| - Credit in the release notes if you want it. | ||
|
|
||
| ## What's in scope | ||
|
|
||
| - Anything that discloses a user's health data off their device. | ||
| - Anything that lets a third party read, write to, or hijack the Bluetooth | ||
| session with someone's band. | ||
| - Local data-at-rest problems: the database, exports, the App Group container, | ||
| widget snapshots. | ||
| - The optional companion worker in | ||
| [backend](https://github.com/OpenStrap/backend): auth, the import endpoints, | ||
| the opt-in telemetry and health-upload paths. | ||
| - Anything that causes the app to send data anywhere the user did not agree to. | ||
|
|
||
| ## What's out of scope | ||
|
|
||
| - The band's own firmware. We don't ship it, can't patch it, and won't publish | ||
| attacks against it. | ||
| - WHOOP's own apps and services. Please report those to WHOOP. | ||
| - The fact that sideloaded builds are unsigned, or that a rooted/jailbroken | ||
| device can read app storage. Both are known properties of the distribution | ||
| model, documented in the README. | ||
| - Metric accuracy. Wrong numbers are bugs — open a normal issue. | ||
|
|
||
| ## Where your data actually is | ||
|
|
||
| Worth knowing before you go looking: OpenStrap computes and stores your health | ||
| data on-device, and there's no account or server holding it. Two qualifications, | ||
| so the boundary is exact: | ||
|
|
||
| - **Anonymous diagnostics** (Firebase crash/performance, never health data) are | ||
| **on by default in GitHub release builds** and absent from App Store / Play | ||
| Store builds. Switchable off in-app. | ||
| - **Health-data contribution** uploads the local database, but is opt-in, off by | ||
| default, and compiled out of store builds entirely. | ||
|
|
||
| Everything else the companion worker does — legacy import, an update pointer — | ||
| is optional and carries no health data. See [edge's PRIVACY.md](https://github.com/OpenStrap/edge/blob/main/PRIVACY.md). | ||
|
|
||
| That means the realistic attack surface is the phone, the Bluetooth link, and | ||
| the local database — not a cloud backend. Reports focused there are the most | ||
| useful. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '1,220p' .github/workflows/test.ymlRepository: OpenStrap/protocol
Length of output: 1080
Run CI on every push, not only
main.push.branches: [main]skips direct pushes to feature branches. Drop the branch filter if this workflow should run for every push.🤖 Prompt for AI Agents