feat(fleet): per-worker key enrollment (client side)#37
Conversation
|
Warning Review limit reached
Next review available in: 58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughThe change adds repository-wide Beads task tracking and agent hooks, plus Android support for per-worker heartbeat keys with enrollment, persistence, authentication fallback, tests, documentation, and a version update. ChangesBeads integration
Per-worker heartbeat keys
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.beads/hooks/pre-commit:
- Around line 60-93: Capture the exit status returned by call_lefthook run
"pre-commit" "$@" immediately after it executes, then preserve and return that
status after the Beads integration block, including when bd is unavailable.
Ensure Beads handling cannot overwrite a non-zero Lefthook result, while still
propagating Beads failures when Lefthook succeeds.
In @.beads/hooks/pre-push:
- Around line 4-37: Capture the exit status of `git lfs pre-push "$@"`
immediately in a dedicated variable, then preserve and return that status when
the Beads integration block is skipped or completes successfully. Update the
final hook exit handling so Beads-specific skips do not overwrite a non-zero LFS
result, while still propagating Beads failures when LFS succeeds.
In @.beads/README.md:
- Line 64: The installation instructions execute an unverified remote script via
curl | bash. Replace this command with a pinned release or package/artifact
installation method, and document checksum or signature verification before
running any downloaded installer.
In @.gitignore:
- Around line 18-23: Remove the broad *.db entry from the root .gitignore, or
replace it with a .beads/-scoped pattern so only Beads database artifacts are
ignored; retain the other Beads/Dolt ignore rules.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b767b0b4-7f58-472e-b442-45e67d5e85a9
📒 Files selected for processing (28)
.agents/skills/beads/SKILL.md.agents/skills/beads/agents/openai.yaml.beads/.gitignore.beads/README.md.beads/config.yaml.beads/hooks/commit-msg.beads/hooks/post-checkout.beads/hooks/post-commit.beads/hooks/post-merge.beads/hooks/pre-commit.beads/hooks/pre-push.beads/hooks/prepare-commit-msg.beads/interactions.jsonl.beads/metadata.json.claude/settings.json.codex/config.toml.codex/hooks.json.gitignoreCHANGELOG.mdCLAUDE.mdapp/src/main/java/com/cashpilot/android/model/Heartbeat.ktapp/src/main/java/com/cashpilot/android/model/Settings.ktapp/src/main/java/com/cashpilot/android/service/HeartbeatService.ktapp/src/main/java/com/cashpilot/android/util/SettingsStore.ktapp/src/test/java/com/cashpilot/android/PerWorkerKeyTest.ktdocs/AUTOPILOT-WORKLOG.mddocs/GOAL.mdgradle.properties
| call_lefthook run "pre-commit" "$@" | ||
|
|
||
| # --- BEGIN BEADS INTEGRATION v1.1.0 --- | ||
| # This section is managed by beads. Do not remove these markers. | ||
| if command -v bd >/dev/null 2>&1; then | ||
| export BD_GIT_HOOK=1 | ||
| _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} | ||
| _bd_used_perl=0 | ||
| if command -v timeout >/dev/null 2>&1; then | ||
| timeout "$_bd_timeout" bd hooks run pre-commit "$@" | ||
| _bd_exit=$? | ||
| elif command -v gtimeout >/dev/null 2>&1; then | ||
| gtimeout "$_bd_timeout" bd hooks run pre-commit "$@" | ||
| _bd_exit=$? | ||
| elif command -v perl >/dev/null 2>&1; then | ||
| _bd_used_perl=1 | ||
| perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run pre-commit "$@" | ||
| _bd_exit=$? | ||
| else | ||
| echo >&2 "beads: hook 'pre-commit' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" | ||
| bd hooks run pre-commit "$@" | ||
| _bd_exit=$? | ||
| fi | ||
| if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then | ||
| echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads" | ||
| _bd_exit=0 | ||
| fi | ||
| if [ $_bd_exit -eq 3 ]; then | ||
| echo >&2 "beads: database not initialized — skipping hook 'pre-commit'" | ||
| _bd_exit=0 | ||
| fi | ||
| if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi | ||
| fi | ||
| # --- END BEADS INTEGRATION v1.1.0 --- |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Lefthook exit code is silently overwritten when bd is absent.
call_lefthook run "pre-commit" "$@" may return non-zero (lint failures), but the subsequent Beads if block evaluates to 0 when bd isn't installed, making the script exit 0. The commit proceeds despite lefthook failures. This affects any contributor without bd on their PATH.
🔒 Capture and propagate lefthook exit code
call_lefthook run "pre-commit" "$@"
+_lefthook_exit=$?
# --- BEGIN BEADS INTEGRATION v1.1.0 ---
# This section is managed by beads. Do not remove these markers.
@@ -91,3 +92,5 @@
fi
# --- END BEADS INTEGRATION v1.1.0 ---
+exit $_lefthook_exit📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| call_lefthook run "pre-commit" "$@" | |
| # --- BEGIN BEADS INTEGRATION v1.1.0 --- | |
| # This section is managed by beads. Do not remove these markers. | |
| if command -v bd >/dev/null 2>&1; then | |
| export BD_GIT_HOOK=1 | |
| _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} | |
| _bd_used_perl=0 | |
| if command -v timeout >/dev/null 2>&1; then | |
| timeout "$_bd_timeout" bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| elif command -v gtimeout >/dev/null 2>&1; then | |
| gtimeout "$_bd_timeout" bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| elif command -v perl >/dev/null 2>&1; then | |
| _bd_used_perl=1 | |
| perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| else | |
| echo >&2 "beads: hook 'pre-commit' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" | |
| bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| fi | |
| if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then | |
| echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -eq 3 ]; then | |
| echo >&2 "beads: database not initialized — skipping hook 'pre-commit'" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi | |
| fi | |
| # --- END BEADS INTEGRATION v1.1.0 --- | |
| call_lefthook run "pre-commit" "$@" | |
| _lefthook_exit=$? | |
| # --- BEGIN BEADS INTEGRATION v1.1.0 --- | |
| # This section is managed by beads. Do not remove these markers. | |
| if command -v bd >/dev/null 2>&1; then | |
| export BD_GIT_HOOK=1 | |
| _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} | |
| _bd_used_perl=0 | |
| if command -v timeout >/dev/null 2>&1; then | |
| timeout "$_bd_timeout" bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| elif command -v gtimeout >/dev/null 2>&1; then | |
| gtimeout "$_bd_timeout" bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| elif command -v perl >/dev/null 2>&1; then | |
| _bd_used_perl=1 | |
| perl -e 'alarm shift; exec `@ARGV`' "$_bd_timeout" bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| else | |
| echo >&2 "beads: hook 'pre-commit' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" | |
| bd hooks run pre-commit "$@" | |
| _bd_exit=$? | |
| fi | |
| if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then | |
| echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -eq 3 ]; then | |
| echo >&2 "beads: database not initialized — skipping hook 'pre-commit'" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi | |
| fi | |
| # --- END BEADS INTEGRATION v1.1.0 --- | |
| exit $_lefthook_exit |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.beads/hooks/pre-commit around lines 60 - 93, Capture the exit status
returned by call_lefthook run "pre-commit" "$@" immediately after it executes,
then preserve and return that status after the Beads integration block,
including when bd is unavailable. Ensure Beads handling cannot overwrite a
non-zero Lefthook result, while still propagating Beads failures when Lefthook
succeeds.
| git lfs pre-push "$@" | ||
|
|
||
| # --- BEGIN BEADS INTEGRATION v1.1.0 --- | ||
| # This section is managed by beads. Do not remove these markers. | ||
| if command -v bd >/dev/null 2>&1; then | ||
| export BD_GIT_HOOK=1 | ||
| _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} | ||
| _bd_used_perl=0 | ||
| if command -v timeout >/dev/null 2>&1; then | ||
| timeout "$_bd_timeout" bd hooks run pre-push "$@" | ||
| _bd_exit=$? | ||
| elif command -v gtimeout >/dev/null 2>&1; then | ||
| gtimeout "$_bd_timeout" bd hooks run pre-push "$@" | ||
| _bd_exit=$? | ||
| elif command -v perl >/dev/null 2>&1; then | ||
| _bd_used_perl=1 | ||
| perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run pre-push "$@" | ||
| _bd_exit=$? | ||
| else | ||
| echo >&2 "beads: hook 'pre-push' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" | ||
| bd hooks run pre-push "$@" | ||
| _bd_exit=$? | ||
| fi | ||
| if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then | ||
| echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads" | ||
| _bd_exit=0 | ||
| fi | ||
| if [ $_bd_exit -eq 3 ]; then | ||
| echo >&2 "beads: database not initialized — skipping hook 'pre-push'" | ||
| _bd_exit=0 | ||
| fi | ||
| if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi | ||
| fi | ||
| # --- END BEADS INTEGRATION v1.1.0 --- |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Same exit-code overwrite as pre-commit: LFS failures are silently swallowed when bd is absent.
git lfs pre-push "$@" may return non-zero, but the Beads if block exits 0 when bd isn't found. The push proceeds despite LFS failures. Same root cause and fix pattern as the pre-commit hook.
🔒 Capture and propagate LFS exit code
git lfs pre-push "$@"
+_lfs_exit=$?
# --- BEGIN BEADS INTEGRATION v1.1.0 ---
# This section is managed by beads. Do not remove these markers.
@@ -35,3 +37,5 @@
fi
# --- END BEADS INTEGRATION v1.1.0 ---
+exit $_lfs_exit📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| git lfs pre-push "$@" | |
| # --- BEGIN BEADS INTEGRATION v1.1.0 --- | |
| # This section is managed by beads. Do not remove these markers. | |
| if command -v bd >/dev/null 2>&1; then | |
| export BD_GIT_HOOK=1 | |
| _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} | |
| _bd_used_perl=0 | |
| if command -v timeout >/dev/null 2>&1; then | |
| timeout "$_bd_timeout" bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| elif command -v gtimeout >/dev/null 2>&1; then | |
| gtimeout "$_bd_timeout" bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| elif command -v perl >/dev/null 2>&1; then | |
| _bd_used_perl=1 | |
| perl -e 'alarm shift; exec @ARGV' "$_bd_timeout" bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| else | |
| echo >&2 "beads: hook 'pre-push' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" | |
| bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| fi | |
| if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then | |
| echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -eq 3 ]; then | |
| echo >&2 "beads: database not initialized — skipping hook 'pre-push'" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi | |
| fi | |
| # --- END BEADS INTEGRATION v1.1.0 --- | |
| git lfs pre-push "$@" | |
| _lfs_exit=$? | |
| # --- BEGIN BEADS INTEGRATION v1.1.0 --- | |
| # This section is managed by beads. Do not remove these markers. | |
| if command -v bd >/dev/null 2>&1; then | |
| export BD_GIT_HOOK=1 | |
| _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} | |
| _bd_used_perl=0 | |
| if command -v timeout >/dev/null 2>&1; then | |
| timeout "$_bd_timeout" bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| elif command -v gtimeout >/dev/null 2>&1; then | |
| gtimeout "$_bd_timeout" bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| elif command -v perl >/dev/null 2>&1; then | |
| _bd_used_perl=1 | |
| perl -e 'alarm shift; exec `@ARGV`' "$_bd_timeout" bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| else | |
| echo >&2 "beads: hook 'pre-push' running without timeout; install coreutils or perl to enable BEADS_HOOK_TIMEOUT" | |
| bd hooks run pre-push "$@" | |
| _bd_exit=$? | |
| fi | |
| if [ $_bd_exit -eq 124 ] || { [ $_bd_used_perl -eq 1 ] && [ $_bd_exit -eq 142 ]; }; then | |
| echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -eq 3 ]; then | |
| echo >&2 "beads: database not initialized — skipping hook 'pre-push'" | |
| _bd_exit=0 | |
| fi | |
| if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi | |
| fi | |
| # --- END BEADS INTEGRATION v1.1.0 --- | |
| exit $_lfs_exit |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.beads/hooks/pre-push around lines 4 - 37, Capture the exit status of `git
lfs pre-push "$@"` immediately in a dedicated variable, then preserve and return
that status when the Beads integration block is skipped or completes
successfully. Update the final hook exit handling so Beads-specific skips do not
overwrite a non-zero LFS result, while still propagating Beads failures when LFS
succeeds.
|
|
||
| ```bash | ||
| # Install Beads | ||
| curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not recommend unverified curl | bash installation.
Pin the installer to a release and verify its checksum/signature, or document a package/release-artifact installation path before executing remote code.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.beads/README.md at line 64, The installation instructions execute an
unverified remote script via curl | bash. Replace this command with a pinned
release or package/artifact installation method, and document checksum or
signature verification before running any downloaded installer.
|
|
||
| # Beads / Dolt files (added by bd init) | ||
| .dolt/ | ||
| *.db | ||
| .beads-credential-key | ||
| .beads/proxieddb/ |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if any .db files exist in the Android project source
fd -e db . app/ 2>/dev/null || echo "No .db files found in app/"Repository: GeiserX/CashPilot-android
Length of output: 163
Narrow *.db in .gitignore
.beads/.gitignore already covers Beads DB artifacts, so the root *.db rule is broader than needed and will also ignore future app-level SQLite files. Scope it to .beads/ or drop it if a repo-wide ignore is intentional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.gitignore around lines 18 - 23, Remove the broad *.db entry from the root
.gitignore, or replace it with a .beads/-scoped pattern so only Beads database
artifacts are ignored; retain the other Beads/Dolt ignore rules.
The app authenticated every heartbeat with the shared fleet API key. It now enrolls on first contact: it captures the per-worker `worker_key` the server returns, persists it (DataStore), and authenticates with it thereafter — the shared key becomes an enrollment-only bootstrap. Interoperates with the CashPilot web UI (v1.0.0) and CashPilot-Desktop's fleet server. - Settings.workerKey + activeKey (own key once enrolled, else shared) - SettingsStore persists worker_key - WorkerHeartbeatResponse model; HeartbeatService captures + adopts the issued key (pure HeartbeatService.keyToPersist, unit-tested) - Version 0.1.3 -> 0.2.0; CHANGELOG Verified via CI (no local JDK on the dev machine).
DataClassContractTest destructures Settings via positional component() functions; inserting workerKey mid-class shifted components 3-5 and broke compilation. Move workerKey to the last field so components 1-5 are unchanged.
bacf9f1 to
4a0fa0c
Compare
…iring From the 7-lens PR review: - On a 401 the client now clears its stale per-worker key so the next heartbeat re-enrolls via the shared bootstrap key — closes the permanent-lockout gap when the server forgets a device (DB reset, race, name collision). - Heartbeat loop gates on Settings.activeKey (own key OR shared) instead of the shared apiKey, so heartbeats survive removing the shared bootstrap key after enrollment (matching the "enrollment-only" docs). - Extract the response->persist decision into the pure, unit-tested HeartbeatService.settingsAfterHeartbeat so the real capture wiring is covered. - CHANGELOG updated. Verified by inspection (no local JDK; CI builds/tests).
Brings the Android agent onto the per-worker-key protocol shipped in the CashPilot web UI v1.0.0.
Before: every heartbeat authenticated with the shared fleet API key.
Now: on its first heartbeat the device enrolls — the server returns a per-worker
worker_key, which the app persists (DataStore) and uses for auth thereafter. The configured fleet API key becomes an enrollment-only bootstrap credential. No user setup change; existing devices re-enroll on their next heartbeat.Settings.workerKey+activeKey(own key once enrolled, else the shared bootstrap key)SettingsStorepersistsworker_keyWorkerHeartbeatResponsemodel (kotlinx,ignoreUnknownKeys);HeartbeatServicecaptures + adopts the issued key via the pure, unit-testedHeartbeatService.keyToPersist0.1.3 -> 0.2.0; CHANGELOGInteroperates with the web UI and CashPilot-Desktop's fleet server (the matching server-side change is a separate CashPilot-Desktop PR). Verified via CI (no local JDK on the dev machine). Beads: CashPilot-android-btz/-a5d/-nvn/-20v (closed).
Summary by CodeRabbit
New Features
Documentation
Chores