Skip to content

fix(worker): removing a worker no longer locks the host out permanently - #197

Merged
GeiserX merged 1 commit into
mainfrom
fix/beads-batch-14
Aug 4, 2026
Merged

fix(worker): removing a worker no longer locks the host out permanently#197
GeiserX merged 1 commit into
mainfrom
fix/beads-batch-14

Conversation

@GeiserX

@GeiserX GeiserX commented Aug 4, 2026

Copy link
Copy Markdown
Owner

The defect

Removing a worker in the fleet dashboard deletes its row and its per-worker key on the UI side. The worker never learned that.

app/worker_api.py _active_key() returns _worker_key or API_KEY — once /data/.worker_key exists, the shared key is never sent again. So the host 401s forever, while its service containers keep running and keep earning. Nothing on either side explains it: the UI shows one fewer worker, the worker logs a bare 401.

Both places that described this promised a recovery no code performed:

  • docs/upgrade-v1.md:51 — removing the worker "(clears its enrollment) so the shared key is accepted again". Nothing cleared anything.
  • app/templates/fleet.html:346Remove worker "${name}"? This will unregister it from the fleet.

The only real recovery was SSHing in to delete a file documented nowhere.

The fix

After ten consecutive rejections of our own key, the worker discards it and re-enrols with the shared key.

Bounded deliberately. Discarding on the first failure would re-enrol on any transient blip and widen the window in which the shared key is accepted — the exact weakening per-worker keys exist to close. The threshold sits above the existing _AUTH_FAILURE_ALARM_AFTER = 3, so the operator is told before anything is deleted. A key that cannot be removed from disk is kept, not zeroed in memory: believing we re-enrolled while the stale key survives a restart brings the same lockout back with no failures leading up to it.

The docs and the confirm dialog now describe what actually happens, including the manual shortcut.

Evidence

  • ruff check . + ruff format --check . clean; node --check on all JS
  • 2558 tests pass
  • Negative control: removing the five-line discard branch (module still imports — a behaviour change, not a syntax break) fails test_sustained_rejection_discards_the_stale_key and test_it_says_why_in_the_log, and nothing else. Reverting the two doc changes fails their two tests.
  • Controls that keep the fix bounded: 9 consecutive 401s discard nothing; a flaky link (401, 500 alternating) discards nothing; a success before the threshold saves the key.

Closes CashPilot-u10

Removing a worker in the fleet dashboard deletes its row and its per-worker
key on the UI side. The worker never learned that. `_active_key()` returns
`_worker_key or API_KEY`, so once /data/.worker_key exists the shared key is
never sent again — the host 401s forever while its service containers keep
running and keep earning, and nothing on either side says why.

Both places that described this promised a recovery that no code performed:
docs/upgrade-v1.md said removing the worker "clears its enrollment so the
shared key is accepted again", and the confirm dialog said only "This will
unregister it from the fleet." The real fix was SSHing in to delete a file
documented nowhere.

After ten consecutive rejections of our own key the worker now discards it
and re-enrols. Bounded on purpose: discarding on the first failure would
re-enrol on any transient blip and widen the window in which the shared key
is accepted, which is what per-worker keys exist to close. The threshold sits
above the existing alarm, so the operator is told before anything is deleted.
A key that cannot be removed from disk is kept rather than zeroed in memory —
believing we re-enrolled while the stale key survives a restart is worse than
not trying.

Negative control: removing the five-line discard branch fails
test_sustained_rejection_discards_the_stale_key and test_it_says_why_in_the_log
and nothing else; reverting the two doc changes fails their two tests.

Closes CashPilot-u10
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@GeiserX, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 22 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: db0101ca-de17-46ef-a254-9186250f9142

📥 Commits

Reviewing files that changed from the base of the PR and between 9afd3ea and b094e37.

📒 Files selected for processing (4)
  • app/templates/fleet.html
  • app/worker_api.py
  • docs/upgrade-v1.md
  • tests/test_worker_keys.py

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.10%. Comparing base (9afd3ea) to head (b094e37).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #197   +/-   ##
=======================================
  Coverage   95.09%   95.10%           
=======================================
  Files          46       46           
  Lines        5893     5905   +12     
=======================================
+ Hits         5604     5616   +12     
  Misses        289      289           
Files with missing lines Coverage Δ
app/worker_api.py 89.26% <100.00%> (+0.26%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@GeiserX

GeiserX commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@GeiserX

GeiserX commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@GeiserX
GeiserX merged commit aae21a0 into main Aug 4, 2026
8 checks passed
@GeiserX
GeiserX deleted the fix/beads-batch-14 branch August 4, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant