Skip to content

fix(mysterium): let a box keep its UDP range by reserving it, and keep a hold through a refused deploy - #679

Open
GeiserX wants to merge 4 commits into
mainfrom
fix/mysterium-reserve-hold-perip
Open

GeiserX wants to merge 4 commits into
mainfrom
fix/mysterium-reserve-hold-perip

Conversation

@GeiserX

@GeiserX GeiserX commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

Three loose ends from #677.

A box can keep its forwarded UDP range. The Fleet warning for a Mysterium node whose recorded range sits in Linux's ephemeral range (32768-60999) only offered moving the range and changing the router forward. Reserving the range in the host kernel works just as well and needs no router change. The warning now gives that route too: the exact sysctl -w net.ipv4.ip_local_reserved_ports=<start>-<end> line, a note to add it to any ranges already listed there, and the line to put under /etc/sysctl.d/ so it survives a reboot. The guide and UPGRADING step 3 say the same.

The worker can't check the reservation. That sysctl is per network namespace, the worker runs on its own bridge, and the only thing CashPilot runs in the host's namespace is the fence helper, which runs in on mode only. So the warning stays after you reserve the range, and it ends with "If you've reserved this range on the host, you can redeploy."

A refused deploy dropped an in-memory hold. deploy_raw pops the mismatch hold before it reads the running node's identity. If that read fails, the deploy is refused and the old node is left alone, but the hold stayed popped. When the on-disk MISMATCH record had failed to write, the memory copy was the only one, so the hold was gone. The refusal now calls _restore_identity(name, identity_before, held_before), as the stop and rename refusals already did; _roll_back restores it on every create failure. I checked every other exit after the pop: a vanished old container goes on to create the replacement, and _roll_back covers that path too.

Mysterium allows one node per public IP. The guide said so, but the catalog had devices_per_ip: 0 (unlimited). The MystNodes help centre says "You can only run one MystNodes per public IP address": https://help.mystnodes.com/en/articles/3442543-multiple-nodes-on-a-single-ip-address. The catalog now says 1, and the source is listed in docs/research/per-ip-device-limits.md. What users see: a second Mysterium on the same machine, or on another machine behind the same public address, gets "will most likely earn nothing" in preflight instead of "earns less". Deploying stays allowed. The scaling limit was already 1 because the node has volumes, so only its reason text changes. The guide also stops saying the node runs with --net host.

Tests: one for each part, plus seven mutants (dropping the restore, the reserve sentence, the recorded range, the dash form, the sysctl.d note, the "already reserved" line, and setting the value back to 0). Each one fails at least one test.

Summary by CodeRabbit

  • Documentation
    • Added instructions for reserving Mysterium’s existing UDP port range, including how to make the reservation persist across reboots and its limitations.
    • Updated the MystNodes guide to reflect the supported network setup and clarify why port-range warnings may remain after reservation.
  • Bug Fixes
    • Improved deployment error handling when the running container’s identity cannot be read.
  • Service Limits
    • Mysterium deployments now reflect the one-device-per-public-IP limit, with a finding when a second node would earn nothing.

…d a refused deploy popped, and record one node per IP

The ephemeral-range warning now gives the other route: keep the range and
reserve it with net.ipv4.ip_local_reserved_ports, persisted in sysctl.d.
The worker reads its own network namespace, not the host's, so it cannot
see the reservation; the warning says a box that reserved it can redeploy.

A deploy refused because the running node's identity could not be read
dropped a mismatch hold kept only in memory. It is restored now, like the
stop and rename refusals already did.

MystNodes allows one node per public IP; the catalog said unlimited.
@coderabbitai

coderabbitai Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Explain Change →

Note

CodeRabbit ASCII loading animation

Currently processing new changes in this PR. This may take a few minutes, please wait...

Review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 4ac2e19 and dba7a8a.

Files selected for processing: 8

  • UPGRADING.md
  • app/drift.py
  • app/orchestrator.py
  • docs/guides/mysterium.md
  • docs/research/per-ip-device-limits.md
  • services/bandwidth/mysterium.yml
  • tests/test_mysterium_fenced.py
  • tests/test_preflight.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

@codecov

codecov Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.98%. Comparing base (d5a98f2) to head (0d887c4).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #679      +/-   ##
==========================================
+ Coverage   93.96%   93.98%   +0.01%     
==========================================
  Files          74       74              
  Lines       16882    16885       +3     
==========================================
+ Hits        15864    15869       +5     
+ Misses       1018     1016       -2     
Files with missing lines Coverage Δ
app/drift.py 93.33% <100.00%> (+0.04%) ⬆️
app/orchestrator.py 94.44% <100.00%> (+<0.01%) ⬆️

... and 1 file with indirect coverage changes

🚀 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.

…o only the in-memory pop on the refusal

A reserved range keeps the kernel from handing its ports out, but a program
that binds one by number still takes it. The refusal now puts back only the
hold it popped and leaves the record on disk as it is, so a record written
while the deploy ran is not overwritten.

This branch has not been deployed

No deployments
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