A Flutter plugin for the BitBox02 hardware wallet (USB on Linux/Android, BLE on iOS). PRs welcome — this guide covers the branching model, the local PR gate, and the release flow so contributions land predictably.
developis the default branch. Open every feature, fix, and chore PR againstdevelop.mainis the release branch. The only thing that lands there is aRelease: develop -> mainPR, opened automatically by.github/workflows/auto-release-pr.yamlon each push todevelop.- Feature branches:
feat/<short-slug>,fix/<short-slug>,chore/<short-slug>. Branch off the latestdevelop.
CI runs three jobs on every PR and every push to develop / main (.github/workflows/pull-request.yaml):
| Job | What it does |
|---|---|
Flutter analyze + test |
dart format --set-exit-if-changed, flutter analyze --no-fatal-infos, flutter test |
Go unit tests |
go vet ./..., go test -race -timeout 60s ./... against go/api and go/u2fhid |
Workflow YAML lint |
yaml.safe_load on every .github/**/*.y*ml |
Run the same gate locally — see TESTING.md → Fast PR gate. Lint failures upstream are wasted CI minutes; catch them locally.
- Dart side: declare the abstract method on
BitboxUsbPlatform(lib/usb/bitbox_usb_platform_interface.dart) and implement it onBitboxUsbMethodChannel(lib/usb/bitbox_usb_method_channel.dart). - Go side (gomobile-exported): add the corresponding function in
go/api/*.go, wrapped withdefer recoverPanic("<name>")so a Go-side crash returns a zero value instead of taking the engine down. - Native bridges: wire the new method through
android/src/main/kotlin/.../MethodCallRegistry.ktand the iOS handler inios/Classes/BitboxFlutterPlugin.swift. - Testkit: implement the method on
SimulatedBitboxPlatforminlib/testing/bitbox_testkit.dartso consumer apps can exercise it without hardware. Add a method-name constant toSimulatedBitboxMethod. - Tests:
test/bitbox_testkit_test.dart— Dart-side flow against the simulator.go/api/fake_bitbox_test.go— Go-side wiring against thefakeBitboxDevicefake.
- Docs: if behaviour is non-obvious, add a paragraph to TESTING.md so future contributors know what's covered.
Releases are fully automated. Once a contributor's PR is merged into develop:
auto-release-pr.yamlfires on the push todevelopand opens (or refreshes) theRelease: develop -> mainPR.- A maintainer reviews the rollup and merges it. The merge commit on
maintriggersauto-tag.yaml. auto-tag.yamlpatch-bumps the latestvX.Y.Ztag, pushes the new tag, and creates a GitHub Release with auto-generated notes.
No manual tagging. If a hotfix needs a specific version (skip-patch, force-minor), open an issue first — the auto-tagger is intentionally not overridable to avoid drift.
- Title in imperative present:
fix(ble): drop stale read buffer after timeout. - Reference issues by number when applicable:
Closes #123. - Body explains why, not what (the diff shows the what).
- No
Co-Authored-Byor tool-generated trailers in DFXswiss repos.
- Touching
ios/Classes/Bluetooth.swiftrequires extra care: U2FHIDreadFrame()has no SEQ validation, so the BLE bridge MUST not let duplicate notifications leak into the read stream.go/api/ios_bluetooth_regression_test.golocks in the 60s read timeout — keep it. - Touching
go/api/api.goexported (//export) functions: every entry point must start withdefer recoverPanic("<name>"). TheTestExportedAPIsReturnZeroValuesWithoutDeviceInsteadOfCrashingtest exists to enforce that the gomobile boundary never panics into the host engine. - New gomobile-exported types: regenerate the iOS xcframework and
android/libs/api.aarviarun_build_tool_android.shand the equivalent iOS pipeline before release. The checked-in artefacts must match the Go module path declared ingo/go.mod.
- Dart:
dart formatis enforced. Noprintin production code (lib/) — use a passed-in logger or remove.example/lib/may useprintfor demonstration. - Go: gofmt-clean,
go vetclean. Errors propagate; the gomobile boundary ingo/api/is the only place that swallows errors (returning zero values) so panics don't cross into the host engine. - Swift / Kotlin: follow each platform's standard formatter. Native bridges should be thin — push logic into the Go layer wherever possible.
- Never log seed phrases, private keys, or unredacted device serials.
- Don't add network calls — this plugin talks to local hardware only.
- For sensitive issues, mail security@dfx.swiss rather than filing a public issue.