aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-09 18:19:06 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-10 09:57:29 +0200
commit42b562fcf312ddceb78438b5daea49d4c9b465c8 (patch)
tree770913e96292b4c5123f8c5ecba2d62577307f67 /packages/meshbay-node/src/meshbay_node/cli/lifecycle.py
parent25152ddb61a89ea3ea29d3aef5de13343429d32a (diff)
downloadmeshbay-42b562fcf312ddceb78438b5daea49d4c9b465c8.tar.gz
feat(packaging): the Store package declares what the NSIS scripts do
A test-signed install of the MSIX build, in the WindowsApps folder a Store install uses, showed that every script-made piece of the NSIS model breaks there, because each names the install folder and every update deletes it: the firewall rules went stale, the PATH entries piled up pointing at deleted folders, and the Startup-folder .vbs was refused ("Permission denied") right after sign-in. The network capabilities the manifest declared covered nothing: they make rules for sandboxed apps only, and a listener in the package still got the Windows firewall prompt. The package's own startup task was on by default, started the node whatever mode the Node page said, and ran the console executable, whose window stopped the node when closed. The package now declares what Windows then creates at install, carries across updates and removes with the app, all without an administrator prompt (each measured on the real install, through an update and a reboot): - firewall rules for the node, in a custom manifest template, since only a package-level element can hold them; - the startup task, off by default, running meshbay-nodew.exe, a new build of the daemon without a console; - an execution alias for meshbay-node.exe, so the app adds no PATH entry. The node's CLI switches the startup task (platform.startup_task, ctypes over the WinRT ABI): Windows gives the package's identity to the executables in it, not to a powershell.exe the app starts, which got "Element not found". `meshbay-node autostart install | remove | status` therefore works in the Store package from the app and a terminal alike; the app caches the answer, since the Node page polls. Starting at boot stays the .exe installer's: the Store package offers no service mode, and the CLI refuses `service install` there. Process listings count both image names. The Node page's status poll cleared the message of a refused action within five seconds; the two errors are kept apart now (all Windows builds). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/cli/lifecycle.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/cli/lifecycle.py37
1 files changed, 37 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py b/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py
index b461055..1c9e026 100644
--- a/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py
+++ b/packages/meshbay-node/src/meshbay_node/cli/lifecycle.py
@@ -179,6 +179,35 @@ def restart_daemon(args) -> None:
return
+# In the Store package the sign-in start is the package's startup task, which
+# Windows lists in Settings > Apps > Startup (platform.startup_task).
+STORE_SIGNIN = ("In MeshBay from the Microsoft Store, the node starts at sign-in through "
+ "the \"MeshBay Node\" entry of Windows Settings > Apps > Startup "
+ "(meshbay-node autostart install | remove).")
+
+
+def _store_autostart(sub: str) -> None:
+ """autostart install | remove | status in the Store package. The first line
+ is "startup task <state>", which the desktop app reads."""
+ from meshbay_node import platform as _plat
+ action = {"install": "enable", "remove": "disable", "status": "query"}[sub]
+ try:
+ state = _plat.startup_task(action)
+ except RuntimeError as e:
+ print(f"Could not reach {e}")
+ sys.exit(1)
+ print(f"startup task {state}")
+ if state == "DisabledByUser":
+ print("Switched off in Windows Settings > Apps > Startup (\"MeshBay Node\"): "
+ "only you can switch it back on, there.")
+ elif state.endswith("ByPolicy"):
+ print("Set by a policy on this computer.")
+ elif state == "Enabled":
+ print("meshbay-node starts at each sign-in (no window, no admin).")
+ if sub == "install" and not state.startswith("Enabled"):
+ sys.exit(1)
+
+
def autostart(args) -> None:
from meshbay_node import platform as _plat
if sys.platform != "win32":
@@ -186,6 +215,9 @@ def autostart(args) -> None:
"'systemctl --user enable --now meshbay-node'.")
sys.exit(1)
sub = args.subcommand or "status"
+ if sub in ("install", "remove", "status") and _plat.in_store_package():
+ _store_autostart(sub)
+ return
service_mode = _plat.service_status()["installed"]
if sub == "install":
if service_mode:
@@ -226,6 +258,11 @@ def service(args) -> None:
"'systemctl --user enable --now meshbay-node'.")
sys.exit(1)
sub = args.subcommand or "status"
+ if sub == "install" and _plat.in_store_package():
+ print("Starting the node at boot, before anyone signs in, needs MeshBay's .exe "
+ "installer: the Microsoft Store version starts it at sign-in at most. "
+ + STORE_SIGNIN)
+ sys.exit(1)
if sub == "install":
# A node already running in this session holds the control API's port:
# the service's own would exit at once, leaving the old one in charge.